cataphract                               Mon, 29 Aug 2011 05:00:26 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=315672

Log:
- Added libxml_set_external_entity_loader().

Changed paths:
    U   php/php-src/branches/PHP_5_4/UPGRADING
    U   php/php-src/branches/PHP_5_4/ext/libxml/libxml.c
    U   php/php-src/branches/PHP_5_4/ext/libxml/php_libxml.h
    A   
php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
    A   
php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
    A   
php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
    A   
php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt
    U   php/php-src/trunk/UPGRADING
    U   php/php-src/trunk/ext/libxml/libxml.c
    U   php/php-src/trunk/ext/libxml/php_libxml.h
    A   
php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
    A   
php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
    A   
php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
    A   
php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt

Modified: php/php-src/branches/PHP_5_4/UPGRADING
===================================================================
--- php/php-src/branches/PHP_5_4/UPGRADING	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/branches/PHP_5_4/UPGRADING	2011-08-29 05:00:26 UTC (rev 315672)
@@ -415,6 +415,9 @@
          - ldap_control_paged_results()
          - ldap_control_paged_results_response()

+       - libxml
+	     - libxml_set_external_entity_loader()
+
      f. New global constants

        - JSON_PRETTY_PRINT

Modified: php/php-src/branches/PHP_5_4/ext/libxml/libxml.c
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/libxml.c	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/branches/PHP_5_4/ext/libxml/libxml.c	2011-08-29 05:00:26 UTC (rev 315672)
@@ -70,6 +70,7 @@
 static PHP_FUNCTION(libxml_get_last_error);
 static PHP_FUNCTION(libxml_clear_errors);
 static PHP_FUNCTION(libxml_get_errors);
+static PHP_FUNCTION(libxml_set_external_entity_loader);
 static PHP_FUNCTION(libxml_disable_entity_loader);

 static zend_class_entry *libxmlerror_class_entry;
@@ -111,6 +112,9 @@
 	ZEND_ARG_INFO(0, disable)
 ZEND_END_ARG_INFO()

+ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 0, 1)
+	ZEND_ARG_INFO(0, resolver_function)
+ZEND_END_ARG_INFO()
 /* }}} */

 /* {{{ extension definition structures */
@@ -121,6 +125,7 @@
 	PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors)
 	PHP_FE(libxml_get_errors, arginfo_libxml_get_errors)
 	PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader)
+	PHP_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader)
 	PHP_FE_END
 };

@@ -263,8 +268,21 @@
 	libxml_globals->stream_context = NULL;
 	libxml_globals->error_buffer.c = NULL;
 	libxml_globals->error_list = NULL;
+	libxml_globals->defaultEntityLoader = NULL;
+	libxml_globals->entity_loader.fci.size = 0;
 }

+static void _php_libxml_destroy_fci(zend_fcall_info *fci)
+{
+	if (fci->size > 0) {
+		zval_ptr_dtor(&fci->function_name);
+		if (fci->object_ptr != NULL) {
+			zval_ptr_dtor(&fci->object_ptr);
+		}
+		fci->size = 0;
+	}
+}
+
 /* Channel libxml file io layer through the PHP streams subsystem.
  * This allows use of ftps:// and https:// urls */

@@ -280,8 +298,9 @@

 	TSRMLS_FETCH();

-	uri = xmlParseURI((xmlChar *)filename);
-	if (uri && (uri->scheme == NULL || (xmlStrncmp(uri->scheme, "file", 4) == 0))) {
+	uri = xmlParseURI(filename);
+	if (uri && (uri->scheme == NULL ||
+			(xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
 		resolved_path = xmlURIUnescapeString(filename, 0, NULL);
 		isescaped = 1;
 	} else {
@@ -669,7 +688,7 @@
 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
 	}
-
+
 	return SUCCESS;
 }

@@ -723,6 +742,8 @@
 		LIBXML(error_list) = NULL;
 	}
 	xmlResetLastError();
+
+	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);

 	return SUCCESS;
 }
@@ -905,6 +926,156 @@
 }
 /* }}} */

