Skip to content

Commit 2dddab0

Browse files
committed
Avoid "Anonymous class wasn't preloaded" error by lazely loading of not preloaded part of a preloaded script
1 parent ddc3f3d commit 2dddab0

File tree

8 files changed

+73
-12
lines changed

8 files changed

+73
-12
lines changed

Zend/zend.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ ZEND_API char *(*zend_getenv)(char *name, size_t name_len);
8181
ZEND_API zend_string *(*zend_resolve_path)(const char *filename, size_t filename_len);
8282
ZEND_API int (*zend_post_startup_cb)(void) = NULL;
8383
ZEND_API void (*zend_post_shutdown_cb)(void) = NULL;
84+
ZEND_API int (*zend_preload_autoload)(zend_string *filename) = NULL;
8485

8586
void (*zend_on_timeout)(int seconds);
8687

Zend/zend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ extern ZEND_API zend_string *(*zend_resolve_path)(const char *filename, size_t f
292292
extern ZEND_API int (*zend_post_startup_cb)(void);
293293
extern ZEND_API void (*zend_post_shutdown_cb)(void);
294294

295+
/* Callback for loading of not preloaded part of the script */
296+
extern ZEND_API int (*zend_preload_autoload)(zend_string *filename);
297+
295298
ZEND_API ZEND_COLD void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
296299
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
297300
/* If filename is NULL the default filename is used. */

Zend/zend_compile.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,11 +1072,21 @@ ZEND_API int do_bind_class(zval *lcname, zend_string *lc_parent_name) /* {{{ */
10721072
ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(lcname));
10731073
if (ce) {
10741074
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name));
1075+
return FAILURE;
10751076
} else {
1076-
ZEND_ASSERT(EG(current_execute_data)->func->op_array.fn_flags & ZEND_ACC_PRELOADED);
1077-
zend_error_noreturn(E_ERROR, "Class %s wasn't preloaded", Z_STRVAL_P(lcname));
1077+
do {
1078+
if (zend_preload_autoload
1079+
&& zend_preload_autoload(EG(current_execute_data)->func->op_array.filename) == SUCCESS) {
1080+
zv = zend_hash_find_ex(EG(class_table), Z_STR_P(rtd_key), 1);
1081+
if (EXPECTED(zv != NULL)) {
1082+
break;
1083+
}
1084+
}
1085+
ZEND_ASSERT(EG(current_execute_data)->func->op_array.fn_flags & ZEND_ACC_PRELOADED);
1086+
zend_error_noreturn(E_ERROR, "Class %s wasn't preloaded", Z_STRVAL_P(lcname));
1087+
return FAILURE;
1088+
} while (0);
10781089
}
1079-
return FAILURE;
10801090
}
10811091

10821092
/* Register the derived class */

Zend/zend_vm_def.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7317,8 +7317,17 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT)
73177317
zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
73187318
if (UNEXPECTED(zv == NULL)) {
73197319
SAVE_OPLINE();
7320-
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_PRELOADED);
7321-
zend_error_noreturn(E_ERROR, "Anonymous class wasn't preloaded");
7320+
do {
7321+
if (zend_preload_autoload
7322+
&& zend_preload_autoload(EX(func)->op_array.filename) == SUCCESS) {
7323+
zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
7324+
if (EXPECTED(zv != NULL)) {
7325+
break;
7326+
}
7327+
}
7328+
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_PRELOADED);
7329+
zend_error_noreturn(E_ERROR, "Anonymous class wasn't preloaded");
7330+
} while (0);
73227331
}
73237332
ZEND_ASSERT(zv != NULL);
73247333
ce = Z_CE_P(zv);

Zend/zend_vm_execute.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,8 +2449,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE
24492449
zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
24502450
if (UNEXPECTED(zv == NULL)) {
24512451
SAVE_OPLINE();
2452-
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_PRELOADED);
2453-
zend_error_noreturn(E_ERROR, "Anonymous class wasn't preloaded");
2452+
do {
2453+
if (zend_preload_autoload
2454+
&& zend_preload_autoload(EX(func)->op_array.filename) == SUCCESS) {
2455+
zv = zend_hash_find_ex(EG(class_table), rtd_key, 1);
2456+
if (EXPECTED(zv != NULL)) {
2457+
break;
2458+
}
2459+
}
2460+
ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_PRELOADED);
2461+
zend_error_noreturn(E_ERROR, "Anonymous class wasn't preloaded");
2462+
} while (0);
24542463
}
24552464
ZEND_ASSERT(zv != NULL);
24562465
ce = Z_CE_P(zv);

ext/opcache/ZendAccelerator.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,34 @@ static void preload_load(void)
42424242
}
42434243
}
42444244

4245+
static int preload_autoload(zend_string *filename)
4246+
{
4247+
zend_persistent_script *persistent_script;
4248+
zend_op_array *op_array;
4249+
4250+
if (zend_hash_exists(&EG(included_files), filename)) {
4251+
return FAILURE;
4252+
}
4253+
4254+
persistent_script = zend_accel_hash_find(&ZCSG(hash), filename);
4255+
if (!persistent_script) {
4256+
return FAILURE;
4257+
}
4258+
4259+
op_array = zend_accel_load_script(persistent_script, 1);
4260+
if (!op_array) {
4261+
return FAILURE;
4262+
}
4263+
4264+
// TODO: we may need to execute this in some special context ???
4265+
zend_execute(op_array, NULL);
4266+
4267+
destroy_op_array(op_array);
4268+
efree_size(op_array, sizeof(zend_op_array));
4269+
4270+
return SUCCESS;
4271+
}
4272+
42454273
static int accel_preload(const char *config)
42464274
{
42474275
zend_file_handle file_handle;
@@ -4534,6 +4562,8 @@ static int accel_preload(const char *config)
45344562
HANDLE_UNBLOCK_INTERRUPTIONS();
45354563

45364564
zend_shared_alloc_destroy_xlat_table();
4565+
4566+
zend_preload_autoload = preload_autoload;
45374567
} else {
45384568
CG(map_ptr_last) = orig_map_ptr_last;
45394569
}

ext/opcache/tests/bug78937_1.phpt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,5 @@ var_dump(foo());
2020
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6
2121

2222
Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
23-
24-
Fatal error: Anonymous class wasn't preloaded in %spreload_bug78937.inc on line 3
25-
23+
object(class@anonymous)#%d (0) {
24+
}

ext/opcache/tests/bug78937_4.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ var_dump(new Foo);
2121
Warning: Can't preload unlinked class Foo: Unknown parent Bar in %spreload_bug78937.inc on line 6
2222

2323
Warning: Can't preload unlinked class class@anonymous: Unknown parent Bar in %spreload_bug78937.inc on line 3
24-
25-
Fatal error: Class foo wasn't preloaded in %spreload_bug78937.inc on line 6
24+
object(Foo)#%d (0) {
25+
}

0 commit comments

Comments
 (0)