Cleanup SPL autoload implementation

Replace EG(autoload_func) with a C level zend_autoload hook.
This avoids having to do one indirection through PHP function
calls. The need for EG(autoload_func) was a leftover from the
__autoload() implementation.

Additionally, drop special-casing of spl_autoload(), and instead
register it just like any other autoloading function. This fixes
bug #71236 as a side-effect.

Finally, change spl_autoload_functions() to always return an array.
The distinction between false and an empty array no longer makes
sense here.

Closes GH-5696.
This commit is contained in:
Nikita Popov 2020-06-09 17:36:36 +02:00
parent 68dd6cc92b
commit 5b59d4915c
15 changed files with 169 additions and 223 deletions

4
NEWS
View file

@ -160,6 +160,10 @@ PHP NEWS
- SQLite3:
. Added SQLite3::setAuthorizer() and respective class constants. (bohwaz)
- SPL:
. Fixed bug #71236 (Second call of spl_autoload_register() does nothing if it
has no arguments). (Nikita)
- Standard:
. Implemented FR #78638 (__PHP_Incomplete_Class should be final). (Laruence)
. Fixed bug #77204 (getimagesize(): Read error! should mention file path).

View file

@ -29,9 +29,7 @@ string(3) "Bar"
Fatal error: Uncaught Exception: Bar in %s:%d
Stack trace:
#0 [internal function]: {closure}('Bar')
#1 %s(%d): spl_autoload_call('Bar')
#2 [internal function]: {closure}('Foo')
#3 %s(%d): spl_autoload_call('Foo')
#4 {main}
#0 %s(%d): {closure}('Bar')
#1 %s(%d): {closure}('Foo')
#2 {main}
thrown in %s on line %d

View file

@ -19,8 +19,7 @@ foreach (testGenerator() as $i);
--EXPECTF--
Fatal error: Uncaught Error: Call to undefined function foo() in %s:%d
Stack trace:
#0 [internal function]: autoload('SyntaxError')
#1 %s(%d): spl_autoload_call('SyntaxError')
#2 %s(%d): testGenerator()
#3 {main}
#0 %s(%d): autoload('SyntaxError')
#1 %s(%d): testGenerator()
#2 {main}
thrown in %s on line %d

View file

@ -44,6 +44,5 @@ Class A does not exist
Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class A does not exist in %s:%d
Stack trace:
#0 [internal function]: {closure}('A')
#1 %s(%d): spl_autoload_call('A')
#2 {main} in %s on line %d
#0 %s(%d): {closure}('A')
#1 {main} in %s on line %d

View file

@ -46,6 +46,5 @@ Class I does not exist
Fatal error: During inheritance of B with variance dependencies: Uncaught Exception: Class I does not exist in %s:%d
Stack trace:
#0 [internal function]: {closure}('I')
#1 %s(%d): spl_autoload_call('I')
#2 {main} in %s on line %d
#0 %s(%d): {closure}('I')
#1 {main} in %s on line %d

View file

@ -15,7 +15,6 @@ class A implements I {
Fatal error: Uncaught ReflectionException: Class A does not exist in %s:%d
Stack trace:
#0 %s(%d): ReflectionClass->__construct('A')
#1 [internal function]: {closure}('I')
#2 %s(%d): spl_autoload_call('I')
#3 {main}
#1 %s(%d): {closure}('I')
#2 {main}
thrown in %s on line %d

View file

@ -31,6 +31,9 @@ struct _zend_fcall_info;
ZEND_API extern void (*zend_execute_ex)(zend_execute_data *execute_data);
ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
/* The lc_name may be stack allocated! */
ZEND_API extern zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name);
void init_executor(void);
void shutdown_executor(void);
void shutdown_destructors(void);

View file

