mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge branch 'PHP-8.5'
* PHP-8.5: Fix GH-21357: XSLTProcessor works with DOMDocument, but fails with Dom\XMLDocument
This commit is contained in:
@@ -24,7 +24,7 @@ $sheet = Dom\XMLDocument::createFromString(<<<XML
|
|||||||
XML);
|
XML);
|
||||||
|
|
||||||
// Make sure it will auto-register urn:test
|
// Make sure it will auto-register urn:test
|
||||||
$sheet->documentElement->append($sheet->createElementNS('urn:test', 'test:dummy'));
|
$sheet->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:test', 'urn:test');
|
||||||
|
|
||||||
$input = Dom\XMLDocument::createFromString(<<<XML
|
$input = Dom\XMLDocument::createFromString(<<<XML
|
||||||
<root>
|
<root>
|
||||||
|
|||||||
20
ext/xsl/tests/gh21357_1.phpt
Normal file
20
ext/xsl/tests/gh21357_1.phpt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-21357 (XSLTProcessor works with \DOMDocument, but fails with \Dom\XMLDocument)
|
||||||
|
--EXTENSIONS--
|
||||||
|
dom
|
||||||
|
xsl
|
||||||
|
--CREDITS--
|
||||||
|
jacekkow
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
$xml = <<<'XML'
|
||||||
|
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tns="urn:myns" version="1.0">
|
||||||
|
<xsl:template match="tns:referee"/>
|
||||||
|
</xsl:stylesheet>
|
||||||
|
XML;
|
||||||
|
|
||||||
|
$dom = Dom\XMLDocument::createFromString($xml);
|
||||||
|
var_dump(new XSLTProcessor()->importStylesheet($dom));
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
35
ext/xsl/tests/gh21357_2.phpt
Normal file
35
ext/xsl/tests/gh21357_2.phpt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-21357 (XSLTProcessor works with \DOMDocument, but fails with \Dom\XMLDocument)
|
||||||
|
--EXTENSIONS--
|
||||||
|
dom
|
||||||
|
xsl
|
||||||
|
--CREDITS--
|
||||||
|
jacekkow
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
$xsl = '<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsl:stylesheet
|
||||||
|
version="1.0"
|
||||||
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||||
|
xmlns:old="http://something/old/"
|
||||||
|
xmlns:new="http://something/new/">
|
||||||
|
<xsl:template match="node()|@*">
|
||||||
|
<xsl:copy>
|
||||||
|
<xsl:apply-templates select="node()|@*"/>
|
||||||
|
</xsl:copy>
|
||||||
|
</xsl:template>
|
||||||
|
<xsl:template match="old:*">
|
||||||
|
<xsl:element name="{local-name()}" xmlns="http://something/new/" >
|
||||||
|
<xsl:apply-templates select="node()|@*"/>
|
||||||
|
</xsl:element>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>';
|
||||||
|
$dom = Dom\XMLDocument::createFromString($xsl);
|
||||||
|
$xsl = new XSLTProcessor();
|
||||||
|
$xsl->importStylesheet($dom);
|
||||||
|
var_dump($xsl->transformToXml(\Dom\XMLDocument::createFromString('<test xmlns="http://something/old/"/>')));
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
string(138) "<?xml version="1.0"?>
|
||||||
|
<test xmlns="http://something/new/" xmlns:ns_1="http://www.w3.org/2000/xmlns/" ns_1:xmlns="http://something/old/"/>
|
||||||
|
"
|
||||||
@@ -123,65 +123,38 @@ static void xsl_ext_function_trampoline(xmlXPathParserContextPtr ctxt, int nargs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xsl_add_ns_to_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, const xmlNode *cur, const xmlChar *prefix, const xmlChar *uri)
|
static void xsl_add_ns_def(xmlNodePtr node)
|
||||||
{
|
{
|
||||||
const xmlChar *existing_url = xmlHashLookup(table, prefix);
|
if (node->type == XML_ELEMENT_NODE) {
|
||||||
if (existing_url != NULL && !xmlStrEqual(existing_url, uri)) {
|
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
|
||||||
xsltTransformError(NULL, sheet, (xmlNodePtr) cur, "Namespaces prefix %s used for multiple namespaces\n", prefix);
|
if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xmlns_magic_token)) {
|
||||||
sheet->warnings++;
|
xmlNsPtr ns = xmlMalloc(sizeof(*ns));
|
||||||
} else if (existing_url == NULL) {
|
if (!ns) {
|
||||||
xmlHashUpdateEntry(table, prefix, (void *) uri, NULL);
|
return;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds all namespace declaration (not using nsDef) into a hash map that maps prefix to uri. Warns on conflicting declarations. */
|
|
||||||
static void xsl_build_ns_map(xmlHashTablePtr table, xsltStylesheetPtr sheet, php_dom_libxml_ns_mapper *ns_mapper, const xmlDoc *doc)
|
|
||||||
{
|
|
||||||
const xmlNode *cur = xmlDocGetRootElement(doc);
|
|
||||||
|
|
||||||
while (cur != NULL) {
|
|
||||||
if (cur->type == XML_ELEMENT_NODE) {
|
|
||||||
if (cur->ns != NULL && cur->ns->prefix != NULL) {
|
|
||||||
xsl_add_ns_to_map(table, sheet, cur, cur->ns->prefix, cur->ns->href);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) {
|
|
||||||
if (attr->ns != NULL && attr->ns->prefix != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token)
|
|
||||||
&& attr->children != NULL && attr->children->content != NULL) {
|
|
||||||
/* This attribute declares a namespace, get the relevant instance.
|
|
||||||
* The declared namespace is not the same as the namespace of this attribute (which is xmlns). */
|
|
||||||
const xmlChar *prefix = attr->name;
|
|
||||||
xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, (const char *) prefix, (const char *) attr->children->content);
|
|
||||||
xsl_add_ns_to_map(table, sheet, cur, prefix, ns->href);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool should_free;
|
||||||
|
xmlChar *attr_value = php_libxml_attr_value(attr, &should_free);
|
||||||
|
|
||||||
|
memset(ns, 0, sizeof(*ns));
|
||||||
|
ns->type = XML_LOCAL_NAMESPACE;
|
||||||
|
ns->href = should_free ? attr_value : xmlStrdup(attr_value);
|
||||||
|
ns->prefix = attr->ns->prefix ? xmlStrdup(attr->name) : NULL;
|
||||||
|
ns->next = node->nsDef;
|
||||||
|
node->nsDef = ns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur = php_dom_next_in_tree_order(cur, (const xmlNode *) doc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply namespace corrections for new DOM */
|
static void xsl_add_ns_defs(xmlDocPtr doc)
|
||||||
typedef enum {
|
|
||||||
XSL_NS_HASH_CORRECTION_NONE = 0,
|
|
||||||
XSL_NS_HASH_CORRECTION_APPLIED = 1,
|
|
||||||
XSL_NS_HASH_CORRECTION_FAILED = 2
|
|
||||||
} xsl_ns_hash_correction_status;
|
|
||||||
|
|
||||||
static zend_always_inline xsl_ns_hash_correction_status xsl_apply_ns_hash_corrections(xsltStylesheetPtr sheetp, xmlNodePtr nodep, xmlDocPtr doc)
|
|
||||||
{
|
{
|
||||||
if (sheetp->nsHash == NULL) {
|
xmlNodePtr base = (xmlNodePtr) doc;
|
||||||
dom_object *node_intern = php_dom_object_get_data(nodep);
|
xmlNodePtr node = base->children;
|
||||||
if (node_intern != NULL && php_dom_follow_spec_intern(node_intern)) {
|
while (node != NULL) {
|
||||||
sheetp->nsHash = xmlHashCreate(10);
|
xsl_add_ns_def(node);
|
||||||
if (UNEXPECTED(!sheetp->nsHash)) {
|
node = php_dom_next_in_tree_order(node, base);
|
||||||
return XSL_NS_HASH_CORRECTION_FAILED;
|
|
||||||
}
|
|
||||||
xsl_build_ns_map(sheetp->nsHash, sheetp, php_dom_get_ns_mapper(node_intern), doc);
|
|
||||||
return XSL_NS_HASH_CORRECTION_APPLIED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return XSL_NS_HASH_CORRECTION_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#
|
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#
|
||||||
@@ -190,11 +163,11 @@ Since:
|
|||||||
PHP_METHOD(XSLTProcessor, importStylesheet)
|
PHP_METHOD(XSLTProcessor, importStylesheet)
|
||||||
{
|
{
|
||||||
zval *id, *docp = NULL;
|
zval *id, *docp = NULL;
|
||||||
xmlDoc *doc = NULL, *newdoc = NULL;
|
xmlDoc *newdoc = NULL;
|
||||||
xsltStylesheetPtr sheetp;
|
xsltStylesheetPtr sheetp;
|
||||||
bool clone_docu = false;
|
bool clone_docu = false;
|
||||||
xmlNode *nodep = NULL;
|
xmlNode *nodep = NULL;
|
||||||
zval *cloneDocu, rv;
|
zval *cloneDocu, rv, clone_zv;
|
||||||
zend_string *member;
|
zend_string *member;
|
||||||
|
|
||||||
id = ZEND_THIS;
|
id = ZEND_THIS;
|
||||||
@@ -202,51 +175,59 @@ PHP_METHOD(XSLTProcessor, importStylesheet)
|
|||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
nodep = php_libxml_import_node(docp);
|
/* libxslt uses _private, so we must copy the imported
|
||||||
|
* stylesheet document otherwise the node proxies will be a mess.
|
||||||
|
* We will clone the object and detach the libxml internals later. */
|
||||||
|
zend_object *clone = Z_OBJ_HANDLER_P(docp, clone_obj)(Z_OBJ_P(docp));
|
||||||
|
if (!clone) {
|
||||||
|
RETURN_THROWS();
|
||||||
|
}
|
||||||
|
|
||||||
|
ZVAL_OBJ(&clone_zv, clone);
|
||||||
|
nodep = php_libxml_import_node(&clone_zv);
|
||||||
|
|
||||||
if (nodep) {
|
if (nodep) {
|
||||||
doc = nodep->doc;
|
newdoc = nodep->doc;
|
||||||
}
|
}
|
||||||
if (doc == NULL) {
|
if (newdoc == NULL) {
|
||||||
|
OBJ_RELEASE(clone);
|
||||||
zend_argument_type_error(1, "must be a valid XML node");
|
zend_argument_type_error(1, "must be a valid XML node");
|
||||||
RETURN_THROWS();
|
RETURN_THROWS();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* libxslt uses _private, so we must copy the imported
|
php_libxml_node_object *clone_lxml_obj = Z_LIBXML_NODE_P(&clone_zv);
|
||||||
stylesheet document otherwise the node proxies will be a mess */
|
|
||||||
newdoc = xmlCopyDoc(doc, 1);
|
|
||||||
xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL);
|
|
||||||
PHP_LIBXML_SANITIZE_GLOBALS(parse);
|
PHP_LIBXML_SANITIZE_GLOBALS(parse);
|
||||||
ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations")
|
ZEND_DIAGNOSTIC_IGNORED_START("-Wdeprecated-declarations")
|
||||||
xmlSubstituteEntitiesDefault(1);
|
xmlSubstituteEntitiesDefault(1);
|
||||||
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
|
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
|
||||||
ZEND_DIAGNOSTIC_IGNORED_END
|
ZEND_DIAGNOSTIC_IGNORED_END
|
||||||
|
|
||||||
|
if (clone_lxml_obj->document->class_type == PHP_LIBXML_CLASS_MODERN) {
|
||||||
|
xsl_add_ns_defs(newdoc);
|
||||||
|
}
|
||||||
|
|
||||||
sheetp = xsltParseStylesheetDoc(newdoc);
|
sheetp = xsltParseStylesheetDoc(newdoc);
|
||||||
PHP_LIBXML_RESTORE_GLOBALS(parse);
|
PHP_LIBXML_RESTORE_GLOBALS(parse);
|
||||||
|
|
||||||
if (!sheetp) {
|
if (!sheetp) {
|
||||||
xmlFreeDoc(newdoc);
|
OBJ_RELEASE(clone);
|
||||||
RETURN_FALSE;
|
RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
xsl_object *intern = Z_XSL_P(id);
|
xsl_object *intern = Z_XSL_P(id);
|
||||||
|
|
||||||
xsl_ns_hash_correction_status status = xsl_apply_ns_hash_corrections(sheetp, nodep, doc);
|
/* Detach object */
|
||||||
if (UNEXPECTED(status == XSL_NS_HASH_CORRECTION_FAILED)) {
|
clone_lxml_obj->document->ptr = NULL;
|
||||||
xsltFreeStylesheet(sheetp);
|
/* The namespace mappings need to be kept alive.
|
||||||
xmlFreeDoc(newdoc);
|
* This is stored in the ref obj outside of libxml2, but that means that the sheet won't keep it alive
|
||||||
RETURN_FALSE;
|
* unlike with namespaces from old DOM. */
|
||||||
} else if (status == XSL_NS_HASH_CORRECTION_APPLIED) {
|
if (intern->sheet_ref_obj) {
|
||||||
/* The namespace mappings need to be kept alive.
|
php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj);
|
||||||
* This is stored in the ref obj outside of libxml2, but that means that the sheet won't keep it alive
|
|
||||||
* unlike with namespaces from old DOM. */
|
|
||||||
if (intern->sheet_ref_obj) {
|
|
||||||
php_libxml_decrement_doc_ref_directly(intern->sheet_ref_obj);
|
|
||||||
}
|
|
||||||
intern->sheet_ref_obj = Z_LIBXML_NODE_P(docp)->document;
|
|
||||||
intern->sheet_ref_obj->refcount++;
|
|
||||||
}
|
}
|
||||||
|
intern->sheet_ref_obj = clone_lxml_obj->document;
|
||||||
|
intern->sheet_ref_obj->refcount++;
|
||||||
|
OBJ_RELEASE(clone);
|
||||||
|
|
||||||
member = ZSTR_INIT_LITERAL("cloneDocument", 0);
|
member = ZSTR_INIT_LITERAL("cloneDocument", 0);
|
||||||
cloneDocu = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_R, NULL, &rv);
|
cloneDocu = zend_std_read_property(Z_OBJ_P(id), member, BP_VAR_R, NULL, &rv);
|
||||||
|
|||||||
Reference in New Issue
Block a user