Skip to content

Commit 1ed6868

Browse files
committed
Merge branch 'PHP-8.2'
* PHP-8.2: Fix DOMElement::append() and DOMElement::prepend() hierarchy checks Fix spec compliance error for DOMDocument::getElementsByTagNameNS Fix GH-11336: php still tries to unlock the shared memory ZendSem with opcache.file_cache_only=1 but it was never locked Fix GH-11338: SplFileInfo empty getBasename with more than one slash
2 parents f90962d + 0e7ad40 commit 1ed6868

9 files changed

+349
-3
lines changed

ext/dom/parentnode.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,33 @@ static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fr
255255
fragment->last = NULL;
256256
}
257257

258+
static zend_result dom_hierarchy_node_list(xmlNodePtr parentNode, zval *nodes, int nodesc)
259+
{
260+
for (int i = 0; i < nodesc; i++) {
261+
if (Z_TYPE(nodes[i]) == IS_OBJECT) {
262+
const zend_class_entry *ce = Z_OBJCE(nodes[i]);
263+
264+
if (instanceof_function(ce, dom_node_class_entry)) {
265+
if (dom_hierarchy(parentNode, dom_object_get_node(Z_DOMOBJ_P(nodes + i))) != SUCCESS) {
266+
return FAILURE;
267+
}
268+
}
269+
}
270+
}
271+
272+
return SUCCESS;
273+
}
274+
258275
void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc)
259276
{
260277
xmlNode *parentNode = dom_object_get_node(context);
261278
xmlNodePtr newchild, prevsib;
279+
280+
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) {
281+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document));
282+
return;
283+
}
284+
262285
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
263286

264287
if (fragment == NULL) {
@@ -296,6 +319,11 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc)
296319
return;
297320
}
298321

322+
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) {
323+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document));
324+
return;
325+
}
326+
299327
xmlNodePtr newchild, nextsib;
300328
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc);
301329