+static xmlParserInputPtr _php_libxml_user_entity_loader(const char *URL,
+		const char *ID, xmlParserCtxtPtr context)
+{
+    xmlParserInputPtr	ret			= NULL;
+    const char			*resource	= NULL;
+	zval				*public		= NULL,
+						*system		= NULL,
+						*ctxzv		= NULL,
+						**params[]	= {&public, &system, &ctxzv},
+						*retval_ptr	= NULL;
+	int					retval;
+	TSRMLS_FETCH();
+	zend_fcall_info		*fci = &LIBXML(entity_loader).fci;
+
+	ALLOC_INIT_ZVAL(public);
+	if (ID != NULL) {
+		ZVAL_STRING(public, ID, 1);
+	}
+	ALLOC_INIT_ZVAL(system);
+	if (URL != NULL) {
+		ZVAL_STRING(system, URL, 1);
+	}
+	MAKE_STD_ZVAL(ctxzv);
+	array_init_size(ctxzv, 4);
+
+#define ADD_NULL_OR_STRING_KEY(memb) \
+	if (context->memb == NULL) { \
+		add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
+	} else { \
+		add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
+				(char *)context->memb, 1); \
+	}
+
+	ADD_NULL_OR_STRING_KEY(directory)
+	ADD_NULL_OR_STRING_KEY(intSubName)
+	ADD_NULL_OR_STRING_KEY(extSubURI)
+	ADD_NULL_OR_STRING_KEY(extSubSystem)
+
+#undef ADD_NULL_OR_STRING_KEY
+
+	fci->retval_ptr_ptr	= &retval_ptr;
+	fci->params			= params;
+	fci->param_count	= sizeof(params)/sizeof(*params);
+	fci->no_separation	= 1;
+
+	retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
+	if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
+		php_libxml_ctx_error(context,
+				"Call to user entity loader callback '%s' has failed",
+				fci->function_name);
+	} else {
+		retval_ptr = *fci->retval_ptr_ptr;
+		if (retval_ptr == NULL) {
+			php_libxml_ctx_error(context,
+					"Call to user entity loader callback '%s' has failed; "
+					"probably it has thrown an exception",
+					fci->function_name);
+		} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
+is_string:
+			resource = Z_STRVAL_P(retval_ptr);
+		} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
+			php_stream *stream;
+			php_stream_from_zval_no_verify(stream, &retval_ptr);
+			if (stream == NULL) {
+				php_libxml_ctx_error(context,
+						"The user entity loader callback '%s' has returned a "
+						"resource, but it is not a stream",
+						fci->function_name);
+			} else {
+				/* TODO: allow storing the encoding in the stream context? */
+				xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
+				xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
+				if (pib == NULL) {
+					php_libxml_ctx_error(context, "Could not allocate parser "
+							"input buffer");
+				} else {
+					/* make stream not being closed when the zval is freed */
+					zend_list_addref(stream->rsrc_id);
+					pib->context = stream;
+					pib->readcallback = php_libxml_streams_IO_read;
+					pib->closecallback = php_libxml_streams_IO_close;
+
+					ret = xmlNewIOInputStream(context, pib, enc);
+					if (ret == NULL) {
+						xmlFreeParserInputBuffer(pib);
+					}
+				}
+			}
+		} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
+			/* retval not string nor resource nor null; convert to string */
+			SEPARATE_ZVAL(&retval_ptr);
+      		convert_to_string(retval_ptr);
+			goto is_string;
+		} /* else is null; don't try anything */
+	}
+
+	if (ret == NULL) {
+		if (resource == NULL) {
+			if (ID == NULL) {
+				ID = "NULL";
+			}
+			php_libxml_ctx_error(context,
+					"Failed to load external entity \"%s\"\n", ID);
+		} else {
+			/* we got the resource in the form of a string; open it */
+			ret = xmlNewInputFromFile(context, resource);
+		}
+	}
+
+	zval_ptr_dtor(&public);
+	zval_ptr_dtor(&system);
+	zval_ptr_dtor(&ctxzv);
+	if (retval_ptr != NULL) {
+		zval_ptr_dtor(&retval_ptr);
+	}
+    return ret;
+}
+
+/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
+   Changes the default external entity loader */
+static PHP_FUNCTION(libxml_set_external_entity_loader)
+{
+	zend_fcall_info			fci;
+	zend_fcall_info_cache	fcc;
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", &fci, &fcc)
+			== FAILURE) {
+		return;
+	}
+
+	if (fci.size > 0) { /* argument not null */
+		/* save for later invocations with NULL */
+		if (LIBXML(defaultEntityLoader) == NULL) {
+			LIBXML(defaultEntityLoader) = xmlGetExternalEntityLoader();
+		}
+		_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
+		LIBXML(entity_loader).fci = fci;
+		Z_ADDREF_P(fci.function_name);
+		if (fci.object_ptr != NULL) {
+			Z_ADDREF_P(fci.object_ptr);
+		}
+		LIBXML(entity_loader).fcc = fcc;
+		xmlSetExternalEntityLoader(_php_libxml_user_entity_loader);
+	} else {
+		xmlSetExternalEntityLoader(LIBXML(defaultEntityLoader));
+	}
+
+	RETURN_TRUE;
+}
+/* }}} */
+
 /* {{{ Common functions shared by extensions */
 int php_libxml_xmlCheckUTF8(const unsigned char *s)
 {

Modified: php/php-src/branches/PHP_5_4/ext/libxml/php_libxml.h
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/php_libxml.h	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/branches/PHP_5_4/ext/libxml/php_libxml.h	2011-08-29 05:00:26 UTC (rev 315672)
@@ -43,6 +43,11 @@
 	zval *stream_context;
 	smart_str error_buffer;
 	zend_llist *error_list;
+	xmlExternalEntityLoader defaultEntityLoader; /* saved here to allow it restored */
+	struct _php_libxml_entity_resolver {
+		zend_fcall_info			fci;
+		zend_fcall_info_cache	fcc;
+	} entity_loader;
 ZEND_END_MODULE_GLOBALS(libxml)

 typedef struct _libxml_doc_props {

Added: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,48 @@
+--TEST--
+libxml_set_external_entity_loader() basic test
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+DTD;
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) use($dtd){
+		var_dump($public);
+		var_dump($system);
+		var_dump($context);
+		$f = fopen("php://temp", "r+");
+		fwrite($f, $dtd);
+		rewind($f);
+		return $f;
+	}
+);
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECT--
+string(10) "-//FOO/BAR"
+string(25) "http://example.com/foobar";
+array(4) {
+  ["directory"]=>
+  NULL
+  ["intSubName"]=>
+  NULL
+  ["extSubURI"]=>
+  NULL
+  ["extSubSystem"]=>
+  NULL
+}
+bool(true)
+Done.


