Skip to content

Commit 2302b14

Browse files
committed
Use ZPP callable check for spl_autoload_register.
This makes it always throw a TypeError, moreover this makes the error message consistent. Added a warning mentioning that the second parameter is now ignored when passed false. Closes phpGH-5301
1 parent dd6e54b commit 2302b14

11 files changed

+83
-101
lines changed

UPGRADING

+3
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ PHP 8.0 UPGRADE NOTES
372372
. SplDoublyLinkedList::push() now returns void instead of true
373373
. SplDoublyLinkedList::unshift() now returns void instead of true
374374
. SplQueue::enqueue() now returns void instead of true
375+
. spl_autoload_register() will now always throw a TypeError on invalid
376+
arguments, therefore the second argument $do_throw is ignored and a
377+
notice will be emitted if it is set to false.
375378

376379
- Standard:
377380
. assert() will no longer evaluate string arguments, instead they will be

Zend/tests/bug78868.phpt

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function main_autoload($class_name) {
2121
eval("class B {const foo = 1;}");
2222
}
2323

24-
spl_autoload_register('main_autoload', false);
24+
spl_autoload_register('main_autoload');
2525

2626
$classA = new ReflectionClass("A");
2727
$props = $classA->getProperties();

ext/spl/php_spl.c

+24-64
Original file line numberDiff line numberDiff line change
@@ -508,86 +508,46 @@ PHP_FUNCTION(spl_autoload_call)
508508
PHP_FUNCTION(spl_autoload_register)
509509
{
510510
zend_string *func_name;
511-
char *error = NULL;
512511
zend_string *lc_name;
513-
zval *zcallable = NULL;
514512
zend_bool do_throw = 1;
515513
zend_bool prepend = 0;
516514
zend_function *spl_func_ptr;
517515
autoload_func_info alfi;
518516
zend_object *obj_ptr;
517+
zend_fcall_info fci = {0};
519518
zend_fcall_info_cache fcc;
520519

521-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|z!bb", &zcallable, &do_throw, &prepend) == FAILURE) {
522-
RETURN_THROWS();
520+
ZEND_PARSE_PARAMETERS_START(0, 3)
521+
Z_PARAM_OPTIONAL
522+
Z_PARAM_FUNC_OR_NULL(fci, fcc)
523+
Z_PARAM_BOOL(do_throw)
524+
Z_PARAM_BOOL(prepend)
525+
ZEND_PARSE_PARAMETERS_END();
526+
527+
if (!do_throw) {
528+
php_error_docref(NULL, E_NOTICE, "Argument #2 ($do_throw) has been ignored, "
529+
"spl_autoload_register() will always throw");
523530
}
524531

525-
if (zcallable) {
526-
if (!zend_is_callable_ex(zcallable, NULL, 0, &func_name, &fcc, &error)) {
527-
alfi.ce = fcc.calling_scope;
528-
alfi.func_ptr = fcc.function_handler;
529-
obj_ptr = fcc.object;
530-
if (Z_TYPE_P(zcallable) == IS_ARRAY) {
531-
if (!obj_ptr && alfi.func_ptr && !(alfi.func_ptr->common.fn_flags & ZEND_ACC_STATIC)) {
532-
if (do_throw) {
533-
zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array specifies a non static method but no object (%s)", error);
534-
}
535-
if (error) {
536-
efree(error);
537-
}
538-
zend_string_release_ex(func_name, 0);
539-
RETURN_FALSE;
540-
} else if (do_throw) {
541-
zend_throw_exception_ex(spl_ce_LogicException, 0, "Passed array does not specify %s %smethod (%s)", alfi.func_ptr ? "a callable" : "an existing", !obj_ptr ? "static " : "", error);
542-
}
543-
if (error) {
544-
efree(error);
545-
}
546-
zend_string_release_ex(func_name, 0);
547-
RETURN_FALSE;
548-
} else if (Z_TYPE_P(zcallable) == IS_STRING) {
549-
if (do_throw) {
550-
zend_throw_exception_ex(spl_ce_LogicException, 0, "Function '%s' not %s (%s)", ZSTR_VAL(func_name), alfi.func_ptr ? "callable" : "found", error);
551-
}
552-
if (error) {
553-
efree(error);
554-
}
555-
zend_string_release_ex(func_name, 0);
556-
RETURN_FALSE;
557-
} else {
558-
if (do_throw) {
559-
zend_throw_exception_ex(spl_ce_LogicException, 0, "Illegal value passed (%s)", error);
560-
}
561-
if (error) {
562-
efree(error);
563-
}
564-
zend_string_release_ex(func_name, 0);
565-
RETURN_FALSE;
566-
}
567-
} else if (fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
568-
fcc.function_handler->internal_function.handler == zif_spl_autoload_call) {
569-
if (do_throw) {
570-
zend_throw_exception_ex(spl_ce_LogicException, 0, "Function spl_autoload_call() cannot be registered");
571-
}
572-
if (error) {
573-
efree(error);
574-
}
575-
zend_string_release_ex(func_name, 0);
576-
RETURN_FALSE;
577-
}
532+
/* If first arg is not null */
533+
if (ZEND_FCI_INITIALIZED(fci)) {
578534
alfi.ce = fcc.calling_scope;
579535
alfi.func_ptr = fcc.function_handler;
580536
obj_ptr = fcc.object;
581-
if (error) {
582-
efree(error);
583-
}
584537

585-
if (Z_TYPE_P(zcallable) == IS_OBJECT) {
586-
ZVAL_COPY(&alfi.closure, zcallable);
538+
if (fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
539+
fcc.function_handler->internal_function.handler == zif_spl_autoload_call) {
540+
zend_argument_value_error(1, "must not be the spl_autoload_call() function");
541+
RETURN_THROWS();
542+
}
587543

544+
/* fci.function_name contains the callable zval */
545+
func_name = zend_get_callable_name(&fci.function_name);
546+
if (Z_TYPE(fci.function_name) == IS_OBJECT) {
547+
ZVAL_COPY(&alfi.closure, &fci.function_name);
588548
lc_name = zend_string_alloc(ZSTR_LEN(func_name) + sizeof(uint32_t), 0);
589549
zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(func_name), ZSTR_LEN(func_name));
590-
memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE_P(zcallable), sizeof(uint32_t));
550+
memcpy(ZSTR_VAL(lc_name) + ZSTR_LEN(func_name), &Z_OBJ_HANDLE(fci.function_name), sizeof(uint32_t));
591551
ZSTR_VAL(lc_name)[ZSTR_LEN(lc_name)] = '\0';
592552
} else {
593553
ZVAL_UNDEF(&alfi.closure);
@@ -599,7 +559,7 @@ PHP_FUNCTION(spl_autoload_register)
599559
lc_name = zend_string_tolower(func_name);
600560
}
601561
}
602-
zend_string_release_ex(func_name, 0);
562+
zend_string_release(func_name);
603563

604564
if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
605565
if (!Z_ISUNDEF(alfi.closure)) {

ext/spl/php_spl.stub.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function spl_autoload_extensions(?string $file_extensions = null): string {}
1717

1818
function spl_autoload_functions(): array|false {}
1919

20-
function spl_autoload_register($autoload_function = null, bool $throw = true, bool $prepend = false): bool {}
20+
function spl_autoload_register(?callable $autoload_function = null, bool $throw = true, bool $prepend = false): bool {}
2121

2222
function spl_autoload_unregister($autoload_function): bool {}
2323

ext/spl/php_spl_arginfo.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_spl_autoload_functions, 0, 0, MA
2929
ZEND_END_ARG_INFO()
3030

3131
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_register, 0, 0, _IS_BOOL, 0)
32-
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, autoload_function, "null")
32+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, autoload_function, IS_CALLABLE, 1, "null")
3333
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, throw, _IS_BOOL, 0, "true")
3434
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, prepend, _IS_BOOL, 0, "false")
3535
ZEND_END_ARG_INFO()

