Skip to content

PoC modules #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Makefile.objects
# Directories for shared object files generated by `./configure`
libs/
modules/
!Zend/tests/modules/

# Used by build/gen_stub.php
build/PHP-Parser-*
Expand Down
13 changes: 13 additions & 0 deletions Zend/tests/modules/check_deps_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Modules: classes that are never declared are not checked
--ENV--
THIS_IS_DEFINED=1
--FILE--
<?php

require_modules([__DIR__.'/check_deps_001/module.ini']);

?>
==DONE==
--EXPECT--
==DONE==
45 changes: 45 additions & 0 deletions Zend/tests/modules/check_deps_001/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

module Test;

// Early bound, and may not have a DECLARE opcode
class A {}

interface I {}

if (getenv('THIS_IS_DEFINED')) {
// Declared at runtime
class B {
function f() {
// Anon class is usable
return new class implements I {};
}
function g() {
// Anon function is usable
return function (): I {};
}
}
}

// Early bound
function f() {}

if (getenv('THIS_IS_DEFINED')) {
// Declared at runtime
function g() {}
}

if (getenv('THIS_IS_NOT_DEFINED')) {
// Never declared (should not generate an error)
class C implements J {
function f() {
// Anon class is not usable (should not generate an error)
return new class implements J {};
}
function g() {
// Anon function is not usable (should not generate an error)
return function (): J {};
}

}
}
2 changes: 2 additions & 0 deletions Zend/tests/modules/check_deps_001/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test
files=*.inc
18 changes: 18 additions & 0 deletions Zend/tests/modules/check_deps_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--TEST--
Modules: classes with invalid dependencies are not allowed
--FILE--
<?php

require __DIR__ . '/helpers.inc';

try {
require_modules([__DIR__.'/check_deps_002/module.ini']);
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}

?>
==DONE==
--EXPECTF--
Error: Class Test\I not found while compiling module Test%s
==DONE==
5 changes: 5 additions & 0 deletions Zend/tests/modules/check_deps_002/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

module Test;

class A implements I {}
2 changes: 2 additions & 0 deletions Zend/tests/modules/check_deps_002/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test
files=*.inc
20 changes: 20 additions & 0 deletions Zend/tests/modules/check_deps_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Modules: classes with invalid dependencies are not allowed
--ENV--
THIS_IS_DEFINED=1
--FILE--
<?php

require __DIR__ . '/helpers.inc';

try {
require_modules([__DIR__.'/check_deps_003/module.ini']);
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}

?>
==DONE==
--EXPECTF--
Error: Class Test\I not found while compiling module Test%s
==DONE==
7 changes: 7 additions & 0 deletions Zend/tests/modules/check_deps_003/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

module Test;

if (getenv('THIS_IS_DEFINED')) {
class A implements I {}
}
2 changes: 2 additions & 0 deletions Zend/tests/modules/check_deps_003/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test
files=*.inc
20 changes: 20 additions & 0 deletions Zend/tests/modules/check_deps_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Modules: classes with invalid dependencies are not allowed
--ENV--
THIS_IS_DEFINED=1
--FILE--
<?php

require __DIR__ . '/helpers.inc';

try {
require_modules([__DIR__.'/check_deps_004/module.ini']);
} catch (Error $e) {
printf("%s: %s\n", $e::class, $e->getMessage());
}

?>
==DONE==
--EXPECTF--
Error: Class Test\I not found while compiling module Test%s
==DONE==
11 changes: 11 additions & 0 deletions Zend/tests/modules/check_deps_004/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

module Test;

if (getenv('THIS_IS_DEFINED')) {
class A {
function f() {
return new class implements I {};
}
}
}
2 changes: 2 additions & 0 deletions Zend/tests/modules/check_deps_004/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test
files=*.inc
24 changes: 24 additions & 0 deletions Zend/tests/modules/class_alias.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Modules: class alias
--FILE--
<?php

spl_autoload_register(function ($class) {
if (str_starts_with($class, 'Test\\M1')) {
require_modules([__DIR__.'/class_alias/m1/module.ini']);
}
if (str_starts_with($class, 'Test\\M2')) {
require_modules([__DIR__.'/class_alias/m2/module.ini']);
}
if (str_starts_with($class, 'Test\\M3')) {
require_modules([__DIR__.'/class_alias/m3/module.ini']);
}
});