Property changes on: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,39 @@
+--TEST--
+libxml_set_external_entity_loader() error: bad arguments
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar</foo>
+XML;
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+
+var_dump(libxml_set_external_entity_loader([]));
+var_dump(libxml_set_external_entity_loader());
+var_dump(libxml_set_external_entity_loader(function() {}, 2));
+
+var_dump(libxml_set_external_entity_loader(function($a, $b, $c, $d) {}));
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+Warning: libxml_set_external_entity_loader() expects parameter 1 to be a valid callback, array must have exactly two members in %s on line %d
+NULL
+
+Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 0 given in %s on line %d
+NULL
+
+Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 2 given in %s on line %d
+NULL
+bool(true)
+
+Warning: Missing argument 4 for {closure}() in %s on line %d
+
+Warning: DOMDocument::validate(): Could not load the external subset "http://example.com/foobar"; in %s on line %d
+bool(false)
+Done.


Property changes on: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,71 @@
+--TEST--
+libxml_set_external_entity_loader() variation: resolve externals and entities
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar&fooz;</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+<!ENTITY % fooentity PUBLIC
+   "-//FOO/ENTITY"
+   "fooentity.ent">
+%fooentity;
+DTD;
+
+$entity = <<<ENT
+<!ENTITY fooz "baz">
+ENT;
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) use($dtd,$entity){
+		static $first = true;
+		var_dump($public);
+		var_dump($system);
+		var_dump($context);
+		$f = fopen("php://temp", "r+");
+		fwrite($f, $first ? $dtd : $entity);
+		$first = false;
+		rewind($f);
+		return $f;
+	}
+);
+
+$dd = new DOMDocument;
+$dd->resolveExternals = true;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+string(10) "-//FOO/BAR"
+string(25) "http://example.com/foobar";
+array(4) {
+  ["directory"]=>
+  string(36) "%s"
+  ["intSubName"]=>
+  string(3) "foo"
+  ["extSubURI"]=>
+  string(25) "http://example.com/foobar";
+  ["extSubSystem"]=>
+  string(10) "-//FOO/BAR"
+}
+string(13) "-//FOO/ENTITY"
+string(32) "http://example.com/fooentity.ent";
+array(4) {
+  ["directory"]=>
+  string(36) "%s"
+  ["intSubName"]=>
+  string(3) "foo"
+  ["extSubURI"]=>
+  string(25) "http://example.com/foobar";
+  ["extSubSystem"]=>
+  string(10) "-//FOO/BAR"
+}
+bool(true)
+Done.


