Skip to content

Commit 812df2b

Browse files
camporternikic
authored andcommitted
Fix bug #81611
Add zend_fetch_class_with_scope() which accepts a scope to use for self/parent, and use that during constant expression evaluation. Closes GH-7649.
1 parent d9ff09a commit 812df2b

File tree

5 files changed

+103
-1
lines changed

5 files changed

+103
-1
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug #81513 (Future possibility for heap overflow in FPM zlog).
77
(Jakub Zelenka)
88

9+
- Reflection:
10+
. Fixed bug #81611 (ArgumentCountError when getting default value from
11+
ReflectionParameter with new). (Cameron Porter)
12+
913
- XML:
1014
. Fixed bug #79971 (special character is breaking the path in xml function).
1115
(CVE-2021-21707) (cmb)

Zend/zend_ast.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ static zend_result zend_ast_add_unpacked_element(zval *result, zval *expr) {
482482

483483
zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope)
484484
{
485-
return zend_fetch_class(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION);
485+
return zend_fetch_class_with_scope(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION, scope);
486486
}
487487

488488
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)

Zend/zend_execute.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals);
349349
ZEND_API void zend_unset_timeout(void);
350350
ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void);
351351
ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type);
352+
ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, int fetch_type, zend_class_entry *scope);
352353
ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, int fetch_type);
353354

354355
ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name);

Zend/zend_execute_API.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,39 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {
15491549
}
15501550
/* }}} */
15511551

1552+
zend_class_entry *zend_fetch_class_with_scope(
1553+
zend_string *class_name, int fetch_type, zend_class_entry *scope)
1554+
{
1555+
zend_class_entry *ce;
1556+
switch (fetch_type & ZEND_FETCH_CLASS_MASK) {
1557+
case ZEND_FETCH_CLASS_SELF:
1558+
if (UNEXPECTED(!scope)) {
1559+
zend_throw_or_error(fetch_type, NULL, "Cannot access \"self\" when no class scope is active");
1560+
}
1561+
return scope;
1562+
case ZEND_FETCH_CLASS_PARENT:
1563+
if (UNEXPECTED(!scope)) {
1564+
zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active");
1565+
return NULL;
1566+
}
1567+
if (UNEXPECTED(!scope->parent)) {
1568+
zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when current class scope has no parent");
1569+
}
1570+
return scope->parent;
1571+
case 0:
1572+
break;
1573+
/* Other fetch types are not supported by this function. */
1574+
EMPTY_SWITCH_DEFAULT_CASE()
1575+
}
1576+
1577+
ce = zend_lookup_class_ex(class_name, NULL, fetch_type);
1578+
if (!ce) {
1579+
report_class_fetch_error(class_name, fetch_type);
1580+
return NULL;
1581+
}
1582+
return ce;
1583+
}
1584+
15521585
zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *key, int fetch_type) /* {{{ */
15531586
{
15541587
zend_class_entry *ce = zend_lookup_class_ex(class_name, key, fetch_type);

ext/reflection/tests/bug81611.phpt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
--TEST--
2+
Reflection Bug #81611 (ArgumentCountError when getting default value from ReflectionParameter with new)
3+
--FILE--
4+
<?php
5+
6+
class Bar
7+
{
8+
}
9+
10+
class Foo extends Bar
11+
{
12+
public function doFoo(object $test = new self()): object
13+
{
14+
return $test;
15+
}
16+
17+
public function doBar(object $test = new parent()): object
18+
{
19+
return $test;
20+
}
21+
}
22+
23+
$ref = new \ReflectionClass(Foo::class);
24+
25+
foreach (['doFoo', 'doBar'] as $method) {
26+
$params = $ref->getMethod($method)->getParameters();
27+
28+
foreach ($params as $param) {
29+
echo "isDefaultValueAvailable:\n";
30+
var_dump($param->isDefaultValueAvailable());
31+
32+
echo "isDefaultValueConstant:\n";
33+
var_dump($param->isDefaultValueConstant());
34+
35+
echo "getDefaultValueConstantName:\n";
36+
var_dump($param->getDefaultValueConstantName());
37+
38+
echo "getDefaultValue:\n";
39+
var_dump($param->getDefaultValue());
40+
41+
echo "\n";
42+
}
43+
}
44+
?>
45+
--EXPECT--
46+
isDefaultValueAvailable:
47+
bool(true)
48+
isDefaultValueConstant:
49+
bool(false)
50+
getDefaultValueConstantName:
51+
NULL
52+
getDefaultValue:
53+
object(Foo)#2 (0) {
54+
}
55+
56+
isDefaultValueAvailable:
57+
bool(true)
58+
isDefaultValueConstant:
59+
bool(false)
60+
getDefaultValueConstantName:
61+
NULL
62+
getDefaultValue:
63+
object(Bar)#3 (0) {
64+
}

0 commit comments

Comments
 (0)