From 4962f9e8bb1a969257df431aaad537a76f222dea Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Mon, 15 Mar 2021 14:46:43 +0000 Subject: [PATCH 1/6] Allow automatically including files through the use of a classmap rather than calling out to a userland function --- Zend/zend_execute.c | 3 +- Zend/zend_execute.h | 3 + ext/spl/php_spl.c | 80 ++++++++++++++++++++ ext/spl/php_spl.h | 1 + ext/spl/php_spl.stub.php | 4 + ext/spl/php_spl_arginfo.h | 14 +++- ext/spl/tests/autoload_set_classmap_001.phpt | 29 +++++++ ext/spl/tests/autoload_set_classmap_002.phpt | 15 ++++ ext/spl/tests/autoload_set_classmap_003.phpt | 20 +++++ ext/spl/tests/autoload_set_classmap_004.phpt | 16 ++++ 10 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 ext/spl/tests/autoload_set_classmap_001.phpt create mode 100644 ext/spl/tests/autoload_set_classmap_002.phpt create mode 100644 ext/spl/tests/autoload_set_classmap_003.phpt create mode 100644 ext/spl/tests/autoload_set_classmap_004.phpt diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 11a140e845335..e3ed5ddd36d6f 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4189,9 +4189,8 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar } /* }}} */ -#define ZEND_FAKE_OP_ARRAY ((zend_op_array*)(zend_intptr_t)-1) -static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type) /* {{{ */ +zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type) /* {{{ */ { zend_op_array *new_op_array = NULL; zval tmp_inc_filename; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 3339f3992da35..cdcab60901130 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -76,6 +76,9 @@ ZEND_API ZEND_COLD void zend_verify_return_error( ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_value_instanceof_static(zval *zv); +extern zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval *inc_filename, int type); + +#define ZEND_FAKE_OP_ARRAY ((zend_op_array*)(zend_intptr_t)-1) #define ZEND_REF_TYPE_SOURCES(ref) \ (ref)->sources diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 5981d2385f0c7..6d9051460b16b 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -413,6 +413,38 @@ static bool autoload_func_info_equals( } static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { + /* classmap provides for classname => path resolution without needing to invoke a user function */ + if (SPL_G(autoload_classmap)) { + zval * mf = zend_hash_str_find(SPL_G(autoload_classmap), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); + if (mf) { + if (Z_TYPE_P(mf) != IS_STRING) { + zend_throw_exception_ex(zend_ce_error, 0, "Classmap entry \"%s\" expected a string, \"%s\" given.", ZSTR_VAL(class_name), zend_zval_type_name(mf)); + return NULL; + } + + zend_op_array *op_array = zend_include_or_eval(mf, ZEND_REQUIRE_ONCE); + + if (op_array != NULL && op_array != ZEND_FAKE_OP_ARRAY) { + destroy_op_array(op_array); + efree_size(op_array, sizeof(zend_op_array)); + } + + if (EG(exception)) { + return NULL; + } + + zend_class_entry * ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce) { + return ce; + } + else { + /* if the item is in the classmap it must be valid after including the file */ + zend_throw_exception_ex(zend_ce_error, 0, "Classmap entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(class_name), Z_STRVAL_P(mf)); + return NULL; + } + } + } + if (!SPL_G(autoload_functions)) { return NULL; } @@ -625,6 +657,48 @@ PHP_FUNCTION(spl_autoload_functions) } } /* }}} */ + +/* {{{ Assign an array of name => path mappings to the autoloader */ +PHP_FUNCTION(autoload_set_classmap) +{ + HashTable * ht = NULL; + Bucket *bucket; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(ht) + ZEND_PARSE_PARAMETERS_END(); + + if (SPL_G(autoload_classmap)) { + zend_throw_error(NULL, "The autoload classmap can only be initialized once per request."); + RETURN_THROWS(); + } + + ZEND_HASH_FOREACH_BUCKET(ht, bucket) { + if (Z_TYPE(bucket->val) != IS_STRING) { + zend_argument_value_error(1, "Classmap entry for \"%s\" expected string value, found %s.", ZSTR_VAL(bucket->key), zend_zval_type_name(&bucket->val)); + RETURN_THROWS(); + } + } ZEND_HASH_FOREACH_END(); + + ALLOC_HASHTABLE(SPL_G(autoload_classmap)); + zend_hash_init(SPL_G(autoload_classmap), zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(SPL_G(autoload_classmap), ht, (copy_ctor_func_t)zval_add_ref); + + RETURN_TRUE; +} /* }}} */ + +/* {{{ Assign an array of name => path mappings to the autoloader */ +PHP_FUNCTION(autoload_get_classmap) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + if (SPL_G(autoload_classmap)) { + RETURN_ARR(zend_array_dup(SPL_G(autoload_classmap))); + } + + RETURN_EMPTY_ARRAY(); +} /* }}} */ + /* {{{ Return hash id for given object */ PHP_FUNCTION(spl_object_hash) { @@ -732,6 +806,7 @@ PHP_RINIT_FUNCTION(spl) /* {{{ */ { SPL_G(autoload_extensions) = NULL; SPL_G(autoload_functions) = NULL; + SPL_G(autoload_classmap) = NULL; SPL_G(hash_mask_init) = 0; return SUCCESS; } /* }}} */ @@ -742,6 +817,11 @@ PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */ zend_string_release_ex(SPL_G(autoload_extensions), 0); SPL_G(autoload_extensions) = NULL; } + if (SPL_G(autoload_classmap)) { + zend_hash_destroy(SPL_G(autoload_classmap)); + FREE_HASHTABLE(SPL_G(autoload_classmap)); + SPL_G(autoload_classmap) = NULL; + } if (SPL_G(autoload_functions)) { zend_hash_destroy(SPL_G(autoload_functions)); FREE_HASHTABLE(SPL_G(autoload_functions)); diff --git a/ext/spl/php_spl.h b/ext/spl/php_spl.h index 8009cbbb06e9a..3313e28e7ee55 100644 --- a/ext/spl/php_spl.h +++ b/ext/spl/php_spl.h @@ -54,6 +54,7 @@ PHP_MINFO_FUNCTION(spl); ZEND_BEGIN_MODULE_GLOBALS(spl) zend_string *autoload_extensions; HashTable *autoload_functions; + HashTable *autoload_classmap; intptr_t hash_mask_handle; intptr_t hash_mask_handlers; int hash_mask_init; diff --git a/ext/spl/php_spl.stub.php b/ext/spl/php_spl.stub.php index e7cc10dbc3510..6f3501496531a 100644 --- a/ext/spl/php_spl.stub.php +++ b/ext/spl/php_spl.stub.php @@ -34,3 +34,7 @@ function iterator_apply(Traversable $iterator, callable $callback, ?array $args function iterator_count(Traversable $iterator): int {} function iterator_to_array(Traversable $iterator, bool $preserve_keys = true): array {} + +function autoload_set_classmap(array $mapping): bool {} + +function autoload_get_classmap(array $mapping): array {} \ No newline at end of file diff --git a/ext/spl/php_spl_arginfo.h b/ext/spl/php_spl_arginfo.h index bd2a180764f67..a9191dea84535 100644 --- a/ext/spl/php_spl_arginfo.h +++ b/ext/spl/php_spl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 54d193c03c2652ce40adabd10d88666a86e32728 */ + * Stub hash: 70ba950885121dee91431621406a8188a5a40a54 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_implements, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_INFO(0, object_or_class) @@ -61,6 +61,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_iterator_to_array, 0, 1, IS_ARRA ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserve_keys, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_autoload_set_classmap, 0, 1, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO(0, mapping, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_autoload_get_classmap, 0, 1, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, mapping, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(class_implements); ZEND_FUNCTION(class_parents); @@ -77,6 +85,8 @@ ZEND_FUNCTION(spl_object_id); ZEND_FUNCTION(iterator_apply); ZEND_FUNCTION(iterator_count); ZEND_FUNCTION(iterator_to_array); +ZEND_FUNCTION(autoload_set_classmap); +ZEND_FUNCTION(autoload_get_classmap); static const zend_function_entry ext_functions[] = { @@ -95,5 +105,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(iterator_apply, arginfo_iterator_apply) ZEND_FE(iterator_count, arginfo_iterator_count) ZEND_FE(iterator_to_array, arginfo_iterator_to_array) + ZEND_FE(autoload_set_classmap, arginfo_autoload_set_classmap) + ZEND_FE(autoload_get_classmap, arginfo_autoload_get_classmap) ZEND_FE_END }; diff --git a/ext/spl/tests/autoload_set_classmap_001.phpt b/ext/spl/tests/autoload_set_classmap_001.phpt new file mode 100644 index 0000000000000..b407430851054 --- /dev/null +++ b/ext/spl/tests/autoload_set_classmap_001.phpt @@ -0,0 +1,29 @@ +--TEST-- +autoload_set_classmap Basic Operation +--FILE-- + +--EXPECT-- +Example +Foo\NamespacedExample \ No newline at end of file diff --git a/ext/spl/tests/autoload_set_classmap_002.phpt b/ext/spl/tests/autoload_set_classmap_002.phpt new file mode 100644 index 0000000000000..5c859571561e4 --- /dev/null +++ b/ext/spl/tests/autoload_set_classmap_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +autoload_set_classmap Reject non-string values +--FILE-- + 123]); + } + catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); + } + +?> +--EXPECT-- +ValueError - autoload_set_classmap(): Argument #1 ($mapping) Classmap entry for "Foo" expected string value, found int. \ No newline at end of file diff --git a/ext/spl/tests/autoload_set_classmap_003.phpt b/ext/spl/tests/autoload_set_classmap_003.phpt new file mode 100644 index 0000000000000..7866f36693bd5 --- /dev/null +++ b/ext/spl/tests/autoload_set_classmap_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +autoload_set_classmap Throw error if mapped file did not contain class +--FILE-- + $tmp_file]); + + try { + new Foo(); + } + catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); + } + +?> +--EXPECTF-- +Error - Classmap entry "Foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). diff --git a/ext/spl/tests/autoload_set_classmap_004.phpt b/ext/spl/tests/autoload_set_classmap_004.phpt new file mode 100644 index 0000000000000..781eaa2785364 --- /dev/null +++ b/ext/spl/tests/autoload_set_classmap_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +autoload_set_classmap Map can only be initialized once +--FILE-- + '/Foo']); + autoload_set_classmap(['Foo' => '/Foo']); + } + catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); + } + +?> +--EXPECT-- +Error - The autoload classmap can only be initialized once per request. \ No newline at end of file From 5ffad1f51a4e32888bf3876a427332222cd6eb1a Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Mon, 15 Mar 2021 15:29:46 +0000 Subject: [PATCH 2/6] Tweeks to perform message errors, move type checking to perform time --- ext/spl/php_spl.c | 15 +++------------ ext/spl/tests/autoload_set_classmap_002.phpt | 5 +++-- ext/spl/tests/autoload_set_classmap_003.phpt | 2 +- ext/spl/tests/autoload_set_classmap_004.phpt | 4 ++-- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 6d9051460b16b..2000c66bc409e 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -418,12 +418,11 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri zval * mf = zend_hash_str_find(SPL_G(autoload_classmap), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); if (mf) { if (Z_TYPE_P(mf) != IS_STRING) { - zend_throw_exception_ex(zend_ce_error, 0, "Classmap entry \"%s\" expected a string, \"%s\" given.", ZSTR_VAL(class_name), zend_zval_type_name(mf)); + zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" expected a string value, \"%s\" given.", ZSTR_VAL(class_name), zend_zval_type_name(mf)); return NULL; } zend_op_array *op_array = zend_include_or_eval(mf, ZEND_REQUIRE_ONCE); - if (op_array != NULL && op_array != ZEND_FAKE_OP_ARRAY) { destroy_op_array(op_array); efree_size(op_array, sizeof(zend_op_array)); @@ -439,7 +438,7 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri } else { /* if the item is in the classmap it must be valid after including the file */ - zend_throw_exception_ex(zend_ce_error, 0, "Classmap entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(class_name), Z_STRVAL_P(mf)); + zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(class_name), Z_STRVAL_P(mf)); return NULL; } } @@ -662,24 +661,16 @@ PHP_FUNCTION(spl_autoload_functions) PHP_FUNCTION(autoload_set_classmap) { HashTable * ht = NULL; - Bucket *bucket; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(ht) ZEND_PARSE_PARAMETERS_END(); if (SPL_G(autoload_classmap)) { - zend_throw_error(NULL, "The autoload classmap can only be initialized once per request."); + zend_throw_error(NULL, "Classmap Autoloader can only be initialized once per request."); RETURN_THROWS(); } - ZEND_HASH_FOREACH_BUCKET(ht, bucket) { - if (Z_TYPE(bucket->val) != IS_STRING) { - zend_argument_value_error(1, "Classmap entry for \"%s\" expected string value, found %s.", ZSTR_VAL(bucket->key), zend_zval_type_name(&bucket->val)); - RETURN_THROWS(); - } - } ZEND_HASH_FOREACH_END(); - ALLOC_HASHTABLE(SPL_G(autoload_classmap)); zend_hash_init(SPL_G(autoload_classmap), zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); zend_hash_copy(SPL_G(autoload_classmap), ht, (copy_ctor_func_t)zval_add_ref); diff --git a/ext/spl/tests/autoload_set_classmap_002.phpt b/ext/spl/tests/autoload_set_classmap_002.phpt index 5c859571561e4..be9721be9e2ad 100644 --- a/ext/spl/tests/autoload_set_classmap_002.phpt +++ b/ext/spl/tests/autoload_set_classmap_002.phpt @@ -5,11 +5,12 @@ autoload_set_classmap Reject non-string values try { autoload_set_classmap(['Foo' => 123]); + new Foo(); } catch (\Throwable $ex) { echo get_class($ex) . ' - ' . $ex->getMessage(); } ?> ---EXPECT-- -ValueError - autoload_set_classmap(): Argument #1 ($mapping) Classmap entry for "Foo" expected string value, found int. \ No newline at end of file +--EXPECT-- +Error - Error during autoloading from classmap. Entry "Foo" expected a string value, "int" given. diff --git a/ext/spl/tests/autoload_set_classmap_003.phpt b/ext/spl/tests/autoload_set_classmap_003.phpt index 7866f36693bd5..96ae3685ffa49 100644 --- a/ext/spl/tests/autoload_set_classmap_003.phpt +++ b/ext/spl/tests/autoload_set_classmap_003.phpt @@ -17,4 +17,4 @@ autoload_set_classmap Throw error if mapped file did not contain class ?> --EXPECTF-- -Error - Classmap entry "Foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). +Error - Error during autoloading from classmap. Entry "Foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). diff --git a/ext/spl/tests/autoload_set_classmap_004.phpt b/ext/spl/tests/autoload_set_classmap_004.phpt index 781eaa2785364..928b97f1b7eac 100644 --- a/ext/spl/tests/autoload_set_classmap_004.phpt +++ b/ext/spl/tests/autoload_set_classmap_004.phpt @@ -12,5 +12,5 @@ autoload_set_classmap Map can only be initialized once } ?> ---EXPECT-- -Error - The autoload classmap can only be initialized once per request. \ No newline at end of file +--EXPECT-- +Error - Classmap Autoloader can only be initialized once per request. From 443ce839748611b978fb7a47a0329156aae1445e Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Mon, 15 Mar 2021 16:39:07 +0000 Subject: [PATCH 3/6] Recommended array ref changes + Lowercase lookup --- ext/spl/php_spl.c | 23 +++++++-------- ext/spl/tests/autoload_set_classmap_001.phpt | 30 +++++++++++--------- ext/spl/tests/autoload_set_classmap_002.phpt | 18 ++++++------ ext/spl/tests/autoload_set_classmap_003.phpt | 20 ++++++------- ext/spl/tests/autoload_set_classmap_004.phpt | 18 ++++++------ 5 files changed, 55 insertions(+), 54 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 2000c66bc409e..3e0c53e57127b 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -415,10 +415,10 @@ static bool autoload_func_info_equals( static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { /* classmap provides for classname => path resolution without needing to invoke a user function */ if (SPL_G(autoload_classmap)) { - zval * mf = zend_hash_str_find(SPL_G(autoload_classmap), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); + zval * mf = zend_hash_find(SPL_G(autoload_classmap), lc_name); if (mf) { if (Z_TYPE_P(mf) != IS_STRING) { - zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" expected a string value, \"%s\" given.", ZSTR_VAL(class_name), zend_zval_type_name(mf)); + zend_type_error("Error during autoloading from classmap. Entry \"%s\" expected a string value, \"%s\" given.", ZSTR_VAL(lc_name), zend_zval_type_name(mf)); return NULL; } @@ -432,13 +432,13 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri return NULL; } - zend_class_entry * ce = zend_hash_find_ptr(EG(class_table), lc_name); + zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); if (ce) { return ce; } else { /* if the item is in the classmap it must be valid after including the file */ - zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(class_name), Z_STRVAL_P(mf)); + zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(lc_name), Z_STRVAL_P(mf)); return NULL; } } @@ -660,7 +660,7 @@ PHP_FUNCTION(spl_autoload_functions) /* {{{ Assign an array of name => path mappings to the autoloader */ PHP_FUNCTION(autoload_set_classmap) { - HashTable * ht = NULL; + HashTable *ht = NULL; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(ht) @@ -671,10 +671,9 @@ PHP_FUNCTION(autoload_set_classmap) RETURN_THROWS(); } - ALLOC_HASHTABLE(SPL_G(autoload_classmap)); - zend_hash_init(SPL_G(autoload_classmap), zend_hash_num_elements(ht), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(SPL_G(autoload_classmap), ht, (copy_ctor_func_t)zval_add_ref); - + GC_TRY_ADDREF(ht); + SPL_G(autoload_classmap) = ht; + RETURN_TRUE; } /* }}} */ @@ -684,7 +683,8 @@ PHP_FUNCTION(autoload_get_classmap) ZEND_PARSE_PARAMETERS_NONE(); if (SPL_G(autoload_classmap)) { - RETURN_ARR(zend_array_dup(SPL_G(autoload_classmap))); + GC_TRY_ADDREF(SPL_G(autoload_classmap)); + RETURN_ARR(SPL_G(autoload_classmap)); } RETURN_EMPTY_ARRAY(); @@ -809,8 +809,7 @@ PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */ SPL_G(autoload_extensions) = NULL; } if (SPL_G(autoload_classmap)) { - zend_hash_destroy(SPL_G(autoload_classmap)); - FREE_HASHTABLE(SPL_G(autoload_classmap)); + zend_array_release(SPL_G(autoload_classmap)); SPL_G(autoload_classmap) = NULL; } if (SPL_G(autoload_functions)) { diff --git a/ext/spl/tests/autoload_set_classmap_001.phpt b/ext/spl/tests/autoload_set_classmap_001.phpt index b407430851054..9d01c2fd672b6 100644 --- a/ext/spl/tests/autoload_set_classmap_001.phpt +++ b/ext/spl/tests/autoload_set_classmap_001.phpt @@ -3,25 +3,27 @@ autoload_set_classmap Basic Operation --FILE-- --EXPECT-- diff --git a/ext/spl/tests/autoload_set_classmap_002.phpt b/ext/spl/tests/autoload_set_classmap_002.phpt index be9721be9e2ad..f0e16634cb6d8 100644 --- a/ext/spl/tests/autoload_set_classmap_002.phpt +++ b/ext/spl/tests/autoload_set_classmap_002.phpt @@ -3,14 +3,14 @@ autoload_set_classmap Reject non-string values --FILE-- 123]); - new Foo(); - } - catch (\Throwable $ex) { - echo get_class($ex) . ' - ' . $ex->getMessage(); - } +try { + autoload_set_classmap(['foo' => 123]); + new Foo(); +} +catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); +} ?> ---EXPECT-- -Error - Error during autoloading from classmap. Entry "Foo" expected a string value, "int" given. +--EXPECT-- +TypeError - Error during autoloading from classmap. Entry "foo" expected a string value, "int" given. diff --git a/ext/spl/tests/autoload_set_classmap_003.phpt b/ext/spl/tests/autoload_set_classmap_003.phpt index 96ae3685ffa49..17218171b6485 100644 --- a/ext/spl/tests/autoload_set_classmap_003.phpt +++ b/ext/spl/tests/autoload_set_classmap_003.phpt @@ -3,18 +3,18 @@ autoload_set_classmap Throw error if mapped file did not contain class --FILE-- $tmp_file]); +autoload_set_classmap(['foo' => $tmp_file]); - try { - new Foo(); - } - catch (\Throwable $ex) { - echo get_class($ex) . ' - ' . $ex->getMessage(); - } +try { + new Foo(); +} +catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); +} ?> --EXPECTF-- -Error - Error during autoloading from classmap. Entry "Foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). +Error - Error during autoloading from classmap. Entry "foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). diff --git a/ext/spl/tests/autoload_set_classmap_004.phpt b/ext/spl/tests/autoload_set_classmap_004.phpt index 928b97f1b7eac..dce73f50aef2d 100644 --- a/ext/spl/tests/autoload_set_classmap_004.phpt +++ b/ext/spl/tests/autoload_set_classmap_004.phpt @@ -3,14 +3,14 @@ autoload_set_classmap Map can only be initialized once --FILE-- '/Foo']); - autoload_set_classmap(['Foo' => '/Foo']); - } - catch (\Throwable $ex) { - echo get_class($ex) . ' - ' . $ex->getMessage(); - } +try { + autoload_set_classmap(['foo' => '/Foo']); + autoload_set_classmap(['foo' => '/Foo']); +} +catch (\Throwable $ex) { + echo get_class($ex) . ' - ' . $ex->getMessage(); +} ?> ---EXPECT-- -Error - Classmap Autoloader can only be initialized once per request. +--EXPECT-- +Error - Classmap Autoloader can only be initialized once per request. From fbb4b299b0bd4c8ee7df4cd1b60ed1a255cd8f8f Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Mon, 15 Mar 2021 20:40:06 +0000 Subject: [PATCH 4/6] get takesno args, set returns void, misc cleanup --- ext/spl/php_spl.c | 17 +++++++---------- ext/spl/php_spl.stub.php | 4 ++-- ext/spl/php_spl_arginfo.h | 8 +++----- ext/spl/tests/autoload_set_classmap_001.phpt | 15 ++++++++++++--- ext/spl/tests/autoload_set_classmap_002.phpt | 2 +- ext/spl/tests/autoload_set_classmap_003.phpt | 2 +- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 3e0c53e57127b..6d44b661a60ff 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -415,10 +415,10 @@ static bool autoload_func_info_equals( static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { /* classmap provides for classname => path resolution without needing to invoke a user function */ if (SPL_G(autoload_classmap)) { - zval * mf = zend_hash_find(SPL_G(autoload_classmap), lc_name); + zval *mf = zend_hash_find_deref(SPL_G(autoload_classmap), lc_name); if (mf) { if (Z_TYPE_P(mf) != IS_STRING) { - zend_type_error("Error during autoloading from classmap. Entry \"%s\" expected a string value, \"%s\" given.", ZSTR_VAL(lc_name), zend_zval_type_name(mf)); + zend_type_error("Error during autoloading from classmap. Entry \"%s\" expected a string value, %s given", ZSTR_VAL(lc_name), zend_zval_type_name(mf)); return NULL; } @@ -436,11 +436,10 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri if (ce) { return ce; } - else { - /* if the item is in the classmap it must be valid after including the file */ - zend_throw_exception_ex(zend_ce_error, 0, "Error during autoloading from classmap. Entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included).", ZSTR_VAL(lc_name), Z_STRVAL_P(mf)); - return NULL; - } + + /* if the item is in the classmap it must be valid after including the file */ + zend_throw_error(NULL, "Error during autoloading from classmap. Entry \"%s\" failed to load the class from \"%s\" (Class undefined after file included)", ZSTR_VAL(lc_name), Z_STRVAL_P(mf)); + return NULL; } } @@ -660,7 +659,7 @@ PHP_FUNCTION(spl_autoload_functions) /* {{{ Assign an array of name => path mappings to the autoloader */ PHP_FUNCTION(autoload_set_classmap) { - HashTable *ht = NULL; + HashTable *ht; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(ht) @@ -673,8 +672,6 @@ PHP_FUNCTION(autoload_set_classmap) GC_TRY_ADDREF(ht); SPL_G(autoload_classmap) = ht; - - RETURN_TRUE; } /* }}} */ /* {{{ Assign an array of name => path mappings to the autoloader */ diff --git a/ext/spl/php_spl.stub.php b/ext/spl/php_spl.stub.php index 6f3501496531a..81b9d33d24710 100644 --- a/ext/spl/php_spl.stub.php +++ b/ext/spl/php_spl.stub.php @@ -35,6 +35,6 @@ function iterator_count(Traversable $iterator): int {} function iterator_to_array(Traversable $iterator, bool $preserve_keys = true): array {} -function autoload_set_classmap(array $mapping): bool {} +function autoload_set_classmap(array $mapping): void {} -function autoload_get_classmap(array $mapping): array {} \ No newline at end of file +function autoload_get_classmap(): array {} \ No newline at end of file diff --git a/ext/spl/php_spl_arginfo.h b/ext/spl/php_spl_arginfo.h index a9191dea84535..d4400de0a4934 100644 --- a/ext/spl/php_spl_arginfo.h +++ b/ext/spl/php_spl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 70ba950885121dee91431621406a8188a5a40a54 */ + * Stub hash: dc37ffc6559b505fa389a6b0445d00a210e8eddf */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_implements, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_INFO(0, object_or_class) @@ -61,13 +61,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_iterator_to_array, 0, 1, IS_ARRA ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserve_keys, _IS_BOOL, 0, "true") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_autoload_set_classmap, 0, 1, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_autoload_set_classmap, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, mapping, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_autoload_get_classmap, 0, 1, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO(0, mapping, IS_ARRAY, 0) -ZEND_END_ARG_INFO() +#define arginfo_autoload_get_classmap arginfo_spl_autoload_functions ZEND_FUNCTION(class_implements); diff --git a/ext/spl/tests/autoload_set_classmap_001.phpt b/ext/spl/tests/autoload_set_classmap_001.phpt index 9d01c2fd672b6..c996f290ca4a2 100644 --- a/ext/spl/tests/autoload_set_classmap_001.phpt +++ b/ext/spl/tests/autoload_set_classmap_001.phpt @@ -6,6 +6,8 @@ autoload_set_classmap Basic Operation $classes = ['Example', 'Foo\NamespacedExample']; $classes_map = []; +@mkdir(sys_get_temp_dir() . '/foo'); + foreach ($classes as $class) { $p = explode("\\", $class); $class_name = array_pop($p); @@ -14,7 +16,7 @@ foreach ($classes as $class) { $class = strtolower($class); $class_str = ' ---EXPECT-- +--EXPECTF-- Example -Foo\NamespacedExample \ No newline at end of file +Foo\NamespacedExample +Array +( + [example] => /tmp/example.php + [foo\namespacedexample] => %s/namespacedexample.php +) \ No newline at end of file diff --git a/ext/spl/tests/autoload_set_classmap_002.phpt b/ext/spl/tests/autoload_set_classmap_002.phpt index f0e16634cb6d8..6450968f63a2e 100644 --- a/ext/spl/tests/autoload_set_classmap_002.phpt +++ b/ext/spl/tests/autoload_set_classmap_002.phpt @@ -13,4 +13,4 @@ catch (\Throwable $ex) { ?> --EXPECT-- -TypeError - Error during autoloading from classmap. Entry "foo" expected a string value, "int" given. +TypeError - Error during autoloading from classmap. Entry "foo" expected a string value, int given diff --git a/ext/spl/tests/autoload_set_classmap_003.phpt b/ext/spl/tests/autoload_set_classmap_003.phpt index 17218171b6485..1dc7bfd3cdf9d 100644 --- a/ext/spl/tests/autoload_set_classmap_003.phpt +++ b/ext/spl/tests/autoload_set_classmap_003.phpt @@ -17,4 +17,4 @@ catch (\Throwable $ex) { ?> --EXPECTF-- -Error - Error during autoloading from classmap. Entry "foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included). +Error - Error during autoloading from classmap. Entry "foo" failed to load the class from "%s/ThisFileIsEmpty.php" (Class undefined after file included) From 88ed1dd8178e7561217fa042db21d83a32010549 Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Tue, 6 Apr 2021 06:35:10 +0100 Subject: [PATCH 5/6] Include changed to REQUIRE rather than REQUIRE_ONCE --- ext/spl/php_spl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 6d44b661a60ff..ca2081cef735d 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -422,7 +422,7 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri return NULL; } - zend_op_array *op_array = zend_include_or_eval(mf, ZEND_REQUIRE_ONCE); + zend_op_array *op_array = zend_include_or_eval(mf, ZEND_REQUIRE); if (op_array != NULL && op_array != ZEND_FAKE_OP_ARRAY) { destroy_op_array(op_array); efree_size(op_array, sizeof(zend_op_array)); From 8fe9636ef3320162c9282f80b05e122cf544b41e Mon Sep 17 00:00:00 2001 From: Mark Randall Date: Tue, 6 Apr 2021 07:41:12 +0100 Subject: [PATCH 6/6] File path selectors in test case --- ext/spl/tests/autoload_set_classmap_001.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/spl/tests/autoload_set_classmap_001.phpt b/ext/spl/tests/autoload_set_classmap_001.phpt index c996f290ca4a2..a94816d333a96 100644 --- a/ext/spl/tests/autoload_set_classmap_001.phpt +++ b/ext/spl/tests/autoload_set_classmap_001.phpt @@ -35,6 +35,6 @@ Example Foo\NamespacedExample Array ( - [example] => /tmp/example.php + [example] => %s/example.php [foo\namespacedexample] => %s/namespacedexample.php ) \ No newline at end of file