Property changes on: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt	                        (rev 0)
+++ php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,44 @@
+--TEST--
+libxml_set_external_entity_loader() variation: restore original handler; returning NULL
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . "/foobar.dtd");
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "foobar.dtd">
+<foo>bar</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+DTD;
+
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) {
+		var_dump($public,$system);
+		return null;
+	}
+);
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+libxml_set_external_entity_loader(NULL);
+file_put_contents(__DIR__ . "/foobar.dtd", $dtd);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+string(10) "-//FOO/BAR"
+string(46) "%sfoobar.dtd"
+
+Warning: DOMDocument::validate(): Could not load the external subset "foobar.dtd" in %s on line %d
+bool(false)
+bool(true)
+Done.


Property changes on: php/php-src/branches/PHP_5_4/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Modified: php/php-src/trunk/UPGRADING
===================================================================
--- php/php-src/trunk/UPGRADING	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/trunk/UPGRADING	2011-08-29 05:00:26 UTC (rev 315672)
@@ -383,6 +383,9 @@
          - stream_set_chunk_size()
          - socket_import_stream()

+       - libxml
+	     - libxml_set_external_entity_loader()
+
      f. New global constants

        - JSON_PRETTY_PRINT

Modified: php/php-src/trunk/ext/libxml/libxml.c
===================================================================
--- php/php-src/trunk/ext/libxml/libxml.c	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/trunk/ext/libxml/libxml.c	2011-08-29 05:00:26 UTC (rev 315672)
@@ -70,6 +70,7 @@
 static PHP_FUNCTION(libxml_get_last_error);
 static PHP_FUNCTION(libxml_clear_errors);
 static PHP_FUNCTION(libxml_get_errors);
+static PHP_FUNCTION(libxml_set_external_entity_loader);
 static PHP_FUNCTION(libxml_disable_entity_loader);

 static zend_class_entry *libxmlerror_class_entry;
@@ -111,6 +112,9 @@
 	ZEND_ARG_INFO(0, disable)
 ZEND_END_ARG_INFO()

+ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 0, 1)
+	ZEND_ARG_INFO(0, resolver_function)
+ZEND_END_ARG_INFO()
 /* }}} */

 /* {{{ extension definition structures */
@@ -121,6 +125,7 @@
 	PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors)
 	PHP_FE(libxml_get_errors, arginfo_libxml_get_errors)
 	PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader)
+	PHP_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader)
 	PHP_FE_END
 };

@@ -263,8 +268,21 @@
 	libxml_globals->stream_context = NULL;
 	libxml_globals->error_buffer.c = NULL;
 	libxml_globals->error_list = NULL;
+	libxml_globals->defaultEntityLoader = NULL;
+	libxml_globals->entity_loader.fci.size = 0;
 }

