-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix #79969: Wrong error message in strict mode in ReflectionMethod::_construct #5995
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2953,27 +2953,49 @@ ZEND_METHOD(reflection_method, __construct) | |||||
char *name_str, *tmp; | ||||||
size_t name_len, tmp_len; | ||||||
zval ztmp; | ||||||
const char *space; | ||||||
|
||||||
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "zs", &classname, &name_str, &name_len) == FAILURE) { | ||||||
if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) { | ||||||
return; | ||||||
} | ||||||
if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "z|s!", &classname, &name_str, &name_len) == FAILURE) { | ||||||
return; | ||||||
} | ||||||
|
||||||
if ((tmp = strstr(name_str, "::")) == NULL) { | ||||||
zend_throw_exception_ex(reflection_exception_ptr, 0, | ||||||
"Invalid method name %s", name_str); | ||||||
return; | ||||||
} | ||||||
classname = &ztmp; | ||||||
tmp_len = tmp - name_str; | ||||||
ZVAL_STRINGL(classname, name_str, tmp_len); | ||||||
name_len = name_len - (tmp_len + 2); | ||||||
name_str = tmp + 2; | ||||||
orig_obj = NULL; | ||||||
} else if (Z_TYPE_P(classname) == IS_OBJECT) { | ||||||
orig_obj = classname; | ||||||
} else { | ||||||
orig_obj = NULL; | ||||||
switch (Z_TYPE_P(classname)) { | ||||||
case IS_STRING: | ||||||
orig_obj = NULL; | ||||||
|
||||||
if (ZEND_NUM_ARGS() == 1) { | ||||||
name_str = Z_STRVAL_P(classname); | ||||||
name_len = Z_STRLEN_P(classname); | ||||||
|
||||||
if ((tmp = strstr(name_str, "::")) == NULL) { | ||||||
zend_throw_exception_ex(reflection_exception_ptr, 0, "Invalid method name %s", name_str); | ||||||
return; | ||||||
} | ||||||
|
||||||
classname = &ztmp; | ||||||
tmp_len = tmp - name_str; | ||||||
ZVAL_STRINGL(classname, name_str, tmp_len); | ||||||
name_len = name_len - (tmp_len + 2); | ||||||
name_str = tmp + 2; | ||||||
} | ||||||
|
||||||
break; | ||||||
|
||||||
case IS_OBJECT: | ||||||
if (ZEND_NUM_ARGS() == 1 || !name_str) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to explicitly put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to initialize There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, thank you! My C is still a little rusty after so much time... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know what you're talking about :) I needed quite a lot of time to have even a vague idea about what I see in php-src :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
PHP-src is more it's own version of C than actual C at times due to the bazillion macros, so yeah don't worry lol |
||||||
tmp = get_active_class_name(&space); | ||||||
|
||||||
zend_type_error("%s%s%s() expects parameter %d to be %s, %s given", | ||||||
tmp, space, get_active_function_name(), 2, "string", "null"); | ||||||
return; | ||||||
} | ||||||
|
||||||
orig_obj = classname; | ||||||
|
||||||
break; | ||||||
|
||||||
default: | ||||||
_DO_THROW("The parameter class is expected to be either a string or an object"); | ||||||
} | ||||||
|
||||||
object = getThis(); | ||||||
|
@@ -3002,8 +3024,8 @@ ZEND_METHOD(reflection_method, __construct) | |||||
if (classname == &ztmp) { | ||||||
zval_ptr_dtor_str(&ztmp); | ||||||
} | ||||||
_DO_THROW("The parameter class is expected to be either a string or an object"); | ||||||
/* returns out of this function */ | ||||||
|
||||||
return; | ||||||
} | ||||||
|
||||||
if (classname == &ztmp) { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,20 +7,40 @@ Steve Seear <[email protected]> | |
<?php | ||
|
||
try { | ||
new ReflectionMethod(); | ||
} catch (TypeError $re) { | ||
echo "Ok - ".$re->getMessage().PHP_EOL; | ||
new ReflectionMethod(); | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
try { | ||
new ReflectionMethod('a', 'b', 'c'); | ||
} catch (TypeError $re) { | ||
echo "Ok - ".$re->getMessage().PHP_EOL; | ||
new ReflectionMethod('a', 'b', 'c'); | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
|
||
class C { | ||
public function f() {} | ||
} | ||
|
||
try { | ||
new ReflectionMethod(new C); | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
try { | ||
new ReflectionMethod(new C, null); | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
try { | ||
new ReflectionMethod(new C, ""); | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
$rm = new ReflectionMethod('C', 'f'); | ||
|
||
var_dump($rm->isFinal(1)); | ||
|
@@ -43,8 +63,11 @@ var_dump($rm->getName(1)); | |
|
||
?> | ||
--EXPECTF-- | ||
Ok - ReflectionMethod::__construct() expects exactly 1 parameter, 0 given | ||
Ok - ReflectionMethod::__construct() expects exactly 1 parameter, 3 given | ||
string(69) "ReflectionMethod::__construct() expects at least 1 parameter, 0 given" | ||
string(69) "ReflectionMethod::__construct() expects at most 2 parameters, 3 given" | ||
string(76) "ReflectionMethod::__construct() expects parameter 2 to be string, null given" | ||
string(76) "ReflectionMethod::__construct() expects parameter 2 to be string, null given" | ||
string(27) "Method C::() does not exist" | ||
|
||
Warning: ReflectionMethod::isFinal() expects exactly 0 parameters, 1 given in %s on line %d | ||
NULL | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,21 +12,22 @@ class TestClass | |
} | ||
} | ||
|
||
echo "Too few arguments:\n"; | ||
|
||
try { | ||
echo "Too few arguments:\n"; | ||
$methodInfo = new ReflectionMethod(); | ||
} catch (TypeError $re) { | ||
echo "Ok - ".$re->getMessage().PHP_EOL; | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
echo "\nToo many arguments:\n"; | ||
|
||
try { | ||
echo "\nToo many arguments:\n"; | ||
$methodInfo = new ReflectionMethod("TestClass", "foo", true); | ||
} catch (TypeError $re) { | ||
echo "Ok - ".$re->getMessage().PHP_EOL; | ||
} catch (Throwable $e) { | ||
var_dump($e->getMessage()); | ||
} | ||
|
||
|
||
try { | ||
//invalid class | ||
$methodInfo = new ReflectionMethod("InvalidClassName", "foo"); | ||
|
@@ -50,12 +51,12 @@ try{ | |
} | ||
|
||
?> | ||
--EXPECT-- | ||
--EXPECTF-- | ||
Too few arguments: | ||
Ok - ReflectionMethod::__construct() expects exactly 1 parameter, 0 given | ||
string(69) "ReflectionMethod::__construct() expects at least 1 parameter, 0 given" | ||
|
||
Too many arguments: | ||
Ok - ReflectionMethod::__construct() expects exactly 1 parameter, 3 given | ||
string(69) "ReflectionMethod::__construct() expects at most 2 parameters, 3 given" | ||
Ok - Class InvalidClassName does not exist | ||
Ok - The parameter class is expected to be either a string or an object | ||
Ok - ReflectionMethod::__construct() expects exactly 1 parameter, 2 given | ||
Ok - ReflectionMethod::__construct() expects parameter 2 to be string, array given | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: missing new line before EOF |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
--TEST-- | ||
Bug #79969: ReflectionMethod::__construct throws incorrect exception when 2nd parameter is int | ||
--FILE-- | ||
<?php | ||
declare(strict_types=1); | ||
|
||
new ReflectionMethod('xxx', 1); | ||
?> | ||
--EXPECTF-- | ||
Fatal error: Uncaught TypeError: ReflectionMethod::__construct() expects parameter 2 to be string, int given in %s | ||
Stack trace: | ||
#0 %s(%d): ReflectionMethod->__construct('xxx', 1) | ||
#1 {main} | ||
thrown in %s on line %d |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But we'll need @cmb69 's approval for making the second parameter nullable for PHP 7.3. But it should absolutely work for PHP 8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a strict no from me, but I'd rather not change the signature in a stable version. @derickr what do you think about that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, Probably adding the
!
modifier only makes sense for PHP 8.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe to not complicate it further it would be better if I revert the code back to the first commit in this PR, and then apply this feedback. Then, for PHP 8, a different solution like having a nullable 2nd parameter, could be applied.
However, I am not sure if this is desirable and, in case it is, about the procedure to make a change in different PHP versions.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's also a sensible idea. We could also ask from ourselves if this is really needed to be fixed in PHP 7 at all? Personally, I'd only fix it in 8, but ultimately, it's Christoph's and Derick's call :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that fixing the erroneous error message for PHP-7.3 makes sense (given there is no change in the signature, i.e. second parameter is not nullable). After that has been merged into PHP-7.4 and the "master" branch, another PR removing the function overload (i.e. second parameter made nullable) could follow up.