ext/spl/tests/spl_autoload_001.phpt

+4-7
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,10 @@ var_dump(class_exists("TestClass", true));
6464

6565
echo "===NOFUNCTION===\n";
6666

67-
try
68-
{
67+
try {
6968
spl_autoload_register("unavailable_autoload_function");
70-
}
71-
catch(Exception $e)
72-
{
73-
echo 'Exception: ' . $e->getMessage() . "\n";
69+
} catch(\TypeError $e) {
70+
echo $e->getMessage() . \PHP_EOL;
7471
}
7572

7673
?>
@@ -103,4 +100,4 @@ TestFunc2(TestClass)
103100
%stestclass.class.inc
104101
bool(true)
105102
===NOFUNCTION===
106-
Exception: Function 'unavailable_autoload_function' not found (function 'unavailable_autoload_function' not found or invalid function name)
103+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, function 'unavailable_autoload_function' not found or invalid function name

ext/spl/tests/spl_autoload_005.phpt

+4-7
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,10 @@ class MyAutoLoader {
1919
}
2020
}
2121

22-
try
23-
{
22+
try {
2423
spl_autoload_register(array('MyAutoLoader', 'autoLoad'), true);
25-
}
26-
catch(Exception $e)
27-
{
28-
echo 'Exception: ' . $e->getMessage() . "\n";
24+
} catch(\TypeError $e) {
25+
echo $e->getMessage() . \PHP_EOL;
2926
}
3027

3128
// and
@@ -46,7 +43,7 @@ catch(Exception $e)
4643

4744
?>
4845
--EXPECT--
49-
Exception: Passed array specifies a non static method but no object (non-static method MyAutoLoader::autoLoad() cannot be called statically)
46+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, non-static method MyAutoLoader::autoLoad() cannot be called statically
5047
MyAutoLoader::autoLoad(TestClass)
5148
MyAutoLoader::autoThrow(TestClass)
5249
Exception: Unavailable

ext/spl/tests/spl_autoload_007.phpt

+11-14
Original file line numberDiff line numberDiff line change
@@ -40,47 +40,44 @@ $funcs = array(
4040
foreach($funcs as $idx => $func)
4141
{
4242
if ($idx) echo "\n";
43-
try
44-
{
43+
try {
4544
var_dump($func);
4645
spl_autoload_register($func);
4746
echo "ok\n";
48-
}
49-
catch (Exception $e)
50-
{
51-
echo $e->getMessage() . "\n";
47+
} catch(\TypeError $e) {
48+
echo $e->getMessage() . \PHP_EOL;
5249
}
5350
}
5451

5552
?>
5653
--EXPECTF--
5754
string(22) "MyAutoLoader::notExist"
58-
Function 'MyAutoLoader::notExist' not found (class 'MyAutoLoader' does not have a method 'notExist')
55+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, class 'MyAutoLoader' does not have a method 'notExist'
5956

6057
string(22) "MyAutoLoader::noAccess"
61-
Function 'MyAutoLoader::noAccess' not callable (cannot access protected method MyAutoLoader::noAccess())
58+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, cannot access protected method MyAutoLoader::noAccess()
6259

6360
string(22) "MyAutoLoader::autoLoad"
6461
ok
6562

6663
string(22) "MyAutoLoader::dynaLoad"
67-
Function 'MyAutoLoader::dynaLoad' not callable (non-static method MyAutoLoader::dynaLoad() cannot be called statically)
64+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, non-static method MyAutoLoader::dynaLoad() cannot be called statically
6865

6966
array(2) {
7067
[0]=>
7168
string(12) "MyAutoLoader"
7269
[1]=>
7370
string(8) "notExist"
7471
}
75-
Passed array does not specify an existing static method (class 'MyAutoLoader' does not have a method 'notExist')
72+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, class 'MyAutoLoader' does not have a method 'notExist'
7673

7774
array(2) {
7875
[0]=>
7976
string(12) "MyAutoLoader"
8077
[1]=>
8178
string(8) "noAccess"
8279
}
83-
Passed array does not specify a callable static method (cannot access protected method MyAutoLoader::noAccess())
80+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, cannot access protected method MyAutoLoader::noAccess()
8481

8582
array(2) {
8683
[0]=>
@@ -96,7 +93,7 @@ array(2) {
9693
[1]=>
9794
string(8) "dynaLoad"
9895
}
99-
Passed array specifies a non static method but no object (non-static method MyAutoLoader::dynaLoad() cannot be called statically)
96+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, non-static method MyAutoLoader::dynaLoad() cannot be called statically
10097

10198
array(2) {
10299
[0]=>
@@ -105,7 +102,7 @@ array(2) {
105102
[1]=>
106103
string(8) "notExist"
107104
}
108-
Passed array does not specify an existing method (class 'MyAutoLoader' does not have a method 'notExist')
105+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, class 'MyAutoLoader' does not have a method 'notExist'
109106

110107
array(2) {
111108
[0]=>
@@ -114,7 +111,7 @@ array(2) {
114111
[1]=>
115112
string(8) "noAccess"
116113
}
117-
Passed array does not specify a callable static method (cannot access protected method MyAutoLoader::noAccess())
114+
spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, cannot access protected method MyAutoLoader::noAccess()
118115

119116
array(2) {
120117
[0]=>

ext/spl/tests/spl_autoload_008.phpt

+4-6
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,8 @@ foreach($funcs as $idx => $func)
5252

5353
var_dump(class_exists("NoExistingTestClass", true));
5454
}
55-
}
56-
catch (Exception $e)
57-
{
58-
echo get_class($e) . ": " . $e->getMessage() . "\n";
55+
} catch(\TypeError|\Exception $e) {
56+
echo get_class($e) . ': ' . $e->getMessage() . \PHP_EOL;
5957
}
6058

6159
spl_autoload_unregister($func);
@@ -78,7 +76,7 @@ Exception: Bla
7876
int(0)
7977
====2====
8078
string(22) "MyAutoLoader::dynaLoad"
81-
LogicException: Function 'MyAutoLoader::dynaLoad' not callable (non-static method MyAutoLoader::dynaLoad() cannot be called statically)
79+
TypeError: spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, non-static method MyAutoLoader::dynaLoad() cannot be called statically
8280
int(0)
8381
====3====
8482
array(2) {
@@ -98,7 +96,7 @@ array(2) {
9896
[1]=>
9997
string(8) "dynaLoad"
10098
}
101-
LogicException: Passed array specifies a non static method but no object (non-static method MyAutoLoader::dynaLoad() cannot be called statically)
99+
TypeError: spl_autoload_register(): Argument #1 ($autoload_function) must be a valid callback, non-static method MyAutoLoader::dynaLoad() cannot be called statically
102100
int(0)
103101
====5====
104102
array(2) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
spl_autoload_register() function - warn when using spl_autoload_call() as the autoloading function
3+
--FILE--
4+
<?php
5+
6+
try {
7+
spl_autoload_register('spl_autoload_call');
8+
} catch (\ValueError $e) {
9+
echo $e->getMessage() . \PHP_EOL;
10+
}
11+
12+
?>
13+
--EXPECT--
14+
spl_autoload_register(): Argument #1 ($autoload_function) must not be the spl_autoload_call() function
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
spl_autoload_register() function - warn when using false as second argument for spl_autoload_register()
3+
--FILE--
4+
<?php
5+
function customAutolader($class) {
6+
require_once __DIR__ . '/testclass.class.inc';
7+
}
8+
spl_autoload_register('customAutolader', false);
9+
10+
spl_autoload_call('TestClass');
11+
var_dump(class_exists('TestClass', false));
12+
?>
13+
--EXPECTF--
14+
Notice: spl_autoload_register(): Argument #2 ($do_throw) has been ignored, spl_autoload_register() will always throw in %s on line %d
15+
%stestclass.class.inc
16+
bool(true)

0 commit comments

Comments
 (0)