Skip to content

Commit f3a7a8f

Browse files
committed
PoC modules
1 parent 2834f47 commit f3a7a8f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2195
-22
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ Makefile.objects
8181
# Directories for shared object files generated by `./configure`
8282
libs/
8383
modules/
84+
!Zend/tests/modules/
8485

8586
# Used by build/gen_stub.php
8687
build/PHP-Parser-*
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Modules: classes that are never declared are not checked
3+
--ENV--
4+
THIS_IS_DEFINED=1
5+
--FILE--
6+
<?php
7+
8+
require_modules([__DIR__.'/check_deps_001/module.ini']);
9+
10+
?>
11+
==DONE==
12+
--EXPECT--
13+
==DONE==
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
module Test;
4+
5+
// Early bound, and may not have a DECLARE opcode
6+
class A {}
7+
8+
interface I {}
9+
10+
if (getenv('THIS_IS_DEFINED')) {
11+
// Declared at runtime
12+
class B {
13+
function f() {
14+
// Anon class is usable
15+
return new class implements I {};
16+
}
17+
function g() {
18+
// Anon function is usable
19+
return function (): I {};
20+
}
21+
}
22+
}
23+
24+
// Early bound
25+
function f() {}
26+
27+
if (getenv('THIS_IS_DEFINED')) {
28+
// Declared at runtime
29+
function g() {}
30+
}
31+
32+
if (getenv('THIS_IS_NOT_DEFINED')) {
33+
// Never declared (should not generate an error)
34+
class C implements J {
35+
function f() {
36+
// Anon class is not usable (should not generate an error)
37+
return new class implements J {};
38+
}
39+
function g() {
40+
// Anon function is not usable (should not generate an error)
41+
return function (): J {};
42+
}
43+
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test
2+
files=*.inc
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Modules: classes with invalid dependencies are not allowed
3+
--FILE--
4+
<?php
5+
6+
require __DIR__ . '/helpers.inc';
7+
8+
try {
9+
require_modules([__DIR__.'/check_deps_002/module.ini']);
10+
} catch (Error $e) {
11+
printf("%s: %s\n", $e::class, $e->getMessage());
12+
}
13+
14+
?>
15+
==DONE==
16+
--EXPECTF--
17+
Error: Class Test\I not found while compiling module Test%s
18+
==DONE==
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
module Test;
4+
5+
class A implements I {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test
2+
files=*.inc
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Modules: classes with invalid dependencies are not allowed
3+
--ENV--
4+
THIS_IS_DEFINED=1
5+
--FILE--
6+
<?php
7+
8+
require __DIR__ . '/helpers.inc';
9+
10+
try {
11+
require_modules([__DIR__.'/check_deps_003/module.ini']);
12+
} catch (Error $e) {
13+
printf("%s: %s\n", $e::class, $e->getMessage());
14+
}
15+
16+
?>
17+
==DONE==
18+
--EXPECTF--
19+
Error: Class Test\I not found while compiling module Test%s
20+
==DONE==
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
module Test;
4+
5+
if (getenv('THIS_IS_DEFINED')) {
6+
class A implements I {}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test
2+
files=*.inc
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
Modules: classes with invalid dependencies are not allowed
3+
--ENV--
4+
THIS_IS_DEFINED=1
5+
--FILE--
6+
<?php
7+
8+
require __DIR__ . '/helpers.inc';
9+
10+
try {
11+
require_modules([__DIR__.'/check_deps_004/module.ini']);
12+
} catch (Error $e) {
13+
printf("%s: %s\n", $e::class, $e->getMessage());
14+
}
15+
16+
?>
17+
==DONE==
18+
--EXPECTF--
19+
Error: Class Test\I not found while compiling module Test%s
20+
==DONE==
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
module Test;
4+
5+
if (getenv('THIS_IS_DEFINED')) {
6+
class A {
7+
function f() {
8+
return new class implements I {};
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test
2+
files=*.inc

Zend/tests/modules/class_alias.phpt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Modules: class alias
3+
--FILE--
4+
<?php
5+
6+
spl_autoload_register(function ($class) {
7+
if (str_starts_with($class, 'Test\\M1')) {
8+
require_modules([__DIR__.'/class_alias/m1/module.ini']);
9+
}
10+
if (str_starts_with($class, 'Test\\M2')) {
11+
require_modules([__DIR__.'/class_alias/m2/module.ini']);
12+
}
13+
if (str_starts_with($class, 'Test\\M3')) {
14+
require_modules([__DIR__.'/class_alias/m3/module.ini']);
15+
}
16+
});
17+
18+
new \Test\M3\C();
19+
new ReflectionClass(\Test\M2\J::class);
20+
21+
?>
22+
==DONE==
23+
--EXPECT--
24+
==DONE==
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
module Test\M1;
3+
interface I {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test\M1
2+
files=*.inc
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
module Test\M2;
3+
use Test\M1\I;
4+
class_alias(I::class, J::class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test\M2
2+
files=*.inc
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
module Test\M3;
3+
use Test\M2\J;
4+
class C implements J {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Test\M3
2+
files=*.inc

Zend/tests/modules/helpers.inc

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
function create_module($file) {
4+
$contents = file_get_contents($file);
5+
preg_match_all('#^/\\* FILE: (.*) \\*/$#', $contents, $matches);
6+
var_dump($matches);
7+
}

Zend/tests/modules/module_decl.phpt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Module declaration
3+
--FILE--
4+
<?php
5+
6+
require_modules([__DIR__.'/module_decl/module.ini']);
7+
8+
?>
9+
--EXPECT--
10+
Foo
11+
Foo
12+
Foo\C
13+
Foo

Zend/tests/modules/module_decl/a.inc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
module Foo;
4+
5+
printf("%s\n", __MODULE__);
6+
printf("%s\n", __NAMESPACE__);
7+
8+
class C {}
9+
printf("%s\n", get_class(new C()));
10+
printf("%s\n", new \ReflectionClass(new C())->getModuleName());
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Foo
2+
files=*.inc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Module declaration with namespace
3+
--FILE--
4+
<?php
5+
6+
require_modules([__DIR__.'/module_decl_with_namespace/module.ini']);
7+
8+
?>
9+
--EXPECT--
10+
Foo
11+
Foo\Bar
12+
Foo\Bar\C
13+
Foo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
module Foo;
4+
namespace Bar;
5+
6+
printf("%s\n", __MODULE__);
7+
printf("%s\n", __NAMESPACE__);
8+
9+
class C {}
10+
printf("%s\n", get_class(new C()));
11+
printf("%s\n", new \ReflectionClass(new C())->getModuleName());
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module=Foo
2+
files=*.inc

Zend/zend.c

+17
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,20 @@ ZEND_API size_t compiler_globals_offset;
5555
ZEND_API size_t executor_globals_offset;
5656
static HashTable *global_function_table = NULL;
5757
static HashTable *global_class_table = NULL;
58+
static HashTable *global_module_table = NULL;
5859
static HashTable *global_constants_table = NULL;
5960
static HashTable *global_auto_globals_table = NULL;
6061
static HashTable *global_persistent_list = NULL;
6162
TSRMLS_MAIN_CACHE_DEFINE()
6263
# define GLOBAL_FUNCTION_TABLE global_function_table
6364
# define GLOBAL_CLASS_TABLE global_class_table
65+
# define GLOBAL_MODULE_TABLE global_module_table
6466
# define GLOBAL_CONSTANTS_TABLE global_constants_table
6567
# define GLOBAL_AUTO_GLOBALS_TABLE global_auto_globals_table
6668
#else
6769
# define GLOBAL_FUNCTION_TABLE CG(function_table)
6870
# define GLOBAL_CLASS_TABLE CG(class_table)
71+
# define GLOBAL_MODULE_TABLE CG(module_table)
6972
# define GLOBAL_AUTO_GLOBALS_TABLE CG(auto_globals)
7073
# define GLOBAL_CONSTANTS_TABLE EG(zend_constants)
7174
#endif
@@ -723,6 +726,9 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
723726
zend_hash_init(compiler_globals->class_table, 64, NULL, ZEND_CLASS_DTOR, 1);
724727
zend_hash_copy(compiler_globals->class_table, global_class_table, zend_class_add_ref);
725728

729+
compiler_globals->module_table = (HashTable *) malloc(sizeof(HashTable));
730+
zend_hash_init(compiler_globals->module_table, 64, NULL, NULL, 1);
731+
726732
zend_set_default_compile_time_values();
727733

728734
compiler_globals->auto_globals = (HashTable *) malloc(sizeof(HashTable));
@@ -752,6 +758,10 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
752758

753759
static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{{ */
754760
{
761+
if (compiler_globals->module_table != GLOBAL_MODULE_TABLE) {
762+
zend_hash_destroy(compiler_globals->module_table);
763+
free(compiler_globals->module_table);
764+
}
755765
if (compiler_globals->function_table != GLOBAL_FUNCTION_TABLE) {
756766
uint32_t n = compiler_globals->copied_functions_count;
757767

@@ -1005,11 +1015,13 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
10051015

10061016
GLOBAL_FUNCTION_TABLE = (HashTable *) malloc(sizeof(HashTable));
10071017
GLOBAL_CLASS_TABLE = (HashTable *) malloc(sizeof(HashTable));
1018+
GLOBAL_MODULE_TABLE = (HashTable *) malloc(sizeof(HashTable));
10081019
GLOBAL_AUTO_GLOBALS_TABLE = (HashTable *) malloc(sizeof(HashTable));
10091020
GLOBAL_CONSTANTS_TABLE = (HashTable *) malloc(sizeof(HashTable));
10101021

10111022
zend_hash_init(GLOBAL_FUNCTION_TABLE, 1024, NULL, ZEND_FUNCTION_DTOR, 1);
10121023
zend_hash_init(GLOBAL_CLASS_TABLE, 64, NULL, ZEND_CLASS_DTOR, 1);
1024+
zend_hash_init(GLOBAL_MODULE_TABLE, 64, NULL, NULL, 1);
10131025
zend_hash_init(GLOBAL_AUTO_GLOBALS_TABLE, 8, NULL, auto_global_dtor, 1);
10141026
zend_hash_init(GLOBAL_CONSTANTS_TABLE, 128, NULL, ZEND_CONSTANT_DTOR, 1);
10151027

@@ -1028,9 +1040,11 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
10281040
compiler_globals->in_compilation = 0;
10291041
compiler_globals->function_table = (HashTable *) malloc(sizeof(HashTable));
10301042
compiler_globals->class_table = (HashTable *) malloc(sizeof(HashTable));
1043+
compiler_globals->module_table = (HashTable *) malloc(sizeof(HashTable));
10311044

10321045
*compiler_globals->function_table = *GLOBAL_FUNCTION_TABLE;
10331046
*compiler_globals->class_table = *GLOBAL_CLASS_TABLE;
1047+
*compiler_globals->module_table = *GLOBAL_MODULE_TABLE;
10341048
compiler_globals->auto_globals = GLOBAL_AUTO_GLOBALS_TABLE;
10351049

10361050
zend_hash_destroy(executor_globals->zend_constants);
@@ -1110,6 +1124,7 @@ zend_result zend_post_startup(void) /* {{{ */
11101124
#ifdef ZTS
11111125
*GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table;
11121126
*GLOBAL_CLASS_TABLE = *compiler_globals->class_table;
1127+
*GLOBAL_MODULE_TABLE = *compiler_globals->module_table;
11131128
*GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants;
11141129
global_map_ptr_last = compiler_globals->map_ptr_last;
11151130

@@ -1121,6 +1136,8 @@ zend_result zend_post_startup(void) /* {{{ */
11211136
compiler_globals->function_table = NULL;
11221137
free(compiler_globals->class_table);
11231138
compiler_globals->class_table = NULL;
1139+
free(compiler_globals->module_table);
1140+
compiler_globals->module_table = NULL;
11241141
if (compiler_globals->map_ptr_real_base) {
11251142
free(compiler_globals->map_ptr_real_base);
11261143
}

Zend/zend.h

+1
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ struct _zend_class_entry {
225225

226226
union {
227227
struct {
228+
zend_string *module;
228229
zend_string *filename;
229230
uint32_t line_start;
230231
uint32_t line_end;

Zend/zend_API.c

+5
Original file line numberDiff line numberDiff line change
@@ -3581,6 +3581,11 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
35813581
ZVAL_ALIAS_PTR(&zv, ce);
35823582

35833583
ret = zend_hash_add(CG(class_table), lcname, &zv);
3584+
3585+
if (CG(active_module)) {
3586+
zend_hash_add(&CG(active_module)->class_table, lcname, &zv);
3587+
}
3588+
35843589
zend_string_release_ex(lcname, 0);
35853590
if (ret) {
35863591
// avoid notifying at MINIT time

Zend/zend_ast.h

+1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ enum _zend_ast_kind {
143143
ZEND_AST_USE_TRAIT,
144144
ZEND_AST_TRAIT_PRECEDENCE,
145145
ZEND_AST_METHOD_REFERENCE,
146+
ZEND_AST_MODULE,
146147
ZEND_AST_NAMESPACE,
147148
ZEND_AST_USE_ELEM,
148149
ZEND_AST_TRAIT_ALIAS,

0 commit comments

Comments
 (0)