Skip to content

Ensure zend_ast_fetch_class uses scope it is provided to fetch classes #7649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static zend_result zend_ast_add_unpacked_element(zval *result, zval *expr) {

zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope)
{
return zend_fetch_class(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION);
return zend_fetch_class_with_scope(zend_ast_get_str(ast), ast->attr | ZEND_FETCH_CLASS_EXCEPTION, scope);
}

ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope)
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals);
ZEND_API void zend_unset_timeout(void);
ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void);
ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type);
ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, int fetch_type, zend_class_entry *scope);
ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, int fetch_type);

ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name);
Expand Down
18 changes: 14 additions & 4 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1479,21 +1479,25 @@ void zend_unset_timeout(void) /* {{{ */
}
/* }}} */

zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {{{ */
zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, int fetch_type, zend_class_entry *scope) /* {{{ */
{
zend_class_entry *ce, *scope;
zend_class_entry *ce;
int fetch_sub_type = fetch_type & ZEND_FETCH_CLASS_MASK;

check_fetch_type:
switch (fetch_sub_type) {
case ZEND_FETCH_CLASS_SELF:
scope = zend_get_executed_scope();
if (scope == NULL) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could get into trouble here if the expression is supposed to be evaluated at global scope, in which case scope will be NULL and will fall back to the executed scope here. So we'd have to distinguish an explicit scope vs the fallback to the executed scope separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, forgot about global being NULL. Seems like the signature should allow providing the explicit vs execution scope? I'm out of my depth though so feel free to change/modify this.

scope = zend_get_executed_scope();
}
if (UNEXPECTED(!scope)) {
zend_throw_or_error(fetch_type, NULL, "Cannot access \"self\" when no class scope is active");
}
return scope;
case ZEND_FETCH_CLASS_PARENT:
scope = zend_get_executed_scope();
if (scope == NULL) {
scope = zend_get_executed_scope();
}
if (UNEXPECTED(!scope)) {
zend_throw_or_error(fetch_type, NULL, "Cannot access \"parent\" when no class scope is active");
return NULL;
Expand Down Expand Up @@ -1535,6 +1539,12 @@ zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {
}
/* }}} */

zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {{{ */
{
return zend_fetch_class_with_scope(class_name, fetch_type, NULL);
}
/* }}} */

zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *key, int fetch_type) /* {{{ */
{
zend_class_entry *ce = zend_lookup_class_ex(class_name, key, fetch_type);
Expand Down
64 changes: 64 additions & 0 deletions ext/reflection/tests/bug81611.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
--TEST--
Reflection Bug #81611 (ArgumentCountError when getting default value from ReflectionParameter with new)
--FILE--
<?php

class Bar
{
}

class Foo extends Bar
{
public function doFoo(object $test = new self()): object
{
return $test;
}

public function doBar(object $test = new parent()): object
{
return $test;
}
}

$ref = new \ReflectionClass(Foo::class);

foreach (['doFoo', 'doBar'] as $method) {
$params = $ref->getMethod($method)->getParameters();

foreach ($params as $param) {
echo "isDefaultValueAvailable:\n";
var_dump($param->isDefaultValueAvailable());

echo "isDefaultValueConstant:\n";
var_dump($param->isDefaultValueConstant());

echo "getDefaultValueConstantName:\n";
var_dump($param->getDefaultValueConstantName());

echo "getDefaultValue:\n";
var_dump($param->getDefaultValue());

echo "\n";
}
}
?>
--EXPECT--
isDefaultValueAvailable:
bool(true)
isDefaultValueConstant:
bool(false)
getDefaultValueConstantName:
NULL
getDefaultValue:
object(Foo)#2 (0) {
}

isDefaultValueAvailable:
bool(true)
isDefaultValueConstant:
bool(false)
getDefaultValueConstantName:
NULL
getDefaultValue:
object(Bar)#3 (0) {
}