ext/dom/php_dom.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,10 +1221,15 @@ xmlNode *dom_get_elements_by_tag_name_ns_raw(xmlNodePtr nodep, char *ns, char *l
12211221
{
12221222
xmlNodePtr ret = NULL;
12231223

1224+
/* Note: The spec says that ns == '' must be transformed to ns == NULL. In other words, they are equivalent.
1225+
* PHP however does not do this and internally uses the empty string everywhere when the user provides ns == NULL.
1226+
* This is because for PHP ns == NULL has another meaning: "match every namespace" instead of "match the empty namespace". */
1227+
bool ns_match_any = ns == NULL || (ns[0] == '*' && ns[1] == '\0');
1228+
12241229
while (nodep != NULL && (*cur <= index || index == -1)) {
12251230
if (nodep->type == XML_ELEMENT_NODE) {
12261231
if (xmlStrEqual(nodep->name, (xmlChar *)local) || xmlStrEqual((xmlChar *)"*", (xmlChar *)local)) {
1227-
if (ns == NULL || (!strcmp(ns, "") && nodep->ns == NULL) || (nodep->ns != NULL && (xmlStrEqual(nodep->ns->href, (xmlChar *)ns) || xmlStrEqual((xmlChar *)"*", (xmlChar *)ns)))) {
1232+
if (ns_match_any || (!strcmp(ns, "") && nodep->ns == NULL) || (nodep->ns != NULL && xmlStrEqual(nodep->ns->href, (xmlChar *)ns))) {
12281233
if (*cur == index) {
12291234
ret = nodep;
12301235
break;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
--TEST--
2+
DOMDocument::getElementsByTagNameNS() match any namespace
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
/* Sample document taken from https://www.php.net/manual/en/domdocument.getelementsbytagname.php */
9+
$xml = <<<EOD
10+
<?xml version="1.0" ?>
11+
<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
12+
<title>Books of the other guy..</title>
13+
<para>
14+
<xi:include href="book.xml">
15+
<xi:fallback>
16+
<error>xinclude: book.xml not found</error>
17+
</xi:fallback>
18+
</xi:include>
19+
<include>
20+
This is another namespace
21+
</include>
22+
</para>
23+
</chapter>
24+
EOD;
25+
$dom = new DOMDocument;
26+
27+
// load the XML string defined above
28+
$dom->loadXML($xml);
29+
30+
function test($namespace, $local) {
31+
global $dom;
32+
$namespace_str = $namespace !== NULL ? "'$namespace'" : "null";
33+
echo "-- getElementsByTagNameNS($namespace_str, '$local') --\n";
34+
foreach ($dom->getElementsByTagNameNS($namespace, $local) as $element) {
35+
echo 'local name: \'', $element->localName, '\', prefix: \'', $element->prefix, "'\n";
36+
}
37+
}
38+
39+
// Should *also* include objects even without a namespace
40+
test(null, '*');
41+
// Should *also* include objects even without a namespace
42+
test('*', '*');
43+
// Should *only* include objects without a namespace
44+
test('', '*');
45+
// Should *only* include objects with the specified namespace
46+
test('http://www.w3.org/2001/XInclude', '*');
47+
// Should not give any output
48+
test('', 'fallback');
49+
// Should not give any output, because the null namespace is the same as the empty namespace
50+
test(null, 'fallback');
51+
// Should only output the include from the empty namespace
52+
test(null, 'include');
53+
54+
?>
55+
--EXPECT--
56+
-- getElementsByTagNameNS(null, '*') --
57+
local name: 'chapter', prefix: ''
58+
local name: 'title', prefix: ''
59+
local name: 'para', prefix: ''
60+
local name: 'error', prefix: ''
61+
local name: 'include', prefix: ''
62+
-- getElementsByTagNameNS('*', '*') --
63+
local name: 'chapter', prefix: ''
64+
local name: 'title', prefix: ''
65+
local name: 'para', prefix: ''
66+
local name: 'include', prefix: 'xi'
67+
local name: 'fallback', prefix: 'xi'
68+
local name: 'error', prefix: ''
69+
local name: 'include', prefix: ''
70+
-- getElementsByTagNameNS('', '*') --
71+
local name: 'chapter', prefix: ''
72+
local name: 'title', prefix: ''
73+
local name: 'para', prefix: ''
74+
local name: 'error', prefix: ''
75+
local name: 'include', prefix: ''
76+
-- getElementsByTagNameNS('http://www.w3.org/2001/XInclude', '*') --
77+
local name: 'include', prefix: 'xi'
78+
local name: 'fallback', prefix: 'xi'
79+
-- getElementsByTagNameNS('', 'fallback') --
80+
-- getElementsByTagNameNS(null, 'fallback') --
81+
-- getElementsByTagNameNS(null, 'include') --
82+
local name: 'include', prefix: ''
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--TEST--
2+
DOMElement::append() with hierarchy changes and errors
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom_original = new DOMDocument;
9+
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>');
10+
11+
echo "-- Append hello with world --\n";
12+
$dom = clone $dom_original;
13+
$b_hello = $dom->firstChild->firstChild;
14+
$b_world = $b_hello->nextSibling;
15+
$b_hello->append($b_world);
16+
var_dump($dom->saveHTML());
17+
18+
echo "-- Append hello with world's child --\n";
19+
$dom = clone $dom_original;
20+
$b_hello = $dom->firstChild->firstChild;
21+
$b_world = $b_hello->nextSibling;
22+
$b_hello->append($b_world->firstChild);
23+
var_dump($dom->saveHTML());
24+
25+
echo "-- Append world's child with hello --\n";
26+
$dom = clone $dom_original;
27+
$b_hello = $dom->firstChild->firstChild;
28+
$b_world = $b_hello->nextSibling;
29+
$b_world->firstChild->append($b_hello);
30+
var_dump($dom->saveHTML());
31+
32+
echo "-- Append hello with itself --\n";
33+
$dom = clone $dom_original;
34+
$b_hello = $dom->firstChild->firstChild;
35+
try {
36+
$b_hello->append($b_hello);
37+
} catch (\DOMException $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
var_dump($dom->saveHTML());
41+
42+
echo "-- Append world's i tag with the parent --\n";
43+
$dom = clone $dom_original;
44+
$b_hello = $dom->firstChild->firstChild;
45+
$b_world = $b_hello->nextSibling;
46+
try {
47+
$b_world->firstChild->append($b_world);
48+
} catch (\DOMException $e) {
49+
echo $e->getMessage(), "\n";
50+
}
51+
var_dump($dom->saveHTML());
52+
53+
echo "-- Append from another document --\n";
54+
$dom = clone $dom_original;
55+
$dom2 = new DOMDocument;
56+
$dom2->loadXML('<p>other</p>');
57+
try {
58+
$dom->firstChild->firstChild->prepend($dom2->firstChild);
59+
} catch (\DOMException $e) {
60+
echo $e->getMessage(), "\n";
61+
}
62+
var_dump($dom2->saveHTML());
63+
var_dump($dom->saveHTML());
64+
65+
?>
66+
--EXPECT--
67+
-- Append hello with world --
68+
string(39) "<p><b>hello<b><i>world</i></b></b></p>
69+
"
70+
-- Append hello with world's child --
71+
string(39) "<p><b>hello<i>world</i></b><b></b></p>
72+
"
73+
-- Append world's child with hello --
74+
string(39) "<p><b><i>world<b>hello</b></i></b></p>
75+
"
76+
-- Append hello with itself --
77+
Hierarchy Request Error
78+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
79+
"
80+
-- Append world's i tag with the parent --
81+
Hierarchy Request Error
82+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
83+
"
84+
-- Append from another document --
85+
Wrong Document Error
86+
string(13) "<p>other</p>
87+
"
88+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
89+
"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
--TEST--
2+
DOMElement::prepend() with hierarchy changes and errors
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom_original = new DOMDocument;
9+
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>');
10+
11+
echo "-- Prepend hello with world --\n";
12+
$dom = clone $dom_original;
13+
$b_hello = $dom->firstChild->firstChild;
14+
$b_world = $b_hello->nextSibling;
15+
$b_hello->prepend($b_world);
16+
var_dump($dom->saveHTML());
17+
18+
echo "-- Prepend hello with world's child --\n";
19+
$dom = clone $dom_original;
20+
$b_hello = $dom->firstChild->firstChild;
21+
$b_world = $b_hello->nextSibling;
22+
$b_hello->prepend($b_world->firstChild);
23+
var_dump($dom->saveHTML());
24+
25+
echo "-- Prepend world's child with hello --\n";
26+
$dom = clone $dom_original;
27+
$b_hello = $dom->firstChild->firstChild;
28+
$b_world = $b_hello->nextSibling;
29+
$b_world->firstChild->prepend($b_hello);
30+
var_dump($dom->saveHTML());
31+
32+
echo "-- Prepend hello with itself --\n";
33+
$dom = clone $dom_original;
34+
$b_hello = $dom->firstChild->firstChild;
35+
try {
36+
$b_hello->prepend($b_hello);
37+
} catch (\DOMException $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
var_dump($dom->saveHTML());
41+
42+
echo "-- Prepend world's i tag with the parent --\n";
43+
$dom = clone $dom_original;
44+
$b_hello = $dom->firstChild->firstChild;
45+
$b_world = $b_hello->nextSibling;
46+
try {
47+
$b_world->firstChild->prepend($b_world);
48+
} catch (\DOMException $e) {
49+
echo $e->getMessage(), "\n";
50+
}
51+
var_dump($dom->saveHTML());
52+
53+
echo "-- Append from another document --\n";
54+
$dom = clone $dom_original;
55+
$dom2 = new DOMDocument;
56+
$dom2->loadXML('<p>other</p>');
57+
try {
58+
$dom->firstChild->firstChild->prepend($dom2->firstChild);
59+
} catch (\DOMException $e) {
60+
echo $e->getMessage(), "\n";
61+
}
62+
var_dump($dom2->saveHTML());
63+
var_dump($dom->saveHTML());
64+
65+
?>
66+
--EXPECT--
67+
-- Prepend hello with world --
68+
string(39) "<p><b><b><i>world</i></b>hello</b></p>
69+
"
70+
-- Prepend hello with world's child --
71+
string(39) "<p><b><i>world</i>hello</b><b></b></p>
72+
"
73+
-- Prepend world's child with hello --
74+
string(39) "<p><b><i><b>hello</b>world</i></b></p>
75+
"
76+
-- Prepend hello with itself --
77+
Hierarchy Request Error
78+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
79+
"
80+
-- Prepend world's i tag with the parent --
81+
Hierarchy Request Error
82+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
83+
"
84+
-- Append from another document --
85+
Wrong Document Error
86+
string(13) "<p>other</p>
87+
"
88+
string(39) "<p><b>hello</b><b><i>world</i></b></p>
89+
"

ext/opcache/ZendAccelerator.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ static inline void accel_unlock_all(void)
390390
#ifdef ZEND_WIN32
391391
accel_deactivate_sub();
392392
#else
393+
if (lock_file == -1) {
394+
return;
395+
}
396+
393397
struct flock mem_usage_unlock_all;
394398

395399
mem_usage_unlock_all.l_type = F_UNLCK;

ext/opcache/zend_shared_alloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ zend_smm_shared_globals *smm_shared_globals;
5959
#ifdef ZTS
6060
static MUTEX_T zts_lock;
6161
#endif
62-
int lock_file;
62+
int lock_file = -1;
6363
static char lockfile_name[MAXPATHLEN];
6464
#endif
6565

ext/spl/spl_directory.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,9 @@ static void spl_filesystem_info_set_filename(spl_filesystem_object *intern, zend
454454

455455
path_len = ZSTR_LEN(path);
456456
if (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len-1)) {
457-
path_len--;
457+
do {
458+
path_len--;
459+
} while (path_len > 1 && IS_SLASH_AT(ZSTR_VAL(path), path_len - 1));
458460
intern->file_name = zend_string_init(ZSTR_VAL(path), path_len, 0);
459461
} else {
460462
intern->file_name = zend_string_copy(path);

0 commit comments

Comments
 (0)