@ -44,6 +44,7 @@
ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
ZEND_API zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name);
/* true globals */
ZEND_API const zend_fcall_info empty_fcall_info = {0};
@ -140,7 +141,6 @@ void init_executor(void) /* {{{ */
EG(class_table) = CG(class_table);
EG(in_autoload) = NULL;
EG(autoload_func) = NULL;
EG(error_handling) = EH_NORMAL;
EG(flags) = EG_FLAGS_INITIAL;
@ -913,11 +913,9 @@ ZEND_API void zend_call_known_instance_method_with_2_params(
ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *key, uint32_t flags) /* {{{ */
{
zend_class_entry *ce = NULL;
zval args[1], *zv;
zval local_retval;
zval *zv;
zend_string *lc_name;
zend_fcall_info fcall_info;
zend_fcall_info_cache fcall_cache;
zend_string *autoload_name;
if (key) {
lc_name = key;
@ -952,9 +950,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
return ce;
}
/* The compiler is not-reentrant. Make sure we __autoload() only during run-time
* (doesn't impact functionality of __autoload()
*/
/* The compiler is not-reentrant. Make sure we autoload only during run-time. */
if ((flags & ZEND_FETCH_CLASS_NO_AUTOLOAD) || zend_is_compiling()) {
if (!key) {
zend_string_release_ex(lc_name, 0);
@ -962,15 +958,14 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
return NULL;
}
if (!EG(autoload_func)) {
if (!zend_autoload) {
if (!key) {
zend_string_release_ex(lc_name, 0);
}
return NULL;
}
/* Verify class name before passing it to __autoload() */
/* Verify class name before passing it to the autoloader. */
if (!key && strspn(ZSTR_VAL(name), "0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\\") != ZSTR_LEN(name)) {
zend_string_release_ex(lc_name, 0);
return NULL;
@ -988,39 +983,19 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
return NULL;
}
ZVAL_UNDEF(&local_retval);
if (ZSTR_VAL(name)[0] == '\\') {
ZVAL_STRINGL(&args[0], ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1);
autoload_name = zend_string_init(ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1, 0);
} else {
ZVAL_STR_COPY(&args[0], name);
autoload_name = zend_string_copy(name);
}
fcall_info.size = sizeof(fcall_info);
ZVAL_STR_COPY(&fcall_info.function_name, EG(autoload_func)->common.function_name);
fcall_info.retval = &local_retval;
fcall_info.param_count = 1;
fcall_info.params = args;
fcall_info.object = NULL;
fcall_info.no_separation = 1;
fcall_cache.function_handler = EG(autoload_func);
fcall_cache.called_scope = NULL;
fcall_cache.object = NULL;
zend_exception_save();
if ((zend_call_function(&fcall_info, &fcall_cache) == SUCCESS) && !EG(exception)) {
ce = zend_hash_find_ptr(EG(class_table), lc_name);
}
ce = zend_autoload(autoload_name, lc_name);
zend_exception_restore();
zval_ptr_dtor(&args[0]);
zval_ptr_dtor_str(&fcall_info.function_name);
zend_string_release_ex(autoload_name, 0);
zend_hash_del(EG(in_autoload), lc_name);
zval_ptr_dtor(&local_retval);
if (!key) {
zend_string_release_ex(lc_name, 0);
}

View file

@ -176,7 +176,6 @@ struct _zend_executor_globals {
uint32_t persistent_classes_count;
HashTable *in_autoload;
zend_function *autoload_func;
zend_bool full_tables_cleanup;
/* for extended information support */

View file

@ -47,9 +47,6 @@ ZEND_DECLARE_MODULE_GLOBALS(spl)
#define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php"
static zend_function *spl_autoload_fn = NULL;
static zend_function *spl_autoload_call_fn = NULL;
/* {{{ PHP_GINIT_FUNCTION
*/
static PHP_GINIT_FUNCTION(spl)
@ -394,43 +391,37 @@ static void autoload_func_info_dtor(zval *element)
efree(alfi);
}
/* {{{ proto void spl_autoload_call(string class_name)
Try all registered autoload function to load the requested class */
PHP_FUNCTION(spl_autoload_call)
{
zend_string *class_name, *lc_name, *func_name;
autoload_func_info *alfi;
zval retval, params[1];
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) {
RETURN_THROWS();
static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) {
if (!SPL_G(autoload_functions)) {
return NULL;
}
ZVAL_STR(&params[0], class_name);
if (SPL_G(autoload_functions)) {
HashPosition pos;
zend_ulong num_idx;
zend_function *func;
zend_fcall_info fci;
zend_fcall_info_cache fcic;
zend_class_entry *called_scope = zend_get_called_scope(execute_data);
zval params[1];
zval retval;
zend_string *func_name;
zend_class_entry *called_scope = zend_get_called_scope(EG(current_execute_data));
int l_autoload_running = SPL_G(autoload_running);
SPL_G(autoload_running) = 1;
lc_name = zend_string_tolower(class_name);
fci.size = sizeof(fci);
fci.retval = &retval;
fci.param_count = 1;
fci.params = params;
fci.no_separation = 1;
ZVAL_STR(&params[0], class_name);
ZVAL_UNDEF(&fci.function_name); /* Unused */
zend_hash_internal_pointer_reset_ex(SPL_G(autoload_functions), &pos);
while (zend_hash_get_current_key_ex(SPL_G(autoload_functions), &func_name, &num_idx, &pos) == HASH_KEY_IS_STRING) {
alfi = zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
autoload_func_info *alfi =
zend_hash_get_current_data_ptr_ex(SPL_G(autoload_functions), &pos);
func = alfi->func_ptr;
if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
func = emalloc(sizeof(zend_op_array));
@ -462,36 +453,31 @@ PHP_FUNCTION(spl_autoload_call)
break;
}
if (pos + 1 == SPL_G(autoload_functions)->nNumUsed ||
zend_hash_exists(EG(class_table), lc_name)) {
break;
zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name);
if (ce) {
SPL_G(autoload_running) = l_autoload_running;
return ce;
}
zend_hash_move_forward_ex(SPL_G(autoload_functions), &pos);
}
zend_string_release_ex(lc_name, 0);
SPL_G(autoload_running) = l_autoload_running;
} else {
/* do not use or overwrite &EG(autoload_func) here */
zend_fcall_info fcall_info;
zend_fcall_info_cache fcall_cache;
return NULL;
}
ZVAL_UNDEF(&retval);
/* {{{ proto void spl_autoload_call(string class_name)
Try all registered autoload function to load the requested class */
PHP_FUNCTION(spl_autoload_call)
{
zend_string *class_name;
fcall_info.size = sizeof(fcall_info);
ZVAL_UNDEF(&fcall_info.function_name);
fcall_info.retval = &retval;
fcall_info.param_count = 1;
fcall_info.params = params;
fcall_info.object = NULL;
fcall_info.no_separation = 1;
fcall_cache.function_handler = spl_autoload_fn;
fcall_cache.called_scope = NULL;
fcall_cache.object = NULL;
zend_call_function(&fcall_info, &fcall_cache);
zval_ptr_dtor(&retval);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &class_name) == FAILURE) {
RETURN_THROWS();
}
zend_string *lc_name = zend_string_tolower(class_name);
spl_perform_autoload(class_name, lc_name);
zend_string_release(lc_name);
} /* }}} */
#define HT_MOVE_TAIL_TO_HEAD(ht) \
@ -511,7 +497,6 @@ PHP_FUNCTION(spl_autoload_register)
zend_string *lc_name;
zend_bool do_throw = 1;
zend_bool prepend = 0;
zend_function *spl_func_ptr;
autoload_func_info alfi;
zend_object *obj_ptr;
zend_fcall_info fci = {0};
@ -529,6 +514,11 @@ PHP_FUNCTION(spl_autoload_register)
"spl_autoload_register() will always throw");
}
if (!SPL_G(autoload_functions)) {
ALLOC_HASHTABLE(SPL_G(autoload_functions));
zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
}
/* If first arg is not null */
if (ZEND_FCI_INITIALIZED(fci)) {
alfi.ce = fcc.calling_scope;
@ -561,7 +551,7 @@ PHP_FUNCTION(spl_autoload_register)
}
zend_string_release(func_name);
if (SPL_G(autoload_functions) && zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
if (zend_hash_exists(SPL_G(autoload_functions), lc_name)) {
if (!Z_ISUNDEF(alfi.closure)) {
Z_DELREF_P(&alfi.closure);
}
@ -579,28 +569,6 @@ PHP_FUNCTION(spl_autoload_register)
ZVAL_UNDEF(&alfi.obj);
}
if (!SPL_G(autoload_functions)) {
ALLOC_HASHTABLE(SPL_G(autoload_functions));
zend_hash_init(SPL_G(autoload_functions), 1, NULL, autoload_func_info_dtor, 0);
}
spl_func_ptr = spl_autoload_fn;
if (EG(autoload_func) == spl_func_ptr) { /* registered already, so we insert that first */
autoload_func_info spl_alfi;
spl_alfi.func_ptr = spl_func_ptr;
ZVAL_UNDEF(&spl_alfi.obj);
ZVAL_UNDEF(&spl_alfi.closure);
spl_alfi.ce = NULL;
zend_hash_add_mem(SPL_G(autoload_functions), spl_autoload_fn->common.function_name,
&spl_alfi, sizeof(autoload_func_info));
if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
/* Move the newly created element to the head of the hashtable */
HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
}
}
if (UNEXPECTED(alfi.func_ptr == &EG(trampoline))) {
zend_function *copy = emalloc(sizeof(zend_op_array));
@ -626,12 +594,19 @@ PHP_FUNCTION(spl_autoload_register)
}
skip:
zend_string_release_ex(lc_name, 0);
}
if (SPL_G(autoload_functions)) {
EG(autoload_func) = spl_autoload_call_fn;
} else {
EG(autoload_func) = spl_autoload_fn;
autoload_func_info spl_alfi;
spl_alfi.func_ptr = zend_hash_str_find_ptr(
CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
ZVAL_UNDEF(&spl_alfi.obj);
ZVAL_UNDEF(&spl_alfi.closure);
spl_alfi.ce = NULL;
zend_hash_add_mem(SPL_G(autoload_functions), spl_alfi.func_ptr->common.function_name,
&spl_alfi, sizeof(autoload_func_info));
if (prepend && SPL_G(autoload_functions)->nNumOfElements > 1) {
/* Move the newly created element to the head of the hashtable */
HT_MOVE_TAIL_TO_HEAD(SPL_G(autoload_functions));
}
}
RETURN_TRUE;
@ -646,7 +621,6 @@ PHP_FUNCTION(spl_autoload_unregister)
zend_string *lc_name;
zval *zcallable;
int success = FAILURE;
zend_function *spl_func_ptr;
zend_object *obj_ptr;
zend_fcall_info_cache fcc;
@ -686,13 +660,12 @@ PHP_FUNCTION(spl_autoload_unregister)
zend_string_release_ex(func_name, 0);
if (SPL_G(autoload_functions)) {
if (zend_string_equals(lc_name, spl_autoload_call_fn->common.function_name)) {
if (zend_string_equals_literal(lc_name, "spl_autoload_call")) {
/* remove all */
if (!SPL_G(autoload_running)) {
zend_hash_destroy(SPL_G(autoload_functions));
FREE_HASHTABLE(SPL_G(autoload_functions));
SPL_G(autoload_functions) = NULL;
EG(autoload_func) = NULL;
} else {
zend_hash_clean(SPL_G(autoload_functions));
}
@ -707,14 +680,6 @@ PHP_FUNCTION(spl_autoload_unregister)
success = zend_hash_del(SPL_G(autoload_functions), lc_name);
}
}
} else if (zend_string_equals(lc_name, spl_autoload_fn->common.function_name)) {
/* register single spl_autoload() */
spl_func_ptr = spl_autoload_fn;
if (EG(autoload_func) == spl_func_ptr) {
success = SUCCESS;
EG(autoload_func) = NULL;
}
}
zend_string_release_ex(lc_name, 0);
@ -725,21 +690,14 @@ PHP_FUNCTION(spl_autoload_unregister)
Return all registered autoloader functions */
PHP_FUNCTION(spl_autoload_functions)
{
zend_function *fptr;
autoload_func_info *alfi;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
if (!EG(autoload_func)) {
RETURN_FALSE;
}
fptr = spl_autoload_call_fn;
if (EG(autoload_func) == fptr) {
array_init(return_value);
if (SPL_G(autoload_functions)) {
ZEND_HASH_FOREACH_PTR(SPL_G(autoload_functions), alfi) {
if (!Z_ISUNDEF(alfi->closure)) {
Z_ADDREF(alfi->closure);
@ -760,11 +718,7 @@ PHP_FUNCTION(spl_autoload_functions)
add_next_index_str(return_value, zend_string_copy(alfi->func_ptr->common.function_name));
}
} ZEND_HASH_FOREACH_END();
return;
}
array_init(return_value);
add_next_index_str(return_value, zend_string_copy(EG(autoload_func)->common.function_name));
} /* }}} */
/* {{{ proto string spl_object_hash(object obj)
@ -859,6 +813,8 @@ PHP_MINFO_FUNCTION(spl)
*/
PHP_MINIT_FUNCTION(spl)
{
zend_autoload = spl_perform_autoload;
PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU);
@ -868,10 +824,6 @@ PHP_MINIT_FUNCTION(spl)
PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU);
PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU);
spl_autoload_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload", sizeof("spl_autoload") - 1);
spl_autoload_call_fn = zend_hash_str_find_ptr(CG(function_table), "spl_autoload_call", sizeof("spl_autoload_call") - 1);
ZEND_ASSERT(spl_autoload_fn != NULL && spl_autoload_call_fn != NULL);
return SUCCESS;
}
/* }}} */

