2014-09-19 06:01:05 +08:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Zend Engine |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-01-30 17:23:29 +08:00
| Copyright ( c ) Zend Technologies Ltd . ( http : //www.zend.com) |
2014-09-19 06:01:05 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| This source file is subject to version 2.00 of the Zend license , |
| that is bundled with this package in the file LICENSE , and is |
| available through the world - wide - web at the following url : |
| http : //www.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ zend . com so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2018-11-01 23:20:07 +08:00
| Authors : Andi Gutmans < andi @ php . net > |
| Zeev Suraski < zeev @ php . net > |
2014-09-19 06:01:05 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
# include "zend.h"
# include "zend_API.h"
# include "zend_compile.h"
# include "zend_execute.h"
2015-01-09 04:40:36 +08:00
# include "zend_inheritance.h"
2019-05-08 18:30:16 +08:00
# include "zend_interfaces.h"
2014-09-21 05:27:10 +08:00
# include "zend_smart_str.h"
2017-04-11 04:04:18 +08:00
# include "zend_operators.h"
2019-09-11 21:31:04 +08:00
# include "zend_exceptions.h"
2014-09-19 06:01:05 +08:00
2019-05-27 17:39:56 +08:00
static void add_dependency_obligation ( zend_class_entry * ce , zend_class_entry * dependency_ce ) ;
static void add_compatibility_obligation (
2019-06-11 19:15:03 +08:00
zend_class_entry * ce , const zend_function * child_fn , const zend_function * parent_fn ) ;
2019-10-17 19:01:02 +08:00
static void add_property_compatibility_obligation (
zend_class_entry * ce , const zend_property_info * child_prop ,
const zend_property_info * parent_prop ) ;
2019-05-27 17:39:56 +08:00
2018-08-10 10:19:55 +08:00
static void overridden_ptr_dtor ( zval * zv ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2015-08-12 11:15:09 +08:00
efree_size ( Z_PTR_P ( zv ) , sizeof ( zend_function ) ) ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2019-09-25 19:21:13 +08:00
static void zend_type_copy_ctor ( zend_type * type , zend_bool persistent ) {
if ( ZEND_TYPE_HAS_LIST ( * type ) ) {
zend_type_list * old_list = ZEND_TYPE_LIST ( * type ) ;
size_t size = ZEND_TYPE_LIST_SIZE ( old_list - > num_types ) ;
zend_type_list * new_list = ZEND_TYPE_USES_ARENA ( * type )
? zend_arena_alloc ( & CG ( arena ) , size ) : pemalloc ( size , persistent ) ;
memcpy ( new_list , old_list , ZEND_TYPE_LIST_SIZE ( old_list - > num_types ) ) ;
ZEND_TYPE_SET_PTR ( * type , new_list ) ;
2020-01-17 00:04:11 +08:00
zend_type * list_type ;
ZEND_TYPE_LIST_FOREACH ( new_list , list_type ) {
ZEND_ASSERT ( ZEND_TYPE_HAS_NAME ( * list_type ) ) ;
zend_string_addref ( ZEND_TYPE_NAME ( * list_type ) ) ;
2019-09-25 19:21:13 +08:00
} ZEND_TYPE_LIST_FOREACH_END ( ) ;
} else if ( ZEND_TYPE_HAS_NAME ( * type ) ) {
zend_string_addref ( ZEND_TYPE_NAME ( * type ) ) ;
}
}
2014-09-19 06:01:05 +08:00
static zend_property_info * zend_duplicate_property_info_internal ( zend_property_info * property_info ) /* { { { */
{
zend_property_info * new_property_info = pemalloc ( sizeof ( zend_property_info ) , 1 ) ;
memcpy ( new_property_info , property_info , sizeof ( zend_property_info ) ) ;
zend_string_addref ( new_property_info - > name ) ;
2019-09-25 19:21:13 +08:00
zend_type_copy_ctor ( & new_property_info - > type , /* persistent */ 1 ) ;
2019-01-07 19:28:51 +08:00
2014-09-19 06:01:05 +08:00
return new_property_info ;
}
/* }}} */
2019-06-27 16:27:53 +08:00
static zend_function * zend_duplicate_internal_function ( zend_function * func , zend_class_entry * ce ) /* { { { */
2015-03-05 07:10:38 +08:00
{
zend_function * new_function ;
2019-06-27 16:27:53 +08:00
if ( UNEXPECTED ( ce - > type & ZEND_INTERNAL_CLASS ) ) {
new_function = pemalloc ( sizeof ( zend_internal_function ) , 1 ) ;
memcpy ( new_function , func , sizeof ( zend_internal_function ) ) ;
} else {
new_function = zend_arena_alloc ( & CG ( arena ) , sizeof ( zend_internal_function ) ) ;
memcpy ( new_function , func , sizeof ( zend_internal_function ) ) ;
new_function - > common . fn_flags | = ZEND_ACC_ARENA_ALLOCATED ;
}
if ( EXPECTED ( new_function - > common . function_name ) ) {
zend_string_addref ( new_function - > common . function_name ) ;
}
return new_function ;
}
/* }}} */
static zend_function * zend_duplicate_user_function ( zend_function * func ) /* { { { */
{
zend_function * new_function ;
new_function = zend_arena_alloc ( & CG ( arena ) , sizeof ( zend_op_array ) ) ;
memcpy ( new_function , func , sizeof ( zend_op_array ) ) ;
if ( ZEND_MAP_PTR_GET ( func - > op_array . static_variables_ptr ) ) {
/* See: Zend/tests/method_static_var.phpt */
new_function - > op_array . static_variables = ZEND_MAP_PTR_GET ( func - > op_array . static_variables_ptr ) ;
}
if ( ! ( GC_FLAGS ( new_function - > op_array . static_variables ) & IS_ARRAY_IMMUTABLE ) ) {
GC_ADDREF ( new_function - > op_array . static_variables ) ;
}
ZEND_MAP_PTR_INIT ( new_function - > op_array . static_variables_ptr , & new_function - > op_array . static_variables ) ;
return new_function ;
}
/* }}} */
2019-06-27 18:11:08 +08:00
static zend_always_inline zend_function * zend_duplicate_function ( zend_function * func , zend_class_entry * ce , zend_bool is_interface ) /* { { { */
2019-06-27 16:27:53 +08:00
{
2015-03-06 22:07:36 +08:00
if ( UNEXPECTED ( func - > type = = ZEND_INTERNAL_FUNCTION ) ) {
2019-06-27 16:27:53 +08:00
return zend_duplicate_internal_function ( func , ce ) ;
2015-03-05 07:10:38 +08:00
} else {
if ( func - > op_array . refcount ) {
( * func - > op_array . refcount ) + + ;
}
2019-06-27 18:11:08 +08:00
if ( is_interface
2019-08-29 20:33:31 +08:00
| | EXPECTED ( ! func - > op_array . static_variables ) ) {
2015-03-05 07:10:38 +08:00
/* reuse the same op_array structure */
return func ;
}
2019-06-27 16:27:53 +08:00
return zend_duplicate_user_function ( func ) ;
2015-03-05 07:10:38 +08:00
}
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void do_inherit_parent_constructor ( zend_class_entry * ce ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2018-09-05 18:16:10 +08:00
zend_class_entry * parent = ce - > parent ;
ZEND_ASSERT ( parent ! = NULL ) ;
2014-09-19 06:01:05 +08:00
/* You cannot change create_object */
2018-09-05 18:16:10 +08:00
ce - > create_object = parent - > create_object ;
2014-09-19 06:01:05 +08:00
/* Inherit special functions if needed */
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > get_iterator ) ) {
2018-09-05 18:16:10 +08:00
ce - > get_iterator = parent - > get_iterator ;
2014-09-19 06:01:05 +08:00
}
2018-10-17 20:52:50 +08:00
if ( parent - > iterator_funcs_ptr ) {
/* Must be initialized through iface->interface_gets_implemented() */
ZEND_ASSERT ( ce - > iterator_funcs_ptr ) ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __get ) ) {
2018-09-05 18:16:10 +08:00
ce - > __get = parent - > __get ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __set ) ) {
2018-09-05 18:16:10 +08:00
ce - > __set = parent - > __set ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __unset ) ) {
2018-09-05 18:16:10 +08:00
ce - > __unset = parent - > __unset ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __isset ) ) {
2018-09-05 18:16:10 +08:00
ce - > __isset = parent - > __isset ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __call ) ) {
2018-09-05 18:16:10 +08:00
ce - > __call = parent - > __call ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __callstatic ) ) {
2018-09-05 18:16:10 +08:00
ce - > __callstatic = parent - > __callstatic ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __tostring ) ) {
2018-09-05 18:16:10 +08:00
ce - > __tostring = parent - > __tostring ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > clone ) ) {
2018-09-05 18:16:10 +08:00
ce - > clone = parent - > clone ;
2014-09-19 06:01:05 +08:00
}
2019-02-15 18:20:28 +08:00
if ( EXPECTED ( ! ce - > serialize_func ) ) {
ce - > serialize_func = parent - > serialize_func ;
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > serialize ) ) {
2018-09-05 18:16:10 +08:00
ce - > serialize = parent - > serialize ;
2014-09-19 06:01:05 +08:00
}
2019-02-15 18:20:28 +08:00
if ( EXPECTED ( ! ce - > unserialize_func ) ) {
ce - > unserialize_func = parent - > unserialize_func ;
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > unserialize ) ) {
2018-09-05 18:16:10 +08:00
ce - > unserialize = parent - > unserialize ;
2014-09-19 06:01:05 +08:00
}
if ( ! ce - > destructor ) {
2018-09-05 18:16:10 +08:00
ce - > destructor = parent - > destructor ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( ! ce - > __debugInfo ) ) {
2018-09-05 18:16:10 +08:00
ce - > __debugInfo = parent - > __debugInfo ;
2014-09-19 06:01:05 +08:00
}
2015-03-05 07:10:38 +08:00
2014-09-19 06:01:05 +08:00
if ( ce - > constructor ) {
2018-09-05 18:16:10 +08:00
if ( parent - > constructor & & UNEXPECTED ( parent - > constructor - > common . fn_flags & ZEND_ACC_FINAL ) ) {
2015-03-05 07:10:38 +08:00
zend_error_noreturn ( E_ERROR , " Cannot override final %s::%s() with %s::%s() " ,
2018-09-05 18:16:10 +08:00
ZSTR_VAL ( parent - > name ) , ZSTR_VAL ( parent - > constructor - > common . function_name ) ,
2015-06-30 18:59:27 +08:00
ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( ce - > constructor - > common . function_name ) ) ;
2014-09-19 06:01:05 +08:00
}
return ;
}
2018-09-05 18:16:10 +08:00
ce - > constructor = parent - > constructor ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
char * zend_visibility_string ( uint32_t fn_flags ) /* { { { */
{
if ( fn_flags & ZEND_ACC_PUBLIC ) {
return " public " ;
2018-09-11 16:56:45 +08:00
} else if ( fn_flags & ZEND_ACC_PRIVATE ) {
return " private " ;
} else {
ZEND_ASSERT ( fn_flags & ZEND_ACC_PROTECTED ) ;
return " protected " ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2019-10-17 19:01:02 +08:00
static zend_string * resolve_class_name ( zend_class_entry * scope , zend_string * name ) {
ZEND_ASSERT ( scope ) ;
if ( zend_string_equals_literal_ci ( name , " parent " ) & & scope - > parent ) {
if ( scope - > ce_flags & ZEND_ACC_RESOLVED_PARENT ) {
return scope - > parent - > name ;
2019-05-09 16:41:06 +08:00
} else {
2019-10-17 19:01:02 +08:00
return scope - > parent_name ;
2019-05-09 16:41:06 +08:00
}
2019-05-08 18:11:12 +08:00
} else if ( zend_string_equals_literal_ci ( name , " self " ) ) {
2019-10-17 19:01:02 +08:00
return scope - > name ;
2019-05-08 18:11:12 +08:00
} else {
return name ;
2016-06-04 22:44:49 +08:00
}
}
2019-05-09 16:41:06 +08:00
static zend_bool class_visible ( zend_class_entry * ce ) {
if ( ce - > type = = ZEND_INTERNAL_CLASS ) {
return ! ( CG ( compiler_options ) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES ) ;
} else {
ZEND_ASSERT ( ce - > type = = ZEND_USER_CLASS ) ;
return ! ( CG ( compiler_options ) & ZEND_COMPILE_IGNORE_OTHER_FILES )
| | ce - > info . user . filename = = CG ( compiled_filename ) ;
}
}
2019-09-25 19:21:13 +08:00
static zend_class_entry * lookup_class (
zend_class_entry * scope , zend_string * name , zend_bool register_unresolved ) {
2019-05-09 16:41:06 +08:00
zend_class_entry * ce ;
if ( ! CG ( in_compilation ) ) {
2019-05-27 17:39:56 +08:00
uint32_t flags = ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD ;
ce = zend_lookup_class_ex ( name , NULL , flags ) ;
2019-05-08 18:30:16 +08:00
if ( ce ) {
return ce ;
}
2019-05-27 17:39:56 +08:00
2019-09-25 19:21:13 +08:00
if ( register_unresolved ) {
/* We'll autoload this class and process delayed variance obligations later. */
if ( ! CG ( delayed_autoloads ) ) {
ALLOC_HASHTABLE ( CG ( delayed_autoloads ) ) ;
zend_hash_init ( CG ( delayed_autoloads ) , 0 , NULL , NULL , 0 ) ;
}
zend_hash_add_empty_element ( CG ( delayed_autoloads ) , name ) ;
2019-05-27 17:39:56 +08:00
}
2019-05-08 18:30:16 +08:00
} else {
2019-05-24 20:41:38 +08:00
ce = zend_lookup_class_ex ( name , NULL , ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
2019-05-08 18:30:16 +08:00
if ( ce & & class_visible ( ce ) ) {
return ce ;
}
2019-05-09 16:41:06 +08:00
2019-05-27 17:39:56 +08:00
/* The current class may not be registered yet, so check for it explicitly. */
2019-10-17 19:01:02 +08:00
if ( zend_string_equals_ci ( scope - > name , name ) ) {
return scope ;
2019-05-27 17:39:56 +08:00
}
2019-05-09 16:41:06 +08:00
}
return NULL ;
}
2019-05-27 17:39:56 +08:00
/* Instanceof that's safe to use on unlinked classes. */
2019-05-08 18:30:16 +08:00
static zend_bool unlinked_instanceof ( zend_class_entry * ce1 , zend_class_entry * ce2 ) {
2019-05-27 17:39:56 +08:00
zend_class_entry * ce ;
if ( ce1 = = ce2 ) {
return 1 ;
}
2019-10-25 00:11:41 +08:00
if ( ce1 - > ce_flags & ZEND_ACC_LINKED ) {
2019-05-08 18:30:16 +08:00
return instanceof_function ( ce1 , ce2 ) ;
}
2019-05-27 17:39:56 +08:00
ce = ce1 ;
while ( ce - > parent ) {
if ( ce - > ce_flags & ZEND_ACC_RESOLVED_PARENT ) {
ce = ce - > parent ;
} else {
ce = zend_lookup_class_ex ( ce - > parent_name , NULL ,
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
if ( ! ce ) {
break ;
}
}
if ( ce = = ce2 ) {
return 1 ;
}
}
if ( ce1 - > num_interfaces ) {
uint32_t i ;
2019-10-25 00:11:41 +08:00
if ( ce1 - > ce_flags & ZEND_ACC_RESOLVED_INTERFACES ) {
/* Unlike the normal instanceof_function(), we have to perform a recursive
* check here , as the parent interfaces might not have been fully copied yet . */
for ( i = 0 ; i < ce1 - > num_interfaces ; i + + ) {
if ( unlinked_instanceof ( ce1 - > interfaces [ i ] , ce2 ) ) {
return 1 ;
}
}
} else {
for ( i = 0 ; i < ce1 - > num_interfaces ; i + + ) {
ce = zend_lookup_class_ex (
ce1 - > interface_names [ i ] . name , ce1 - > interface_names [ i ] . lc_name ,
ZEND_FETCH_CLASS_ALLOW_UNLINKED | ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
if ( ce & & unlinked_instanceof ( ce , ce2 ) ) {
return 1 ;
}
2019-05-27 17:39:56 +08:00
}
}
}
return 0 ;
2019-05-08 18:30:16 +08:00
}
2019-09-25 19:21:13 +08:00
static zend_bool zend_type_contains_traversable ( zend_type type ) {
2020-01-17 00:04:11 +08:00
zend_type * single_type ;
ZEND_TYPE_FOREACH ( type , single_type ) {
if ( ZEND_TYPE_HAS_NAME ( * single_type )
& & zend_string_equals_literal_ci ( ZEND_TYPE_NAME ( * single_type ) , " Traversable " ) ) {
return 1 ;
}
} ZEND_TYPE_FOREACH_END ( ) ;
2019-09-25 19:21:13 +08:00
return 0 ;
}
2019-05-09 16:41:06 +08:00
/* Unresolved means that class declarations that are currently not available are needed to
* determine whether the inheritance is valid or not . At runtime UNRESOLVED should be treated
* as an ERROR . */
typedef enum {
INHERITANCE_UNRESOLVED = - 1 ,
INHERITANCE_ERROR = 0 ,
INHERITANCE_SUCCESS = 1 ,
} inheritance_status ;
2019-09-25 19:21:13 +08:00
static inheritance_status zend_perform_covariant_class_type_check (
zend_class_entry * fe_scope , zend_string * fe_class_name ,
zend_class_entry * proto_scope , zend_type proto_type ,
zend_bool register_unresolved ) {
zend_bool have_unresolved = 0 ;
zend_class_entry * fe_ce = NULL ;
if ( ZEND_TYPE_FULL_MASK ( proto_type ) & MAY_BE_OBJECT ) {
/* Currently, any class name would be allowed here. We still perform a class lookup
* for forward - compatibility reasons , as we may have named types in the future that
* are not classes ( such as enums or typedefs ) . */
if ( ! fe_ce ) fe_ce = lookup_class ( fe_scope , fe_class_name , register_unresolved ) ;
if ( ! fe_ce ) {
have_unresolved = 1 ;
} else {
return INHERITANCE_SUCCESS ;
}
2019-05-08 17:24:08 +08:00
}
2019-09-25 19:21:13 +08:00
if ( ZEND_TYPE_FULL_MASK ( proto_type ) & MAY_BE_ITERABLE ) {
if ( ! fe_ce ) fe_ce = lookup_class ( fe_scope , fe_class_name , register_unresolved ) ;
if ( ! fe_ce ) {
have_unresolved = 1 ;
} else if ( unlinked_instanceof ( fe_ce , zend_ce_traversable ) ) {
return INHERITANCE_SUCCESS ;
2019-05-08 18:30:16 +08:00
}
2019-09-25 19:21:13 +08:00
}
2015-01-09 04:40:36 +08:00
2020-01-17 00:04:11 +08:00
zend_type * single_type ;
ZEND_TYPE_FOREACH ( proto_type , single_type ) {
if ( ZEND_TYPE_HAS_NAME ( * single_type ) ) {
2019-09-25 19:21:13 +08:00
zend_string * proto_class_name =
2020-01-17 00:04:11 +08:00
resolve_class_name ( proto_scope , ZEND_TYPE_NAME ( * single_type ) ) ;
2019-09-25 19:21:13 +08:00
if ( zend_string_equals_ci ( fe_class_name , proto_class_name ) ) {
return INHERITANCE_SUCCESS ;
}
2019-05-08 18:11:12 +08:00
2020-01-17 00:04:11 +08:00
/* Make sure to always load both classes, to avoid only registering one of them as
* a delayed autoload . */
2019-09-25 19:21:13 +08:00
if ( ! fe_ce ) fe_ce = lookup_class ( fe_scope , fe_class_name , register_unresolved ) ;
zend_class_entry * proto_ce =
lookup_class ( proto_scope , proto_class_name , register_unresolved ) ;
if ( ! fe_ce | | ! proto_ce ) {
have_unresolved = 1 ;
} else if ( unlinked_instanceof ( fe_ce , proto_ce ) ) {
return INHERITANCE_SUCCESS ;
2019-05-08 18:30:16 +08:00
}
2020-01-17 00:04:11 +08:00
}
} ZEND_TYPE_FOREACH_END ( ) ;
2019-09-25 19:21:13 +08:00
return have_unresolved ? INHERITANCE_UNRESOLVED : INHERITANCE_ERROR ;
}
static inheritance_status zend_perform_covariant_type_check (
zend_class_entry * fe_scope , zend_type fe_type ,
zend_class_entry * proto_scope , zend_type proto_type ) /* {{{ */
{
ZEND_ASSERT ( ZEND_TYPE_IS_SET ( fe_type ) & & ZEND_TYPE_IS_SET ( proto_type ) ) ;
/* Builtin types may be removed, but not added */
uint32_t fe_type_mask = ZEND_TYPE_PURE_MASK ( fe_type ) ;
uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK ( proto_type ) ;
uint32_t added_types = fe_type_mask & ~ proto_type_mask ;
if ( added_types ) {
// TODO: Make "iterable" an alias of "array|Traversable" instead,
// so these special cases will be handled automatically.
if ( added_types = = MAY_BE_ITERABLE
& & ( proto_type_mask & MAY_BE_ARRAY )
& & zend_type_contains_traversable ( proto_type ) ) {
/* Replacing array|Traversable with iterable is okay */
} else if ( added_types = = MAY_BE_ARRAY & & ( proto_type_mask & MAY_BE_ITERABLE ) ) {
/* Replacing iterable with array is okay */
} else {
/* Otherwise adding new types is illegal */
return INHERITANCE_ERROR ;
}
}
if ( ZEND_TYPE_HAS_NAME ( fe_type ) ) {
zend_string * fe_class_name = resolve_class_name ( fe_scope , ZEND_TYPE_NAME ( fe_type ) ) ;
inheritance_status status = zend_perform_covariant_class_type_check (
fe_scope , fe_class_name , proto_scope , proto_type , /* register_unresolved */ 0 ) ;
if ( status ! = INHERITANCE_UNRESOLVED ) {
return status ;
2019-05-08 18:30:16 +08:00
}
2019-09-25 19:21:13 +08:00
zend_perform_covariant_class_type_check (
fe_scope , fe_class_name , proto_scope , proto_type , /* register_unresolved */ 1 ) ;
return INHERITANCE_UNRESOLVED ;
}
if ( ZEND_TYPE_HAS_LIST ( fe_type ) ) {
2020-01-17 00:04:11 +08:00
zend_type * list_type ;
2019-09-25 19:21:13 +08:00
zend_bool all_success = 1 ;
/* First try to check whether we can succeed without resolving anything */
2020-01-17 00:04:11 +08:00
ZEND_TYPE_LIST_FOREACH ( ZEND_TYPE_LIST ( fe_type ) , list_type ) {
ZEND_ASSERT ( ZEND_TYPE_HAS_NAME ( * list_type ) ) ;
2019-10-17 19:01:02 +08:00
zend_string * fe_class_name =
2020-01-17 00:04:11 +08:00
resolve_class_name ( fe_scope , ZEND_TYPE_NAME ( * list_type ) ) ;
2019-09-25 19:21:13 +08:00
inheritance_status status = zend_perform_covariant_class_type_check (
fe_scope , fe_class_name , proto_scope , proto_type , /* register_unresolved */ 0 ) ;
if ( status = = INHERITANCE_ERROR ) {
return INHERITANCE_ERROR ;
}
if ( status ! = INHERITANCE_SUCCESS ) {
all_success = 0 ;
2019-05-08 18:11:12 +08:00
}
2019-09-25 19:21:13 +08:00
} ZEND_TYPE_LIST_FOREACH_END ( ) ;
2020-01-19 15:50:48 +08:00
/* All individual checks succeeded, overall success */
2019-09-25 19:21:13 +08:00
if ( all_success ) {
2019-05-08 18:30:16 +08:00
return INHERITANCE_SUCCESS ;
2019-05-08 17:24:08 +08:00
}
2019-09-25 19:21:13 +08:00
/* Register all classes that may have to be resolved */
2020-01-17 00:04:11 +08:00
ZEND_TYPE_LIST_FOREACH ( ZEND_TYPE_LIST ( fe_type ) , list_type ) {
ZEND_ASSERT ( ZEND_TYPE_HAS_NAME ( * list_type ) ) ;
2019-09-25 19:21:13 +08:00
zend_string * fe_class_name =
2020-01-17 00:04:11 +08:00
resolve_class_name ( fe_scope , ZEND_TYPE_NAME ( * list_type ) ) ;
2019-09-25 19:21:13 +08:00
zend_perform_covariant_class_type_check (
fe_scope , fe_class_name , proto_scope , proto_type , /* register_unresolved */ 1 ) ;
} ZEND_TYPE_LIST_FOREACH_END ( ) ;
return INHERITANCE_UNRESOLVED ;
2016-03-28 18:54:25 +08:00
}
2019-09-25 19:21:13 +08:00
return INHERITANCE_SUCCESS ;
2015-01-09 04:40:36 +08:00
}
/* }}} */
2019-05-08 18:30:16 +08:00
static inheritance_status zend_do_perform_arg_type_hint_check (
const zend_function * fe , zend_arg_info * fe_arg_info ,
const zend_function * proto , zend_arg_info * proto_arg_info ) /* {{{ */
2017-01-01 22:53:24 +08:00
{
if ( ! ZEND_TYPE_IS_SET ( fe_arg_info - > type ) ) {
/* Child with no type is always compatible */
2019-05-09 16:41:06 +08:00
return INHERITANCE_SUCCESS ;
2017-01-01 22:53:24 +08:00
}
if ( ! ZEND_TYPE_IS_SET ( proto_arg_info - > type ) ) {
/* Child defines a type, but parent doesn't, violates LSP */
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2017-01-01 22:53:24 +08:00
}
2019-05-08 17:24:08 +08:00
/* Contravariant type check is performed as a covariant type check with swapped
* argument order . */
2019-05-08 18:30:16 +08:00
return zend_perform_covariant_type_check (
2019-09-25 19:21:13 +08:00
proto - > common . scope , proto_arg_info - > type , fe - > common . scope , fe_arg_info - > type ) ;
2017-01-01 22:53:24 +08:00
}
/* }}} */
2019-05-08 18:30:16 +08:00
static inheritance_status zend_do_perform_implementation_check (
2019-09-25 19:21:13 +08:00
const zend_function * fe , const zend_function * proto ) /* {{{ */
2014-09-19 06:01:05 +08:00
{
2020-01-06 19:06:51 +08:00
uint32_t i , num_args , proto_num_args , fe_num_args ;
2019-05-09 16:41:06 +08:00
inheritance_status status , local_status ;
2020-01-06 19:06:51 +08:00
zend_bool proto_is_variadic , fe_is_variadic ;
2014-09-19 06:01:05 +08:00
/* If it's a user function then arg_info == NULL means we don't have any parameters but
* we still need to do the arg number checks . We are only willing to ignore this for internal
* functions because extensions don ' t always define arg_info .
*/
2018-09-05 18:16:10 +08:00
if ( ! proto - > common . arg_info & & proto - > common . type ! = ZEND_USER_FUNCTION ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_SUCCESS ;
2014-09-19 06:01:05 +08:00
}
/* Checks for constructors only if they are declared in an interface,
* or explicitly marked as abstract
*/
2019-06-25 23:26:56 +08:00
ZEND_ASSERT ( ! ( ( fe - > common . fn_flags & ZEND_ACC_CTOR )
2014-09-19 06:01:05 +08:00
& & ( ( proto - > common . scope - > ce_flags & ZEND_ACC_INTERFACE ) = = 0
2019-06-25 23:26:56 +08:00
& & ( proto - > common . fn_flags & ZEND_ACC_ABSTRACT ) = = 0 ) ) ) ;
2014-09-19 06:01:05 +08:00
2016-07-05 20:31:32 +08:00
/* If the prototype method is private do not enforce a signature */
2019-06-25 23:26:56 +08:00
ZEND_ASSERT ( ! ( proto - > common . fn_flags & ZEND_ACC_PRIVATE ) ) ;
2014-09-19 06:01:05 +08:00
2020-01-06 19:06:51 +08:00
/* The number of required arguments cannot increase. */
if ( proto - > common . required_num_args < fe - > common . required_num_args ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2014-09-19 06:01:05 +08:00
}
/* by-ref constraints on return values are covariant */
if ( ( proto - > common . fn_flags & ZEND_ACC_RETURN_REFERENCE )
& & ! ( fe - > common . fn_flags & ZEND_ACC_RETURN_REFERENCE ) ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2014-09-19 06:01:05 +08:00
}
2020-01-06 19:06:51 +08:00
proto_is_variadic = ( proto - > common . fn_flags & ZEND_ACC_VARIADIC ) ! = 0 ;
fe_is_variadic = ( fe - > common . fn_flags & ZEND_ACC_VARIADIC ) ! = 0 ;
/* A variadic function cannot become non-variadic */
if ( proto_is_variadic & & ! fe_is_variadic ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2014-09-19 06:01:05 +08:00
}
2020-01-06 19:06:51 +08:00
/* The variadic argument is not included in the stored argument count. */
proto_num_args = proto - > common . num_args + proto_is_variadic ;
fe_num_args = fe - > common . num_args + fe_is_variadic ;
num_args = MAX ( proto_num_args , fe_num_args ) ;
2014-09-19 06:01:05 +08:00
2019-05-09 16:41:06 +08:00
status = INHERITANCE_SUCCESS ;
2014-09-19 06:01:05 +08:00
for ( i = 0 ; i < num_args ; i + + ) {
2020-01-06 19:06:51 +08:00
zend_arg_info * proto_arg_info =
i < proto_num_args ? & proto - > common . arg_info [ i ] :
proto_is_variadic ? & proto - > common . arg_info [ proto_num_args - 1 ] : NULL ;
zend_arg_info * fe_arg_info =
i < fe_num_args ? & fe - > common . arg_info [ i ] :
fe_is_variadic ? & fe - > common . arg_info [ fe_num_args - 1 ] : NULL ;
if ( ! proto_arg_info ) {
/* A new (optional) argument has been added, which is fine. */
continue ;
}
if ( ! fe_arg_info ) {
/* An argument has been removed. This is considered illegal, because arity checks
* work based on a model where passing more than the declared number of parameters
* to a function is an error . */
return INHERITANCE_ERROR ;
2014-09-19 06:01:05 +08:00
}
2017-01-01 22:53:24 +08:00
2019-09-25 19:21:13 +08:00
local_status = zend_do_perform_arg_type_hint_check ( fe , fe_arg_info , proto , proto_arg_info ) ;
2019-06-26 06:00:31 +08:00
if ( UNEXPECTED ( local_status ! = INHERITANCE_SUCCESS ) ) {
if ( UNEXPECTED ( local_status = = INHERITANCE_ERROR ) ) {
return INHERITANCE_ERROR ;
}
ZEND_ASSERT ( local_status = = INHERITANCE_UNRESOLVED ) ;
2019-05-09 16:41:06 +08:00
status = INHERITANCE_UNRESOLVED ;
2016-04-06 22:17:10 +08:00
}
2015-01-09 04:40:36 +08:00
/* by-ref constraints on arguments are invariant */
2019-09-20 23:01:19 +08:00
if ( ZEND_ARG_SEND_MODE ( fe_arg_info ) ! = ZEND_ARG_SEND_MODE ( proto_arg_info ) ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2015-01-09 04:40:36 +08:00
}
}
2014-09-19 06:01:05 +08:00
2015-03-23 01:50:36 +08:00
/* Check return type compatibility, but only if the prototype already specifies
* a return type . Adding a new return type is always valid . */
if ( proto - > common . fn_flags & ZEND_ACC_HAS_RETURN_TYPE ) {
/* Removing a return type is not valid. */
if ( ! ( fe - > common . fn_flags & ZEND_ACC_HAS_RETURN_TYPE ) ) {
2019-05-09 16:41:06 +08:00
return INHERITANCE_ERROR ;
2015-01-09 04:40:36 +08:00
}
2017-01-01 22:53:24 +08:00
2019-05-08 18:30:16 +08:00
local_status = zend_perform_covariant_type_check (
2019-09-25 19:21:13 +08:00
fe - > common . scope , fe - > common . arg_info [ - 1 ] . type ,
proto - > common . scope , proto - > common . arg_info [ - 1 ] . type ) ;
2019-06-26 06:00:31 +08:00
if ( UNEXPECTED ( local_status ! = INHERITANCE_SUCCESS ) ) {
if ( UNEXPECTED ( local_status = = INHERITANCE_ERROR ) ) {
return INHERITANCE_ERROR ;
}
ZEND_ASSERT ( local_status = = INHERITANCE_UNRESOLVED ) ;
2019-05-09 16:41:06 +08:00
status = INHERITANCE_UNRESOLVED ;
2016-04-06 23:24:34 +08:00
}
2015-01-09 04:40:36 +08:00
}
2019-05-09 16:41:06 +08:00
return status ;
2015-01-09 04:40:36 +08:00
}
/* }}} */
2014-09-19 06:01:05 +08:00
2015-08-19 19:40:56 +08:00
static ZEND_COLD void zend_append_type_hint ( smart_str * str , const zend_function * fptr , zend_arg_info * arg_info , int return_hint ) /* { { { */
2015-01-09 04:40:36 +08:00
{
2019-09-19 18:11:29 +08:00
if ( ZEND_TYPE_IS_SET ( arg_info - > type ) ) {
zend_string * type_str = zend_type_to_string_resolved ( arg_info - > type , fptr - > common . scope ) ;
smart_str_append ( str , type_str ) ;
zend_string_release ( type_str ) ;
2015-01-09 04:40:36 +08:00
if ( ! return_hint ) {
smart_str_appendc ( str , ' ' ) ;
2014-09-19 06:01:05 +08:00
}
}
}
/* }}} */
2015-08-19 19:40:56 +08:00
static ZEND_COLD zend_string * zend_get_function_declaration ( const zend_function * fptr ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2014-09-21 05:27:10 +08:00
smart_str str = { 0 } ;
2014-09-19 06:01:05 +08:00
if ( fptr - > op_array . fn_flags & ZEND_ACC_RETURN_REFERENCE ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " & " ) ;
2014-09-19 06:01:05 +08:00
}
if ( fptr - > common . scope ) {
2015-06-29 06:28:52 +08:00
/* cut off on NULL byte ... class@anonymous */
2015-06-30 18:59:27 +08:00
smart_str_appendl ( & str , ZSTR_VAL ( fptr - > common . scope - > name ) , strlen ( ZSTR_VAL ( fptr - > common . scope - > name ) ) ) ;
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " :: " ) ;
2014-09-19 06:01:05 +08:00
}
2014-09-22 02:47:07 +08:00
smart_str_append ( & str , fptr - > common . function_name ) ;
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' ( ' ) ;
2014-09-19 06:01:05 +08:00
if ( fptr - > common . arg_info ) {
2014-12-22 21:44:39 +08:00
uint32_t i , num_args , required ;
2014-09-19 06:01:05 +08:00
zend_arg_info * arg_info = fptr - > common . arg_info ;
required = fptr - > common . required_num_args ;
2014-12-22 21:44:39 +08:00
num_args = fptr - > common . num_args ;
if ( fptr - > common . fn_flags & ZEND_ACC_VARIADIC ) {
num_args + + ;
}
for ( i = 0 ; i < num_args ; ) {
2015-01-09 04:40:36 +08:00
zend_append_type_hint ( & str , fptr , arg_info , 0 ) ;
2014-09-19 06:01:05 +08:00
2019-09-20 23:01:19 +08:00
if ( ZEND_ARG_SEND_MODE ( arg_info ) ) {
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' & ' ) ;
2014-09-19 06:01:05 +08:00
}
2019-09-20 23:01:19 +08:00
if ( ZEND_ARG_IS_VARIADIC ( arg_info ) ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " ... " ) ;
2014-09-19 06:01:05 +08:00
}
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' $ ' ) ;
2014-09-19 06:01:05 +08:00
if ( arg_info - > name ) {
2014-12-03 21:56:09 +08:00
if ( fptr - > type = = ZEND_INTERNAL_FUNCTION ) {
smart_str_appends ( & str , ( ( zend_internal_arg_info * ) arg_info ) - > name ) ;
} else {
2015-06-30 18:59:27 +08:00
smart_str_appendl ( & str , ZSTR_VAL ( arg_info - > name ) , ZSTR_LEN ( arg_info - > name ) ) ;
2014-12-03 21:56:09 +08:00
}
2014-09-19 06:01:05 +08:00
} else {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " param " ) ;
smart_str_append_unsigned ( & str , i ) ;
2014-09-19 06:01:05 +08:00
}
2014-09-21 05:27:10 +08:00
2019-09-20 23:01:19 +08:00
if ( i > = required & & ! ZEND_ARG_IS_VARIADIC ( arg_info ) ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " = " ) ;
2014-09-19 06:01:05 +08:00
if ( fptr - > type = = ZEND_USER_FUNCTION ) {
zend_op * precv = NULL ;
{
uint32_t idx = i ;
2014-09-21 05:27:10 +08:00
zend_op * op = fptr - > op_array . opcodes ;
zend_op * end = op + fptr - > op_array . last ;
2014-09-19 06:01:05 +08:00
+ + idx ;
while ( op < end ) {
if ( ( op - > opcode = = ZEND_RECV | | op - > opcode = = ZEND_RECV_INIT )
& & op - > op1 . num = = ( zend_ulong ) idx )
{
precv = op ;
}
+ + op ;
}
}
if ( precv & & precv - > opcode = = ZEND_RECV_INIT & & precv - > op2_type ! = IS_UNUSED ) {
2017-10-04 21:53:01 +08:00
zval * zv = RT_CONSTANT ( precv , precv - > op2 ) ;
2014-09-19 06:01:05 +08:00
2017-10-10 15:11:05 +08:00
if ( Z_TYPE_P ( zv ) = = IS_FALSE ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " false " ) ;
2014-09-19 06:01:05 +08:00
} else if ( Z_TYPE_P ( zv ) = = IS_TRUE ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " true " ) ;
2014-09-19 06:01:05 +08:00
} else if ( Z_TYPE_P ( zv ) = = IS_NULL ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " NULL " ) ;
2014-09-19 06:01:05 +08:00
} else if ( Z_TYPE_P ( zv ) = = IS_STRING ) {
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' \' ' ) ;
smart_str_appendl ( & str , Z_STRVAL_P ( zv ) , MIN ( Z_STRLEN_P ( zv ) , 10 ) ) ;
2014-09-19 06:01:05 +08:00
if ( Z_STRLEN_P ( zv ) > 10 ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " ... " ) ;
2014-09-19 06:01:05 +08:00
}
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' \' ' ) ;
2014-09-19 06:01:05 +08:00
} else if ( Z_TYPE_P ( zv ) = = IS_ARRAY ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " Array " ) ;
2014-09-19 06:01:05 +08:00
} else if ( Z_TYPE_P ( zv ) = = IS_CONSTANT_AST ) {
2017-10-10 15:11:05 +08:00
zend_ast * ast = Z_ASTVAL_P ( zv ) ;
if ( ast - > kind = = ZEND_AST_CONSTANT ) {
smart_str_append ( & str , zend_ast_get_constant_name ( ast ) ) ;
} else {
smart_str_appends ( & str , " <expression> " ) ;
}
2014-09-19 06:01:05 +08:00
} else {
2017-11-16 22:09:32 +08:00
zend_string * tmp_zv_str ;
zend_string * zv_str = zval_get_tmp_string ( zv , & tmp_zv_str ) ;
2014-09-22 02:47:07 +08:00
smart_str_append ( & str , zv_str ) ;
2017-11-16 22:09:32 +08:00
zend_tmp_string_release ( tmp_zv_str ) ;
2014-09-19 06:01:05 +08:00
}
}
} else {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " NULL " ) ;
2014-09-19 06:01:05 +08:00
}
}
2014-12-22 21:44:39 +08:00
if ( + + i < num_args ) {
2014-09-21 05:27:10 +08:00
smart_str_appends ( & str , " , " ) ;
2014-09-19 06:01:05 +08:00
}
arg_info + + ;
}
}
2014-09-21 05:27:10 +08:00
smart_str_appendc ( & str , ' ) ' ) ;
2015-01-09 04:40:36 +08:00
if ( fptr - > common . fn_flags & ZEND_ACC_HAS_RETURN_TYPE ) {
smart_str_appends ( & str , " : " ) ;
zend_append_type_hint ( & str , fptr , fptr - > common . arg_info - 1 , 1 ) ;
}
2014-09-21 05:27:10 +08:00
smart_str_0 ( & str ) ;
return str . s ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2019-05-27 17:39:56 +08:00
static zend_always_inline uint32_t func_lineno ( const zend_function * fn ) {
2019-03-27 20:02:28 +08:00
return fn - > common . type = = ZEND_USER_FUNCTION ? fn - > op_array . line_start : 0 ;
}
2019-05-08 18:30:16 +08:00
static void ZEND_COLD emit_incompatible_method_error (
2019-05-27 17:39:56 +08:00
const zend_function * child , const zend_function * parent ,
2019-09-25 19:21:13 +08:00
inheritance_status status ) {
2019-05-08 18:30:16 +08:00
zend_string * parent_prototype = zend_get_function_declaration ( parent ) ;
zend_string * child_prototype = zend_get_function_declaration ( child ) ;
if ( status = = INHERITANCE_UNRESOLVED ) {
2019-09-25 19:21:13 +08:00
/* Fetch the first unresolved class from registered autoloads */
zend_string * unresolved_class = NULL ;
ZEND_HASH_FOREACH_STR_KEY ( CG ( delayed_autoloads ) , unresolved_class ) {
break ;
} ZEND_HASH_FOREACH_END ( ) ;
ZEND_ASSERT ( unresolved_class ) ;
2019-05-24 15:33:58 +08:00
zend_error_at ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
2019-05-08 18:30:16 +08:00
" Could not check compatibility between %s and %s, because class %s is not available " ,
ZSTR_VAL ( child_prototype ) , ZSTR_VAL ( parent_prototype ) , ZSTR_VAL ( unresolved_class ) ) ;
} else {
2019-05-24 15:33:58 +08:00
zend_error_at ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Declaration of %s must be compatible with %s " ,
ZSTR_VAL ( child_prototype ) , ZSTR_VAL ( parent_prototype ) ) ;
2019-05-08 18:30:16 +08:00
}
zend_string_efree ( child_prototype ) ;
zend_string_efree ( parent_prototype ) ;
}
2019-06-25 21:37:42 +08:00
static void perform_delayable_implementation_check (
zend_class_entry * ce , const zend_function * fe ,
2019-06-25 21:53:46 +08:00
const zend_function * proto )
2019-06-25 21:37:42 +08:00
{
2019-09-25 19:21:13 +08:00
inheritance_status status = zend_do_perform_implementation_check ( fe , proto ) ;
2019-06-25 21:37:42 +08:00
if ( UNEXPECTED ( status ! = INHERITANCE_SUCCESS ) ) {
if ( EXPECTED ( status = = INHERITANCE_UNRESOLVED ) ) {
2019-06-25 21:53:46 +08:00
add_compatibility_obligation ( ce , fe , proto ) ;
2019-06-26 06:00:31 +08:00
} else {
ZEND_ASSERT ( status = = INHERITANCE_ERROR ) ;
2019-09-25 19:21:13 +08:00
emit_incompatible_method_error ( fe , proto , status ) ;
2019-06-25 21:37:42 +08:00
}
}
}
2019-06-27 07:13:06 +08:00
static zend_always_inline inheritance_status do_inheritance_check_on_method_ex ( zend_function * child , zend_function * parent , zend_class_entry * ce , zval * child_zv , zend_bool check_only , zend_bool checked ) /* { { { */
2014-09-19 06:01:05 +08:00
{
uint32_t child_flags ;
uint32_t parent_flags = parent - > common . fn_flags ;
2019-06-26 05:32:22 +08:00
zend_function * proto ;
2014-09-19 06:01:05 +08:00
2019-06-27 07:13:06 +08:00
if ( ! checked & & UNEXPECTED ( parent_flags & ZEND_ACC_FINAL ) ) {
if ( check_only ) {
return INHERITANCE_ERROR ;
}
2019-03-27 20:02:28 +08:00
zend_error_at_noreturn ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Cannot override final method %s::%s() " ,
ZEND_FN_SCOPE_NAME ( parent ) , ZSTR_VAL ( child - > common . function_name ) ) ;
2014-09-19 06:01:05 +08:00
}
child_flags = child - > common . fn_flags ;
/* You cannot change from static to non static and vice versa.
*/
2019-06-27 07:13:06 +08:00
if ( ! checked & & UNEXPECTED ( ( child_flags & ZEND_ACC_STATIC ) ! = ( parent_flags & ZEND_ACC_STATIC ) ) ) {
if ( check_only ) {
return INHERITANCE_ERROR ;
}
2018-07-09 21:28:13 +08:00
if ( child_flags & ZEND_ACC_STATIC ) {
2019-03-27 20:02:28 +08:00
zend_error_at_noreturn ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Cannot make non static method %s::%s() static in class %s " ,
ZEND_FN_SCOPE_NAME ( parent ) , ZSTR_VAL ( child - > common . function_name ) , ZEND_FN_SCOPE_NAME ( child ) ) ;
2014-09-19 06:01:05 +08:00
} else {
2019-03-27 20:02:28 +08:00
zend_error_at_noreturn ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Cannot make static method %s::%s() non static in class %s " ,
ZEND_FN_SCOPE_NAME ( parent ) , ZSTR_VAL ( child - > common . function_name ) , ZEND_FN_SCOPE_NAME ( child ) ) ;
2014-09-19 06:01:05 +08:00
}
}
/* Disallow making an inherited method abstract. */
2019-06-27 07:13:06 +08:00
if ( ! checked & & UNEXPECTED ( ( child_flags & ZEND_ACC_ABSTRACT ) > ( parent_flags & ZEND_ACC_ABSTRACT ) ) ) {
if ( check_only ) {
return INHERITANCE_ERROR ;
}
2019-03-27 20:02:28 +08:00
zend_error_at_noreturn ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Cannot make non abstract method %s::%s() abstract in class %s " ,
ZEND_FN_SCOPE_NAME ( parent ) , ZSTR_VAL ( child - > common . function_name ) , ZEND_FN_SCOPE_NAME ( child ) ) ;
2014-09-19 06:01:05 +08:00
}
2019-06-27 07:13:06 +08:00
if ( ! check_only & & ( parent_flags & ( ZEND_ACC_PRIVATE | ZEND_ACC_CHANGED ) ) ) {
2014-09-19 06:01:05 +08:00
child - > common . fn_flags | = ZEND_ACC_CHANGED ;
}
2019-06-26 05:32:22 +08:00
if ( parent_flags & ZEND_ACC_PRIVATE ) {
2019-06-27 07:13:06 +08:00
return INHERITANCE_SUCCESS ;
2019-06-26 05:32:22 +08:00
}
2018-10-03 23:32:25 +08:00
2019-06-26 05:32:22 +08:00
proto = parent - > common . prototype ?
parent - > common . prototype : parent ;
if ( parent_flags & ZEND_ACC_CTOR ) {
/* ctors only have a prototype if is abstract (or comes from an interface) */
/* and if that is the case, we want to check inheritance against it */
if ( ! ( proto - > common . fn_flags & ZEND_ACC_ABSTRACT ) ) {
2019-06-27 07:13:06 +08:00
return INHERITANCE_SUCCESS ;
2019-06-26 05:32:22 +08:00
}
parent = proto ;
}
2019-12-18 22:53:09 +08:00
if ( ! check_only & & child - > common . prototype ! = proto ) {
2019-06-26 05:32:22 +08:00
do {
if ( child - > common . scope ! = ce
& & child - > type = = ZEND_USER_FUNCTION
& & ! child - > op_array . static_variables ) {
if ( ce - > ce_flags & ZEND_ACC_INTERFACE ) {
/* Few parent interfaces contain the same method */
2018-09-05 18:16:10 +08:00
break ;
2019-12-18 22:53:09 +08:00
} else if ( child_zv ) {
2019-06-26 05:32:22 +08:00
/* op_array wasn't duplicated yet */
zend_function * new_function = zend_arena_alloc ( & CG ( arena ) , sizeof ( zend_op_array ) ) ;
memcpy ( new_function , child , sizeof ( zend_op_array ) ) ;
Z_PTR_P ( child_zv ) = child = new_function ;
2018-09-05 18:16:10 +08:00
}
}
2019-06-26 05:32:22 +08:00
child - > common . prototype = proto ;
} while ( 0 ) ;
}
2018-09-05 18:16:10 +08:00
2019-06-26 05:32:22 +08:00
/* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */
2019-06-27 07:13:06 +08:00
if ( ! checked & & ( child_flags & ZEND_ACC_PPP_MASK ) > ( parent_flags & ZEND_ACC_PPP_MASK ) ) {
if ( check_only ) {
return INHERITANCE_ERROR ;
}
2019-06-26 05:32:22 +08:00
zend_error_at_noreturn ( E_COMPILE_ERROR , NULL , func_lineno ( child ) ,
" Access level to %s::%s() must be %s (as in class %s)%s " ,
ZEND_FN_SCOPE_NAME ( child ) , ZSTR_VAL ( child - > common . function_name ) , zend_visibility_string ( parent_flags ) , ZEND_FN_SCOPE_NAME ( parent ) , ( parent_flags & ZEND_ACC_PUBLIC ) ? " " : " or weaker " ) ;
}
2019-06-27 07:13:06 +08:00
if ( ! checked ) {
if ( check_only ) {
2019-09-25 19:21:13 +08:00
return zend_do_perform_implementation_check ( child , parent ) ;
2019-06-27 07:13:06 +08:00
}
2019-06-27 07:25:57 +08:00
perform_delayable_implementation_check ( ce , child , parent ) ;
2019-06-27 07:13:06 +08:00
}
return INHERITANCE_SUCCESS ;
}
/* }}} */
2019-06-27 18:11:08 +08:00
static zend_never_inline void do_inheritance_check_on_method ( zend_function * child , zend_function * parent , zend_class_entry * ce , zval * child_zv ) /* { { { */
2019-06-27 07:13:06 +08:00
{
do_inheritance_check_on_method_ex ( child , parent , ce , child_zv , 0 , 0 ) ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2019-06-27 18:11:08 +08:00
static zend_always_inline void do_inherit_method ( zend_string * key , zend_function * parent , zend_class_entry * ce , zend_bool is_interface , zend_bool checked ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2017-11-24 19:01:19 +08:00
zval * child = zend_hash_find_ex ( & ce - > function_table , key , 1 ) ;
2014-09-19 06:01:05 +08:00
2015-03-05 07:10:38 +08:00
if ( child ) {
2016-01-13 16:41:57 +08:00
zend_function * func = ( zend_function * ) Z_PTR_P ( child ) ;
2019-06-27 18:11:08 +08:00
if ( is_interface & & UNEXPECTED ( func = = parent ) ) {
2018-10-03 19:35:36 +08:00
/* The same method in interface may be inherited few times */
2019-06-27 18:11:08 +08:00
return ;
2018-10-03 17:32:33 +08:00
}
2019-06-27 18:11:08 +08:00
if ( checked ) {
do_inheritance_check_on_method_ex ( func , parent , ce , child , 0 , checked ) ;
2019-06-27 07:13:06 +08:00
} else {
2019-06-27 18:11:08 +08:00
do_inheritance_check_on_method ( func , parent , ce , child ) ;
}
} else {
2019-06-27 07:13:06 +08:00
2019-06-27 18:11:08 +08:00
if ( is_interface | | ( parent - > common . fn_flags & ( ZEND_ACC_ABSTRACT ) ) ) {
ce - > ce_flags | = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ;
}
parent = zend_duplicate_function ( parent , ce , is_interface ) ;
2019-06-27 07:13:06 +08:00
2019-06-27 18:11:08 +08:00
if ( ! is_interface ) {
2019-06-27 07:13:06 +08:00
_zend_hash_append_ptr ( & ce - > function_table , key , parent ) ;
2019-06-27 18:11:08 +08:00
} else {
zend_hash_add_new_ptr ( & ce - > function_table , key , parent ) ;
2019-06-27 07:13:06 +08:00
}
2019-06-27 18:11:08 +08:00
}
2019-06-27 07:13:06 +08:00
}
/* }}} */
2019-10-17 19:01:02 +08:00
inheritance_status property_types_compatible (
const zend_property_info * parent_info , const zend_property_info * child_info ) {
2019-09-20 23:01:19 +08:00
if ( ZEND_TYPE_PURE_MASK ( parent_info - > type ) = = ZEND_TYPE_PURE_MASK ( child_info - > type )
& & ZEND_TYPE_NAME ( parent_info - > type ) = = ZEND_TYPE_NAME ( child_info - > type ) ) {
2019-10-17 19:01:02 +08:00
return INHERITANCE_SUCCESS ;
2019-01-07 19:28:51 +08:00
}
2019-09-25 19:21:13 +08:00
if ( ZEND_TYPE_IS_SET ( parent_info - > type ) ! = ZEND_TYPE_IS_SET ( child_info - > type ) ) {
2019-10-17 19:01:02 +08:00
return INHERITANCE_ERROR ;
2019-01-07 19:28:51 +08:00
}
2019-09-25 19:21:13 +08:00
/* Perform a covariant type check in both directions to determined invariance. */
inheritance_status status1 = zend_perform_covariant_type_check (
child_info - > ce , child_info - > type , parent_info - > ce , parent_info - > type ) ;
inheritance_status status2 = zend_perform_covariant_type_check (
parent_info - > ce , parent_info - > type , child_info - > ce , child_info - > type ) ;
if ( status1 = = INHERITANCE_SUCCESS & & status2 = = INHERITANCE_SUCCESS ) {
2019-10-17 19:01:02 +08:00
return INHERITANCE_SUCCESS ;
2019-01-07 19:28:51 +08:00
}
2019-09-25 19:21:13 +08:00
if ( status1 = = INHERITANCE_ERROR | | status2 = = INHERITANCE_ERROR ) {
return INHERITANCE_ERROR ;
2019-10-17 19:01:02 +08:00
}
2019-12-04 18:07:22 +08:00
ZEND_ASSERT ( status1 = = INHERITANCE_UNRESOLVED | | status2 = = INHERITANCE_UNRESOLVED ) ;
2019-09-25 19:21:13 +08:00
return INHERITANCE_UNRESOLVED ;
2019-10-17 19:01:02 +08:00
}
static void emit_incompatible_property_error (
const zend_property_info * child , const zend_property_info * parent ) {
2019-10-17 19:42:47 +08:00
zend_string * type_str = zend_type_to_string_resolved ( parent - > type , parent - > ce ) ;
2019-10-17 19:01:02 +08:00
zend_error_noreturn ( E_COMPILE_ERROR ,
2019-10-17 19:42:47 +08:00
" Type of %s::$%s must be %s (as in class %s) " ,
2019-10-17 19:01:02 +08:00
ZSTR_VAL ( child - > ce - > name ) ,
2019-12-09 15:48:33 +08:00
zend_get_unmangled_property_name ( child - > name ) ,
2019-10-17 19:42:47 +08:00
ZSTR_VAL ( type_str ) ,
2019-10-17 19:01:02 +08:00
ZSTR_VAL ( parent - > ce - > name ) ) ;
2019-01-07 19:28:51 +08:00
}
2015-03-04 22:41:01 +08:00
static void do_inherit_property ( zend_property_info * parent_info , zend_string * key , zend_class_entry * ce ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2017-11-24 19:01:19 +08:00
zval * child = zend_hash_find_ex ( & ce - > properties_info , key , 1 ) ;
2015-03-05 04:06:07 +08:00
zend_property_info * child_info ;
2014-09-19 06:01:05 +08:00
2015-03-05 04:06:07 +08:00
if ( UNEXPECTED ( child ) ) {
child_info = Z_PTR_P ( child ) ;
2018-09-12 16:43:15 +08:00
if ( parent_info - > flags & ( ZEND_ACC_PRIVATE | ZEND_ACC_CHANGED ) ) {
2014-09-19 06:01:05 +08:00
child_info - > flags | = ZEND_ACC_CHANGED ;
2018-09-12 16:43:15 +08:00
}
if ( ! ( parent_info - > flags & ZEND_ACC_PRIVATE ) ) {
2015-03-05 07:10:38 +08:00
if ( UNEXPECTED ( ( parent_info - > flags & ZEND_ACC_STATIC ) ! = ( child_info - > flags & ZEND_ACC_STATIC ) ) ) {
2015-03-04 22:41:01 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Cannot redeclare %s%s::$%s as %s%s::$%s " ,
2020-01-22 00:19:07 +08:00
( parent_info - > flags & ZEND_ACC_STATIC ) ? " static " : " non static " , ZSTR_VAL ( parent_info - > ce - > name ) , ZSTR_VAL ( key ) ,
2015-06-30 18:59:27 +08:00
( child_info - > flags & ZEND_ACC_STATIC ) ? " static " : " non static " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( key ) ) ;
2015-03-04 22:41:01 +08:00
}
2015-03-05 07:10:38 +08:00
if ( UNEXPECTED ( ( child_info - > flags & ZEND_ACC_PPP_MASK ) > ( parent_info - > flags & ZEND_ACC_PPP_MASK ) ) ) {
2020-01-22 00:19:07 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Access level to %s::$%s must be %s (as in class %s)%s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( key ) , zend_visibility_string ( parent_info - > flags ) , ZSTR_VAL ( parent_info - > ce - > name ) , ( parent_info - > flags & ZEND_ACC_PUBLIC ) ? " " : " or weaker " ) ;
2015-03-04 22:41:01 +08:00
} else if ( ( child_info - > flags & ZEND_ACC_STATIC ) = = 0 ) {
int parent_num = OBJ_PROP_TO_NUM ( parent_info - > offset ) ;
int child_num = OBJ_PROP_TO_NUM ( child_info - > offset ) ;
2016-05-06 14:48:58 +08:00
/* Don't keep default properties in GC (they may be freed by opcache) */
2016-04-19 06:37:24 +08:00
zval_ptr_dtor_nogc ( & ( ce - > default_properties_table [ parent_num ] ) ) ;
2015-03-04 22:41:01 +08:00
ce - > default_properties_table [ parent_num ] = ce - > default_properties_table [ child_num ] ;
ZVAL_UNDEF ( & ce - > default_properties_table [ child_num ] ) ;
child_info - > offset = parent_info - > offset ;
}
2019-01-07 19:28:51 +08:00
if ( UNEXPECTED ( ZEND_TYPE_IS_SET ( parent_info - > type ) ) ) {
2019-10-17 19:01:02 +08:00
inheritance_status status = property_types_compatible ( parent_info , child_info ) ;
if ( status = = INHERITANCE_ERROR ) {
emit_incompatible_property_error ( child_info , parent_info ) ;
}
if ( status = = INHERITANCE_UNRESOLVED ) {
add_property_compatibility_obligation ( ce , child_info , parent_info ) ;
2019-01-07 19:28:51 +08:00
}
} else if ( UNEXPECTED ( ZEND_TYPE_IS_SET ( child_info - > type ) & & ! ZEND_TYPE_IS_SET ( parent_info - > type ) ) ) {
zend_error_noreturn ( E_COMPILE_ERROR ,
" Type of %s::$%s must not be defined (as in class %s) " ,
ZSTR_VAL ( ce - > name ) ,
ZSTR_VAL ( key ) ,
2020-01-22 00:19:07 +08:00
ZSTR_VAL ( parent_info - > ce - > name ) ) ;
2019-01-07 19:28:51 +08:00
}
2015-03-04 22:41:01 +08:00
}
} else {
2018-09-11 16:56:45 +08:00
if ( UNEXPECTED ( ce - > type & ZEND_INTERNAL_CLASS ) ) {
child_info = zend_duplicate_property_info_internal ( parent_info ) ;
2015-03-04 22:41:01 +08:00
} else {
2018-09-11 16:56:45 +08:00
child_info = parent_info ;
2014-09-19 06:01:05 +08:00
}
2015-03-04 22:41:01 +08:00
_zend_hash_append_ptr ( & ce - > properties_info , key , child_info ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2014-12-14 06:06:14 +08:00
static inline void do_implement_interface ( zend_class_entry * ce , zend_class_entry * iface ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2014-12-14 06:06:14 +08:00
if ( ! ( ce - > ce_flags & ZEND_ACC_INTERFACE ) & & iface - > interface_gets_implemented & & iface - > interface_gets_implemented ( iface , ce ) = = FAILURE ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_CORE_ERROR , " Class %s could not implement interface %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( iface - > name ) ) ;
2014-09-19 06:01:05 +08:00
}
2019-09-11 22:27:28 +08:00
/* This should be prevented by the class lookup logic. */
ZEND_ASSERT ( ce ! = iface ) ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2018-08-23 22:16:28 +08:00
static void zend_do_inherit_interfaces ( zend_class_entry * ce , const zend_class_entry * iface ) /* { { { */
2014-09-19 06:01:05 +08:00
{
/* expects interface to be contained in ce's interface list already */
uint32_t i , ce_num , if_num = iface - > num_interfaces ;
zend_class_entry * entry ;
ce_num = ce - > num_interfaces ;
if ( ce - > type = = ZEND_INTERNAL_CLASS ) {
ce - > interfaces = ( zend_class_entry * * ) realloc ( ce - > interfaces , sizeof ( zend_class_entry * ) * ( ce_num + if_num ) ) ;
} else {
ce - > interfaces = ( zend_class_entry * * ) erealloc ( ce - > interfaces , sizeof ( zend_class_entry * ) * ( ce_num + if_num ) ) ;
}
/* Inherit the interfaces, only if they're not already inherited by the class */
while ( if_num - - ) {
entry = iface - > interfaces [ if_num ] ;
for ( i = 0 ; i < ce_num ; i + + ) {
if ( ce - > interfaces [ i ] = = entry ) {
break ;
}
}
if ( i = = ce_num ) {
ce - > interfaces [ ce - > num_interfaces + + ] = entry ;
}
}
2019-05-27 17:39:56 +08:00
ce - > ce_flags | = ZEND_ACC_RESOLVED_INTERFACES ;
2014-09-19 06:01:05 +08:00
/* and now call the implementing handlers */
while ( ce_num < ce - > num_interfaces ) {
2014-12-14 06:06:14 +08:00
do_implement_interface ( ce , ce - > interfaces [ ce_num + + ] ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2015-12-08 17:40:42 +08:00
static void do_inherit_class_constant ( zend_string * name , zend_class_constant * parent_const , zend_class_entry * ce ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2017-11-24 19:01:19 +08:00
zval * zv = zend_hash_find_ex ( & ce - > constants_table , name , 1 ) ;
zend_class_constant * c ;
2015-12-08 17:40:42 +08:00
2017-11-24 19:01:19 +08:00
if ( zv ! = NULL ) {
c = ( zend_class_constant * ) Z_PTR_P ( zv ) ;
2015-12-08 17:40:42 +08:00
if ( UNEXPECTED ( ( Z_ACCESS_FLAGS ( c - > value ) & ZEND_ACC_PPP_MASK ) > ( Z_ACCESS_FLAGS ( parent_const - > value ) & ZEND_ACC_PPP_MASK ) ) ) {
zend_error_noreturn ( E_COMPILE_ERROR , " Access level to %s::%s must be %s (as in class %s)%s " ,
2020-01-22 00:19:07 +08:00
ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( name ) , zend_visibility_string ( Z_ACCESS_FLAGS ( parent_const - > value ) ) , ZSTR_VAL ( parent_const - > ce - > name ) , ( Z_ACCESS_FLAGS ( parent_const - > value ) & ZEND_ACC_PUBLIC ) ? " " : " or weaker " ) ;
2015-03-05 04:06:07 +08:00
}
2015-12-08 17:40:42 +08:00
} else if ( ! ( Z_ACCESS_FLAGS ( parent_const - > value ) & ZEND_ACC_PRIVATE ) ) {
2017-10-10 15:11:05 +08:00
if ( Z_TYPE ( parent_const - > value ) = = IS_CONSTANT_AST ) {
2015-03-05 04:06:07 +08:00
ce - > ce_flags & = ~ ZEND_ACC_CONSTANTS_UPDATED ;
2014-09-19 06:01:05 +08:00
}
2017-10-31 06:20:38 +08:00
if ( ce - > type & ZEND_INTERNAL_CLASS ) {
c = pemalloc ( sizeof ( zend_class_constant ) , 1 ) ;
memcpy ( c , parent_const , sizeof ( zend_class_constant ) ) ;
parent_const = c ;
}
2017-10-31 04:13:10 +08:00
_zend_hash_append_ptr ( & ce - > constants_table , name , parent_const ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2019-01-07 19:28:51 +08:00
void zend_build_properties_info_table ( zend_class_entry * ce )
{
zend_property_info * * table , * prop ;
2019-02-15 20:42:37 +08:00
size_t size ;
2019-01-07 19:28:51 +08:00
if ( ce - > default_properties_count = = 0 ) {
return ;
}
ZEND_ASSERT ( ce - > properties_info_table = = NULL ) ;
2019-02-15 20:42:37 +08:00
size = sizeof ( zend_property_info * ) * ce - > default_properties_count ;
2019-01-07 19:28:51 +08:00
if ( ce - > type = = ZEND_USER_CLASS ) {
2019-02-15 20:42:37 +08:00
ce - > properties_info_table = table = zend_arena_alloc ( & CG ( arena ) , size ) ;
2019-01-07 19:28:51 +08:00
} else {
2019-02-15 20:42:37 +08:00
ce - > properties_info_table = table = pemalloc ( size , 1 ) ;
2019-01-07 19:28:51 +08:00
}
2019-02-15 20:42:37 +08:00
/* Dead slots may be left behind during inheritance. Make sure these are NULLed out. */
memset ( table , 0 , size ) ;
2019-01-07 19:28:51 +08:00
if ( ce - > parent & & ce - > parent - > default_properties_count ! = 0 ) {
zend_property_info * * parent_table = ce - > parent - > properties_info_table ;
memcpy (
table , parent_table ,
sizeof ( zend_property_info * ) * ce - > parent - > default_properties_count
) ;
/* Child did not add any new properties, we are done */
if ( ce - > default_properties_count = = ce - > parent - > default_properties_count ) {
return ;
}
}
ZEND_HASH_FOREACH_PTR ( & ce - > properties_info , prop ) {
if ( prop - > ce = = ce & & ( prop - > flags & ZEND_ACC_STATIC ) = = 0 ) {
table [ OBJ_PROP_TO_NUM ( prop - > offset ) ] = prop ;
}
} ZEND_HASH_FOREACH_END ( ) ;
}
2019-06-27 07:13:06 +08:00
ZEND_API void zend_do_inheritance_ex ( zend_class_entry * ce , zend_class_entry * parent_ce , zend_bool checked ) /* { { { */
2014-09-19 06:01:05 +08:00
{
zend_property_info * property_info ;
zend_function * func ;
zend_string * key ;
2015-03-05 07:10:38 +08:00
if ( UNEXPECTED ( ce - > ce_flags & ZEND_ACC_INTERFACE ) ) {
2014-12-06 12:42:52 +08:00
/* Interface can only inherit other interfaces */
2015-03-05 07:10:38 +08:00
if ( UNEXPECTED ( ! ( parent_ce - > ce_flags & ZEND_ACC_INTERFACE ) ) ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Interface %s may not inherit from class (%s) " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( parent_ce - > name ) ) ;
2014-12-06 12:42:52 +08:00
}
2015-03-05 04:06:07 +08:00
} else if ( UNEXPECTED ( parent_ce - > ce_flags & ( ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_FINAL ) ) ) {
2014-12-06 12:42:52 +08:00
/* Class declaration must not extend traits or interfaces */
if ( parent_ce - > ce_flags & ZEND_ACC_INTERFACE ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s cannot extend from interface %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( parent_ce - > name ) ) ;
2014-12-06 12:42:52 +08:00
} else if ( parent_ce - > ce_flags & ZEND_ACC_TRAIT ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s cannot extend from trait %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( parent_ce - > name ) ) ;
2014-12-06 12:42:52 +08:00
}
/* Class must not extend a final class */
if ( parent_ce - > ce_flags & ZEND_ACC_FINAL ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s may not inherit from final class (%s) " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( parent_ce - > name ) ) ;
2014-12-06 12:42:52 +08:00
}
2014-09-19 06:01:05 +08:00
}
2018-09-18 16:41:40 +08:00
if ( ce - > parent_name ) {
2018-08-24 05:20:57 +08:00
zend_string_release_ex ( ce - > parent_name , 0 ) ;
}
2014-09-19 06:01:05 +08:00
ce - > parent = parent_ce ;
2019-05-27 17:39:56 +08:00
ce - > ce_flags | = ZEND_ACC_RESOLVED_PARENT ;
2014-12-06 12:42:52 +08:00
2014-09-19 06:01:05 +08:00
/* Inherit interfaces */
2018-08-23 22:16:28 +08:00
if ( parent_ce - > num_interfaces ) {
if ( ! ( ce - > ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES ) ) {
zend_do_inherit_interfaces ( ce , parent_ce ) ;
} else {
uint32_t i ;
for ( i = 0 ; i < parent_ce - > num_interfaces ; i + + ) {
do_implement_interface ( ce , parent_ce - > interfaces [ i ] ) ;
}
}
}
2014-09-19 06:01:05 +08:00
/* Inherit properties */
if ( parent_ce - > default_properties_count ) {
2015-04-22 17:53:54 +08:00
zval * src , * dst , * end ;
2014-09-19 06:01:05 +08:00
if ( ce - > default_properties_count ) {
2015-04-22 17:53:54 +08:00
zval * table = pemalloc ( sizeof ( zval ) * ( ce - > default_properties_count + parent_ce - > default_properties_count ) , ce - > type = = ZEND_INTERNAL_CLASS ) ;
src = ce - > default_properties_table + ce - > default_properties_count ;
end = table + parent_ce - > default_properties_count ;
dst = end + ce - > default_properties_count ;
ce - > default_properties_table = table ;
do {
dst - - ;
src - - ;
2019-10-24 22:36:25 +08:00
ZVAL_COPY_VALUE_PROP ( dst , src ) ;
2015-04-22 17:53:54 +08:00
} while ( dst ! = end ) ;
pefree ( src , ce - > type = = ZEND_INTERNAL_CLASS ) ;
end = ce - > default_properties_table ;
} else {
end = pemalloc ( sizeof ( zval ) * parent_ce - > default_properties_count , ce - > type = = ZEND_INTERNAL_CLASS ) ;
dst = end + parent_ce - > default_properties_count ;
ce - > default_properties_table = end ;
}
src = parent_ce - > default_properties_table + parent_ce - > default_properties_count ;
2017-10-31 04:13:10 +08:00
if ( UNEXPECTED ( parent_ce - > type ! = ce - > type ) ) {
/* User class extends internal */
do {
dst - - ;
src - - ;
2019-10-24 22:36:25 +08:00
ZVAL_COPY_OR_DUP_PROP ( dst , src ) ;
2017-10-10 15:11:05 +08:00
if ( Z_OPT_TYPE_P ( dst ) = = IS_CONSTANT_AST ) {
2014-09-19 06:01:05 +08:00
ce - > ce_flags & = ~ ZEND_ACC_CONSTANTS_UPDATED ;
}
continue ;
2017-10-31 04:13:10 +08:00
} while ( dst ! = end ) ;
} else {
do {
dst - - ;
src - - ;
2019-10-24 22:36:25 +08:00
ZVAL_COPY_PROP ( dst , src ) ;
2017-10-31 04:13:10 +08:00
if ( Z_OPT_TYPE_P ( dst ) = = IS_CONSTANT_AST ) {
ce - > ce_flags & = ~ ZEND_ACC_CONSTANTS_UPDATED ;
}
continue ;
} while ( dst ! = end ) ;
}
2014-09-19 06:01:05 +08:00
ce - > default_properties_count + = parent_ce - > default_properties_count ;
}
2015-04-22 17:53:54 +08:00
if ( parent_ce - > default_static_members_count ) {
zval * src , * dst , * end ;
if ( ce - > default_static_members_count ) {
zval * table = pemalloc ( sizeof ( zval ) * ( ce - > default_static_members_count + parent_ce - > default_static_members_count ) , ce - > type = = ZEND_INTERNAL_CLASS ) ;
src = ce - > default_static_members_table + ce - > default_static_members_count ;
end = table + parent_ce - > default_static_members_count ;
dst = end + ce - > default_static_members_count ;
ce - > default_static_members_table = table ;
do {
dst - - ;
src - - ;
ZVAL_COPY_VALUE ( dst , src ) ;
} while ( dst ! = end ) ;
pefree ( src , ce - > type = = ZEND_INTERNAL_CLASS ) ;
end = ce - > default_static_members_table ;
} else {
end = pemalloc ( sizeof ( zval ) * parent_ce - > default_static_members_count , ce - > type = = ZEND_INTERNAL_CLASS ) ;
dst = end + parent_ce - > default_static_members_count ;
ce - > default_static_members_table = end ;
}
2017-10-31 04:13:10 +08:00
if ( UNEXPECTED ( parent_ce - > type ! = ce - > type ) ) {
/* User class extends internal */
2018-10-19 18:21:31 +08:00
if ( CE_STATIC_MEMBERS ( parent_ce ) = = NULL ) {
zend_class_init_statics ( parent_ce ) ;
}
2017-10-31 04:13:10 +08:00
if ( UNEXPECTED ( zend_update_class_constants ( parent_ce ) ! = SUCCESS ) ) {
ZEND_ASSERT ( 0 ) ;
}
src = CE_STATIC_MEMBERS ( parent_ce ) + parent_ce - > default_static_members_count ;
do {
dst - - ;
src - - ;
2018-06-22 18:58:48 +08:00
if ( Z_TYPE_P ( src ) = = IS_INDIRECT ) {
ZVAL_INDIRECT ( dst , Z_INDIRECT_P ( src ) ) ;
2017-12-26 18:30:25 +08:00
} else {
2018-06-22 18:58:48 +08:00
ZVAL_INDIRECT ( dst , src ) ;
2017-12-26 18:30:25 +08:00
}
2017-10-31 04:13:10 +08:00
} while ( dst ! = end ) ;
} else if ( ce - > type = = ZEND_USER_CLASS ) {
2018-10-19 18:21:31 +08:00
if ( CE_STATIC_MEMBERS ( parent_ce ) = = NULL ) {
2019-06-25 01:32:27 +08:00
ZEND_ASSERT ( parent_ce - > ce_flags & ( ZEND_ACC_IMMUTABLE | ZEND_ACC_PRELOADED ) ) ;
2018-10-19 18:21:31 +08:00
zend_class_init_statics ( parent_ce ) ;
}
2018-10-17 20:52:50 +08:00
src = CE_STATIC_MEMBERS ( parent_ce ) + parent_ce - > default_static_members_count ;
2017-10-31 04:13:10 +08:00
do {
dst - - ;
src - - ;
2018-06-22 18:58:48 +08:00
if ( Z_TYPE_P ( src ) = = IS_INDIRECT ) {
ZVAL_INDIRECT ( dst , Z_INDIRECT_P ( src ) ) ;
2017-12-26 18:30:25 +08:00
} else {
2018-06-22 18:58:48 +08:00
ZVAL_INDIRECT ( dst , src ) ;
2017-12-26 18:30:25 +08:00
}
2018-06-22 18:58:48 +08:00
if ( Z_TYPE_P ( Z_INDIRECT_P ( dst ) ) = = IS_CONSTANT_AST ) {
2017-10-31 04:13:10 +08:00
ce - > ce_flags & = ~ ZEND_ACC_CONSTANTS_UPDATED ;
}
} while ( dst ! = end ) ;
} else {
src = parent_ce - > default_static_members_table + parent_ce - > default_static_members_count ;
do {
dst - - ;
src - - ;
2018-06-22 18:58:48 +08:00
if ( Z_TYPE_P ( src ) = = IS_INDIRECT ) {
ZVAL_INDIRECT ( dst , Z_INDIRECT_P ( src ) ) ;
} else {
ZVAL_INDIRECT ( dst , src ) ;
2015-07-10 15:27:06 +08:00
}
2017-10-31 04:13:10 +08:00
} while ( dst ! = end ) ;
}
2015-04-22 17:53:54 +08:00
ce - > default_static_members_count + = parent_ce - > default_static_members_count ;
2018-10-17 20:52:50 +08:00
if ( ! ZEND_MAP_PTR ( ce - > static_members_table ) ) {
ZEND_ASSERT ( ce - > type = = ZEND_INTERNAL_CLASS ) ;
if ( ! EG ( current_execute_data ) ) {
ZEND_MAP_PTR_NEW ( ce - > static_members_table ) ;
} else {
/* internal class loaded by dl() */
ZEND_MAP_PTR_INIT ( ce - > static_members_table , & ce - > default_static_members_table ) ;
2018-10-02 00:05:31 +08:00
}
2014-09-19 06:01:05 +08:00
}
}
ZEND_HASH_FOREACH_PTR ( & ce - > properties_info , property_info ) {
if ( property_info - > ce = = ce ) {
if ( property_info - > flags & ZEND_ACC_STATIC ) {
property_info - > offset + = parent_ce - > default_static_members_count ;
} else {
2014-11-06 19:50:03 +08:00
property_info - > offset + = parent_ce - > default_properties_count * sizeof ( zval ) ;
2014-09-19 06:01:05 +08:00
}
}
} ZEND_HASH_FOREACH_END ( ) ;
2015-03-05 04:06:07 +08:00
if ( zend_hash_num_elements ( & parent_ce - > properties_info ) ) {
zend_hash_extend ( & ce - > properties_info ,
zend_hash_num_elements ( & ce - > properties_info ) +
zend_hash_num_elements ( & parent_ce - > properties_info ) , 0 ) ;
2015-03-04 22:41:01 +08:00
2015-03-05 04:06:07 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > properties_info , key , property_info ) {
do_inherit_property ( property_info , key , ce ) ;
} ZEND_HASH_FOREACH_END ( ) ;
}
2014-09-19 06:01:05 +08:00
2015-03-05 04:06:07 +08:00
if ( zend_hash_num_elements ( & parent_ce - > constants_table ) ) {
2015-12-08 17:40:42 +08:00
zend_class_constant * c ;
2015-03-05 04:06:07 +08:00
zend_hash_extend ( & ce - > constants_table ,
zend_hash_num_elements ( & ce - > constants_table ) +
zend_hash_num_elements ( & parent_ce - > constants_table ) , 0 ) ;
2014-09-19 06:01:05 +08:00
2015-12-08 17:40:42 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > constants_table , key , c ) {
do_inherit_class_constant ( key , c , ce ) ;
2015-03-05 04:06:07 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
}
if ( zend_hash_num_elements ( & parent_ce - > function_table ) ) {
zend_hash_extend ( & ce - > function_table ,
zend_hash_num_elements ( & ce - > function_table ) +
zend_hash_num_elements ( & parent_ce - > function_table ) , 0 ) ;
2019-06-27 07:13:06 +08:00
if ( checked ) {
2019-06-27 18:11:08 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > function_table , key , func ) {
do_inherit_method ( key , func , ce , 0 , 1 ) ;
} ZEND_HASH_FOREACH_END ( ) ;
2019-06-27 07:13:06 +08:00
} else {
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > function_table , key , func ) {
2019-06-27 18:11:08 +08:00
do_inherit_method ( key , func , ce , 0 , 0 ) ;
2019-06-27 07:13:06 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
}
2015-03-05 04:06:07 +08:00
}
2014-09-19 06:01:05 +08:00
2014-12-14 06:06:14 +08:00
do_inherit_parent_constructor ( ce ) ;
2014-09-19 06:01:05 +08:00
2018-08-27 21:40:25 +08:00
if ( ce - > type = = ZEND_INTERNAL_CLASS ) {
if ( ce - > ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ) {
ce - > ce_flags | = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ;
}
2014-09-19 06:01:05 +08:00
}
2019-01-07 19:28:51 +08:00
ce - > ce_flags | = parent_ce - > ce_flags & ( ZEND_HAS_STATIC_IN_METHODS | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_USE_GUARDS ) ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2015-12-08 17:40:42 +08:00
static zend_bool do_inherit_constant_check ( HashTable * child_constants_table , zend_class_constant * parent_constant , zend_string * name , const zend_class_entry * iface ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2017-11-24 19:01:19 +08:00
zval * zv = zend_hash_find_ex ( child_constants_table , name , 1 ) ;
2015-12-08 17:40:42 +08:00
zend_class_constant * old_constant ;
2014-09-19 06:01:05 +08:00
2017-11-24 19:01:19 +08:00
if ( zv ! = NULL ) {
old_constant = ( zend_class_constant * ) Z_PTR_P ( zv ) ;
2015-12-08 17:40:42 +08:00
if ( old_constant - > ce ! = parent_constant - > ce ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Cannot inherit previously-inherited or override constant %s from interface %s " , ZSTR_VAL ( name ) , ZSTR_VAL ( iface - > name ) ) ;
2014-09-19 06:01:05 +08:00
}
return 0 ;
}
return 1 ;
}
/* }}} */
2015-12-08 17:40:42 +08:00
static void do_inherit_iface_constant ( zend_string * name , zend_class_constant * c , zend_class_entry * ce , zend_class_entry * iface ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2015-12-08 17:40:42 +08:00
if ( do_inherit_constant_check ( & ce - > constants_table , c , name , iface ) ) {
2016-01-21 13:30:41 +08:00
zend_class_constant * ct ;
2017-10-10 15:11:05 +08:00
if ( Z_TYPE ( c - > value ) = = IS_CONSTANT_AST ) {
2014-09-19 06:01:05 +08:00
ce - > ce_flags & = ~ ZEND_ACC_CONSTANTS_UPDATED ;
}
2017-10-31 06:20:38 +08:00
if ( ce - > type & ZEND_INTERNAL_CLASS ) {
ct = pemalloc ( sizeof ( zend_class_constant ) , 1 ) ;
memcpy ( ct , c , sizeof ( zend_class_constant ) ) ;
c = ct ;
}
2017-10-31 04:13:10 +08:00
zend_hash_update_ptr ( & ce - > constants_table , name , c ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2019-06-27 18:11:08 +08:00
static void do_interface_implementation ( zend_class_entry * ce , zend_class_entry * iface ) /* { { { */
{
zend_function * func ;
zend_string * key ;
zend_class_constant * c ;
ZEND_HASH_FOREACH_STR_KEY_PTR ( & iface - > constants_table , key , c ) {
do_inherit_iface_constant ( key , c , ce , iface ) ;
} ZEND_HASH_FOREACH_END ( ) ;
ZEND_HASH_FOREACH_STR_KEY_PTR ( & iface - > function_table , key , func ) {
do_inherit_method ( key , func , ce , 1 , 0 ) ;
} ZEND_HASH_FOREACH_END ( ) ;
do_implement_interface ( ce , iface ) ;
if ( iface - > num_interfaces ) {
zend_do_inherit_interfaces ( ce , iface ) ;
}
}
/* }}} */
2014-12-14 06:06:14 +08:00
ZEND_API void zend_do_implement_interface ( zend_class_entry * ce , zend_class_entry * iface ) /* { { { */
2014-09-19 06:01:05 +08:00
{
uint32_t i , ignore = 0 ;
uint32_t current_iface_num = ce - > num_interfaces ;
uint32_t parent_iface_num = ce - > parent ? ce - > parent - > num_interfaces : 0 ;
zend_string * key ;
2015-12-08 17:40:42 +08:00
zend_class_constant * c ;
2014-09-19 06:01:05 +08:00
2018-09-18 16:41:40 +08:00
ZEND_ASSERT ( ce - > ce_flags & ZEND_ACC_LINKED ) ;
2018-08-23 22:16:28 +08:00
2014-09-19 06:01:05 +08:00
for ( i = 0 ; i < ce - > num_interfaces ; i + + ) {
if ( ce - > interfaces [ i ] = = NULL ) {
memmove ( ce - > interfaces + i , ce - > interfaces + i + 1 , sizeof ( zend_class_entry * ) * ( - - ce - > num_interfaces - i ) ) ;
i - - ;
} else if ( ce - > interfaces [ i ] = = iface ) {
2015-03-05 07:10:38 +08:00
if ( EXPECTED ( i < parent_iface_num ) ) {
2014-09-19 06:01:05 +08:00
ignore = 1 ;
} else {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s cannot implement previously implemented interface %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( iface - > name ) ) ;
2014-09-19 06:01:05 +08:00
}
}
}
if ( ignore ) {
/* Check for attempt to redeclare interface constants */
2015-12-08 17:40:42 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & ce - > constants_table , key , c ) {
do_inherit_constant_check ( & iface - > constants_table , c , key , iface ) ;
2014-09-19 06:01:05 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
} else {
if ( ce - > num_interfaces > = current_iface_num ) {
if ( ce - > type = = ZEND_INTERNAL_CLASS ) {
ce - > interfaces = ( zend_class_entry * * ) realloc ( ce - > interfaces , sizeof ( zend_class_entry * ) * ( + + current_iface_num ) ) ;
} else {
ce - > interfaces = ( zend_class_entry * * ) erealloc ( ce - > interfaces , sizeof ( zend_class_entry * ) * ( + + current_iface_num ) ) ;
}
}
ce - > interfaces [ ce - > num_interfaces + + ] = iface ;
2019-06-27 18:11:08 +08:00
do_interface_implementation ( ce , iface ) ;
2018-08-23 22:16:28 +08:00
}
}
/* }}} */
2019-09-11 21:31:04 +08:00
static void zend_do_implement_interfaces ( zend_class_entry * ce , zend_class_entry * * interfaces ) /* { { { */
2018-08-23 22:16:28 +08:00
{
2019-09-11 21:31:04 +08:00
zend_class_entry * iface ;
uint32_t num_parent_interfaces = ce - > parent ? ce - > parent - > num_interfaces : 0 ;
uint32_t num_interfaces = num_parent_interfaces ;
2018-08-23 22:16:28 +08:00
zend_string * key ;
zend_class_constant * c ;
uint32_t i , j ;
for ( i = 0 ; i < ce - > num_interfaces ; i + + ) {
2019-09-11 21:31:04 +08:00
iface = interfaces [ num_parent_interfaces + i ] ;
2019-05-27 17:39:56 +08:00
if ( ! ( iface - > ce_flags & ZEND_ACC_LINKED ) ) {
add_dependency_obligation ( ce , iface ) ;
2018-08-23 22:16:28 +08:00
}
if ( UNEXPECTED ( ! ( iface - > ce_flags & ZEND_ACC_INTERFACE ) ) ) {
efree ( interfaces ) ;
zend_error_noreturn ( E_ERROR , " %s cannot implement %s - it is not an interface " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( iface - > name ) ) ;
return ;
}
for ( j = 0 ; j < num_interfaces ; j + + ) {
if ( interfaces [ j ] = = iface ) {
2019-09-11 21:31:04 +08:00
if ( j > = num_parent_interfaces ) {
2018-08-23 22:16:28 +08:00
efree ( interfaces ) ;
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s cannot implement previously implemented interface %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( iface - > name ) ) ;
return ;
}
/* skip duplications */
ZEND_HASH_FOREACH_STR_KEY_PTR ( & ce - > constants_table , key , c ) {
do_inherit_constant_check ( & iface - > constants_table , c , key , iface ) ;
} ZEND_HASH_FOREACH_END ( ) ;
iface = NULL ;
break ;
}
}
if ( iface ) {
interfaces [ num_interfaces ] = iface ;
num_interfaces + + ;
}
}
for ( i = 0 ; i < ce - > num_interfaces ; i + + ) {
zend_string_release_ex ( ce - > interface_names [ i ] . name , 0 ) ;
zend_string_release_ex ( ce - > interface_names [ i ] . lc_name , 0 ) ;
}
efree ( ce - > interface_names ) ;
ce - > num_interfaces = num_interfaces ;
ce - > interfaces = interfaces ;
2019-05-27 17:39:56 +08:00
ce - > ce_flags | = ZEND_ACC_RESOLVED_INTERFACES ;
2018-08-23 22:16:28 +08:00
2019-09-11 21:31:04 +08:00
i = num_parent_interfaces ;
2018-08-23 22:16:28 +08:00
for ( ; i < ce - > num_interfaces ; i + + ) {
2019-06-27 18:11:08 +08:00
do_interface_implementation ( ce , ce - > interfaces [ i ] ) ;
2018-08-23 22:16:28 +08:00
}
2014-09-19 06:01:05 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void zend_add_magic_methods ( zend_class_entry * ce , zend_string * mname , zend_function * fe ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2019-02-15 18:20:28 +08:00
if ( zend_string_equals_literal ( mname , " serialize " ) ) {
ce - > serialize_func = fe ;
} else if ( zend_string_equals_literal ( mname , " unserialize " ) ) {
ce - > unserialize_func = fe ;
2019-02-15 18:28:14 +08:00
} else if ( ZSTR_VAL ( mname ) [ 0 ] ! = ' _ ' | | ZSTR_VAL ( mname ) [ 1 ] ! = ' _ ' ) {
2018-07-02 22:03:02 +08:00
/* pass */
} else if ( zend_string_equals_literal ( mname , ZEND_CLONE_FUNC_NAME ) ) {
2017-04-22 21:44:24 +08:00
ce - > clone = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_CONSTRUCTOR_FUNC_NAME ) ) {
2018-09-05 18:16:10 +08:00
ce - > constructor = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_DESTRUCTOR_FUNC_NAME ) ) {
2018-09-05 18:16:10 +08:00
ce - > destructor = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_GET_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __get = fe ;
2015-02-04 20:24:13 +08:00
ce - > ce_flags | = ZEND_ACC_USE_GUARDS ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_SET_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __set = fe ;
2015-02-04 20:24:13 +08:00
ce - > ce_flags | = ZEND_ACC_USE_GUARDS ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_CALL_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __call = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_UNSET_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __unset = fe ;
2015-02-04 20:24:13 +08:00
ce - > ce_flags | = ZEND_ACC_USE_GUARDS ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_ISSET_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __isset = fe ;
2015-02-04 20:24:13 +08:00
ce - > ce_flags | = ZEND_ACC_USE_GUARDS ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_CALLSTATIC_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __callstatic = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_TOSTRING_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __tostring = fe ;
2016-01-05 23:27:18 +08:00
} else if ( zend_string_equals_literal ( mname , ZEND_DEBUGINFO_FUNC_NAME ) ) {
2014-09-19 06:01:05 +08:00
ce - > __debugInfo = fe ;
}
}
/* }}} */
2018-08-10 10:19:55 +08:00
static void zend_add_trait_method ( zend_class_entry * ce , const char * name , zend_string * key , zend_function * fn , HashTable * * overridden ) /* { { { */
2014-09-19 06:01:05 +08:00
{
zend_function * existing_fn = NULL ;
zend_function * new_fn ;
if ( ( existing_fn = zend_hash_find_ptr ( & ce - > function_table , key ) ) ! = NULL ) {
2018-08-22 00:40:06 +08:00
/* if it is the same function with the same visibility and has not been assigned a class scope yet, regardless
* of where it is coming from there is no conflict and we do not need to add it again */
2018-08-04 00:30:03 +08:00
if ( existing_fn - > op_array . opcodes = = fn - > op_array . opcodes & &
2018-08-22 00:40:06 +08:00
( existing_fn - > common . fn_flags & ZEND_ACC_PPP_MASK ) = = ( fn - > common . fn_flags & ZEND_ACC_PPP_MASK ) & &
( existing_fn - > common . scope - > ce_flags & ZEND_ACC_TRAIT ) = = ZEND_ACC_TRAIT ) {
2017-10-01 04:24:38 +08:00
return ;
}
2014-09-19 06:01:05 +08:00
if ( existing_fn - > common . scope = = ce ) {
/* members from the current class override trait methods */
2018-08-10 10:19:55 +08:00
/* use temporary *overridden HashTable to detect hidden conflict */
if ( * overridden ) {
if ( ( existing_fn = zend_hash_find_ptr ( * overridden , key ) ) ! = NULL ) {
2014-09-19 06:01:05 +08:00
if ( existing_fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) {
/* Make sure the trait method is compatible with previosly declared abstract method */
2019-06-25 21:53:46 +08:00
perform_delayable_implementation_check ( ce , fn , existing_fn ) ;
2017-05-21 20:41:55 +08:00
}
if ( fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) {
2014-09-19 06:01:05 +08:00
/* Make sure the abstract declaration is compatible with previous declaration */
2019-06-25 21:53:46 +08:00
perform_delayable_implementation_check ( ce , existing_fn , fn ) ;
2014-09-19 06:01:05 +08:00
return ;
}
}
} else {
2018-08-10 10:19:55 +08:00
ALLOC_HASHTABLE ( * overridden ) ;
zend_hash_init_ex ( * overridden , 8 , NULL , overridden_ptr_dtor , 0 , 0 ) ;
2014-09-19 06:01:05 +08:00
}
2018-08-10 10:19:55 +08:00
zend_hash_update_mem ( * overridden , key , fn , sizeof ( zend_function ) ) ;
2014-09-19 06:01:05 +08:00
return ;
2019-12-18 22:53:09 +08:00
} else if ( ( fn - > common . fn_flags & ZEND_ACC_ABSTRACT )
& & ! ( existing_fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) ) {
2014-09-19 06:01:05 +08:00
/* Make sure the abstract declaration is compatible with previous declaration */
2019-06-25 21:53:46 +08:00
perform_delayable_implementation_check ( ce , existing_fn , fn ) ;
2014-09-19 06:01:05 +08:00
return ;
2019-12-18 22:53:09 +08:00
} else if ( UNEXPECTED ( ( existing_fn - > common . scope - > ce_flags & ZEND_ACC_TRAIT )
& & ! ( existing_fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) ) ) {
2014-09-19 06:01:05 +08:00
/* two traits can't define the same non-abstract method */
# if 1
zend_error_noreturn ( E_COMPILE_ERROR , " Trait method %s has not been applied, because there are collisions with other trait methods on %s " ,
2015-06-30 18:59:27 +08:00
name , ZSTR_VAL ( ce - > name ) ) ;
2014-09-19 06:01:05 +08:00
# else /* TODO: better error message */
zend_error_noreturn ( E_COMPILE_ERROR , " Trait method %s::%s has not been applied as %s::%s, because of collision with %s::%s " ,
2015-06-30 18:59:27 +08:00
ZSTR_VAL ( fn - > common . scope - > name ) , ZSTR_VAL ( fn - > common . function_name ) ,
ZSTR_VAL ( ce - > name ) , name ,
ZSTR_VAL ( existing_fn - > common . scope - > name ) , ZSTR_VAL ( existing_fn - > common . function_name ) ) ;
2014-09-19 06:01:05 +08:00
# endif
} else {
/* inherited members are overridden by members inserted by traits */
/* check whether the trait method fulfills the inheritance requirements */
2018-10-03 23:32:25 +08:00
do_inheritance_check_on_method ( fn , existing_fn , ce , NULL ) ;
2015-04-10 22:50:15 +08:00
fn - > common . prototype = NULL ;
2014-09-19 06:01:05 +08:00
}
}
2018-08-30 04:40:17 +08:00
if ( UNEXPECTED ( fn - > type = = ZEND_INTERNAL_FUNCTION ) ) {
new_fn = zend_arena_alloc ( & CG ( arena ) , sizeof ( zend_internal_function ) ) ;
memcpy ( new_fn , fn , sizeof ( zend_internal_function ) ) ;
new_fn - > common . fn_flags | = ZEND_ACC_ARENA_ALLOCATED ;
} else {
new_fn = zend_arena_alloc ( & CG ( arena ) , sizeof ( zend_op_array ) ) ;
memcpy ( new_fn , fn , sizeof ( zend_op_array ) ) ;
2018-11-14 21:32:07 +08:00
new_fn - > op_array . fn_flags | = ZEND_ACC_TRAIT_CLONE ;
2018-10-17 20:52:50 +08:00
new_fn - > op_array . fn_flags & = ~ ZEND_ACC_IMMUTABLE ;
2018-08-30 04:40:17 +08:00
}
2018-10-30 20:08:45 +08:00
function_add_ref ( new_fn ) ;
2014-09-19 06:01:05 +08:00
fn = zend_hash_update_ptr ( & ce - > function_table , key , new_fn ) ;
2014-12-14 06:06:14 +08:00
zend_add_magic_methods ( ce , key , fn ) ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
static void zend_fixup_trait_method ( zend_function * fn , zend_class_entry * ce ) /* { { { */
{
if ( ( fn - > common . scope - > ce_flags & ZEND_ACC_TRAIT ) = = ZEND_ACC_TRAIT ) {
fn - > common . scope = ce ;
if ( fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) {
ce - > ce_flags | = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ;
}
2015-05-04 10:35:55 +08:00
if ( fn - > type = = ZEND_USER_FUNCTION & & fn - > op_array . static_variables ) {
2014-09-19 06:01:05 +08:00
ce - > ce_flags | = ZEND_HAS_STATIC_IN_METHODS ;
}
}
}
/* }}} */
2018-12-19 07:49:56 +08:00
static void zend_traits_copy_functions ( zend_string * fnname , zend_function * fn , zend_class_entry * ce , HashTable * * overridden , HashTable * exclude_table , zend_class_entry * * aliases ) /* { { { */
2014-09-19 06:01:05 +08:00
{
zend_trait_alias * alias , * * alias_ptr ;
zend_string * lcname ;
zend_function fn_copy ;
2018-07-11 23:56:10 +08:00
int i ;
2014-09-19 06:01:05 +08:00
/* apply aliases which are qualified with a class name, there should not be any ambiguity */
if ( ce - > trait_aliases ) {
alias_ptr = ce - > trait_aliases ;
alias = * alias_ptr ;
2018-07-11 23:56:10 +08:00
i = 0 ;
2014-09-19 06:01:05 +08:00
while ( alias ) {
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
if ( alias - > alias ! = NULL
2018-07-11 23:56:10 +08:00
& & ( ! aliases [ i ] | | fn - > common . scope = = aliases [ i ] )
& & ZSTR_LEN ( alias - > trait_method . method_name ) = = ZSTR_LEN ( fnname )
& & ( zend_binary_strcasecmp ( ZSTR_VAL ( alias - > trait_method . method_name ) , ZSTR_LEN ( alias - > trait_method . method_name ) , ZSTR_VAL ( fnname ) , ZSTR_LEN ( fnname ) ) = = 0 ) ) {
2014-09-19 06:01:05 +08:00
fn_copy = * fn ;
/* if it is 0, no modifieres has been changed */
if ( alias - > modifiers ) {
fn_copy . common . fn_flags = alias - > modifiers | ( fn - > common . fn_flags ^ ( fn - > common . fn_flags & ZEND_ACC_PPP_MASK ) ) ;
}
2014-12-24 20:04:51 +08:00
lcname = zend_string_tolower ( alias - > alias ) ;
2018-08-10 10:19:55 +08:00
zend_add_trait_method ( ce , ZSTR_VAL ( alias - > alias ) , lcname , & fn_copy , overridden ) ;
2018-05-28 21:27:12 +08:00
zend_string_release_ex ( lcname , 0 ) ;
2014-09-19 06:01:05 +08:00
/* Record the trait from which this alias was resolved. */
2018-07-11 23:56:10 +08:00
if ( ! aliases [ i ] ) {
aliases [ i ] = fn - > common . scope ;
}
if ( ! alias - > trait_method . class_name ) {
/* TODO: try to avoid this assignment (it's necessary only for reflection) */
alias - > trait_method . class_name = zend_string_copy ( fn - > common . scope - > name ) ;
2014-09-19 06:01:05 +08:00
}
}
alias_ptr + + ;
alias = * alias_ptr ;
2018-07-11 23:56:10 +08:00
i + + ;
2014-09-19 06:01:05 +08:00
}
}
if ( exclude_table = = NULL | | zend_hash_find ( exclude_table , fnname ) = = NULL ) {
/* is not in hashtable, thus, function is not to be excluded */
2015-07-10 22:26:53 +08:00
/* And how about ZEND_OVERLOADED_FUNCTION? */
memcpy ( & fn_copy , fn , fn - > type = = ZEND_USER_FUNCTION ? sizeof ( zend_op_array ) : sizeof ( zend_internal_function ) ) ;
2014-09-19 06:01:05 +08:00
/* apply aliases which have not alias name, just setting visibility */
if ( ce - > trait_aliases ) {
alias_ptr = ce - > trait_aliases ;
alias = * alias_ptr ;
2018-07-11 23:56:10 +08:00
i = 0 ;
2014-09-19 06:01:05 +08:00
while ( alias ) {
/* Scope unset or equal to the function we compare to, and the alias applies to fn */
if ( alias - > alias = = NULL & & alias - > modifiers ! = 0
2018-07-11 23:56:10 +08:00
& & ( ! aliases [ i ] | | fn - > common . scope = = aliases [ i ] )
& & ( ZSTR_LEN ( alias - > trait_method . method_name ) = = ZSTR_LEN ( fnname ) )
& & ( zend_binary_strcasecmp ( ZSTR_VAL ( alias - > trait_method . method_name ) , ZSTR_LEN ( alias - > trait_method . method_name ) , ZSTR_VAL ( fnname ) , ZSTR_LEN ( fnname ) ) = = 0 ) ) {
2014-09-19 06:01:05 +08:00
fn_copy . common . fn_flags = alias - > modifiers | ( fn - > common . fn_flags ^ ( fn - > common . fn_flags & ZEND_ACC_PPP_MASK ) ) ;
/** Record the trait from which this alias was resolved. */
2018-07-11 23:56:10 +08:00
if ( ! aliases [ i ] ) {
aliases [ i ] = fn - > common . scope ;
}
if ( ! alias - > trait_method . class_name ) {
/* TODO: try to avoid this assignment (it's necessary only for reflection) */
alias - > trait_method . class_name = zend_string_copy ( fn - > common . scope - > name ) ;
2014-09-19 06:01:05 +08:00
}
}
alias_ptr + + ;
alias = * alias_ptr ;
2018-07-11 23:56:10 +08:00
i + + ;
2014-09-19 06:01:05 +08:00
}
}
2018-08-10 10:19:55 +08:00
zend_add_trait_method ( ce , ZSTR_VAL ( fn - > common . function_name ) , fnname , & fn_copy , overridden ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2018-08-23 07:02:26 +08:00
static uint32_t zend_check_trait_usage ( zend_class_entry * ce , zend_class_entry * trait , zend_class_entry * * traits ) /* { { { */
2014-09-19 06:01:05 +08:00
{
uint32_t i ;
2015-03-05 07:10:38 +08:00
if ( UNEXPECTED ( ( trait - > ce_flags & ZEND_ACC_TRAIT ) ! = ZEND_ACC_TRAIT ) ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Class %s is not a trait, Only traits may be used in 'as' and 'insteadof' statements " , ZSTR_VAL ( trait - > name ) ) ;
2018-07-11 23:56:10 +08:00
return 0 ;
2014-09-19 06:01:05 +08:00
}
for ( i = 0 ; i < ce - > num_traits ; i + + ) {
2018-08-23 07:02:26 +08:00
if ( traits [ i ] = = trait ) {
2018-07-11 23:56:10 +08:00
return i ;
2014-09-19 06:01:05 +08:00
}
}
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Required Trait %s wasn't added to %s " , ZSTR_VAL ( trait - > name ) , ZSTR_VAL ( ce - > name ) ) ;
2018-07-11 23:56:10 +08:00
return 0 ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2018-08-23 07:02:26 +08:00
static void zend_traits_init_trait_structures ( zend_class_entry * ce , zend_class_entry * * traits , HashTable * * * exclude_tables_ptr , zend_class_entry * * * aliases_ptr ) /* { { { */
2014-09-19 06:01:05 +08:00
{
size_t i , j = 0 ;
2015-03-05 21:06:59 +08:00
zend_trait_precedence * * precedences ;
2014-09-19 06:01:05 +08:00
zend_trait_precedence * cur_precedence ;
zend_trait_method_reference * cur_method_ref ;
zend_string * lcname ;
2018-07-11 23:56:10 +08:00
HashTable * * exclude_tables = NULL ;
zend_class_entry * * aliases = NULL ;
zend_class_entry * trait ;
2014-09-19 06:01:05 +08:00
/* resolve class references */
if ( ce - > trait_precedences ) {
2018-07-11 23:56:10 +08:00
exclude_tables = ecalloc ( ce - > num_traits , sizeof ( HashTable * ) ) ;
2014-09-19 06:01:05 +08:00
i = 0 ;
2015-03-05 21:06:59 +08:00
precedences = ce - > trait_precedences ;
ce - > trait_precedences = NULL ;
while ( ( cur_precedence = precedences [ i ] ) ) {
2014-09-19 06:01:05 +08:00
/** Resolve classes for all precedence operations. */
2018-07-11 23:56:10 +08:00
cur_method_ref = & cur_precedence - > trait_method ;
trait = zend_fetch_class ( cur_method_ref - > class_name ,
ZEND_FETCH_CLASS_TRAIT | ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
if ( ! trait ) {
zend_error_noreturn ( E_COMPILE_ERROR , " Could not find trait %s " , ZSTR_VAL ( cur_method_ref - > class_name ) ) ;
}
2018-08-23 07:02:26 +08:00
zend_check_trait_usage ( ce , trait , traits ) ;
2018-07-11 23:56:10 +08:00
/** Ensure that the preferred method is actually available. */
lcname = zend_string_tolower ( cur_method_ref - > method_name ) ;
2018-09-19 18:54:37 +08:00
if ( ! zend_hash_exists ( & trait - > function_table , lcname ) ) {
2018-07-11 23:56:10 +08:00
zend_error_noreturn ( E_COMPILE_ERROR ,
" A precedence rule was defined for %s::%s but this method does not exist " ,
ZSTR_VAL ( trait - > name ) ,
ZSTR_VAL ( cur_method_ref - > method_name ) ) ;
}
2014-09-19 06:01:05 +08:00
2018-07-11 23:56:10 +08:00
/** With the other traits, we are more permissive.
We do not give errors for those . This allows to be more
defensive in such definitions .
However , we want to make sure that the insteadof declaration
is consistent in itself .
*/
2014-09-19 06:01:05 +08:00
2018-07-11 23:56:10 +08:00
for ( j = 0 ; j < cur_precedence - > num_excludes ; j + + ) {
zend_string * class_name = cur_precedence - > exclude_class_names [ j ] ;
zend_class_entry * exclude_ce = zend_fetch_class ( class_name , ZEND_FETCH_CLASS_TRAIT | ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
uint32_t trait_num ;
2014-09-19 06:01:05 +08:00
2018-07-11 23:56:10 +08:00
if ( ! exclude_ce ) {
zend_error_noreturn ( E_COMPILE_ERROR , " Could not find trait %s " , ZSTR_VAL ( class_name ) ) ;
}
2018-08-23 07:02:26 +08:00
trait_num = zend_check_trait_usage ( ce , exclude_ce , traits ) ;
2018-07-11 23:56:10 +08:00
if ( ! exclude_tables [ trait_num ] ) {
ALLOC_HASHTABLE ( exclude_tables [ trait_num ] ) ;
zend_hash_init ( exclude_tables [ trait_num ] , 0 , NULL , NULL , 0 ) ;
}
if ( zend_hash_add_empty_element ( exclude_tables [ trait_num ] , lcname ) = = NULL ) {
zend_error_noreturn ( E_COMPILE_ERROR , " Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times " , ZSTR_VAL ( precedences [ i ] - > trait_method . method_name ) , ZSTR_VAL ( exclude_ce - > name ) ) ;
}
2014-09-19 06:01:05 +08:00
2018-07-11 23:56:10 +08:00
/* make sure that the trait method is not from a class mentioned in
exclude_from_classes , for consistency */
if ( trait = = exclude_ce ) {
zend_error_noreturn ( E_COMPILE_ERROR ,
" Inconsistent insteadof definition. "
" The method %s is to be used from %s, but %s is also on the exclude list " ,
ZSTR_VAL ( cur_method_ref - > method_name ) ,
ZSTR_VAL ( trait - > name ) ,
ZSTR_VAL ( trait - > name ) ) ;
2014-09-19 06:01:05 +08:00
}
}
2018-07-11 23:56:10 +08:00
zend_string_release_ex ( lcname , 0 ) ;
2014-09-19 06:01:05 +08:00
i + + ;
}
2015-03-05 21:06:59 +08:00
ce - > trait_precedences = precedences ;
2014-09-19 06:01:05 +08:00
}
if ( ce - > trait_aliases ) {
2018-07-11 23:56:10 +08:00
i = 0 ;
while ( ce - > trait_aliases [ i ] ) {
i + + ;
}
aliases = ecalloc ( i , sizeof ( zend_class_entry * ) ) ;
2014-09-19 06:01:05 +08:00
i = 0 ;
while ( ce - > trait_aliases [ i ] ) {
/** For all aliases with an explicit class name, resolve the class now. */
2018-07-11 23:56:10 +08:00
if ( ce - > trait_aliases [ i ] - > trait_method . class_name ) {
cur_method_ref = & ce - > trait_aliases [ i ] - > trait_method ;
trait = zend_fetch_class ( cur_method_ref - > class_name , ZEND_FETCH_CLASS_TRAIT | ZEND_FETCH_CLASS_NO_AUTOLOAD ) ;
if ( ! trait ) {
2015-06-30 18:59:27 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " Could not find trait %s " , ZSTR_VAL ( cur_method_ref - > class_name ) ) ;
2014-09-19 06:01:05 +08:00
}
2018-08-23 07:02:26 +08:00
zend_check_trait_usage ( ce , trait , traits ) ;
2018-07-11 23:56:10 +08:00
aliases [ i ] = trait ;
2014-09-19 06:01:05 +08:00
/** And, ensure that the referenced method is resolvable, too. */
2014-12-24 20:04:51 +08:00
lcname = zend_string_tolower ( cur_method_ref - > method_name ) ;
2018-09-19 18:54:37 +08:00
if ( ! zend_hash_exists ( & trait - > function_table , lcname ) ) {
2018-07-11 23:56:10 +08:00
zend_error_noreturn ( E_COMPILE_ERROR , " An alias was defined for %s::%s but this method does not exist " , ZSTR_VAL ( trait - > name ) , ZSTR_VAL ( cur_method_ref - > method_name ) ) ;
2014-09-19 06:01:05 +08:00
}
2018-09-19 18:54:37 +08:00
zend_string_release_ex ( lcname , 0 ) ;
2014-09-19 06:01:05 +08:00
}
i + + ;
}
}
2018-07-11 23:56:10 +08:00
* exclude_tables_ptr = exclude_tables ;
* aliases_ptr = aliases ;
2014-09-19 06:01:05 +08:00
}
/* }}} */
2018-08-23 07:02:26 +08:00
static void zend_do_traits_method_binding ( zend_class_entry * ce , zend_class_entry * * traits , HashTable * * exclude_tables , zend_class_entry * * aliases ) /* { { { */
2014-09-19 06:01:05 +08:00
{
uint32_t i ;
2018-08-10 10:19:55 +08:00
HashTable * overridden = NULL ;
2014-09-19 06:01:05 +08:00
zend_string * key ;
zend_function * fn ;
2018-07-11 23:56:10 +08:00
if ( exclude_tables ) {
for ( i = 0 ; i < ce - > num_traits ; i + + ) {
2018-08-23 07:02:26 +08:00
if ( traits [ i ] ) {
/* copies functions, applies defined aliasing, and excludes unused trait methods */
ZEND_HASH_FOREACH_STR_KEY_PTR ( & traits [ i ] - > function_table , key , fn ) {
zend_traits_copy_functions ( key , fn , ce , & overridden , exclude_tables [ i ] , aliases ) ;
} ZEND_HASH_FOREACH_END ( ) ;
if ( exclude_tables [ i ] ) {
zend_hash_destroy ( exclude_tables [ i ] ) ;
FREE_HASHTABLE ( exclude_tables [ i ] ) ;
exclude_tables [ i ] = NULL ;
}
2018-07-11 23:56:10 +08:00
}
}
} else {
for ( i = 0 ; i < ce - > num_traits ; i + + ) {
2018-08-23 07:02:26 +08:00
if ( traits [ i ] ) {
ZEND_HASH_FOREACH_STR_KEY_PTR ( & traits [ i ] - > function_table , key , fn ) {
zend_traits_copy_functions ( key , fn , ce , & overridden , NULL , aliases ) ;
} ZEND_HASH_FOREACH_END ( ) ;
}
2014-09-19 06:01:05 +08:00
}
}
ZEND_HASH_FOREACH_PTR ( & ce - > function_table , fn ) {
zend_fixup_trait_method ( fn , ce ) ;
} ZEND_HASH_FOREACH_END ( ) ;
2018-08-10 10:19:55 +08:00
if ( overridden ) {
zend_hash_destroy ( overridden ) ;
FREE_HASHTABLE ( overridden ) ;
2014-09-19 06:01:05 +08:00
}
}
/* }}} */
2018-08-23 07:02:26 +08:00
static zend_class_entry * find_first_definition ( zend_class_entry * ce , zend_class_entry * * traits , size_t current_trait , zend_string * prop_name , zend_class_entry * coliding_ce ) /* { { { */
2014-09-19 06:01:05 +08:00
{
size_t i ;
if ( coliding_ce = = ce ) {
for ( i = 0 ; i < current_trait ; i + + ) {
2018-08-23 07:02:26 +08:00
if ( traits [ i ]
& & zend_hash_exists ( & traits [ i ] - > properties_info , prop_name ) ) {
return traits [ i ] ;
2014-09-19 06:01:05 +08:00
}
}
}
return coliding_ce ;
}
/* }}} */
2018-08-23 07:02:26 +08:00
static void zend_do_traits_property_binding ( zend_class_entry * ce , zend_class_entry * * traits ) /* { { { */
2014-09-19 06:01:05 +08:00
{
size_t i ;
zend_property_info * property_info ;
zend_property_info * coliding_prop ;
zend_string * prop_name ;
const char * class_name_unused ;
zend_bool not_compatible ;
zval * prop_value ;
uint32_t flags ;
zend_string * doc_comment ;
/* In the following steps the properties are inserted into the property table
* for that , a very strict approach is applied :
* - check for compatibility , if not compatible with any property in class - > fatal
* - if compatible , then strict notice
*/
for ( i = 0 ; i < ce - > num_traits ; i + + ) {
2018-08-23 07:02:26 +08:00
if ( ! traits [ i ] ) {
continue ;
}
ZEND_HASH_FOREACH_PTR ( & traits [ i ] - > properties_info , property_info ) {
2014-09-19 06:01:05 +08:00
/* first get the unmangeld name if necessary,
* then check whether the property is already there
*/
flags = property_info - > flags ;
2015-06-03 18:43:05 +08:00
if ( flags & ZEND_ACC_PUBLIC ) {
2014-09-19 06:01:05 +08:00
prop_name = zend_string_copy ( property_info - > name ) ;
} else {
const char * pname ;
size_t pname_len ;
/* for private and protected we need to unmangle the names */
zend_unmangle_property_name_ex ( property_info - > name ,
& class_name_unused , & pname , & pname_len ) ;
prop_name = zend_string_init ( pname , pname_len , 0 ) ;
}
/* next: check for conflicts with current class */
if ( ( coliding_prop = zend_hash_find_ptr ( & ce - > properties_info , prop_name ) ) ! = NULL ) {
2018-09-11 16:56:45 +08:00
if ( ( coliding_prop - > flags & ZEND_ACC_PRIVATE ) & & coliding_prop - > ce ! = ce ) {
2014-09-19 06:01:05 +08:00
zend_hash_del ( & ce - > properties_info , prop_name ) ;
flags | = ZEND_ACC_CHANGED ;
} else {
2017-09-27 08:31:03 +08:00
not_compatible = 1 ;
2017-12-31 12:35:25 +08:00
2014-09-19 06:01:05 +08:00
if ( ( coliding_prop - > flags & ( ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC ) )
2019-01-07 19:28:51 +08:00
= = ( flags & ( ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC ) ) & &
2019-10-17 19:01:02 +08:00
property_types_compatible ( property_info , coliding_prop ) = = INHERITANCE_SUCCESS
2019-01-07 19:28:51 +08:00
) {
2017-09-27 08:31:03 +08:00
/* the flags are identical, thus, the properties may be compatible */
2017-12-02 00:08:11 +08:00
zval * op1 , * op2 ;
2017-12-17 00:10:09 +08:00
zval op1_tmp , op2_tmp ;
2017-09-27 08:31:03 +08:00
2014-09-19 06:01:05 +08:00
if ( flags & ZEND_ACC_STATIC ) {
2017-12-02 00:08:11 +08:00
op1 = & ce - > default_static_members_table [ coliding_prop - > offset ] ;
2018-08-23 07:02:26 +08:00
op2 = & traits [ i ] - > default_static_members_table [ property_info - > offset ] ;
2018-06-22 18:58:48 +08:00
ZVAL_DEINDIRECT ( op1 ) ;
ZVAL_DEINDIRECT ( op2 ) ;
2014-09-19 06:01:05 +08:00
} else {
2017-12-02 00:08:11 +08:00
op1 = & ce - > default_properties_table [ OBJ_PROP_TO_NUM ( coliding_prop - > offset ) ] ;
2018-08-23 07:02:26 +08:00
op2 = & traits [ i ] - > default_properties_table [ OBJ_PROP_TO_NUM ( property_info - > offset ) ] ;
2017-09-27 08:31:03 +08:00
}
/* if any of the values is a constant, we try to resolve it */
2017-12-17 00:10:09 +08:00
if ( UNEXPECTED ( Z_TYPE_P ( op1 ) = = IS_CONSTANT_AST ) ) {
ZVAL_COPY_OR_DUP ( & op1_tmp , op1 ) ;
zval_update_constant_ex ( & op1_tmp , ce ) ;
op1 = & op1_tmp ;
2014-09-19 06:01:05 +08:00
}
2017-12-17 00:10:09 +08:00
if ( UNEXPECTED ( Z_TYPE_P ( op2 ) = = IS_CONSTANT_AST ) ) {
ZVAL_COPY_OR_DUP ( & op2_tmp , op2 ) ;
zval_update_constant_ex ( & op2_tmp , ce ) ;
op2 = & op2_tmp ;
2017-09-27 08:31:03 +08:00
}
2017-12-02 00:08:11 +08:00
not_compatible = fast_is_not_identical_function ( op1 , op2 ) ;
2017-12-17 00:10:09 +08:00
if ( op1 = = & op1_tmp ) {
zval_ptr_dtor_nogc ( & op1_tmp ) ;
}
if ( op2 = = & op2_tmp ) {
zval_ptr_dtor_nogc ( & op2_tmp ) ;
}
2014-09-19 06:01:05 +08:00
}
if ( not_compatible ) {
zend_error_noreturn ( E_COMPILE_ERROR ,
" %s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed " ,
2018-08-23 07:02:26 +08:00
ZSTR_VAL ( find_first_definition ( ce , traits , i , prop_name , coliding_prop - > ce ) - > name ) ,
2015-06-30 18:59:27 +08:00
ZSTR_VAL ( property_info - > ce - > name ) ,
ZSTR_VAL ( prop_name ) ,
ZSTR_VAL ( ce - > name ) ) ;
2014-09-19 06:01:05 +08:00
}
2015-03-15 16:47:25 +08:00
2018-05-28 21:27:12 +08:00
zend_string_release_ex ( prop_name , 0 ) ;
2015-03-15 16:47:25 +08:00
continue ;
2014-09-19 06:01:05 +08:00
}
}
/* property not found, so lets add it */
if ( flags & ZEND_ACC_STATIC ) {
2018-08-23 07:02:26 +08:00
prop_value = & traits [ i ] - > default_static_members_table [ property_info - > offset ] ;
2018-06-22 18:58:48 +08:00
ZEND_ASSERT ( Z_TYPE_P ( prop_value ) ! = IS_INDIRECT ) ;
2014-09-19 06:01:05 +08:00
} else {
2018-08-23 07:02:26 +08:00
prop_value = & traits [ i ] - > default_properties_table [ OBJ_PROP_TO_NUM ( property_info - > offset ) ] ;
2014-09-19 06:01:05 +08:00
}
2017-11-01 10:25:10 +08:00
Z_TRY_ADDREF_P ( prop_value ) ;
2014-09-19 06:01:05 +08:00
doc_comment = property_info - > doc_comment ? zend_string_copy ( property_info - > doc_comment ) : NULL ;
2019-09-25 19:21:13 +08:00
zend_type_copy_ctor ( & property_info - > type , /* persistent */ 0 ) ;
2019-01-07 19:28:51 +08:00
zend_declare_typed_property ( ce , prop_name , prop_value , flags , doc_comment , property_info - > type ) ;
2018-05-28 21:27:12 +08:00
zend_string_release_ex ( prop_name , 0 ) ;
2014-09-19 06:01:05 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
}
}
/* }}} */
2018-07-11 23:56:10 +08:00
static void zend_do_check_for_inconsistent_traits_aliasing ( zend_class_entry * ce , zend_class_entry * * aliases ) /* { { { */
2014-09-19 06:01:05 +08:00
{
int i = 0 ;
zend_trait_alias * cur_alias ;
zend_string * lc_method_name ;
if ( ce - > trait_aliases ) {
while ( ce - > trait_aliases [ i ] ) {
cur_alias = ce - > trait_aliases [ i ] ;
/** The trait for this alias has not been resolved, this means, this
alias was not applied . Abort with an error . */
2018-07-11 23:56:10 +08:00
if ( ! aliases [ i ] ) {
2014-09-19 06:01:05 +08:00
if ( cur_alias - > alias ) {
/** Plain old inconsistency/typo/bug */
zend_error_noreturn ( E_COMPILE_ERROR ,
" An alias (%s) was defined for method %s(), but this method does not exist " ,
2015-06-30 18:59:27 +08:00
ZSTR_VAL ( cur_alias - > alias ) ,
2018-07-11 23:56:10 +08:00
ZSTR_VAL ( cur_alias - > trait_method . method_name ) ) ;
2014-09-19 06:01:05 +08:00
} else {
/** Here are two possible cases:
2019-02-19 00:35:35 +08:00
1 ) this is an attempt to modify the visibility
2014-09-19 06:01:05 +08:00
of a method introduce as part of another alias .
Since that seems to violate the DRY principle ,
we check against it and abort .
2 ) it is just a plain old inconsitency / typo / bug
as in the case where alias is set . */
2014-12-24 20:04:51 +08:00
lc_method_name = zend_string_tolower (
2018-07-11 23:56:10 +08:00
cur_alias - > trait_method . method_name ) ;
2014-09-19 06:01:05 +08:00
if ( zend_hash_exists ( & ce - > function_table ,
lc_method_name ) ) {
2018-05-28 21:27:12 +08:00
zend_string_release_ex ( lc_method_name , 0 ) ;
2014-09-19 06:01:05 +08:00
zend_error_noreturn ( E_COMPILE_ERROR ,
2014-11-20 03:59:31 +08:00
" The modifiers for the trait alias %s() need to be changed in the same statement in which the alias is defined. Error " ,
2018-07-11 23:56:10 +08:00
ZSTR_VAL ( cur_alias - > trait_method . method_name ) ) ;
2014-09-19 06:01:05 +08:00
} else {
2018-05-28 21:27:12 +08:00
zend_string_release_ex ( lc_method_name , 0 ) ;
2014-09-19 06:01:05 +08:00
zend_error_noreturn ( E_COMPILE_ERROR ,
" The modifiers of the trait method %s() are changed, but this method does not exist. Error " ,
2018-07-11 23:56:10 +08:00
ZSTR_VAL ( cur_alias - > trait_method . method_name ) ) ;
2014-09-19 06:01:05 +08:00
}
}
}
i + + ;
}
}
}
/* }}} */
2018-08-27 21:40:25 +08:00
static void zend_do_bind_traits ( zend_class_entry * ce ) /* { { { */
2014-09-19 06:01:05 +08:00
{
2018-07-11 23:56:10 +08:00
HashTable * * exclude_tables ;
zend_class_entry * * aliases ;
2018-08-23 07:02:26 +08:00
zend_class_entry * * traits , * trait ;
uint32_t i , j ;
2014-09-19 06:01:05 +08:00
2018-08-23 22:16:28 +08:00
ZEND_ASSERT ( ce - > num_traits > 0 ) ;
2014-09-19 06:01:05 +08:00
2018-08-23 07:02:26 +08:00
traits = emalloc ( sizeof ( zend_class_entry * ) * ce - > num_traits ) ;
for ( i = 0 ; i < ce - > num_traits ; i + + ) {
trait = zend_fetch_class_by_name ( ce - > trait_names [ i ] . name ,
ce - > trait_names [ i ] . lc_name , ZEND_FETCH_CLASS_TRAIT ) ;
if ( UNEXPECTED ( trait = = NULL ) ) {
return ;
}
if ( UNEXPECTED ( ! ( trait - > ce_flags & ZEND_ACC_TRAIT ) ) ) {
zend_error_noreturn ( E_ERROR , " %s cannot use %s - it is not a trait " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( trait - > name ) ) ;
return ;
}
for ( j = 0 ; j < i ; j + + ) {
if ( traits [ j ] = = trait ) {
/* skip duplications */
trait = NULL ;
break ;
}
}
traits [ i ] = trait ;
}
2014-09-19 06:01:05 +08:00
/* complete initialization of trait strutures in ce */
2018-08-23 07:02:26 +08:00
zend_traits_init_trait_structures ( ce , traits , & exclude_tables , & aliases ) ;
2014-09-19 06:01:05 +08:00
/* first care about all methods to be flattened into the class */
2018-08-23 07:02:26 +08:00
zend_do_traits_method_binding ( ce , traits , exclude_tables , aliases ) ;
2014-09-19 06:01:05 +08:00
/* Aliases which have not been applied indicate typos/bugs. */
2018-07-11 23:56:10 +08:00
zend_do_check_for_inconsistent_traits_aliasing ( ce , aliases ) ;
if ( aliases ) {
efree ( aliases ) ;
}
if ( exclude_tables ) {
efree ( exclude_tables ) ;
}
2014-09-19 06:01:05 +08:00
/* then flatten the properties into it, to, mostly to notfiy developer about problems */
2018-08-23 07:02:26 +08:00
zend_do_traits_property_binding ( ce , traits ) ;
efree ( traits ) ;
2015-03-31 22:10:22 +08:00
}
/* }}} */
2018-08-27 17:56:17 +08:00
# define MAX_ABSTRACT_INFO_CNT 3
# define MAX_ABSTRACT_INFO_FMT "%s%s%s%s"
# define DISPLAY_ABSTRACT_FN(idx) \
ai . afn [ idx ] ? ZEND_FN_SCOPE_NAME ( ai . afn [ idx ] ) : " " , \
ai . afn [ idx ] ? " :: " : " " , \
ai . afn [ idx ] ? ZSTR_VAL ( ai . afn [ idx ] - > common . function_name ) : " " , \
ai . afn [ idx ] & & ai . afn [ idx + 1 ] ? " , " : ( ai . afn [ idx ] & & ai . cnt > MAX_ABSTRACT_INFO_CNT ? " , ... " : " " )
typedef struct _zend_abstract_info {
zend_function * afn [ MAX_ABSTRACT_INFO_CNT + 1 ] ;
int cnt ;
int ctor ;
} zend_abstract_info ;
static void zend_verify_abstract_class_function ( zend_function * fn , zend_abstract_info * ai ) /* { { { */
{
if ( fn - > common . fn_flags & ZEND_ACC_ABSTRACT ) {
if ( ai - > cnt < MAX_ABSTRACT_INFO_CNT ) {
ai - > afn [ ai - > cnt ] = fn ;
}
2019-02-14 18:12:50 +08:00
if ( fn - > common . fn_flags & ZEND_ACC_CTOR ) {
2018-08-27 17:56:17 +08:00
if ( ! ai - > ctor ) {
ai - > cnt + + ;
ai - > ctor = 1 ;
} else {
ai - > afn [ ai - > cnt ] = NULL ;
}
} else {
ai - > cnt + + ;
}
}
}
/* }}} */
void zend_verify_abstract_class ( zend_class_entry * ce ) /* { { { */
{
zend_function * func ;
zend_abstract_info ai ;
2018-08-27 21:40:25 +08:00
ZEND_ASSERT ( ( ce - > ce_flags & ( ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ) ) = = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ) ;
memset ( & ai , 0 , sizeof ( ai ) ) ;
2018-08-27 17:56:17 +08:00
2018-08-27 21:40:25 +08:00
ZEND_HASH_FOREACH_PTR ( & ce - > function_table , func ) {
zend_verify_abstract_class_function ( func , & ai ) ;
} ZEND_HASH_FOREACH_END ( ) ;
2018-08-27 17:56:17 +08:00
2018-08-27 21:40:25 +08:00
if ( ai . cnt ) {
zend_error_noreturn ( E_ERROR , " Class %s contains %d abstract method%s and must therefore be declared abstract or implement the remaining methods ( " MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT " ) " ,
ZSTR_VAL ( ce - > name ) , ai . cnt ,
ai . cnt > 1 ? " s " : " " ,
DISPLAY_ABSTRACT_FN ( 0 ) ,
DISPLAY_ABSTRACT_FN ( 1 ) ,
DISPLAY_ABSTRACT_FN ( 2 )
) ;
} else {
/* now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed */
ce - > ce_flags & = ~ ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ;
}
}
/* }}} */
2019-05-27 17:39:56 +08:00
typedef struct {
2019-10-17 19:01:02 +08:00
enum {
OBLIGATION_DEPENDENCY ,
OBLIGATION_COMPATIBILITY ,
OBLIGATION_PROPERTY_COMPATIBILITY
} type ;
2019-05-27 17:39:56 +08:00
union {
zend_class_entry * dependency_ce ;
struct {
2020-01-28 17:41:11 +08:00
/* Traits may use temporary on-stack functions during inheritance checks,
* so use copies of functions here as well . */
zend_function parent_fn ;
zend_function child_fn ;
2019-05-27 17:39:56 +08:00
} ;
2019-10-17 19:01:02 +08:00
struct {
const zend_property_info * parent_prop ;
const zend_property_info * child_prop ;
} ;
2019-05-27 17:39:56 +08:00
} ;
} variance_obligation ;
static void variance_obligation_dtor ( zval * zv ) {
efree ( Z_PTR_P ( zv ) ) ;
}
static void variance_obligation_ht_dtor ( zval * zv ) {
zend_hash_destroy ( Z_PTR_P ( zv ) ) ;
FREE_HASHTABLE ( Z_PTR_P ( zv ) ) ;
}
static HashTable * get_or_init_obligations_for_class ( zend_class_entry * ce ) {
HashTable * ht ;
zend_ulong key ;
if ( ! CG ( delayed_variance_obligations ) ) {
ALLOC_HASHTABLE ( CG ( delayed_variance_obligations ) ) ;
zend_hash_init ( CG ( delayed_variance_obligations ) , 0 , NULL , variance_obligation_ht_dtor , 0 ) ;
}
key = ( zend_ulong ) ( uintptr_t ) ce ;
ht = zend_hash_index_find_ptr ( CG ( delayed_variance_obligations ) , key ) ;
if ( ht ) {
return ht ;
}
ALLOC_HASHTABLE ( ht ) ;
zend_hash_init ( ht , 0 , NULL , variance_obligation_dtor , 0 ) ;
zend_hash_index_add_new_ptr ( CG ( delayed_variance_obligations ) , key , ht ) ;
ce - > ce_flags | = ZEND_ACC_UNRESOLVED_VARIANCE ;
return ht ;
}
static void add_dependency_obligation ( zend_class_entry * ce , zend_class_entry * dependency_ce ) {
HashTable * obligations = get_or_init_obligations_for_class ( ce ) ;
variance_obligation * obligation = emalloc ( sizeof ( variance_obligation ) ) ;
obligation - > type = OBLIGATION_DEPENDENCY ;
obligation - > dependency_ce = dependency_ce ;
zend_hash_next_index_insert_ptr ( obligations , obligation ) ;
}
static void add_compatibility_obligation (
2019-06-11 19:15:03 +08:00
zend_class_entry * ce , const zend_function * child_fn , const zend_function * parent_fn ) {
2019-05-27 17:39:56 +08:00
HashTable * obligations = get_or_init_obligations_for_class ( ce ) ;
variance_obligation * obligation = emalloc ( sizeof ( variance_obligation ) ) ;
obligation - > type = OBLIGATION_COMPATIBILITY ;
2020-01-30 18:55:38 +08:00
/* Copy functions, because they may be stack-allocated in the case of traits. */
if ( child_fn - > common . type = = ZEND_INTERNAL_FUNCTION ) {
memcpy ( & obligation - > child_fn , child_fn , sizeof ( zend_internal_function ) ) ;
} else {
memcpy ( & obligation - > child_fn , child_fn , sizeof ( zend_op_array ) ) ;
}
if ( parent_fn - > common . type = = ZEND_INTERNAL_FUNCTION ) {
memcpy ( & obligation - > parent_fn , parent_fn , sizeof ( zend_internal_function ) ) ;
} else {
memcpy ( & obligation - > parent_fn , parent_fn , sizeof ( zend_op_array ) ) ;
}
2019-05-27 17:39:56 +08:00
zend_hash_next_index_insert_ptr ( obligations , obligation ) ;
}
2019-10-17 19:01:02 +08:00
static void add_property_compatibility_obligation (
zend_class_entry * ce , const zend_property_info * child_prop ,
const zend_property_info * parent_prop ) {
HashTable * obligations = get_or_init_obligations_for_class ( ce ) ;
variance_obligation * obligation = emalloc ( sizeof ( variance_obligation ) ) ;
obligation - > type = OBLIGATION_PROPERTY_COMPATIBILITY ;
obligation - > child_prop = child_prop ;
obligation - > parent_prop = parent_prop ;
zend_hash_next_index_insert_ptr ( obligations , obligation ) ;
}
2019-05-27 17:39:56 +08:00
static void resolve_delayed_variance_obligations ( zend_class_entry * ce ) ;
static int check_variance_obligation ( zval * zv ) {
variance_obligation * obligation = Z_PTR_P ( zv ) ;
if ( obligation - > type = = OBLIGATION_DEPENDENCY ) {
zend_class_entry * dependency_ce = obligation - > dependency_ce ;
if ( dependency_ce - > ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE ) {
resolve_delayed_variance_obligations ( dependency_ce ) ;
}
if ( ! ( dependency_ce - > ce_flags & ZEND_ACC_LINKED ) ) {
return ZEND_HASH_APPLY_KEEP ;
}
2019-10-17 19:01:02 +08:00
} else if ( obligation - > type = = OBLIGATION_COMPATIBILITY ) {
2019-05-27 17:39:56 +08:00
inheritance_status status = zend_do_perform_implementation_check (
2020-01-28 17:44:37 +08:00
& obligation - > child_fn , & obligation - > parent_fn ) ;
2019-06-26 06:00:31 +08:00
if ( UNEXPECTED ( status ! = INHERITANCE_SUCCESS ) ) {
if ( EXPECTED ( status = = INHERITANCE_UNRESOLVED ) ) {
return ZEND_HASH_APPLY_KEEP ;
}
ZEND_ASSERT ( status = = INHERITANCE_ERROR ) ;
2020-01-28 17:44:37 +08:00
emit_incompatible_method_error ( & obligation - > child_fn , & obligation - > parent_fn , status ) ;
2019-05-27 17:39:56 +08:00
}
/* Either the compatibility check was successful or only threw a warning. */
2019-10-17 19:01:02 +08:00
} else {
ZEND_ASSERT ( obligation - > type = = OBLIGATION_PROPERTY_COMPATIBILITY ) ;
inheritance_status status =
property_types_compatible ( obligation - > parent_prop , obligation - > child_prop ) ;
if ( status ! = INHERITANCE_SUCCESS ) {
if ( status = = INHERITANCE_UNRESOLVED ) {
return ZEND_HASH_APPLY_KEEP ;
}
ZEND_ASSERT ( status = = INHERITANCE_ERROR ) ;
emit_incompatible_property_error ( obligation - > child_prop , obligation - > parent_prop ) ;
}
2019-05-27 17:39:56 +08:00
}
return ZEND_HASH_APPLY_REMOVE ;
}
static void load_delayed_classes ( ) {
HashTable * delayed_autoloads = CG ( delayed_autoloads ) ;
zend_string * name ;
if ( ! delayed_autoloads ) {
return ;
}
/* Take ownership of this HT, to avoid concurrent modification during autoloading. */
CG ( delayed_autoloads ) = NULL ;
ZEND_HASH_FOREACH_STR_KEY ( delayed_autoloads , name ) {
zend_lookup_class ( name ) ;
} ZEND_HASH_FOREACH_END ( ) ;
zend_hash_destroy ( delayed_autoloads ) ;
FREE_HASHTABLE ( delayed_autoloads ) ;
}
static void resolve_delayed_variance_obligations ( zend_class_entry * ce ) {
HashTable * all_obligations = CG ( delayed_variance_obligations ) , * obligations ;
zend_ulong num_key = ( zend_ulong ) ( uintptr_t ) ce ;
ZEND_ASSERT ( all_obligations ! = NULL ) ;
obligations = zend_hash_index_find_ptr ( all_obligations , num_key ) ;
ZEND_ASSERT ( obligations ! = NULL ) ;
zend_hash_apply ( obligations , check_variance_obligation ) ;
if ( zend_hash_num_elements ( obligations ) = = 0 ) {
ce - > ce_flags & = ~ ZEND_ACC_UNRESOLVED_VARIANCE ;
ce - > ce_flags | = ZEND_ACC_LINKED ;
zend_hash_index_del ( all_obligations , num_key ) ;
}
}
static void report_variance_errors ( zend_class_entry * ce ) {
HashTable * all_obligations = CG ( delayed_variance_obligations ) , * obligations ;
variance_obligation * obligation ;
zend_ulong num_key = ( zend_ulong ) ( uintptr_t ) ce ;
ZEND_ASSERT ( all_obligations ! = NULL ) ;
obligations = zend_hash_index_find_ptr ( all_obligations , num_key ) ;
ZEND_ASSERT ( obligations ! = NULL ) ;
ZEND_HASH_FOREACH_PTR ( obligations , obligation ) {
2019-10-17 19:01:02 +08:00
if ( obligation - > type = = OBLIGATION_COMPATIBILITY ) {
2019-09-25 19:21:13 +08:00
/* Just used to populate the delayed_autoloads table,
* which will be used when printing the " unresolved " error . */
inheritance_status status = zend_do_perform_implementation_check (
2020-01-28 17:44:37 +08:00
& obligation - > child_fn , & obligation - > parent_fn ) ;
2019-10-17 19:01:02 +08:00
ZEND_ASSERT ( status = = INHERITANCE_UNRESOLVED ) ;
2019-10-17 19:42:47 +08:00
emit_incompatible_method_error (
2020-01-28 17:44:37 +08:00
& obligation - > child_fn , & obligation - > parent_fn , status ) ;
2019-10-17 19:01:02 +08:00
} else if ( obligation - > type = = OBLIGATION_PROPERTY_COMPATIBILITY ) {
emit_incompatible_property_error ( obligation - > child_prop , obligation - > parent_prop ) ;
} else {
zend_error_noreturn ( E_CORE_ERROR , " Bug #78647 " ) ;
}
2019-05-27 17:39:56 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
/* Only warnings were thrown above -- that means that there are incompatibilities, but only
* ones that we permit . Mark all classes with open obligations as fully linked . */
ce - > ce_flags & = ~ ZEND_ACC_UNRESOLVED_VARIANCE ;
ce - > ce_flags | = ZEND_ACC_LINKED ;
zend_hash_index_del ( all_obligations , num_key ) ;
}
2019-09-11 21:31:04 +08:00
static void check_unrecoverable_load_failure ( zend_class_entry * ce ) {
/* If this class has been used while unlinked through a variance obligation, it is not legal
* to remove the class from the class table and throw an exception , because there is already
* a dependence on the inheritance hierarchy of this specific class . Instead we fall back to
* a fatal error , as would happen if we did not allow exceptions in the first place . */
if ( ce - > ce_flags & ZEND_ACC_HAS_UNLINKED_USES ) {
zend_string * exception_str ;
zval exception_zv ;
ZEND_ASSERT ( EG ( exception ) & & " Exception must have been thrown " ) ;
ZVAL_OBJ ( & exception_zv , EG ( exception ) ) ;
Z_ADDREF ( exception_zv ) ;
zend_clear_exception ( ) ;
exception_str = zval_get_string ( & exception_zv ) ;
zend_error_noreturn ( E_ERROR ,
" During inheritance of %s with variance dependencies: Uncaught %s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( exception_str ) ) ;
}
}
ZEND_API int zend_do_link_class ( zend_class_entry * ce , zend_string * lc_parent_name ) /* { { { */
2018-08-27 21:40:25 +08:00
{
2019-09-11 21:31:04 +08:00
/* Load parent/interface dependencies first, so we can still gracefully abort linking
* with an exception and remove the class from the class table . This is only possible
* if no variance obligations on the current class have been added during autoloading . */
zend_class_entry * parent = NULL ;
zend_class_entry * * interfaces = NULL ;
2019-05-24 20:28:44 +08:00
if ( ce - > parent_name ) {
2019-09-11 21:31:04 +08:00
parent = zend_fetch_class_by_name (
ce - > parent_name , lc_parent_name ,
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION ) ;
if ( ! parent ) {
check_unrecoverable_load_failure ( ce ) ;
return FAILURE ;
}
}
if ( ce - > num_interfaces ) {
/* Also copy the parent interfaces here, so we don't need to reallocate later. */
uint32_t i , num_parent_interfaces = parent ? parent - > num_interfaces : 0 ;
interfaces = emalloc (
sizeof ( zend_class_entry * ) * ( ce - > num_interfaces + num_parent_interfaces ) ) ;
if ( num_parent_interfaces ) {
memcpy ( interfaces , parent - > interfaces ,
sizeof ( zend_class_entry * ) * num_parent_interfaces ) ;
}
for ( i = 0 ; i < ce - > num_interfaces ; i + + ) {
zend_class_entry * iface = zend_fetch_class_by_name (
ce - > interface_names [ i ] . name , ce - > interface_names [ i ] . lc_name ,
ZEND_FETCH_CLASS_INTERFACE |
ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION ) ;
if ( ! iface ) {
check_unrecoverable_load_failure ( ce ) ;
efree ( interfaces ) ;
return FAILURE ;
}
interfaces [ num_parent_interfaces + i ] = iface ;
}
}
if ( parent ) {
2019-05-27 17:39:56 +08:00
if ( ! ( parent - > ce_flags & ZEND_ACC_LINKED ) ) {
add_dependency_obligation ( ce , parent ) ;
}
2018-08-27 21:40:25 +08:00
zend_do_inheritance ( ce , parent ) ;
}
if ( ce - > ce_flags & ZEND_ACC_IMPLEMENT_TRAITS ) {
zend_do_bind_traits ( ce ) ;
}
if ( ce - > ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES ) {
2019-09-11 21:31:04 +08:00
zend_do_implement_interfaces ( ce , interfaces ) ;
2018-08-27 21:40:25 +08:00
}
if ( ( ce - > ce_flags & ( ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ) ) = = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ) {
zend_verify_abstract_class ( ce ) ;
2018-08-27 17:56:17 +08:00
}
2019-01-07 19:28:51 +08:00
zend_build_properties_info_table ( ce ) ;
2019-05-27 17:39:56 +08:00
if ( ! ( ce - > ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE ) ) {
ce - > ce_flags | = ZEND_ACC_LINKED ;
2019-09-11 21:31:04 +08:00
return SUCCESS ;
2019-05-27 17:39:56 +08:00
}
2019-09-11 22:27:28 +08:00
ce - > ce_flags | = ZEND_ACC_NEARLY_LINKED ;
2019-05-27 17:39:56 +08:00
load_delayed_classes ( ) ;
if ( ce - > ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE ) {
resolve_delayed_variance_obligations ( ce ) ;
if ( ! ( ce - > ce_flags & ZEND_ACC_LINKED ) ) {
report_variance_errors ( ce ) ;
}
}
2019-09-11 21:31:04 +08:00
return SUCCESS ;
2018-08-27 17:56:17 +08:00
}
2019-06-27 07:13:06 +08:00
/* }}} */
2019-05-09 16:41:06 +08:00
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
2019-06-27 07:13:06 +08:00
static inheritance_status zend_can_early_bind ( zend_class_entry * ce , zend_class_entry * parent_ce ) /* { { { */
{
2019-05-09 16:41:06 +08:00
zend_string * key ;
zend_function * parent_func ;
2019-10-17 19:01:02 +08:00
zend_property_info * parent_info ;
2019-06-25 17:41:06 +08:00
2019-06-27 07:13:06 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > function_table , key , parent_func ) {
zval * zv = zend_hash_find_ex ( & ce - > function_table , key , 1 ) ;
if ( zv ) {
zend_function * child_func = Z_FUNC_P ( zv ) ;
inheritance_status status =
do_inheritance_check_on_method_ex ( child_func , parent_func , ce , NULL , 1 , 0 ) ;
2019-06-25 22:43:46 +08:00
2019-06-27 07:13:06 +08:00
if ( UNEXPECTED ( status ! = INHERITANCE_SUCCESS ) ) {
2019-06-27 07:25:57 +08:00
return status ;
2019-06-25 22:43:46 +08:00
}
2019-05-09 16:41:06 +08:00
}
2019-06-27 07:13:06 +08:00
} ZEND_HASH_FOREACH_END ( ) ;
2019-06-25 17:41:06 +08:00
2019-10-17 19:01:02 +08:00
ZEND_HASH_FOREACH_STR_KEY_PTR ( & parent_ce - > properties_info , key , parent_info ) {
zval * zv ;
if ( ( parent_info - > flags & ZEND_ACC_PRIVATE ) | | ! ZEND_TYPE_IS_SET ( parent_info - > type ) ) {
continue ;
}
zv = zend_hash_find_ex ( & ce - > properties_info , key , 1 ) ;
if ( zv ) {
zend_property_info * child_info = Z_PTR_P ( zv ) ;
if ( ZEND_TYPE_IS_SET ( child_info - > type ) ) {
inheritance_status status = property_types_compatible ( parent_info , child_info ) ;
if ( UNEXPECTED ( status ! = INHERITANCE_SUCCESS ) ) {
2019-10-17 19:42:47 +08:00
return status ;
2019-10-17 19:01:02 +08:00
}
}
}
} ZEND_HASH_FOREACH_END ( ) ;
2019-06-27 07:25:57 +08:00
return INHERITANCE_SUCCESS ;
2019-06-27 07:13:06 +08:00
}
/* }}} */
2019-06-26 06:00:31 +08:00
2019-06-27 07:13:06 +08:00
zend_bool zend_try_early_bind ( zend_class_entry * ce , zend_class_entry * parent_ce , zend_string * lcname , zval * delayed_early_binding ) /* { { { */
{
inheritance_status status = zend_can_early_bind ( ce , parent_ce ) ;
2019-06-26 06:00:31 +08:00
2019-06-27 07:13:06 +08:00
if ( EXPECTED ( status ! = INHERITANCE_UNRESOLVED ) ) {
if ( delayed_early_binding ) {
if ( UNEXPECTED ( zend_hash_set_bucket_key ( EG ( class_table ) , ( Bucket * ) delayed_early_binding , lcname ) = = NULL ) ) {
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 ) ) ;
return 0 ;
}
} else {
if ( UNEXPECTED ( zend_hash_add_ptr ( CG ( class_table ) , lcname , ce ) = = NULL ) ) {
2019-06-26 06:00:31 +08:00
return 0 ;
}
2019-05-09 16:41:06 +08:00
}
2019-06-27 07:13:06 +08:00
zend_do_inheritance_ex ( ce , parent_ce , status = = INHERITANCE_SUCCESS ) ;
zend_build_properties_info_table ( ce ) ;
if ( ( ce - > ce_flags & ( ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS ) ) = = ZEND_ACC_IMPLICIT_ABSTRACT_CLASS ) {
zend_verify_abstract_class ( ce ) ;
}
ZEND_ASSERT ( ! ( ce - > ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE ) ) ;
ce - > ce_flags | = ZEND_ACC_LINKED ;
return 1 ;
}
return 0 ;
2019-05-09 16:41:06 +08:00
}
2018-08-27 17:56:17 +08:00
/* }}} */