new \Test\M3\C();
new ReflectionClass(\Test\M2\J::class);

?>
==DONE==
--EXPECT--
==DONE==
3 changes: 3 additions & 0 deletions Zend/tests/modules/class_alias/m1/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?php
module Test\M1;
interface I {}
2 changes: 2 additions & 0 deletions Zend/tests/modules/class_alias/m1/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test\M1
files=*.inc
4 changes: 4 additions & 0 deletions Zend/tests/modules/class_alias/m2/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
module Test\M2;
use Test\M1\I;
class_alias(I::class, J::class);
2 changes: 2 additions & 0 deletions Zend/tests/modules/class_alias/m2/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test\M2
files=*.inc
4 changes: 4 additions & 0 deletions Zend/tests/modules/class_alias/m3/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?php
module Test\M3;
use Test\M2\J;
class C implements J {}
2 changes: 2 additions & 0 deletions Zend/tests/modules/class_alias/m3/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Test\M3
files=*.inc
7 changes: 7 additions & 0 deletions Zend/tests/modules/helpers.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

function create_module($file) {
$contents = file_get_contents($file);
preg_match_all('#^/\\* FILE: (.*) \\*/$#', $contents, $matches);
var_dump($matches);
}
13 changes: 13 additions & 0 deletions Zend/tests/modules/module_decl.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Module declaration
--FILE--
<?php

require_modules([__DIR__.'/module_decl/module.ini']);

?>
--EXPECT--
Foo
Foo
Foo\C
Foo
11 changes: 11 additions & 0 deletions Zend/tests/modules/module_decl/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

module Foo;

printf("%s\n", __MODULE__);
printf("%s\n", __NAMESPACE__);

class C {}
printf("%s\n", get_class(new C()));
printf("%s\n", new \ReflectionClass(new C())->getModuleName());

2 changes: 2 additions & 0 deletions Zend/tests/modules/module_decl/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Foo
files=*.inc
13 changes: 13 additions & 0 deletions Zend/tests/modules/module_decl_with_namespace.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
--TEST--
Module declaration with namespace
--FILE--
<?php

require_modules([__DIR__.'/module_decl_with_namespace/module.ini']);

?>
--EXPECT--
Foo
Foo\Bar
Foo\Bar\C
Foo
12 changes: 12 additions & 0 deletions Zend/tests/modules/module_decl_with_namespace/a.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

module Foo;
namespace Bar;

printf("%s\n", __MODULE__);
printf("%s\n", __NAMESPACE__);

class C {}
printf("%s\n", get_class(new C()));
printf("%s\n", new \ReflectionClass(new C())->getModuleName());