View file

@ -10,12 +10,11 @@ function class_uses($what, bool $autoload = true): array|false {}
function spl_autoload(string $class_name, ?string $file_extensions = null): void {}
// This silently ignores non-string class names
function spl_autoload_call($class_name): void {}
function spl_autoload_call(string $class_name): void {}
function spl_autoload_extensions(?string $file_extensions = null): string {}
function spl_autoload_functions(): array|false {}
function spl_autoload_functions(): array {}
function spl_autoload_register(?callable $autoload_function = null, bool $throw = true, bool $prepend = false): bool {}

View file

@ -18,14 +18,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload, 0, 1, IS_VOID, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_call, 0, 1, IS_VOID, 0)
ZEND_ARG_INFO(0, class_name)
ZEND_ARG_TYPE_INFO(0, class_name, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_extensions, 0, 0, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, file_extensions, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_spl_autoload_functions, 0, 0, MAY_BE_ARRAY|MAY_BE_FALSE)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_functions, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_register, 0, 0, _IS_BOOL, 0)
@ -38,8 +38,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_autoload_unregister, 0, 1, _
ZEND_ARG_INFO(0, autoload_function)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_classes, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO()
#define arginfo_spl_classes arginfo_spl_autoload_functions
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_spl_object_hash, 0, 1, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, obj, IS_OBJECT, 0)

