Browse Source

- Added libxml_set_external_entity_loader().

pull/7/head
Gustavo André dos Santos Lopes 15 years ago
parent
commit
1d5e508658
  1. 3
      UPGRADING
  2. 177
      ext/libxml/libxml.c
  3. 5
      ext/libxml/php_libxml.h
  4. 48
      ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt
  5. 39
      ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt
  6. 71
      ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt
  7. 44
      ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt

3
UPGRADING

@ -383,6 +383,9 @@ UPGRADE NOTES - PHP X.Y
- stream_set_chunk_size()
- socket_import_stream()
- libxml
- libxml_set_external_entity_loader()
f. New global constants
- JSON_PRETTY_PRINT

177
ext/libxml/libxml.c

@ -70,6 +70,7 @@ static PHP_FUNCTION(libxml_use_internal_errors);
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_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0)
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 @@ static const zend_function_entry libxml_functions[] = {
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,6 +268,19 @@ static PHP_GINIT_FUNCTION(libxml)
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.
@ -280,8 +298,9 @@ static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char
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 @@ static PHP_MINIT_FUNCTION(libxml)
xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
}
return SUCCESS;
}
@ -723,6 +742,8 @@ static PHP_RSHUTDOWN_FUNCTION(libxml)
LIBXML(error_list) = NULL;
}
xmlResetLastError();
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
return SUCCESS;
}
@ -905,6 +926,156 @@ static PHP_FUNCTION(libxml_disable_entity_loader)
}
/* }}} */
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)
{

5
ext/libxml/php_libxml.h

@ -43,6 +43,11 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml)
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 {

48
ext/libxml/tests/libxml_set_external_entity_loader_basic.phpt

@ -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.

39
ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt

@ -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.

71
ext/libxml/tests/libxml_set_external_entity_loader_variation1.phpt

@ -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.

44
ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt

@ -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.
Loading…
Cancel
Save