2 changes: 2 additions & 0 deletions Zend/tests/modules/module_decl_with_namespace/module.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module=Foo
files=*.inc
17 changes: 17 additions & 0 deletions Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,20 @@ ZEND_API size_t compiler_globals_offset;
ZEND_API size_t executor_globals_offset;
static HashTable *global_function_table = NULL;
static HashTable *global_class_table = NULL;
static HashTable *global_module_table = NULL;
static HashTable *global_constants_table = NULL;
static HashTable *global_auto_globals_table = NULL;
static HashTable *global_persistent_list = NULL;
TSRMLS_MAIN_CACHE_DEFINE()
# define GLOBAL_FUNCTION_TABLE global_function_table
# define GLOBAL_CLASS_TABLE global_class_table
# define GLOBAL_MODULE_TABLE global_module_table
# define GLOBAL_CONSTANTS_TABLE global_constants_table
# define GLOBAL_AUTO_GLOBALS_TABLE global_auto_globals_table
#else
# define GLOBAL_FUNCTION_TABLE CG(function_table)
# define GLOBAL_CLASS_TABLE CG(class_table)
# define GLOBAL_MODULE_TABLE CG(module_table)
# define GLOBAL_AUTO_GLOBALS_TABLE CG(auto_globals)
# define GLOBAL_CONSTANTS_TABLE EG(zend_constants)
#endif
Expand Down Expand Up @@ -723,6 +726,9 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{
zend_hash_init(compiler_globals->class_table, 64, NULL, ZEND_CLASS_DTOR, 1);
zend_hash_copy(compiler_globals->class_table, global_class_table, zend_class_add_ref);

compiler_globals->module_table = (HashTable *) malloc(sizeof(HashTable));
zend_hash_init(compiler_globals->module_table, 64, NULL, NULL, 1);

zend_set_default_compile_time_values();

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

static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{{ */
{
if (compiler_globals->module_table != GLOBAL_MODULE_TABLE) {
zend_hash_destroy(compiler_globals->module_table);
free(compiler_globals->module_table);
}
if (compiler_globals->function_table != GLOBAL_FUNCTION_TABLE) {
uint32_t n = compiler_globals->copied_functions_count;

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

GLOBAL_FUNCTION_TABLE = (HashTable *) malloc(sizeof(HashTable));
GLOBAL_CLASS_TABLE = (HashTable *) malloc(sizeof(HashTable));
GLOBAL_MODULE_TABLE = (HashTable *) malloc(sizeof(HashTable));
GLOBAL_AUTO_GLOBALS_TABLE = (HashTable *) malloc(sizeof(HashTable));
GLOBAL_CONSTANTS_TABLE = (HashTable *) malloc(sizeof(HashTable));

zend_hash_init(GLOBAL_FUNCTION_TABLE, 1024, NULL, ZEND_FUNCTION_DTOR, 1);
zend_hash_init(GLOBAL_CLASS_TABLE, 64, NULL, ZEND_CLASS_DTOR, 1);
zend_hash_init(GLOBAL_MODULE_TABLE, 64, NULL, NULL, 1);
zend_hash_init(GLOBAL_AUTO_GLOBALS_TABLE, 8, NULL, auto_global_dtor, 1);
zend_hash_init(GLOBAL_CONSTANTS_TABLE, 128, NULL, ZEND_CONSTANT_DTOR, 1);

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

*compiler_globals->function_table = *GLOBAL_FUNCTION_TABLE;
*compiler_globals->class_table = *GLOBAL_CLASS_TABLE;
*compiler_globals->module_table = *GLOBAL_MODULE_TABLE;
compiler_globals->auto_globals = GLOBAL_AUTO_GLOBALS_TABLE;

zend_hash_destroy(executor_globals->zend_constants);
Expand Down Expand Up @@ -1110,6 +1124,7 @@ zend_result zend_post_startup(void) /* {{{ */
#ifdef ZTS
*GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table;
*GLOBAL_CLASS_TABLE = *compiler_globals->class_table;
*GLOBAL_MODULE_TABLE = *compiler_globals->module_table;
*GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants;
global_map_ptr_last = compiler_globals->map_ptr_last;

Expand All @@ -1121,6 +1136,8 @@ zend_result zend_post_startup(void) /* {{{ */
compiler_globals->function_table = NULL;
free(compiler_globals->class_table);
compiler_globals->class_table = NULL;
free(compiler_globals->module_table);
compiler_globals->module_table = NULL;
if (compiler_globals->map_ptr_real_base) {
free(compiler_globals->map_ptr_real_base);
}
Expand Down
1 change: 1 addition & 0 deletions Zend/zend.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ struct _zend_class_entry {

union {
struct {
zend_string *user_module;
zend_string *filename;
uint32_t line_start;
uint32_t line_end;
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3581,6 +3581,11 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
ZVAL_ALIAS_PTR(&zv, ce);

ret = zend_hash_add(CG(class_table), lcname, &zv);

if (CG(active_module)) {
zend_hash_add(&CG(active_module)->class_table, lcname, &zv);
}

zend_string_release_ex(lcname, 0);
if (ret) {
// avoid notifying at MINIT time
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ enum _zend_ast_kind {
ZEND_AST_USE_TRAIT,
ZEND_AST_TRAIT_PRECEDENCE,
ZEND_AST_METHOD_REFERENCE,
ZEND_AST_MODULE,
ZEND_AST_NAMESPACE,
ZEND_AST_USE_ELEM,
ZEND_AST_TRAIT_ALIAS,
Expand Down
Loading
Loading