View file

@ -0,0 +1,23 @@
--TEST--
Bug #71236: Second call of spl_autoload_register() does nothing if it has no arguments
--FILE--
<?php
spl_autoload_register(function ($class) {});
spl_autoload_register();
var_dump(spl_autoload_functions());
?>
--EXPECT--
array(2) {
[0]=>
object(Closure)#1 (1) {
["parameter"]=>
array(1) {
["$class"]=>
string(10) "<required>"
}
}
[1]=>
string(12) "spl_autoload"
}

View file

@ -1,9 +1,5 @@
--TEST--
SPL: spl_autoload_functions()
--SKIPIF--
<?php
if (spl_autoload_functions() !== false) die('skip __autoload() registered by php.ini');
?>
--FILE--
<?php
@ -40,7 +36,8 @@ var_dump(spl_autoload_functions());
?>
--EXPECT--
bool(false)
array(0) {
}
array(1) {
[0]=>
string(12) "spl_autoload"
@ -59,9 +56,11 @@ array(2) {
[1]=>
string(16) "SplAutoloadTest2"
}
bool(false)
array(0) {
}
array(1) {
[0]=>
string(12) "spl_autoload"
}
bool(false)
array(0) {
}

View file

@ -47,7 +47,6 @@ autoload_first
Fatal error: Uncaught Exception: first in %sspl_autoload_012.php:%d
Stack trace:
#0 [internal function]: autoload_first('ThisClassDoesNo...')
#1 [internal function]: spl_autoload_call('ThisClassDoesNo...')
#2 %sspl_autoload_012.php(%d): class_exists('ThisClassDoesNo...')
#3 {main}
#1 %sspl_autoload_012.php(%d): class_exists('ThisClassDoesNo...')
#2 {main}
thrown in %s on line %d