+static void _php_libxml_destroy_fci(zend_fcall_info *fci)
+{
+	if (fci->size > 0) {
+		zval_ptr_dtor(&fci->function_name);
+		if (fci->object_ptr != NULL) {
+			zval_ptr_dtor(&fci->object_ptr);
+		}
+		fci->size = 0;
+	}
+}
+
 /* Channel libxml file io layer through the PHP streams subsystem.
  * This allows use of ftps:// and https:// urls */

@@ -280,8 +298,9 @@

 	TSRMLS_FETCH();

-	uri = xmlParseURI((xmlChar *)filename);
-	if (uri && (uri->scheme == NULL || (xmlStrncmp(uri->scheme, "file", 4) == 0))) {
+	uri = xmlParseURI(filename);
+	if (uri && (uri->scheme == NULL ||
+			(xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
 		resolved_path = xmlURIUnescapeString(filename, 0, NULL);
 		isescaped = 1;
 	} else {
@@ -669,7 +688,7 @@
 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
 	}
-
+
 	return SUCCESS;
 }

@@ -723,6 +742,8 @@
 		LIBXML(error_list) = NULL;
 	}
 	xmlResetLastError();
+
+	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);

 	return SUCCESS;
 }
@@ -905,6 +926,156 @@
 }
 /* }}} */

