@@ -384,6 +384,12 @@ static void zend_reset_import_tables(void) /* {{{ */
384384 FC (imports_const ) = NULL ;
385385 }
386386
387+ if (FC (imports_type )) {
388+ zend_hash_destroy (FC (imports_type ));
389+ efree (FC (imports_type ));
390+ FC (imports_type ) = NULL ;
391+ }
392+
387393 zend_hash_clean (& FC (seen_symbols ));
388394}
389395/* }}} */
@@ -404,6 +410,7 @@ void zend_file_context_begin(zend_file_context *prev_context) /* {{{ */
404410 FC (imports ) = NULL ;
405411 FC (imports_function ) = NULL ;
406412 FC (imports_const ) = NULL ;
413+ FC (imports_type ) = NULL ;
407414 FC (current_namespace ) = NULL ;
408415 FC (in_namespace ) = 0 ;
409416 FC (has_bracketed_namespaces ) = 0 ;
@@ -1225,6 +1232,14 @@ static void str_dtor(zval *zv) /* {{{ */ {
12251232}
12261233/* }}} */
12271234
1235+ static void ast_ref_dtor (zval * zv ) /* {{{ */ {
1236+ /* The AST was created via zend_ast_copy, so we need to destroy the ref */
1237+ zend_ast * ast = Z_PTR_P (zv );
1238+ zend_ast_ref * ref = (zend_ast_ref * )((char * )ast - sizeof (zend_ast_ref ));
1239+ zend_ast_ref_destroy (ref );
1240+ }
1241+ /* }}} */
1242+
12281243static uint32_t zend_add_try_element (uint32_t try_op ) /* {{{ */
12291244{
12301245 zend_op_array * op_array = CG (active_op_array );
@@ -7170,6 +7185,9 @@ ZEND_API void zend_set_function_arg_flags(zend_function *func) /* {{{ */
71707185}
71717186/* }}} */
71727187
7188+ /* Forward declaration for type alias support */
7189+ static zend_type zend_compile_typename (zend_ast * ast );
7190+
71737191static zend_type zend_compile_single_typename (zend_ast * ast )
71747192{
71757193 ZEND_ASSERT (!(ast -> attr & ZEND_TYPE_NULLABLE ));
@@ -7201,6 +7219,17 @@ static zend_type zend_compile_single_typename(zend_ast *ast)
72017219
72027220 return (zend_type ) ZEND_TYPE_INIT_CODE (type_code , 0 , 0 );
72037221 } else {
7222+ /* Check for type alias (only for unqualified names) */
7223+ if (ast -> attr == ZEND_NAME_NOT_FQ && FC (imports_type )) {
7224+ zend_string * lookup_name = zend_string_tolower (type_name );
7225+ zend_ast * alias_ast = zend_hash_find_ptr (FC (imports_type ), lookup_name );
7226+ zend_string_release_ex (lookup_name , 0 );
7227+ if (alias_ast ) {
7228+ /* Recursively compile the aliased type */
7229+ return zend_compile_typename (alias_ast );
7230+ }
7231+ }
7232+
72047233 const char * correct_name ;
72057234 uint32_t fetch_type = zend_get_class_fetch_type_ast (ast );
72067235 zend_string * class_name = type_name ;
@@ -9745,6 +9774,145 @@ static void zend_compile_use(zend_ast *ast) /* {{{ */
97459774}
97469775/* }}} */
97479776
9777+ static void zend_compile_use_type_alias (zend_ast * ast ) /* {{{ */
9778+ {
9779+ zend_ast * type_ast = ast -> child [0 ];
9780+ zend_ast * alias_ast = ast -> child [1 ];
9781+ zend_string * alias_name = zend_ast_get_str (alias_ast );
9782+ zend_string * lookup_name = zend_string_tolower (alias_name );
9783+
9784+ /* Initialize imports_type hash table if needed */
9785+ if (!FC (imports_type )) {
9786+ FC (imports_type ) = emalloc (sizeof (HashTable ));
9787+ zend_hash_init (FC (imports_type ), 8 , NULL , ast_ref_dtor , 0 );
9788+ }
9789+
9790+ /* Check if alias is already in use */
9791+ if (zend_hash_exists (FC (imports_type ), lookup_name )) {
9792+ zend_error_noreturn (E_COMPILE_ERROR ,
9793+ "Cannot use type alias %s because the name is already in use" ,
9794+ ZSTR_VAL (alias_name ));
9795+ }
9796+
9797+ /* Copy the type AST to heap so it can be properly freed */
9798+ zend_ast_ref * ast_ref = zend_ast_copy (type_ast );
9799+ zend_ast * copied_type_ast = GC_AST (ast_ref );
9800+
9801+ /* Store the copied type AST in the hash table */
9802+ zend_hash_add_ptr (FC (imports_type ), lookup_name , copied_type_ast );
9803+
9804+ zend_string_release_ex (lookup_name , 0 );
9805+ }
9806+ /* }}} */
9807+
9808+ static void zend_compile_include_types (zend_ast * ast ) /* {{{ */
9809+ {
9810+ zend_ast * filename_ast = ast -> child [0 ];
9811+ zval * filename_zv = zend_ast_get_zval (filename_ast );
9812+ zend_string * filename = Z_STR_P (filename_zv );
9813+
9814+ /* Resolve the path relative to current file */
9815+ zend_string * resolved_path = zend_resolve_path (filename );
9816+ if (!resolved_path ) {
9817+ zend_error_noreturn (E_COMPILE_ERROR ,
9818+ "include types: Failed to resolve path '%s'" , ZSTR_VAL (filename ));
9819+ }
9820+
9821+ /* Open and read the file */
9822+ zend_file_handle file_handle ;
9823+ zend_stream_init_filename_ex (& file_handle , resolved_path );
9824+ if (zend_stream_open (& file_handle ) == FAILURE ) {
9825+ zend_string_release_ex (resolved_path , 0 );
9826+ zend_error_noreturn (E_COMPILE_ERROR ,
9827+ "include types: Failed to open '%s'" , ZSTR_VAL (filename ));
9828+ }
9829+
9830+ /* Get file contents */
9831+ char * buf ;
9832+ size_t len ;
9833+ if (zend_stream_fixup (& file_handle , & buf , & len ) == FAILURE ) {
9834+ zend_destroy_file_handle (& file_handle );
9835+ zend_string_release_ex (resolved_path , 0 );
9836+ zend_error_noreturn (E_COMPILE_ERROR ,
9837+ "include types: Failed to read '%s'" , ZSTR_VAL (filename ));
9838+ }
9839+
9840+ /* Compile to AST */
9841+ zend_string * code = zend_string_init (buf , len , 0 );
9842+ zend_arena * ast_arena = NULL ;
9843+ zend_ast * types_ast = zend_compile_string_to_ast (code , & ast_arena , resolved_path );
9844+ zend_string_release_ex (code , 0 );
9845+ zend_destroy_file_handle (& file_handle );
9846+
9847+ if (!types_ast ) {
9848+ zend_string_release_ex (resolved_path , 0 );
9849+ zend_error_noreturn (E_COMPILE_ERROR ,
9850+ "include types: Failed to parse '%s'" , ZSTR_VAL (filename ));
9851+ }
9852+
9853+ /* Validate and process the AST - must only contain type aliases */
9854+ if (types_ast -> kind != ZEND_AST_STMT_LIST ) {
9855+ zend_ast_destroy (types_ast );
9856+ zend_arena_destroy (ast_arena );
9857+ zend_string_release_ex (resolved_path , 0 );
9858+ zend_error_noreturn (E_COMPILE_ERROR ,
9859+ "include types: Invalid types file '%s'" , ZSTR_VAL (filename ));
9860+ }
9861+
9862+ zend_ast_list * list = zend_ast_get_list (types_ast );
9863+ for (uint32_t i = 0 ; i < list -> children ; i ++ ) {
9864+ zend_ast * stmt = list -> child [i ];
9865+ if (stmt == NULL ) {
9866+ continue ;
9867+ }
9868+ if (stmt -> kind != ZEND_AST_TYPE_ALIAS ) {
9869+ zend_ast_destroy (types_ast );
9870+ zend_arena_destroy (ast_arena );
9871+ zend_string_release_ex (resolved_path , 0 );
9872+ zend_error_noreturn (E_COMPILE_ERROR ,
9873+ "include types: Types file '%s' must only contain type aliases (use type ... as ...;)" ,
9874+ ZSTR_VAL (filename ));
9875+ }
9876+
9877+ /* Process the type alias - copy AST to current arena */
9878+ zend_ast * type_ast = stmt -> child [0 ];
9879+ zend_ast * alias_ast = stmt -> child [1 ];
9880+ zend_string * alias_name = zend_ast_get_str (alias_ast );
9881+ zend_string * lookup_name = zend_string_tolower (alias_name );
9882+
9883+ /* Initialize imports_type hash table if needed */
9884+ if (!FC (imports_type )) {
9885+ FC (imports_type ) = emalloc (sizeof (HashTable ));
9886+ zend_hash_init (FC (imports_type ), 8 , NULL , ast_ref_dtor , 0 );
9887+ }
9888+
9889+ /* Check if alias is already in use */
9890+ if (zend_hash_exists (FC (imports_type ), lookup_name )) {
9891+ zend_string_release_ex (lookup_name , 0 );
9892+ zend_ast_destroy (types_ast );
9893+ zend_arena_destroy (ast_arena );
9894+ zend_string_release_ex (resolved_path , 0 );
9895+ zend_error_noreturn (E_COMPILE_ERROR ,
9896+ "include types: Cannot use type alias %s because the name is already in use" ,
9897+ ZSTR_VAL (alias_name ));
9898+ }
9899+
9900+ /* Copy the type AST to heap so it survives arena destruction */
9901+ zend_ast_ref * ast_ref = zend_ast_copy (type_ast );
9902+ zend_ast * copied_type_ast = GC_AST (ast_ref );
9903+
9904+ /* Store the copied type AST (the ref keeps it alive) */
9905+ zend_hash_add_ptr (FC (imports_type ), lookup_name , copied_type_ast );
9906+ zend_string_release_ex (lookup_name , 0 );
9907+ }
9908+
9909+ /* Clean up the types file AST - but the copied ASTs remain in current arena */
9910+ zend_ast_destroy (types_ast );
9911+ zend_arena_destroy (ast_arena );
9912+ zend_string_release_ex (resolved_path , 0 );
9913+ }
9914+ /* }}} */
9915+
97489916static void zend_compile_group_use (const zend_ast * ast ) /* {{{ */
97499917{
97509918 uint32_t i ;
@@ -11873,6 +12041,12 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1187312041 case ZEND_AST_USE :
1187412042 zend_compile_use (ast );
1187512043 break ;
12044+ case ZEND_AST_TYPE_ALIAS :
12045+ zend_compile_use_type_alias (ast );
12046+ break ;
12047+ case ZEND_AST_INCLUDE_TYPES :
12048+ zend_compile_include_types (ast );
12049+ break ;
1187612050 case ZEND_AST_CONST_DECL :
1187712051 zend_compile_const_decl (ast );
1187812052 break ;
0 commit comments