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