+static xmlParserInputPtr _php_libxml_user_entity_loader(const char *URL,
+		const char *ID, xmlParserCtxtPtr context)
+{
+    xmlParserInputPtr	ret			= NULL;
+    const char			*resource	= NULL;
+	zval				*public		= NULL,
+						*system		= NULL,
+						*ctxzv		= NULL,
+						**params[]	= {&public, &system, &ctxzv},
+						*retval_ptr	= NULL;
+	int					retval;
+	TSRMLS_FETCH();
+	zend_fcall_info		*fci = &LIBXML(entity_loader).fci;
+
+	ALLOC_INIT_ZVAL(public);
+	if (ID != NULL) {
+		ZVAL_STRING(public, ID, 1);
+	}
+	ALLOC_INIT_ZVAL(system);
+	if (URL != NULL) {
+		ZVAL_STRING(system, URL, 1);
+	}
+	MAKE_STD_ZVAL(ctxzv);
+	array_init_size(ctxzv, 4);
+
+#define ADD_NULL_OR_STRING_KEY(memb) \
+	if (context->memb == NULL) { \
+		add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
+	} else { \
+		add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
+				(char *)context->memb, 1); \
+	}
+
+	ADD_NULL_OR_STRING_KEY(directory)
+	ADD_NULL_OR_STRING_KEY(intSubName)
+	ADD_NULL_OR_STRING_KEY(extSubURI)
+	ADD_NULL_OR_STRING_KEY(extSubSystem)
+
+#undef ADD_NULL_OR_STRING_KEY
+
+	fci->retval_ptr_ptr	= &retval_ptr;
+	fci->params			= params;
+	fci->param_count	= sizeof(params)/sizeof(*params);
+	fci->no_separation	= 1;
+
+	retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
+	if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
+		php_libxml_ctx_error(context,
+				"Call to user entity loader callback '%s' has failed",
+				fci->function_name);
+	} else {
+		retval_ptr = *fci->retval_ptr_ptr;
+		if (retval_ptr == NULL) {
+			php_libxml_ctx_error(context,
+					"Call to user entity loader callback '%s' has failed; "
+					"probably it has thrown an exception",
+					fci->function_name);
+		} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
+is_string:
+			resource = Z_STRVAL_P(retval_ptr);
+		} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
+			php_stream *stream;
+			php_stream_from_zval_no_verify(stream, &retval_ptr);
+			if (stream == NULL) {
+				php_libxml_ctx_error(context,
+						"The user entity loader callback '%s' has returned a "
+						"resource, but it is not a stream",
+						fci->function_name);
+			} else {
+				/* TODO: allow storing the encoding in the stream context? */
+				xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
+				xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
+				if (pib == NULL) {
+					php_libxml_ctx_error(context, "Could not allocate parser "
+							"input buffer");
+				} else {
+					/* make stream not being closed when the zval is freed */
+					zend_list_addref(stream->rsrc_id);
+					pib->context = stream;
+					pib->readcallback = php_libxml_streams_IO_read;
+					pib->closecallback = php_libxml_streams_IO_close;
+
+					ret = xmlNewIOInputStream(context, pib, enc);
+					if (ret == NULL) {
+						xmlFreeParserInputBuffer(pib);
+					}
+				}
+			}
+		} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
+			/* retval not string nor resource nor null; convert to string */
+			SEPARATE_ZVAL(&retval_ptr);
+      		convert_to_string(retval_ptr);
+			goto is_string;
+		} /* else is null; don't try anything */
+	}
+
+	if (ret == NULL) {
+		if (resource == NULL) {
+			if (ID == NULL) {
+				ID = "NULL";
+			}
+			php_libxml_ctx_error(context,
+					"Failed to load external entity \"%s\"\n", ID);
+		} else {
+			/* we got the resource in the form of a string; open it */
+			ret = xmlNewInputFromFile(context, resource);
+		}
+	}
+
+	zval_ptr_dtor(&public);
+	zval_ptr_dtor(&system);
+	zval_ptr_dtor(&ctxzv);
+	if (retval_ptr != NULL) {
+		zval_ptr_dtor(&retval_ptr);
+	}
+    return ret;
+}
+
+/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
+   Changes the default external entity loader */
+static PHP_FUNCTION(libxml_set_external_entity_loader)
+{
+	zend_fcall_info			fci;
+	zend_fcall_info_cache	fcc;
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", &fci, &fcc)
+			== FAILURE) {
+		return;
+	}
+
+	if (fci.size > 0) { /* argument not null */
+		/* save for later invocations with NULL */
+		if (LIBXML(defaultEntityLoader) == NULL) {
+			LIBXML(defaultEntityLoader) = xmlGetExternalEntityLoader();
+		}
+		_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
+		LIBXML(entity_loader).fci = fci;
+		Z_ADDREF_P(fci.function_name);
+		if (fci.object_ptr != NULL) {
+			Z_ADDREF_P(fci.object_ptr);
+		}
+		LIBXML(entity_loader).fcc = fcc;
+		xmlSetExternalEntityLoader(_php_libxml_user_entity_loader);
+	} else {
+		xmlSetExternalEntityLoader(LIBXML(defaultEntityLoader));
+	}
+
+	RETURN_TRUE;
+}
+/* }}} */
+
 /* {{{ Common functions shared by extensions */
 int php_libxml_xmlCheckUTF8(const unsigned char *s)
 {

Modified: php/php-src/trunk/ext/libxml/php_libxml.h
===================================================================
--- php/php-src/trunk/ext/libxml/php_libxml.h	2011-08-29 04:20:12 UTC (rev 315671)
+++ php/php-src/trunk/ext/libxml/php_libxml.h	2011-08-29 05:00:26 UTC (rev 315672)
@@ -43,6 +43,11 @@
 	zval *stream_context;
 	smart_str error_buffer;
 	zend_llist *error_list;
+	xmlExternalEntityLoader defaultEntityLoader; /* saved here to allow it restored */
+	struct _php_libxml_entity_resolver {
+		zend_fcall_info			fci;
+		zend_fcall_info_cache	fcc;
+	} entity_loader;
 ZEND_END_MODULE_GLOBALS(libxml)

 typedef struct _libxml_doc_props {

Added: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
===================================================================
--- php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,48 @@
+--TEST--
+libxml_set_external_entity_loader() basic test
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+DTD;
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) use($dtd){
+		var_dump($public);
+		var_dump($system);
+		var_dump($context);
+		$f = fopen("php://temp", "r+");
+		fwrite($f, $dtd);
+		rewind($f);
+		return $f;
+	}
+);
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECT--
+string(10) "-//FOO/BAR"
+string(25) "http://example.com/foobar";
+array(4) {
+  ["directory"]=>
+  NULL
+  ["intSubName"]=>
+  NULL
+  ["extSubURI"]=>
+  NULL
+  ["extSubSystem"]=>
+  NULL
+}
+bool(true)
+Done.


Property changes on: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
===================================================================
--- php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,39 @@
+--TEST--
+libxml_set_external_entity_loader() error: bad arguments
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar</foo>
+XML;
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+
+var_dump(libxml_set_external_entity_loader([]));
+var_dump(libxml_set_external_entity_loader());
+var_dump(libxml_set_external_entity_loader(function() {}, 2));
+
+var_dump(libxml_set_external_entity_loader(function($a, $b, $c, $d) {}));
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+Warning: libxml_set_external_entity_loader() expects parameter 1 to be a valid callback, array must have exactly two members in %s on line %d
+NULL
+
+Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 0 given in %s on line %d
+NULL
+
+Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 2 given in %s on line %d
+NULL
+bool(true)
+
+Warning: Missing argument 4 for {closure}() in %s on line %d
+
+Warning: DOMDocument::validate(): Could not load the external subset "http://example.com/foobar"; in %s on line %d
+bool(false)
+Done.


Property changes on: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
===================================================================
--- php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,71 @@
+--TEST--
+libxml_set_external_entity_loader() variation: resolve externals and entities
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "http://example.com/foobar";>
+<foo>bar&fooz;</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+<!ENTITY % fooentity PUBLIC
+   "-//FOO/ENTITY"
+   "fooentity.ent">
+%fooentity;
+DTD;
+
+$entity = <<<ENT
+<!ENTITY fooz "baz">
+ENT;
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) use($dtd,$entity){
+		static $first = true;
+		var_dump($public);
+		var_dump($system);
+		var_dump($context);
+		$f = fopen("php://temp", "r+");
+		fwrite($f, $first ? $dtd : $entity);
+		$first = false;
+		rewind($f);
+		return $f;
+	}
+);
+
+$dd = new DOMDocument;
+$dd->resolveExternals = true;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+string(10) "-//FOO/BAR"
+string(25) "http://example.com/foobar";
+array(4) {
+  ["directory"]=>
+  string(36) "%s"
+  ["intSubName"]=>
+  string(3) "foo"
+  ["extSubURI"]=>
+  string(25) "http://example.com/foobar";
+  ["extSubSystem"]=>
+  string(10) "-//FOO/BAR"
+}
+string(13) "-//FOO/ENTITY"
+string(32) "http://example.com/fooentity.ent";
+array(4) {
+  ["directory"]=>
+  string(36) "%s"
+  ["intSubName"]=>
+  string(3) "foo"
+  ["extSubURI"]=>
+  string(25) "http://example.com/foobar";
+  ["extSubSystem"]=>
+  string(10) "-//FOO/BAR"
+}
+bool(true)
+Done.


Property changes on: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native

Added: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt
===================================================================
--- php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt	2011-08-29 05:00:26 UTC (rev 315672)
@@ -0,0 +1,44 @@
+--TEST--
+libxml_set_external_entity_loader() variation: restore original handler; returning NULL
+--SKIPIF--
+<?php if (!extension_loaded('dom')) die('skip'); ?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . "/foobar.dtd");
+--FILE--
+<?php
+$xml = <<<XML
+<!DOCTYPE foo PUBLIC "-//FOO/BAR" "foobar.dtd">
+<foo>bar</foo>
+XML;
+
+$dtd = <<<DTD
+<!ELEMENT foo (#PCDATA)>
+DTD;
+
+
+libxml_set_external_entity_loader(
+	function ($public, $system, $context) {
+		var_dump($public,$system);
+		return null;
+	}
+);
+
+$dd = new DOMDocument;
+$r = $dd->loadXML($xml);
+var_dump($dd->validate());
+
+libxml_set_external_entity_loader(NULL);
+file_put_contents(__DIR__ . "/foobar.dtd", $dtd);
+var_dump($dd->validate());
+
+echo "Done.\n";
+
+--EXPECTF--
+string(10) "-//FOO/BAR"
+string(46) "%sfoobar.dtd"
+
+Warning: DOMDocument::validate(): Could not load the external subset "foobar.dtd" in %s on line %d
+bool(false)
+bool(true)
+Done.


Property changes on: php/php-src/trunk/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt
___________________________________________________________________
Added: svn:keywords
   + Id Rev Revision
Added: svn:eol-style
   + native
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to