2006-05-10 07:53:23 +08:00
/*
2003-02-01 09:49:15 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Zend Engine |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-01-30 17:23:29 +08:00
| Copyright ( c ) Zend Technologies Ltd . ( http : //www.zend.com) |
2003-02-01 09:49:15 +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 |
2003-06-11 04:04:29 +08:00
| available through the world - wide - web at the following url : |
2003-02-01 09:49:15 +08:00
| 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 > |
| Dmitry Stogov < dmitry @ php . net > |
2003-02-01 09:49:15 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
2002-02-07 22:08:43 +08:00
# include "zend.h"
# include "zend_globals.h"
# include "zend_variables.h"
# include "zend_API.h"
# include "zend_objects.h"
2002-05-31 20:09:19 +08:00
# include "zend_objects_API.h"
2023-01-16 19:27:33 +08:00
# include "zend_object_handlers.h"
2003-11-25 04:57:54 +08:00
# include "zend_interfaces.h"
2015-07-04 02:41:17 +08:00
# include "zend_exceptions.h"
2008-08-15 05:36:56 +08:00
# include "zend_closures.h"
2009-06-18 21:46:16 +08:00
# include "zend_compile.h"
2013-12-13 03:25:10 +08:00
# include "zend_hash.h"
2002-02-07 22:08:43 +08:00
2002-02-21 19:50:44 +08:00
# define DEBUG_OBJECT_HANDLERS 0
2002-02-07 22:08:43 +08:00
2017-09-18 18:13:24 +08:00
# define ZEND_WRONG_PROPERTY_OFFSET 0
2014-02-10 14:04:30 +08:00
/* guard flags */
2023-08-24 19:58:13 +08:00
# define IN_GET ZEND_GUARD_PROPERTY_GET
# define IN_SET ZEND_GUARD_PROPERTY_SET
# define IN_UNSET ZEND_GUARD_PROPERTY_UNSET
# define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
2003-01-12 22:39:45 +08:00
2002-09-04 17:07:58 +08:00
/*
__X accessors explanation :
if we have __get and property that is not part of the properties array is
requested , we call __get handler . If it fails , we return uninitialized .
if we have __set and property that is not part of the properties array is
set , we call __set handler . If it fails , we do not change the array .
for both handlers above , when we are inside __get / __set , no further calls for
2007-10-11 09:03:19 +08:00
__get / __set for this property of this object will be made , to prevent endless
2007-03-24 01:16:55 +08:00
recursion and enable accessors to change properties array .
2002-09-04 17:07:58 +08:00
if we have __call and method which is not part of the class function table is
2006-05-10 07:53:23 +08:00
called , we cal __call handler .
2002-09-04 17:07:58 +08:00
*/
2010-05-24 22:11:39 +08:00
ZEND_API void rebuild_object_properties ( zend_object * zobj ) /* { { { */
{
if ( ! zobj - > properties ) {
zend_property_info * prop_info ;
zend_class_entry * ce = zobj - > ce ;
2021-02-27 00:27:55 +08:00
int i ;
2010-05-24 22:11:39 +08:00
2017-09-20 07:25:56 +08:00
zobj - > properties = zend_new_array ( ce - > default_properties_count ) ;
2010-05-24 22:11:39 +08:00
if ( ce - > default_properties_count ) {
2018-03-23 05:13:45 +08:00
zend_hash_real_init_mixed ( zobj - > properties ) ;
2021-02-27 00:27:55 +08:00
for ( i = 0 ; i < ce - > default_properties_count ; i + + ) {
prop_info = ce - > properties_info_table [ i ] ;
2015-08-26 08:27:05 +08:00
2021-02-27 00:27:55 +08:00
if ( ! prop_info ) {
continue ;
2012-05-13 13:12:48 +08:00
}
2021-02-27 00:27:55 +08:00
if ( UNEXPECTED ( Z_TYPE_P ( OBJ_PROP ( zobj , prop_info - > offset ) ) = = IS_UNDEF ) ) {
HT_FLAGS ( zobj - > properties ) | = HASH_FLAG_HAS_EMPTY_IND ;
2018-09-13 04:47:52 +08:00
}
2021-02-27 00:27:55 +08:00
_zend_hash_append_ind ( zobj - > properties , prop_info - > name ,
OBJ_PROP ( zobj , prop_info - > offset ) ) ;
2010-05-24 22:11:39 +08:00
}
}
}
}
2011-09-15 22:50:38 +08:00
/* }}} */
2010-05-24 22:11:39 +08:00
2021-03-02 03:37:37 +08:00
ZEND_API HashTable * zend_std_build_object_properties_array ( zend_object * zobj ) /* { { { */
{
zend_property_info * prop_info ;
zend_class_entry * ce = zobj - > ce ;
HashTable * ht ;
zval * prop ;
int i ;
ZEND_ASSERT ( ! zobj - > properties ) ;
ht = zend_new_array ( ce - > default_properties_count ) ;
if ( ce - > default_properties_count ) {
zend_hash_real_init_mixed ( ht ) ;
for ( i = 0 ; i < ce - > default_properties_count ; i + + ) {
prop_info = ce - > properties_info_table [ i ] ;
if ( ! prop_info ) {
continue ;
}
prop = OBJ_PROP ( zobj , prop_info - > offset ) ;
if ( UNEXPECTED ( Z_TYPE_P ( prop ) = = IS_UNDEF ) ) {
continue ;
}
2022-06-09 16:48:52 +08:00
if ( Z_ISREF_P ( prop ) & & Z_REFCOUNT_P ( prop ) = = 1 ) {
prop = Z_REFVAL_P ( prop ) ;
}
2021-03-02 03:37:37 +08:00
Z_TRY_ADDREF_P ( prop ) ;
_zend_hash_append ( ht , prop_info - > name , prop ) ;
}
}
return ht ;
}
/* }}} */
2019-01-31 23:47:58 +08:00
ZEND_API HashTable * zend_std_get_properties ( zend_object * zobj ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2010-05-24 22:11:39 +08:00
if ( ! zobj - > properties ) {
rebuild_object_properties ( zobj ) ;
}
2002-02-07 22:08:43 +08:00
return zobj - > properties ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2019-01-31 23:47:58 +08:00
ZEND_API HashTable * zend_std_get_gc ( zend_object * zobj , zval * * table , int * n ) /* { { { */
2011-11-02 14:31:33 +08:00
{
2019-01-31 23:47:58 +08:00
if ( zobj - > handlers - > get_properties ! = zend_std_get_properties ) {
2011-11-02 14:31:33 +08:00
* table = NULL ;
* n = 0 ;
2019-01-31 23:47:58 +08:00
return zobj - > handlers - > get_properties ( zobj ) ;
2011-11-02 14:31:33 +08:00
} else {
2015-05-07 02:33:49 +08:00
if ( zobj - > properties ) {
* table = NULL ;
* n = 0 ;
return zobj - > properties ;
} else {
* table = zobj - > properties_table ;
* n = zobj - > ce - > default_properties_count ;
return NULL ;
}
2011-11-02 14:31:33 +08:00
}
}
/* }}} */
2019-01-31 23:47:58 +08:00
ZEND_API HashTable * zend_std_get_debug_info ( zend_object * object , int * is_temp ) /* { { { */
2007-11-03 03:40:39 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = object - > ce ;
2014-04-26 04:32:51 +08:00
zval retval ;
HashTable * ht ;
2014-02-18 11:13:00 +08:00
if ( ! ce - > __debugInfo ) {
* is_temp = 0 ;
2019-01-31 23:47:58 +08:00
return object - > handlers - > get_properties ( object ) ;
2014-02-18 11:13:00 +08:00
}
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method_with_0_params ( ce - > __debugInfo , object , & retval ) ;
2014-04-26 04:32:51 +08:00
if ( Z_TYPE ( retval ) = = IS_ARRAY ) {
2016-11-29 03:59:57 +08:00
if ( ! Z_REFCOUNTED ( retval ) ) {
2014-06-03 06:43:53 +08:00
* is_temp = 1 ;
2015-02-14 03:20:39 +08:00
return zend_array_dup ( Z_ARRVAL ( retval ) ) ;
2014-06-03 06:43:53 +08:00
} else if ( Z_REFCOUNT ( retval ) < = 1 ) {
2014-02-18 11:13:00 +08:00
* is_temp = 1 ;
2015-09-15 04:04:27 +08:00
ht = Z_ARR ( retval ) ;
2014-02-18 11:13:00 +08:00
return ht ;
} else {
* is_temp = 0 ;
zval_ptr_dtor ( & retval ) ;
2014-06-03 06:43:53 +08:00
return Z_ARRVAL ( retval ) ;
2014-02-18 11:13:00 +08:00
}
2014-04-26 04:32:51 +08:00
} else if ( Z_TYPE ( retval ) = = IS_NULL ) {
2014-02-18 11:13:00 +08:00
* is_temp = 1 ;
2017-09-20 07:25:56 +08:00
ht = zend_new_array ( 0 ) ;
2014-04-26 04:32:51 +08:00
return ht ;
2014-02-18 11:13:00 +08:00
}
2015-04-01 18:32:23 +08:00
zend_error_noreturn ( E_ERROR , ZEND_DEBUGINFO_FUNC_NAME " () must return an array " ) ;
2014-04-12 01:33:56 +08:00
return NULL ; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
2007-11-03 03:40:39 +08:00
}
/* }}} */
2002-02-07 22:08:43 +08:00
2018-07-31 17:24:53 +08:00
static void zend_std_call_getter ( zend_object * zobj , zend_string * prop_name , zval * retval ) /* { { { */
2002-09-04 17:07:58 +08:00
{
2018-07-31 17:24:53 +08:00
zval member ;
ZVAL_STR ( & member , prop_name ) ;
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method_with_1_params ( zobj - > ce - > __get , zobj , retval , & member ) ;
2002-09-04 17:07:58 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-09-04 17:07:58 +08:00
2018-07-31 17:24:53 +08:00
static void zend_std_call_setter ( zend_object * zobj , zend_string * prop_name , zval * value ) /* { { { */
2002-09-04 17:07:58 +08:00
{
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zval args [ 2 ] ;
2018-07-31 17:24:53 +08:00
ZVAL_STR ( & args [ 0 ] , prop_name ) ;
2018-06-22 19:29:54 +08:00
ZVAL_COPY_VALUE ( & args [ 1 ] , value ) ;
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method ( zobj - > ce - > __set , zobj , NULL , 2 , args ) ;
2002-09-04 17:07:58 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-09-04 17:07:58 +08:00
2018-07-31 17:24:53 +08:00
static void zend_std_call_unsetter ( zend_object * zobj , zend_string * prop_name ) /* { { { */
2005-07-08 00:07:09 +08:00
{
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zval member ;
2018-07-31 17:24:53 +08:00
ZVAL_STR ( & member , prop_name ) ;
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method_with_1_params ( zobj - > ce - > __unset , zobj , NULL , & member ) ;
2005-07-08 00:07:09 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2005-07-08 00:07:09 +08:00
2018-07-31 17:24:53 +08:00
static void zend_std_call_issetter ( zend_object * zobj , zend_string * prop_name , zval * retval ) /* { { { */
2005-07-08 00:07:09 +08:00
{
2018-07-31 17:24:53 +08:00
zval member ;
ZVAL_STR ( & member , prop_name ) ;
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method_with_1_params ( zobj - > ce - > __isset , zobj , retval , & member ) ;
2005-07-08 00:07:09 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-04 20:12:34 +08:00
2018-09-13 06:41:19 +08:00
2023-01-03 23:38:22 +08:00
static zend_always_inline bool is_derived_class ( const zend_class_entry * child_class , const zend_class_entry * parent_class ) /* { { { */
2018-09-13 06:41:19 +08:00
{
child_class = child_class - > parent ;
while ( child_class ) {
if ( child_class = = parent_class ) {
return 1 ;
}
child_class = child_class - > parent ;
}
return 0 ;
}
/* }}} */
2023-01-03 23:38:22 +08:00
static zend_never_inline int is_protected_compatible_scope ( const zend_class_entry * ce , const zend_class_entry * scope ) /* { { { */
2018-09-13 06:41:19 +08:00
{
return scope & &
( is_derived_class ( ce , scope ) | | is_derived_class ( scope , ce ) ) ;
}
/* }}} */
2023-01-03 23:38:22 +08:00
static zend_never_inline zend_property_info * zend_get_parent_private_property ( zend_class_entry * scope , const zend_class_entry * ce , zend_string * member ) /* { { { */
2018-09-12 23:59:12 +08:00
{
zval * zv ;
zend_property_info * prop_info ;
if ( scope ! = ce & & scope & & is_derived_class ( ce , scope ) ) {
zv = zend_hash_find ( & scope - > properties_info , member ) ;
if ( zv ! = NULL ) {
prop_info = ( zend_property_info * ) Z_PTR_P ( zv ) ;
if ( ( prop_info - > flags & ZEND_ACC_PRIVATE )
& & prop_info - > ce = = scope ) {
return prop_info ;
}
}
}
return NULL ;
}
2018-09-13 15:31:49 +08:00
/* }}} */
2023-01-03 23:38:22 +08:00
static ZEND_COLD zend_never_inline void zend_bad_property_access ( const zend_property_info * property_info , const zend_class_entry * ce , const zend_string * member ) /* { { { */
2018-09-13 15:31:49 +08:00
{
zend_throw_error ( NULL , " Cannot access %s property %s::$%s " , zend_visibility_string ( property_info - > flags ) , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
}
/* }}} */
static ZEND_COLD zend_never_inline void zend_bad_property_name ( void ) /* { { { */
{
2020-05-26 20:10:57 +08:00
zend_throw_error ( NULL , " Cannot access property starting with \" \\ 0 \" " ) ;
2018-09-13 15:31:49 +08:00
}
/* }}} */
2018-09-12 23:59:12 +08:00
Add flag to forbid dynamic property creation on internal classes
While performing resource -> object migrations, we're adding
defensive classes that are final, non-serializable and non-clonable
(unless they are, of course). This path adds a ZEND_ACC_NO_DYNAMIC_PROPERTIES
flag, that also forbids the creation of dynamic properties on these objects.
This is a subset of #3931 and targeted at internal usage only
(though may be extended to userland at some point in the future).
It's already possible to achieve this (what the removed
WeakRef/WeakMap code does), but there's some caveats: First, this
simple approach is only possible if the class has no declared
properties, otherwise it's necessary to special-case those
properties. Second, it's easy to make it overly strict, e.g. by
forbidding isset($obj->prop) as well. And finally, it requires a
lot of boilerplate code for each class.
Closes GH-5572.
2020-05-14 17:51:36 +08:00
static ZEND_COLD zend_never_inline void zend_forbidden_dynamic_property (
2023-01-03 23:38:22 +08:00
const zend_class_entry * ce , const zend_string * member ) {
Add flag to forbid dynamic property creation on internal classes
While performing resource -> object migrations, we're adding
defensive classes that are final, non-serializable and non-clonable
(unless they are, of course). This path adds a ZEND_ACC_NO_DYNAMIC_PROPERTIES
flag, that also forbids the creation of dynamic properties on these objects.
This is a subset of #3931 and targeted at internal usage only
(though may be extended to userland at some point in the future).
It's already possible to achieve this (what the removed
WeakRef/WeakMap code does), but there's some caveats: First, this
simple approach is only possible if the class has no declared
properties, otherwise it's necessary to special-case those
properties. Second, it's easy to make it overly strict, e.g. by
forbidding isset($obj->prop) as well. And finally, it requires a
lot of boilerplate code for each class.
Closes GH-5572.
2020-05-14 17:51:36 +08:00
zend_throw_error ( NULL , " Cannot create dynamic property %s::$%s " ,
ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
}
2022-01-28 17:38:38 +08:00
static ZEND_COLD zend_never_inline bool zend_deprecated_dynamic_property (
2023-01-03 23:38:22 +08:00
zend_object * obj , const zend_string * member ) {
2022-01-28 17:38:38 +08:00
GC_ADDREF ( obj ) ;
2021-10-11 21:49:27 +08:00
zend_error ( E_DEPRECATED , " Creation of dynamic property %s::$%s is deprecated " ,
2022-01-28 17:38:38 +08:00
ZSTR_VAL ( obj - > ce - > name ) , ZSTR_VAL ( member ) ) ;
if ( UNEXPECTED ( GC_DELREF ( obj ) = = 0 ) ) {
2022-02-28 21:37:17 +08:00
zend_class_entry * ce = obj - > ce ;
2022-01-28 17:38:38 +08:00
zend_objects_store_del ( obj ) ;
2022-02-28 21:37:17 +08:00
if ( ! EG ( exception ) ) {
/* We cannot continue execution and have to throw an exception */
zend_throw_error ( NULL , " Cannot create dynamic property %s::$%s " ,
ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
}
2022-01-28 17:38:38 +08:00
return 0 ;
}
return 1 ;
2021-10-11 21:49:27 +08:00
}
2021-06-02 16:02:56 +08:00
static ZEND_COLD zend_never_inline void zend_readonly_property_modification_scope_error (
2023-01-03 23:38:22 +08:00
const zend_class_entry * ce , const zend_string * member , const zend_class_entry * scope , const char * operation ) {
2021-06-02 16:02:56 +08:00
zend_throw_error ( NULL , " Cannot %s readonly property %s::$%s from %s%s " ,
operation , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ,
scope ? " scope " : " global scope " , scope ? ZSTR_VAL ( scope - > name ) : " " ) ;
}
static ZEND_COLD zend_never_inline void zend_readonly_property_unset_error (
zend_class_entry * ce , zend_string * member ) {
zend_throw_error ( NULL , " Cannot unset readonly property %s::$%s " ,
ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
}
2023-01-03 23:38:22 +08:00
static zend_always_inline uintptr_t zend_get_property_offset ( zend_class_entry * ce , zend_string * member , int silent , void * * cache_slot , const zend_property_info * * info_ptr ) /* { { { */
2003-02-07 18:05:36 +08:00
{
2014-10-21 00:33:35 +08:00
zval * zv ;
2018-09-12 23:59:12 +08:00
zend_property_info * property_info ;
2014-10-21 00:33:35 +08:00
uint32_t flags ;
2016-04-28 09:13:34 +08:00
zend_class_entry * scope ;
2019-01-16 17:49:28 +08:00
uintptr_t offset ;
2003-03-19 00:30:23 +08:00
2014-07-08 00:54:31 +08:00
if ( cache_slot & & EXPECTED ( ce = = CACHED_PTR_EX ( cache_slot ) ) ) {
2019-01-16 16:56:53 +08:00
* info_ptr = CACHED_PTR_EX ( cache_slot + 2 ) ;
2017-10-25 16:45:17 +08:00
return ( uintptr_t ) CACHED_PTR_EX ( cache_slot + 1 ) ;
2010-05-24 22:11:39 +08:00
}
2018-09-12 23:59:12 +08:00
if ( UNEXPECTED ( zend_hash_num_elements ( & ce - > properties_info ) = = 0 )
| | UNEXPECTED ( ( zv = zend_hash_find ( & ce - > properties_info , member ) ) = = NULL ) ) {
2018-09-13 15:31:49 +08:00
if ( UNEXPECTED ( ZSTR_VAL ( member ) [ 0 ] = = ' \0 ' ) & & ZSTR_LEN ( member ) ! = 0 ) {
2018-09-12 23:59:12 +08:00
if ( ! silent ) {
2018-09-13 15:31:49 +08:00
zend_bad_property_name ( ) ;
2018-09-12 14:57:36 +08:00
}
2018-09-12 23:59:12 +08:00
return ZEND_WRONG_PROPERTY_OFFSET ;
}
dynamic :
if ( cache_slot ) {
CACHE_POLYMORPHIC_PTR_EX ( cache_slot , ce , ( void * ) ZEND_DYNAMIC_PROPERTY_OFFSET ) ;
2019-01-07 19:28:51 +08:00
CACHE_PTR_EX ( cache_slot + 2 , NULL ) ;
2018-09-12 23:59:12 +08:00
}
return ZEND_DYNAMIC_PROPERTY_OFFSET ;
}
property_info = ( zend_property_info * ) Z_PTR_P ( zv ) ;
flags = property_info - > flags ;
if ( flags & ( ZEND_ACC_CHANGED | ZEND_ACC_PRIVATE | ZEND_ACC_PROTECTED ) ) {
2018-09-13 06:41:19 +08:00
if ( UNEXPECTED ( EG ( fake_scope ) ) ) {
2018-09-12 23:59:12 +08:00
scope = EG ( fake_scope ) ;
2018-09-11 16:56:45 +08:00
} else {
2018-09-12 23:59:12 +08:00
scope = zend_get_executed_scope ( ) ;
}
2018-09-13 06:41:19 +08:00
if ( property_info - > ce ! = scope ) {
2018-09-13 14:31:38 +08:00
if ( flags & ZEND_ACC_CHANGED ) {
zend_property_info * p = zend_get_parent_private_property ( scope , ce , member ) ;
2020-07-15 20:59:55 +08:00
/* If there is a public/protected instance property on ce, don't try to use a
* private static property on scope . If both are static , prefer the static
* property on scope . This will throw a static property notice , rather than
* a visibility error . */
if ( p & & ( ! ( p - > flags & ZEND_ACC_STATIC ) | | ( flags & ZEND_ACC_STATIC ) ) ) {
2018-09-13 14:31:38 +08:00
property_info = p ;
flags = property_info - > flags ;
goto found ;
} else if ( flags & ZEND_ACC_PUBLIC ) {
goto found ;
}
}
2018-09-13 06:41:19 +08:00
if ( flags & ZEND_ACC_PRIVATE ) {
2018-09-12 23:59:12 +08:00
if ( property_info - > ce ! = ce ) {
goto dynamic ;
2018-09-11 16:56:45 +08:00
} else {
2018-09-12 23:59:12 +08:00
wrong :
/* Information was available, but we were denied access. Error out. */
if ( ! silent ) {
2018-09-13 15:31:49 +08:00
zend_bad_property_access ( property_info , ce , member ) ;
2018-09-11 16:56:45 +08:00
}
2018-09-12 23:59:12 +08:00
return ZEND_WRONG_PROPERTY_OFFSET ;
2018-09-11 16:56:45 +08:00
}
2018-09-13 06:41:19 +08:00
} else {
ZEND_ASSERT ( flags & ZEND_ACC_PROTECTED ) ;
2018-09-13 13:49:21 +08:00
if ( UNEXPECTED ( ! is_protected_compatible_scope ( property_info - > ce , scope ) ) ) {
2018-09-13 06:41:19 +08:00
goto wrong ;
}
2018-07-18 19:49:00 +08:00
}
}
2018-09-12 14:57:36 +08:00
}
2018-09-12 23:59:12 +08:00
found :
if ( UNEXPECTED ( flags & ZEND_ACC_STATIC ) ) {
2014-10-21 00:33:35 +08:00
if ( ! silent ) {
2018-09-12 23:59:12 +08:00
zend_error ( E_NOTICE , " Accessing static property %s::$%s as non static " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
2003-03-19 00:30:23 +08:00
}
2018-09-12 23:59:12 +08:00
return ZEND_DYNAMIC_PROPERTY_OFFSET ;
2014-10-21 00:33:35 +08:00
}
2019-01-16 17:49:28 +08:00
offset = property_info - > offset ;
2019-09-23 22:53:54 +08:00
if ( EXPECTED ( ! ZEND_TYPE_IS_SET ( property_info - > type ) ) ) {
2019-01-16 17:49:28 +08:00
property_info = NULL ;
} else {
2019-01-16 16:56:53 +08:00
* info_ptr = property_info ;
}
2019-01-16 17:49:28 +08:00
if ( cache_slot ) {
CACHE_POLYMORPHIC_PTR_EX ( cache_slot , ce , ( void * ) ( uintptr_t ) offset ) ;
CACHE_PTR_EX ( cache_slot + 2 , property_info ) ;
}
return offset ;
2003-02-07 18:05:36 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-07 18:05:36 +08:00
2019-05-29 16:31:46 +08:00
static ZEND_COLD void zend_wrong_offset ( zend_class_entry * ce , zend_string * member ) /* { { { */
2019-01-16 16:56:53 +08:00
{
2023-01-03 23:38:22 +08:00
const zend_property_info * dummy ;
2019-01-16 16:56:53 +08:00
/* Trigger the correct error */
zend_get_property_offset ( ce , member , 0 , NULL , & dummy ) ;
}
/* }}} */
2023-01-03 23:38:22 +08:00
ZEND_API zend_property_info * zend_get_property_info ( const zend_class_entry * ce , zend_string * member , int silent ) /* { { { */
2010-04-20 18:57:45 +08:00
{
2015-01-13 16:33:00 +08:00
zval * zv ;
2018-09-12 23:59:12 +08:00
zend_property_info * property_info ;
2015-01-13 16:33:00 +08:00
uint32_t flags ;
2016-04-28 09:13:34 +08:00
zend_class_entry * scope ;
2015-01-13 16:33:00 +08:00
2018-09-12 23:59:12 +08:00
if ( UNEXPECTED ( zend_hash_num_elements ( & ce - > properties_info ) = = 0 )
| | EXPECTED ( ( zv = zend_hash_find ( & ce - > properties_info , member ) ) = = NULL ) ) {
2018-09-13 15:31:49 +08:00
if ( UNEXPECTED ( ZSTR_VAL ( member ) [ 0 ] = = ' \0 ' ) & & ZSTR_LEN ( member ) ! = 0 ) {
2018-09-12 23:59:12 +08:00
if ( ! silent ) {
2018-09-13 15:31:49 +08:00
zend_bad_property_name ( ) ;
2018-09-12 14:57:36 +08:00
}
2018-09-12 23:59:12 +08:00
return ZEND_WRONG_PROPERTY_INFO ;
}
dynamic :
return NULL ;
}
property_info = ( zend_property_info * ) Z_PTR_P ( zv ) ;
flags = property_info - > flags ;
if ( flags & ( ZEND_ACC_CHANGED | ZEND_ACC_PRIVATE | ZEND_ACC_PROTECTED ) ) {
2018-09-13 06:41:19 +08:00
if ( UNEXPECTED ( EG ( fake_scope ) ) ) {
2018-09-12 23:59:12 +08:00
scope = EG ( fake_scope ) ;
2018-09-11 16:56:45 +08:00
} else {
2018-09-12 23:59:12 +08:00
scope = zend_get_executed_scope ( ) ;
}
2018-09-13 06:41:19 +08:00
if ( property_info - > ce ! = scope ) {
2018-09-13 14:31:38 +08:00
if ( flags & ZEND_ACC_CHANGED ) {
zend_property_info * p = zend_get_parent_private_property ( scope , ce , member ) ;
if ( p ) {
property_info = p ;
flags = property_info - > flags ;
goto found ;
} else if ( flags & ZEND_ACC_PUBLIC ) {
goto found ;
}
}
2018-09-13 06:41:19 +08:00
if ( flags & ZEND_ACC_PRIVATE ) {
2018-09-12 23:59:12 +08:00
if ( property_info - > ce ! = ce ) {
goto dynamic ;
2018-09-11 16:56:45 +08:00
} else {
2018-09-12 23:59:12 +08:00
wrong :
/* Information was available, but we were denied access. Error out. */
if ( ! silent ) {
2018-09-13 15:31:49 +08:00
zend_bad_property_access ( property_info , ce , member ) ;
2018-09-11 16:56:45 +08:00
}
2018-09-12 23:59:12 +08:00
return ZEND_WRONG_PROPERTY_INFO ;
2018-09-11 16:56:45 +08:00
}
2018-09-13 06:41:19 +08:00
} else {
ZEND_ASSERT ( flags & ZEND_ACC_PROTECTED ) ;
2018-09-13 13:49:21 +08:00
if ( UNEXPECTED ( ! is_protected_compatible_scope ( property_info - > ce , scope ) ) ) {
2018-09-13 06:41:19 +08:00
goto wrong ;
}
2018-07-18 19:49:00 +08:00
}
}
2018-09-12 14:57:36 +08:00
}
2018-09-12 23:59:12 +08:00
found :
if ( UNEXPECTED ( flags & ZEND_ACC_STATIC ) ) {
2015-01-13 16:33:00 +08:00
if ( ! silent ) {
2018-09-12 23:59:12 +08:00
zend_error ( E_NOTICE , " Accessing static property %s::$%s as non static " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( member ) ) ;
2015-01-13 16:33:00 +08:00
}
}
return property_info ;
2010-04-20 18:57:45 +08:00
}
/* }}} */
2023-01-16 21:55:12 +08:00
ZEND_API zend_result zend_check_property_access ( const zend_object * zobj , zend_string * prop_info_name , bool is_dynamic ) /* { { { */
2003-12-19 04:07:30 +08:00
{
zend_property_info * property_info ;
2014-04-02 03:27:51 +08:00
const char * class_name = NULL ;
const char * prop_name ;
zend_string * member ;
2014-09-16 06:23:58 +08:00
size_t prop_name_len ;
2003-12-19 04:07:30 +08:00
2015-06-30 18:59:27 +08:00
if ( ZSTR_VAL ( prop_info_name ) [ 0 ] = = 0 ) {
2018-10-20 23:11:17 +08:00
if ( is_dynamic ) {
return SUCCESS ;
}
2014-09-16 06:23:58 +08:00
zend_unmangle_property_name_ex ( prop_info_name , & class_name , & prop_name , & prop_name_len ) ;
2014-08-26 01:24:55 +08:00
member = zend_string_init ( prop_name , prop_name_len , 0 ) ;
2018-09-11 16:56:45 +08:00
property_info = zend_get_property_info ( zobj - > ce , member , 1 ) ;
zend_string_release_ex ( member , 0 ) ;
2018-10-20 23:11:17 +08:00
if ( property_info = = NULL | | property_info = = ZEND_WRONG_PROPERTY_INFO ) {
2014-10-17 23:10:05 +08:00
return FAILURE ;
}
2018-10-20 23:11:17 +08:00
2018-09-11 16:56:45 +08:00
if ( class_name [ 0 ] ! = ' * ' ) {
if ( ! ( property_info - > flags & ZEND_ACC_PRIVATE ) ) {
/* we we're looking for a private prop but found a non private one of the same name */
return FAILURE ;
} else if ( strcmp ( ZSTR_VAL ( prop_info_name ) + 1 , ZSTR_VAL ( property_info - > name ) + 1 ) ) {
/* we we're looking for a private prop but found a private one of the same name but another class */
return FAILURE ;
}
} else {
ZEND_ASSERT ( property_info - > flags & ZEND_ACC_PROTECTED ) ;
}
2018-10-11 05:18:34 +08:00
return SUCCESS ;
2018-09-11 16:56:45 +08:00
} else {
property_info = zend_get_property_info ( zobj - > ce , prop_info_name , 1 ) ;
if ( property_info = = NULL ) {
2018-10-20 23:11:17 +08:00
ZEND_ASSERT ( is_dynamic ) ;
2018-09-11 16:56:45 +08:00
return SUCCESS ;
} else if ( property_info = = ZEND_WRONG_PROPERTY_INFO ) {
2005-06-06 15:52:08 +08:00
return FAILURE ;
}
2018-10-11 05:18:34 +08:00
return ( property_info - > flags & ZEND_ACC_PUBLIC ) ? SUCCESS : FAILURE ;
2003-12-19 04:07:30 +08:00
}
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-12-19 04:07:30 +08:00
2015-01-31 18:12:31 +08:00
static void zend_property_guard_dtor ( zval * el ) /* { { { */ {
2016-04-27 05:24:20 +08:00
uint32_t * ptr = ( uint32_t * ) Z_PTR_P ( el ) ;
2023-02-19 03:31:28 +08:00
if ( EXPECTED ( ! ( ( ( uintptr_t ) ptr ) & 1 ) ) ) {
2016-04-27 05:24:20 +08:00
efree_size ( ptr , sizeof ( uint32_t ) ) ;
}
2015-01-31 18:12:31 +08:00
}
/* }}} */
2023-08-24 19:58:13 +08:00
static zend_always_inline zval * zend_get_guard_value ( zend_object * zobj )
{
return zobj - > properties_table + zobj - > ce - > default_properties_count ;
}
2016-04-30 13:48:47 +08:00
ZEND_API uint32_t * zend_get_property_guard ( zend_object * zobj , zend_string * member ) /* { { { */
2005-11-15 21:35:23 +08:00
{
2015-02-04 20:24:13 +08:00
HashTable * guards ;
2016-04-27 05:24:20 +08:00
zval * zv ;
uint32_t * ptr ;
2015-02-04 20:24:13 +08:00
2023-08-24 19:58:13 +08:00
2018-02-27 22:38:56 +08:00
ZEND_ASSERT ( zobj - > ce - > ce_flags & ZEND_ACC_USE_GUARDS ) ;
2023-08-24 19:58:13 +08:00
zv = zend_get_guard_value ( zobj ) ;
2016-04-27 05:24:20 +08:00
if ( EXPECTED ( Z_TYPE_P ( zv ) = = IS_STRING ) ) {
zend_string * str = Z_STR_P ( zv ) ;
if ( EXPECTED ( str = = member ) | |
2023-01-07 20:49:24 +08:00
/* str and member don't necessarily have a pre-calculated hash value here */
EXPECTED ( zend_string_equal_content ( str , member ) ) ) {
2023-08-24 19:58:13 +08:00
return & Z_GUARD_P ( zv ) ;
} else if ( EXPECTED ( Z_GUARD_P ( zv ) = = 0 ) ) {
2018-07-04 17:08:07 +08:00
zval_ptr_dtor_str ( zv ) ;
2016-04-27 05:24:20 +08:00
ZVAL_STR_COPY ( zv , member ) ;
2023-08-24 19:58:13 +08:00
return & Z_GUARD_P ( zv ) ;
2016-04-27 05:24:20 +08:00
} else {
ALLOC_HASHTABLE ( guards ) ;
zend_hash_init ( guards , 8 , NULL , zend_property_guard_dtor , 0 ) ;
/* mark pointer as "special" using low bit */
2017-04-02 19:30:35 +08:00
zend_hash_add_new_ptr ( guards , str ,
2023-08-24 19:58:13 +08:00
( void * ) ( ( ( uintptr_t ) & Z_GUARD_P ( zv ) ) | 1 ) ) ;
2018-07-04 17:08:07 +08:00
zval_ptr_dtor_str ( zv ) ;
2016-04-27 05:24:20 +08:00
ZVAL_ARR ( zv , guards ) ;
}
} else if ( EXPECTED ( Z_TYPE_P ( zv ) = = IS_ARRAY ) ) {
guards = Z_ARRVAL_P ( zv ) ;
2015-02-04 20:24:13 +08:00
ZEND_ASSERT ( guards ! = NULL ) ;
2016-04-27 05:24:20 +08:00
zv = zend_hash_find ( guards , member ) ;
if ( zv ! = NULL ) {
2023-02-19 03:31:28 +08:00
return ( uint32_t * ) ( ( ( uintptr_t ) Z_PTR_P ( zv ) ) & ~ 1 ) ;
2015-02-04 20:24:13 +08:00
}
} else {
2016-04-27 05:24:20 +08:00
ZEND_ASSERT ( Z_TYPE_P ( zv ) = = IS_UNDEF ) ;
ZVAL_STR_COPY ( zv , member ) ;
2023-08-24 19:58:13 +08:00
Z_GUARD_P ( zv ) & = ~ ZEND_GUARD_PROPERTY_MASK ;
return & Z_GUARD_P ( zv ) ;
2016-04-27 05:24:20 +08:00
}
/* we have to allocate uint32_t separately because ht->arData may be reallocated */
ptr = ( uint32_t * ) emalloc ( sizeof ( uint32_t ) ) ;
* ptr = 0 ;
return ( uint32_t * ) zend_hash_add_new_ptr ( guards , member , ptr ) ;
2005-11-15 21:35:23 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2005-11-15 21:35:23 +08:00
2023-08-24 19:58:13 +08:00
ZEND_API uint32_t * zend_get_recursion_guard ( zend_object * zobj )
{
if ( ! ( zobj - > ce - > ce_flags & ZEND_ACC_USE_GUARDS ) ) {
return NULL ;
}
zval * zv = zend_get_guard_value ( zobj ) ;
return & Z_GUARD_P ( zv ) ;
}
2019-01-31 23:47:58 +08:00
ZEND_API zval * zend_std_read_property ( zend_object * zobj , zend_string * name , int type , void * * cache_slot , zval * rv ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2014-02-10 14:04:30 +08:00
zval * retval ;
2017-10-25 16:45:17 +08:00
uintptr_t property_offset ;
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = NULL ;
2017-10-26 10:08:39 +08:00
uint32_t * guard = NULL ;
2019-01-31 23:47:58 +08:00
zend_string * tmp_name = NULL ;
2002-02-07 22:08:43 +08:00
# if DEBUG_OBJECT_HANDLERS
2019-01-31 23:47:58 +08:00
fprintf ( stderr , " Read object #%d property: %s \n " , zobj - > handle , ZSTR_VAL ( name ) ) ;
2007-10-11 09:03:19 +08:00
# endif
2003-02-04 20:12:34 +08:00
2004-07-19 22:26:53 +08:00
/* make zend_get_property_info silent if we have getter - we may want to use it */
2019-01-16 16:56:53 +08:00
property_offset = zend_get_property_offset ( zobj - > ce , name , ( type = = BP_VAR_IS ) | | ( zobj - > ce - > __get ! = NULL ) , cache_slot , & prop_info ) ;
2003-02-04 20:12:34 +08:00
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
retval = OBJ_PROP ( zobj , property_offset ) ;
if ( EXPECTED ( Z_TYPE_P ( retval ) ! = IS_UNDEF ) ) {
2021-06-02 16:02:56 +08:00
if ( prop_info & & UNEXPECTED ( prop_info - > flags & ZEND_ACC_READONLY )
& & ( type = = BP_VAR_W | | type = = BP_VAR_RW | | type = = BP_VAR_UNSET ) ) {
if ( Z_TYPE_P ( retval ) = = IS_OBJECT ) {
2021-07-20 18:43:54 +08:00
/* For objects, W/RW/UNSET fetch modes might not actually modify object.
2021-06-02 16:02:56 +08:00
* Similar as with magic __get ( ) allow them , but return the value as a copy
* to make sure no actual modification is possible . */
ZVAL_COPY ( rv , retval ) ;
retval = rv ;
2023-03-01 05:54:38 +08:00
} else if ( Z_PROP_FLAG_P ( retval ) & IS_PROP_REINITABLE ) {
Z_PROP_FLAG_P ( retval ) & = ~ IS_PROP_REINITABLE ;
2021-06-02 16:02:56 +08:00
} else {
zend_readonly_property_modification_error ( prop_info ) ;
retval = & EG ( uninitialized_zval ) ;
}
}
2017-09-18 18:13:24 +08:00
goto exit ;
2022-03-11 01:06:03 +08:00
} else {
if ( prop_info & & UNEXPECTED ( prop_info - > flags & ZEND_ACC_READONLY ) ) {
if ( type = = BP_VAR_W | | type = = BP_VAR_RW ) {
zend_readonly_property_indirect_modification_error ( prop_info ) ;
retval = & EG ( uninitialized_zval ) ;
goto exit ;
} else if ( type = = BP_VAR_UNSET ) {
retval = & EG ( uninitialized_zval ) ;
goto exit ;
}
}
2017-09-18 18:13:24 +08:00
}
2023-03-01 05:54:38 +08:00
if ( UNEXPECTED ( Z_PROP_FLAG_P ( retval ) & IS_PROP_UNINIT ) ) {
2019-10-28 20:34:33 +08:00
/* Skip __get() for uninitialized typed properties */
goto uninit_error ;
}
2017-09-18 18:13:24 +08:00
} else if ( EXPECTED ( IS_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) ) {
if ( EXPECTED ( zobj - > properties ! = NULL ) ) {
2017-10-23 23:29:13 +08:00
if ( ! IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) {
2017-10-25 16:45:17 +08:00
uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET ( property_offset ) ;
2017-10-23 23:29:13 +08:00
2017-10-24 15:28:19 +08:00
if ( EXPECTED ( idx < zobj - > properties - > nNumUsed * sizeof ( Bucket ) ) ) {
Bucket * p = ( Bucket * ) ( ( char * ) zobj - > properties - > arData + idx ) ;
2017-10-23 23:29:13 +08:00
2021-12-22 17:54:32 +08:00
if ( EXPECTED ( p - > key = = name ) | |
( EXPECTED ( p - > h = = ZSTR_H ( name ) ) & &
EXPECTED ( p - > key ! = NULL ) & &
EXPECTED ( zend_string_equal_content ( p - > key , name ) ) ) ) {
2017-10-23 23:29:13 +08:00
retval = & p - > val ;
goto exit ;
}
}
2017-10-24 15:28:19 +08:00
CACHE_PTR_EX ( cache_slot + 1 , ( void * ) ZEND_DYNAMIC_PROPERTY_OFFSET ) ;
2017-10-23 23:29:13 +08:00
}
2018-07-31 17:24:53 +08:00
retval = zend_hash_find ( zobj - > properties , name ) ;
2017-10-23 23:29:13 +08:00
if ( EXPECTED ( retval ) ) {
if ( cache_slot ) {
2017-10-25 16:45:17 +08:00
uintptr_t idx = ( char * ) retval - ( char * ) zobj - > properties - > arData ;
2017-10-24 15:28:19 +08:00
CACHE_PTR_EX ( cache_slot + 1 , ( void * ) ZEND_ENCODE_DYN_PROP_OFFSET ( idx ) ) ;
2017-10-23 23:29:13 +08:00
}
goto exit ;
}
2014-02-10 14:04:30 +08:00
}
2015-04-01 05:37:59 +08:00
} else if ( UNEXPECTED ( EG ( exception ) ) ) {
retval = & EG ( uninitialized_zval ) ;
goto exit ;
2014-02-10 14:04:30 +08:00
}
2016-03-26 18:43:56 +08:00
/* magic isset */
if ( ( type = = BP_VAR_IS ) & & zobj - > ce - > __isset ) {
2017-10-24 02:07:49 +08:00
zval tmp_result ;
2018-07-31 17:24:53 +08:00
guard = zend_get_property_guard ( zobj , name ) ;
2016-03-26 18:43:56 +08:00
if ( ! ( ( * guard ) & IN_ISSET ) ) {
2018-07-31 17:24:53 +08:00
if ( ! tmp_name & & ! ZSTR_IS_INTERNED ( name ) ) {
tmp_name = zend_string_copy ( name ) ;
2017-10-26 18:05:23 +08:00
}
2018-07-10 18:17:33 +08:00
GC_ADDREF ( zobj ) ;
2016-03-26 18:43:56 +08:00
ZVAL_UNDEF ( & tmp_result ) ;
* guard | = IN_ISSET ;
2018-07-31 17:24:53 +08:00
zend_std_call_issetter ( zobj , name , & tmp_result ) ;
2016-03-26 18:43:56 +08:00
* guard & = ~ IN_ISSET ;
2017-12-31 12:35:25 +08:00
2016-03-26 18:43:56 +08:00
if ( ! zend_is_true ( & tmp_result ) ) {
retval = & EG ( uninitialized_zval ) ;
2018-07-10 18:17:33 +08:00
OBJ_RELEASE ( zobj ) ;
2016-03-26 18:43:56 +08:00
zval_ptr_dtor ( & tmp_result ) ;
goto exit ;
}
zval_ptr_dtor ( & tmp_result ) ;
2018-07-10 18:17:33 +08:00
if ( zobj - > ce - > __get & & ! ( ( * guard ) & IN_GET ) ) {
goto call_getter ;
}
OBJ_RELEASE ( zobj ) ;
} else if ( zobj - > ce - > __get & & ! ( ( * guard ) & IN_GET ) ) {
goto call_getter_addref ;
2016-03-26 18:43:56 +08:00
}
2018-07-10 18:17:33 +08:00
} else if ( zobj - > ce - > __get ) {
/* magic get */
2018-07-31 17:24:53 +08:00
guard = zend_get_property_guard ( zobj , name ) ;
2014-02-10 14:04:30 +08:00
if ( ! ( ( * guard ) & IN_GET ) ) {
2002-09-04 17:07:58 +08:00
/* have getter - try with it! */
2018-07-10 18:17:33 +08:00
call_getter_addref :
GC_ADDREF ( zobj ) ;
call_getter :
2014-02-10 14:04:30 +08:00
* guard | = IN_GET ; /* prevent circular getting */
2018-07-31 17:24:53 +08:00
zend_std_call_getter ( zobj , name , rv ) ;
2014-02-10 14:04:30 +08:00
* guard & = ~ IN_GET ;
2002-09-04 17:07:58 +08:00
2014-02-27 19:40:13 +08:00
if ( Z_TYPE_P ( rv ) ! = IS_UNDEF ) {
retval = rv ;
2007-10-07 13:22:07 +08:00
if ( ! Z_ISREF_P ( rv ) & &
2007-01-10 23:58:08 +08:00
( type = = BP_VAR_W | | type = = BP_VAR_RW | | type = = BP_VAR_UNSET ) ) {
2010-05-06 20:52:27 +08:00
if ( UNEXPECTED ( Z_TYPE_P ( rv ) ! = IS_OBJECT ) ) {
2018-07-31 17:24:53 +08:00
zend_error ( E_NOTICE , " Indirect modification of overloaded property %s::$%s has no effect " , ZSTR_VAL ( zobj - > ce - > name ) , ZSTR_VAL ( name ) ) ;
2006-07-21 18:32:17 +08:00
}
}
2002-09-04 17:07:58 +08:00
} else {
2014-02-27 19:40:13 +08:00
retval = & EG ( uninitialized_zval ) ;
2002-09-04 17:07:58 +08:00
}
2019-01-07 19:28:51 +08:00
2019-01-16 16:56:53 +08:00
if ( UNEXPECTED ( prop_info ) ) {
2022-08-26 22:33:47 +08:00
zend_verify_prop_assignable_by_ref_ex ( prop_info , retval , ( zobj - > ce - > __get - > common . fn_flags & ZEND_ACC_STRICT_TYPES ) ! = 0 , ZEND_VERIFY_PROP_ASSIGNABLE_BY_REF_CONTEXT_MAGIC_GET ) ;
2019-01-07 19:28:51 +08:00
}
2018-07-10 18:17:33 +08:00
OBJ_RELEASE ( zobj ) ;
2014-10-17 23:10:05 +08:00
goto exit ;
2018-09-27 20:58:26 +08:00
} else if ( UNEXPECTED ( IS_WRONG_PROPERTY_OFFSET ( property_offset ) ) ) {
/* Trigger the correct error */
2019-01-16 16:56:53 +08:00
zend_get_property_offset ( zobj - > ce , name , 0 , NULL , & prop_info ) ;
2018-09-27 20:58:26 +08:00
ZEND_ASSERT ( EG ( exception ) ) ;
2017-11-29 14:46:21 +08:00
retval = & EG ( uninitialized_zval ) ;
goto exit ;
2014-02-10 14:04:30 +08:00
}
2002-02-07 22:08:43 +08:00
}
2017-10-26 18:05:23 +08:00
2019-10-28 20:34:33 +08:00
uninit_error :
2019-01-07 19:28:51 +08:00
if ( type ! = BP_VAR_IS ) {
2019-01-16 16:56:53 +08:00
if ( UNEXPECTED ( prop_info ) ) {
2019-01-07 19:28:51 +08:00
zend_throw_error ( NULL , " Typed property %s::$%s must not be accessed before initialization " ,
ZSTR_VAL ( prop_info - > ce - > name ) ,
ZSTR_VAL ( name ) ) ;
} else {
2019-10-01 19:20:42 +08:00
zend_error ( E_WARNING , " Undefined property: %s::$%s " , ZSTR_VAL ( zobj - > ce - > name ) , ZSTR_VAL ( name ) ) ;
2019-01-07 19:28:51 +08:00
}
2014-10-17 23:10:05 +08:00
}
retval = & EG ( uninitialized_zval ) ;
2014-03-26 22:07:31 +08:00
2014-02-10 14:04:30 +08:00
exit :
2018-07-31 17:24:53 +08:00
zend_tmp_string_release ( tmp_name ) ;
2015-09-10 14:39:25 +08:00
2014-02-10 14:04:30 +08:00
return retval ;
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2021-11-24 21:35:43 +08:00
static zend_always_inline bool property_uses_strict_types ( void ) {
2020-09-01 21:14:32 +08:00
zend_execute_data * execute_data = EG ( current_execute_data ) ;
return execute_data
& & execute_data - > func
& & ZEND_CALL_USES_STRICT_TYPES ( EG ( current_execute_data ) ) ;
}
2021-06-02 16:02:56 +08:00
static bool verify_readonly_initialization_access (
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info , const zend_class_entry * ce ,
2021-06-02 16:02:56 +08:00
zend_string * name , const char * operation ) {
zend_class_entry * scope ;
if ( UNEXPECTED ( EG ( fake_scope ) ) ) {
scope = EG ( fake_scope ) ;
} else {
scope = zend_get_executed_scope ( ) ;
}
if ( prop_info - > ce = = scope ) {
return true ;
}
/* We may have redeclared a parent property. In that case the parent should still be
* allowed to initialize it . */
if ( scope & & is_derived_class ( ce , scope ) ) {
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = zend_hash_find_ptr ( & scope - > properties_info , name ) ;
2021-06-02 16:02:56 +08:00
if ( prop_info ) {
/* This should be ensured by inheritance. */
ZEND_ASSERT ( prop_info - > flags & ZEND_ACC_READONLY ) ;
if ( prop_info - > ce = = scope ) {
return true ;
}
}
}
zend_readonly_property_modification_scope_error ( prop_info - > ce , name , scope , operation ) ;
return false ;
}
2019-01-31 23:47:58 +08:00
ZEND_API zval * zend_std_write_property ( zend_object * zobj , zend_string * name , zval * value , void * * cache_slot ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2019-01-07 19:28:51 +08:00
zval * variable_ptr , tmp ;
2017-10-25 16:45:17 +08:00
uintptr_t property_offset ;
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = NULL ;
2019-01-10 00:09:48 +08:00
ZEND_ASSERT ( ! Z_ISREF_P ( value ) ) ;
2004-07-19 22:26:53 +08:00
2019-01-16 16:56:53 +08:00
property_offset = zend_get_property_offset ( zobj - > ce , name , ( zobj - > ce - > __set ! = NULL ) , cache_slot , & prop_info ) ;
2003-02-04 20:12:34 +08:00
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
variable_ptr = OBJ_PROP ( zobj , property_offset ) ;
if ( Z_TYPE_P ( variable_ptr ) ! = IS_UNDEF ) {
2019-01-07 19:28:51 +08:00
Z_TRY_ADDREF_P ( value ) ;
if ( UNEXPECTED ( prop_info ) ) {
2023-03-04 02:53:42 +08:00
if ( UNEXPECTED ( ( prop_info - > flags & ZEND_ACC_READONLY ) & & ! ( Z_PROP_FLAG_P ( variable_ptr ) & IS_PROP_REINITABLE ) ) ) {
Z_TRY_DELREF_P ( value ) ;
zend_readonly_property_modification_error ( prop_info ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
2019-01-07 19:28:51 +08:00
ZVAL_COPY_VALUE ( & tmp , value ) ;
2022-12-29 03:30:51 +08:00
// Increase refcount to prevent object from being released in __toString()
GC_ADDREF ( zobj ) ;
bool type_matched = zend_verify_property_type ( prop_info , & tmp , property_uses_strict_types ( ) ) ;
if ( UNEXPECTED ( GC_DELREF ( zobj ) = = 0 ) ) {
zend_object_released_while_assigning_to_property_error ( prop_info ) ;
zend_objects_store_del ( zobj ) ;
zval_ptr_dtor ( & tmp ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
if ( UNEXPECTED ( ! type_matched ) ) {
2019-01-07 19:28:51 +08:00
Z_TRY_DELREF_P ( value ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
2023-03-04 02:53:42 +08:00
Z_PROP_FLAG_P ( variable_ptr ) & = ~ IS_PROP_REINITABLE ;
2019-01-07 19:28:51 +08:00
value = & tmp ;
}
2023-03-03 07:02:42 +08:00
found : ;
zend_refcounted * garbage = NULL ;
variable_ptr = zend_assign_to_variable_ex (
variable_ptr , value , IS_TMP_VAR , property_uses_strict_types ( ) , & garbage ) ;
if ( garbage ) {
if ( GC_DELREF ( garbage ) = = 0 ) {
zend_execute_data * execute_data = EG ( current_execute_data ) ;
// Assign to result variable before calling the destructor as it may release the object
if ( execute_data
& & EX ( func )
& & ZEND_USER_CODE ( EX ( func ) - > common . type )
& & EX ( opline )
& & EX ( opline ) - > opcode = = ZEND_ASSIGN_OBJ
& & EX ( opline ) - > result_type ) {
ZVAL_COPY_DEREF ( EX_VAR ( EX ( opline ) - > result . var ) , variable_ptr ) ;
variable_ptr = NULL ;
}
rc_dtor_func ( garbage ) ;
} else {
gc_check_possible_root_no_ref ( garbage ) ;
}
}
2019-01-07 19:28:51 +08:00
goto exit ;
2017-09-18 18:13:24 +08:00
}
2023-03-01 05:54:38 +08:00
if ( Z_PROP_FLAG_P ( variable_ptr ) & IS_PROP_UNINIT ) {
2021-03-31 11:49:41 +08:00
/* Writes to uninitialized typed properties bypass __set(). */
2019-10-24 22:36:25 +08:00
goto write_std_property ;
}
2017-09-18 18:13:24 +08:00
} else if ( EXPECTED ( IS_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) ) {
if ( EXPECTED ( zobj - > properties ! = NULL ) ) {
2015-06-17 17:50:16 +08:00
if ( UNEXPECTED ( GC_REFCOUNT ( zobj - > properties ) > 1 ) ) {
if ( EXPECTED ( ! ( GC_FLAGS ( zobj - > properties ) & IS_ARRAY_IMMUTABLE ) ) ) {
2017-10-27 06:28:58 +08:00
GC_DELREF ( zobj - > properties ) ;
2015-06-17 17:50:16 +08:00
}
zobj - > properties = zend_array_dup ( zobj - > properties ) ;
}
2018-07-31 17:24:53 +08:00
if ( ( variable_ptr = zend_hash_find ( zobj - > properties , name ) ) ! = NULL ) {
2019-01-07 19:28:51 +08:00
Z_TRY_ADDREF_P ( value ) ;
goto found ;
2002-02-07 22:08:43 +08:00
}
}
2015-04-01 05:37:59 +08:00
} else if ( UNEXPECTED ( EG ( exception ) ) ) {
2019-01-07 19:28:51 +08:00
variable_ptr = & EG ( error_zval ) ;
2015-04-01 05:37:59 +08:00
goto exit ;
2014-02-10 14:04:30 +08:00
}
2014-03-26 22:07:31 +08:00
/* magic set */
2014-02-10 14:04:30 +08:00
if ( zobj - > ce - > __set ) {
2018-07-31 17:24:53 +08:00
uint32_t * guard = zend_get_property_guard ( zobj , name ) ;
2005-11-15 21:35:23 +08:00
2019-01-07 19:28:51 +08:00
if ( ! ( ( * guard ) & IN_SET ) ) {
2018-07-10 18:17:33 +08:00
GC_ADDREF ( zobj ) ;
2014-02-10 14:04:30 +08:00
( * guard ) | = IN_SET ; /* prevent circular setting */
2018-07-31 17:24:53 +08:00
zend_std_call_setter ( zobj , name , value ) ;
2014-02-10 14:04:30 +08:00
( * guard ) & = ~ IN_SET ;
2018-07-10 18:17:33 +08:00
OBJ_RELEASE ( zobj ) ;
2019-01-07 19:28:51 +08:00
variable_ptr = value ;
2017-09-18 18:13:24 +08:00
} else if ( EXPECTED ( ! IS_WRONG_PROPERTY_OFFSET ( property_offset ) ) ) {
2014-03-07 06:03:25 +08:00
goto write_std_property ;
2014-02-10 14:04:30 +08:00
} else {
2018-09-27 20:58:26 +08:00
/* Trigger the correct error */
2019-01-16 16:56:53 +08:00
zend_wrong_offset ( zobj - > ce , name ) ;
2018-09-27 20:58:26 +08:00
ZEND_ASSERT ( EG ( exception ) ) ;
2019-01-07 19:28:51 +08:00
variable_ptr = & EG ( error_zval ) ;
2018-09-27 20:58:26 +08:00
goto exit ;
2002-10-10 01:14:25 +08:00
}
2018-09-27 20:58:26 +08:00
} else {
ZEND_ASSERT ( ! IS_WRONG_PROPERTY_OFFSET ( property_offset ) ) ;
2014-03-07 06:03:25 +08:00
write_std_property :
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
2019-01-07 19:28:51 +08:00
variable_ptr = OBJ_PROP ( zobj , property_offset ) ;
2020-10-16 22:57:50 +08:00
Z_TRY_ADDREF_P ( value ) ;
2019-01-16 16:56:53 +08:00
if ( UNEXPECTED ( prop_info ) ) {
2021-06-02 16:02:56 +08:00
if ( UNEXPECTED ( ( prop_info - > flags & ZEND_ACC_READONLY )
& & ! verify_readonly_initialization_access ( prop_info , zobj - > ce , name , " initialize " ) ) ) {
Z_TRY_DELREF_P ( value ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
2019-01-07 19:28:51 +08:00
ZVAL_COPY_VALUE ( & tmp , value ) ;
2022-12-29 03:30:51 +08:00
// Increase refcount to prevent object from being released in __toString()
GC_ADDREF ( zobj ) ;
bool type_matched = zend_verify_property_type ( prop_info , & tmp , property_uses_strict_types ( ) ) ;
if ( UNEXPECTED ( GC_DELREF ( zobj ) = = 0 ) ) {
zend_object_released_while_assigning_to_property_error ( prop_info ) ;
zend_objects_store_del ( zobj ) ;
zval_ptr_dtor ( & tmp ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
if ( UNEXPECTED ( ! type_matched ) ) {
2019-01-07 19:28:51 +08:00
zval_ptr_dtor ( value ) ;
goto exit ;
}
value = & tmp ;
2021-05-04 20:44:54 +08:00
Z_PROP_FLAG_P ( variable_ptr ) = 0 ;
2019-01-07 19:28:51 +08:00
goto found ; /* might have been updated via e.g. __toString() */
}
ZVAL_COPY_VALUE ( variable_ptr , value ) ;
2014-02-10 14:04:30 +08:00
} else {
Add flag to forbid dynamic property creation on internal classes
While performing resource -> object migrations, we're adding
defensive classes that are final, non-serializable and non-clonable
(unless they are, of course). This path adds a ZEND_ACC_NO_DYNAMIC_PROPERTIES
flag, that also forbids the creation of dynamic properties on these objects.
This is a subset of #3931 and targeted at internal usage only
(though may be extended to userland at some point in the future).
It's already possible to achieve this (what the removed
WeakRef/WeakMap code does), but there's some caveats: First, this
simple approach is only possible if the class has no declared
properties, otherwise it's necessary to special-case those
properties. Second, it's easy to make it overly strict, e.g. by
forbidding isset($obj->prop) as well. And finally, it requires a
lot of boilerplate code for each class.
Closes GH-5572.
2020-05-14 17:51:36 +08:00
if ( UNEXPECTED ( zobj - > ce - > ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES ) ) {
zend_forbidden_dynamic_property ( zobj - > ce , name ) ;
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
2021-10-11 21:49:27 +08:00
if ( UNEXPECTED ( ! ( zobj - > ce - > ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES ) ) ) {
2022-01-28 17:38:38 +08:00
if ( UNEXPECTED ( ! zend_deprecated_dynamic_property ( zobj , name ) ) ) {
variable_ptr = & EG ( error_zval ) ;
goto exit ;
}
2021-10-11 21:49:27 +08:00
}
2020-10-16 22:57:50 +08:00
Z_TRY_ADDREF_P ( value ) ;
2014-02-10 14:04:30 +08:00
if ( ! zobj - > properties ) {
rebuild_object_properties ( zobj ) ;
}
2019-01-07 19:28:51 +08:00
variable_ptr = zend_hash_add_new ( zobj - > properties , name , value ) ;
2014-02-10 14:04:30 +08:00
}
2002-09-04 17:07:58 +08:00
}
2006-07-26 23:29:27 +08:00
2014-03-26 22:07:31 +08:00
exit :
2019-01-07 19:28:51 +08:00
return variable_ptr ;
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2018-09-13 15:31:49 +08:00
static ZEND_COLD zend_never_inline void zend_bad_array_access ( zend_class_entry * ce ) /* { { { */
{
zend_throw_error ( NULL , " Cannot use object of type %s as array " , ZSTR_VAL ( ce - > name ) ) ;
}
/* }}} */
2019-01-31 23:47:58 +08:00
ZEND_API zval * zend_std_read_dimension ( zend_object * object , zval * offset , int type , zval * rv ) /* { { { */
2003-07-07 18:53:27 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = object - > ce ;
zval tmp_offset ;
2006-05-10 07:53:23 +08:00
2021-12-02 07:43:37 +08:00
/* arrayaccess_funcs_ptr is set if (and only if) the class implements zend_ce_arrayaccess */
zend_class_arrayaccess_funcs * funcs = ce - > arrayaccess_funcs_ptr ;
if ( EXPECTED ( funcs ) ) {
2016-03-20 20:33:00 +08:00
if ( offset = = NULL ) {
2004-07-14 17:01:58 +08:00
/* [] construct */
2017-10-26 21:03:42 +08:00
ZVAL_NULL ( & tmp_offset ) ;
2005-02-02 15:19:22 +08:00
} else {
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( & tmp_offset , offset ) ;
2004-07-14 17:01:58 +08:00
}
2016-03-20 20:33:00 +08:00
2019-01-31 23:47:58 +08:00
GC_ADDREF ( object ) ;
2016-03-20 20:33:00 +08:00
if ( type = = BP_VAR_IS ) {
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_1_params ( funcs - > zf_offsetexists , object , rv , & tmp_offset ) ;
2016-03-20 20:33:00 +08:00
if ( UNEXPECTED ( Z_ISUNDEF_P ( rv ) ) ) {
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2016-03-20 20:33:00 +08:00
return NULL ;
}
if ( ! i_zend_is_true ( rv ) ) {
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2016-03-20 20:33:00 +08:00
zval_ptr_dtor ( rv ) ;
return & EG ( uninitialized_zval ) ;
}
zval_ptr_dtor ( rv ) ;
}
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_1_params ( funcs - > zf_offsetget , object , rv , & tmp_offset ) ;
2004-07-14 17:04:13 +08:00
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2004-07-14 17:04:13 +08:00
2014-02-27 19:40:13 +08:00
if ( UNEXPECTED ( Z_TYPE_P ( rv ) = = IS_UNDEF ) ) {
2010-05-06 20:52:27 +08:00
if ( UNEXPECTED ( ! EG ( exception ) ) ) {
2015-07-08 01:10:22 +08:00
zend_throw_error ( NULL , " Undefined offset for object of type %s used as array " , ZSTR_VAL ( ce - > name ) ) ;
2003-12-11 17:56:06 +08:00
}
2014-02-10 14:04:30 +08:00
return NULL ;
2003-12-11 17:56:06 +08:00
}
2014-02-27 19:40:13 +08:00
return rv ;
2003-11-25 04:57:54 +08:00
} else {
2018-09-13 15:31:49 +08:00
zend_bad_array_access ( ce ) ;
2014-02-10 14:04:30 +08:00
return NULL ;
2003-11-25 04:57:54 +08:00
}
2003-07-07 18:53:27 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-07-07 18:53:27 +08:00
2019-01-31 23:47:58 +08:00
ZEND_API void zend_std_write_dimension ( zend_object * object , zval * offset , zval * value ) /* { { { */
2003-07-07 17:00:36 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = object - > ce ;
zval tmp_offset ;
2005-02-02 15:19:22 +08:00
2021-12-02 07:43:37 +08:00
zend_class_arrayaccess_funcs * funcs = ce - > arrayaccess_funcs_ptr ;
if ( EXPECTED ( funcs ) ) {
2003-12-23 00:27:14 +08:00
if ( ! offset ) {
2017-10-26 21:03:42 +08:00
ZVAL_NULL ( & tmp_offset ) ;
2005-02-02 15:19:22 +08:00
} else {
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( & tmp_offset , offset ) ;
2003-12-23 00:27:14 +08:00
}
2019-01-31 23:47:58 +08:00
GC_ADDREF ( object ) ;
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_2_params ( funcs - > zf_offsetset , object , NULL , & tmp_offset , value ) ;
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2003-11-25 04:57:54 +08:00
} else {
2018-09-13 15:31:49 +08:00
zend_bad_array_access ( ce ) ;
2003-11-25 04:57:54 +08:00
}
2003-07-07 17:00:36 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-07-07 17:00:36 +08:00
2019-01-31 23:47:58 +08:00
ZEND_API int zend_std_has_dimension ( zend_object * object , zval * offset , int check_empty ) /* { { { */
2003-11-11 00:14:44 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = object - > ce ;
zval retval , tmp_offset ;
2003-11-25 04:57:54 +08:00
int result ;
2006-05-10 07:53:23 +08:00
2021-12-02 07:43:37 +08:00
zend_class_arrayaccess_funcs * funcs = ce - > arrayaccess_funcs_ptr ;
if ( EXPECTED ( funcs ) ) {
2018-07-10 17:11:10 +08:00
ZVAL_COPY_DEREF ( & tmp_offset , offset ) ;
2019-01-31 23:47:58 +08:00
GC_ADDREF ( object ) ;
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_1_params ( funcs - > zf_offsetexists , object , & retval , & tmp_offset ) ;
2018-07-31 17:23:46 +08:00
result = i_zend_is_true ( & retval ) ;
zval_ptr_dtor ( & retval ) ;
if ( check_empty & & result & & EXPECTED ( ! EG ( exception ) ) ) {
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_1_params ( funcs - > zf_offsetget , object , & retval , & tmp_offset ) ;
2014-12-14 06:06:14 +08:00
result = i_zend_is_true ( & retval ) ;
2005-03-19 23:32:18 +08:00
zval_ptr_dtor ( & retval ) ;
}
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2003-11-25 04:57:54 +08:00
} else {
2018-09-13 15:31:49 +08:00
zend_bad_array_access ( ce ) ;
2003-11-25 04:57:54 +08:00
return 0 ;
}
2005-07-08 00:07:09 +08:00
return result ;
2003-11-11 00:14:44 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-11-11 00:14:44 +08:00
2019-01-31 23:47:58 +08:00
ZEND_API zval * zend_std_get_property_ptr_ptr ( zend_object * zobj , zend_string * name , int type , void * * cache_slot ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2014-10-17 23:10:05 +08:00
zval * retval = NULL ;
2017-10-25 16:45:17 +08:00
uintptr_t property_offset ;
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = NULL ;
2006-05-10 07:53:23 +08:00
2002-02-07 22:08:43 +08:00
# if DEBUG_OBJECT_HANDLERS
2020-05-06 04:12:31 +08:00
fprintf ( stderr , " Ptr object #%d property: %s \n " , zobj - > handle , ZSTR_VAL ( name ) ) ;
2006-05-10 07:53:23 +08:00
# endif
2002-02-07 22:08:43 +08:00
2019-01-16 16:56:53 +08:00
property_offset = zend_get_property_offset ( zobj - > ce , name , ( zobj - > ce - > __get ! = NULL ) , cache_slot , & prop_info ) ;
2003-02-05 17:41:31 +08:00
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
retval = OBJ_PROP ( zobj , property_offset ) ;
if ( UNEXPECTED ( Z_TYPE_P ( retval ) = = IS_UNDEF ) ) {
2015-06-17 17:50:16 +08:00
if ( EXPECTED ( ! zobj - > ce - > __get ) | |
2020-08-31 18:17:00 +08:00
UNEXPECTED ( ( * zend_get_property_guard ( zobj , name ) ) & IN_GET ) | |
2023-03-01 05:54:38 +08:00
UNEXPECTED ( prop_info & & ( Z_PROP_FLAG_P ( retval ) & IS_PROP_UNINIT ) ) ) {
2015-06-17 17:50:16 +08:00
if ( UNEXPECTED ( type = = BP_VAR_RW | | type = = BP_VAR_R ) ) {
2019-11-15 19:06:17 +08:00
if ( UNEXPECTED ( prop_info ) ) {
zend_throw_error ( NULL ,
" Typed property %s::$%s must not be accessed before initialization " ,
ZSTR_VAL ( prop_info - > ce - > name ) ,
ZSTR_VAL ( name ) ) ;
retval = & EG ( error_zval ) ;
} else {
2019-11-15 19:07:24 +08:00
zend_error ( E_WARNING , " Undefined property: %s::$%s " , ZSTR_VAL ( zobj - > ce - > name ) , ZSTR_VAL ( name ) ) ;
2023-09-03 07:12:25 +08:00
/* An error handler may set the property */
if ( EXPECTED ( Z_TYPE_P ( retval ) = = IS_UNDEF ) ) {
ZVAL_NULL ( retval ) ;
}
2019-11-15 19:06:17 +08:00
}
2022-03-11 01:06:03 +08:00
} else if ( prop_info & & UNEXPECTED ( prop_info - > flags & ZEND_ACC_READONLY ) ) {
/* Readonly property, delegate to read_property + write_property. */
retval = NULL ;
2023-04-11 04:19:17 +08:00
} else if ( ! prop_info | | ! ZEND_TYPE_IS_SET ( prop_info - > type ) ) {
ZVAL_NULL ( retval ) ;
2015-06-17 17:50:16 +08:00
}
2017-09-18 18:13:24 +08:00
} else {
/* we do have getter - fail and let it try again with usual get/set */
retval = NULL ;
}
2021-06-02 16:02:56 +08:00
} else if ( prop_info & & UNEXPECTED ( prop_info - > flags & ZEND_ACC_READONLY ) ) {
/* Readonly property, delegate to read_property + write_property. */
retval = NULL ;
2017-09-18 18:13:24 +08:00
}
} else if ( EXPECTED ( IS_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) ) {
if ( EXPECTED ( zobj - > properties ) ) {
if ( UNEXPECTED ( GC_REFCOUNT ( zobj - > properties ) > 1 ) ) {
if ( EXPECTED ( ! ( GC_FLAGS ( zobj - > properties ) & IS_ARRAY_IMMUTABLE ) ) ) {
2017-10-27 06:28:58 +08:00
GC_DELREF ( zobj - > properties ) ;
2017-09-18 18:13:24 +08:00
}
zobj - > properties = zend_array_dup ( zobj - > properties ) ;
}
if ( EXPECTED ( ( retval = zend_hash_find ( zobj - > properties , name ) ) ! = NULL ) ) {
return retval ;
}
}
if ( EXPECTED ( ! zobj - > ce - > __get ) | |
UNEXPECTED ( ( * zend_get_property_guard ( zobj , name ) ) & IN_GET ) ) {
Add flag to forbid dynamic property creation on internal classes
While performing resource -> object migrations, we're adding
defensive classes that are final, non-serializable and non-clonable
(unless they are, of course). This path adds a ZEND_ACC_NO_DYNAMIC_PROPERTIES
flag, that also forbids the creation of dynamic properties on these objects.
This is a subset of #3931 and targeted at internal usage only
(though may be extended to userland at some point in the future).
It's already possible to achieve this (what the removed
WeakRef/WeakMap code does), but there's some caveats: First, this
simple approach is only possible if the class has no declared
properties, otherwise it's necessary to special-case those
properties. Second, it's easy to make it overly strict, e.g. by
forbidding isset($obj->prop) as well. And finally, it requires a
lot of boilerplate code for each class.
Closes GH-5572.
2020-05-14 17:51:36 +08:00
if ( UNEXPECTED ( zobj - > ce - > ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES ) ) {
zend_forbidden_dynamic_property ( zobj - > ce , name ) ;
return & EG ( error_zval ) ;
}
2021-10-11 21:49:27 +08:00
if ( UNEXPECTED ( ! ( zobj - > ce - > ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES ) ) ) {
2022-01-28 17:38:38 +08:00
if ( UNEXPECTED ( ! zend_deprecated_dynamic_property ( zobj , name ) ) ) {
return & EG ( error_zval ) ;
}
2021-10-11 21:49:27 +08:00
}
2017-09-18 18:13:24 +08:00
if ( UNEXPECTED ( ! zobj - > properties ) ) {
rebuild_object_properties ( zobj ) ;
}
if ( UNEXPECTED ( type = = BP_VAR_RW | | type = = BP_VAR_R ) ) {
2019-10-01 19:20:42 +08:00
zend_error ( E_WARNING , " Undefined property: %s::$%s " , ZSTR_VAL ( zobj - > ce - > name ) , ZSTR_VAL ( name ) ) ;
2014-02-10 14:04:30 +08:00
}
2023-09-03 21:59:23 +08:00
retval = zend_hash_add ( zobj - > properties , name , & EG ( uninitialized_zval ) ) ;
2002-09-04 17:07:58 +08:00
}
2019-10-10 21:14:04 +08:00
} else if ( zobj - > ce - > __get = = NULL ) {
retval = & EG ( error_zval ) ;
2002-02-07 22:08:43 +08:00
}
2014-02-10 14:04:30 +08:00
2002-02-07 22:08:43 +08:00
return retval ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2019-01-31 23:47:58 +08:00
ZEND_API void zend_std_unset_property ( zend_object * zobj , zend_string * name , void * * cache_slot ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2017-10-25 16:45:17 +08:00
uintptr_t property_offset ;
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = NULL ;
2006-05-10 07:53:23 +08:00
2019-01-16 16:56:53 +08:00
property_offset = zend_get_property_offset ( zobj - > ce , name , ( zobj - > ce - > __unset ! = NULL ) , cache_slot , & prop_info ) ;
2006-05-10 07:53:23 +08:00
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
zval * slot = OBJ_PROP ( zobj , property_offset ) ;
2014-10-17 23:10:05 +08:00
2017-09-18 18:13:24 +08:00
if ( Z_TYPE_P ( slot ) ! = IS_UNDEF ) {
2021-06-02 16:02:56 +08:00
if ( UNEXPECTED ( prop_info & & ( prop_info - > flags & ZEND_ACC_READONLY ) ) ) {
2023-03-01 05:54:38 +08:00
if ( Z_PROP_FLAG_P ( slot ) & IS_PROP_REINITABLE ) {
Z_PROP_FLAG_P ( slot ) & = ~ IS_PROP_REINITABLE ;
} else {
zend_readonly_property_unset_error ( prop_info - > ce , name ) ;
return ;
}
2021-06-02 16:02:56 +08:00
}
2019-01-07 19:28:51 +08:00
if ( UNEXPECTED ( Z_ISREF_P ( slot ) ) & &
( ZEND_DEBUG | | ZEND_REF_HAS_TYPE_SOURCES ( Z_REF_P ( slot ) ) ) ) {
2019-01-16 16:56:53 +08:00
if ( prop_info ) {
2019-01-07 19:28:51 +08:00
ZEND_REF_DEL_TYPE_SOURCE ( Z_REF_P ( slot ) , prop_info ) ;
}
}
2021-07-15 15:29:08 +08:00
zval tmp ;
ZVAL_COPY_VALUE ( & tmp , slot ) ;
2017-09-18 18:13:24 +08:00
ZVAL_UNDEF ( slot ) ;
2021-07-15 15:29:08 +08:00
zval_ptr_dtor ( & tmp ) ;
2017-09-18 18:13:24 +08:00
if ( zobj - > properties ) {
2018-01-22 19:58:16 +08:00
HT_FLAGS ( zobj - > properties ) | = HASH_FLAG_HAS_EMPTY_IND ;
2015-06-17 17:50:16 +08:00
}
2019-01-31 23:47:58 +08:00
return ;
2017-09-18 18:13:24 +08:00
}
2023-03-01 05:54:38 +08:00
if ( UNEXPECTED ( Z_PROP_FLAG_P ( slot ) & IS_PROP_UNINIT ) ) {
2021-06-02 16:02:56 +08:00
if ( UNEXPECTED ( prop_info & & ( prop_info - > flags & ZEND_ACC_READONLY )
& & ! verify_readonly_initialization_access ( prop_info , zobj - > ce , name , " unset " ) ) ) {
return ;
}
2019-10-28 20:34:33 +08:00
/* Reset the IS_PROP_UNINIT flag, if it exists and bypass __unset(). */
Z_PROP_FLAG_P ( slot ) = 0 ;
2019-12-09 15:41:43 +08:00
return ;
2019-10-28 20:34:33 +08:00
}
2017-09-18 18:13:24 +08:00
} else if ( EXPECTED ( IS_DYNAMIC_PROPERTY_OFFSET ( property_offset ) )
& & EXPECTED ( zobj - > properties ! = NULL ) ) {
if ( UNEXPECTED ( GC_REFCOUNT ( zobj - > properties ) > 1 ) ) {
if ( EXPECTED ( ! ( GC_FLAGS ( zobj - > properties ) & IS_ARRAY_IMMUTABLE ) ) ) {
2017-10-27 06:28:58 +08:00
GC_DELREF ( zobj - > properties ) ;
2015-06-17 17:50:16 +08:00
}
2017-09-18 18:13:24 +08:00
zobj - > properties = zend_array_dup ( zobj - > properties ) ;
}
2018-07-31 17:24:53 +08:00
if ( EXPECTED ( zend_hash_del ( zobj - > properties , name ) ! = FAILURE ) ) {
2019-01-31 23:47:58 +08:00
return ;
2014-03-26 22:07:31 +08:00
}
2015-04-01 05:37:59 +08:00
} else if ( UNEXPECTED ( EG ( exception ) ) ) {
2019-01-31 23:47:58 +08:00
return ;
2014-02-10 14:04:30 +08:00
}
2015-01-03 17:22:58 +08:00
2014-03-26 22:07:31 +08:00
/* magic unset */
if ( zobj - > ce - > __unset ) {
2018-07-31 17:24:53 +08:00
uint32_t * guard = zend_get_property_guard ( zobj , name ) ;
2014-03-26 22:07:31 +08:00
if ( ! ( ( * guard ) & IN_UNSET ) ) {
2023-02-04 15:03:10 +08:00
/* have unsetter - try with it! */
2014-03-26 22:07:31 +08:00
( * guard ) | = IN_UNSET ; /* prevent circular unsetting */
2018-07-31 17:24:53 +08:00
zend_std_call_unsetter ( zobj , name ) ;
2014-03-26 22:07:31 +08:00
( * guard ) & = ~ IN_UNSET ;
2018-09-27 20:58:26 +08:00
} else if ( UNEXPECTED ( IS_WRONG_PROPERTY_OFFSET ( property_offset ) ) ) {
/* Trigger the correct error */
2019-01-16 16:56:53 +08:00
zend_wrong_offset ( zobj - > ce , name ) ;
2018-09-27 20:58:26 +08:00
ZEND_ASSERT ( EG ( exception ) ) ;
2019-01-31 23:47:58 +08:00
return ;
2014-03-26 22:07:31 +08:00
} else {
2018-09-27 20:58:26 +08:00
/* Nothing to do: The property already does not exist. */
2005-07-08 00:07:09 +08:00
}
}
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2019-01-31 23:47:58 +08:00
ZEND_API void zend_std_unset_dimension ( zend_object * object , zval * offset ) /* { { { */
2003-07-31 01:12:06 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = object - > ce ;
zval tmp_offset ;
2006-05-10 07:53:23 +08:00
2021-12-02 07:43:37 +08:00
zend_class_arrayaccess_funcs * funcs = ce - > arrayaccess_funcs_ptr ;
if ( EXPECTED ( funcs ) ) {
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( & tmp_offset , offset ) ;
2019-01-31 23:47:58 +08:00
GC_ADDREF ( object ) ;
2021-12-02 07:43:37 +08:00
zend_call_known_instance_method_with_1_params ( funcs - > zf_offsetunset , object , NULL , & tmp_offset ) ;
2019-01-31 23:47:58 +08:00
OBJ_RELEASE ( object ) ;
2017-10-26 21:03:42 +08:00
zval_ptr_dtor ( & tmp_offset ) ;
2003-11-25 04:57:54 +08:00
} else {
2018-09-13 15:31:49 +08:00
zend_bad_array_access ( ce ) ;
2003-11-25 04:57:54 +08:00
}
2003-07-31 01:12:06 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-07-31 01:12:06 +08:00
2018-09-12 23:59:12 +08:00
static zend_never_inline zend_function * zend_get_parent_private_method ( zend_class_entry * scope , zend_class_entry * ce , zend_string * function_name ) /* { { { */
2018-07-31 18:05:57 +08:00
{
zval * func ;
zend_function * fbc ;
2018-09-12 13:31:01 +08:00
if ( scope ! = ce & & scope & & is_derived_class ( ce , scope ) ) {
func = zend_hash_find ( & scope - > function_table , function_name ) ;
if ( func ! = NULL ) {
fbc = Z_FUNC_P ( func ) ;
2020-06-07 08:38:13 +08:00
if ( ( fbc - > common . fn_flags & ZEND_ACC_PRIVATE )
2018-09-12 13:31:01 +08:00
& & fbc - > common . scope = = scope ) {
return fbc ;
2018-07-31 18:05:57 +08:00
}
}
}
return NULL ;
}
/* }}} */
2003-02-03 00:17:25 +08:00
/* Ensures that we're allowed to call a protected method.
*/
2023-01-16 21:55:12 +08:00
ZEND_API bool zend_check_protected ( const zend_class_entry * ce , const zend_class_entry * scope ) /* { { { */
2003-02-03 00:17:25 +08:00
{
2023-01-16 21:55:12 +08:00
const zend_class_entry * fbc_scope = ce ;
2003-02-03 00:17:25 +08:00
/* Is the context that's calling the function, the same as one of
* the function ' s parents ?
*/
while ( fbc_scope ) {
if ( fbc_scope = = scope ) {
return 1 ;
}
fbc_scope = fbc_scope - > parent ;
}
/* Is the function's scope the same as our current object context,
* or any of the parents of our context ?
*/
while ( scope ) {
if ( scope = = ce ) {
return 1 ;
}
scope = scope - > parent ;
}
return 0 ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-03 00:17:25 +08:00
2023-01-16 21:55:12 +08:00
ZEND_API zend_function * zend_get_call_trampoline_func ( const zend_class_entry * ce , zend_string * method_name , bool is_static ) /* { { { */
2009-01-08 06:12:39 +08:00
{
2016-05-07 15:07:04 +08:00
size_t mname_len ;
2015-04-11 04:01:00 +08:00
zend_op_array * func ;
zend_function * fbc = is_static ? ce - > __callstatic : ce - > __call ;
2018-10-17 20:52:50 +08:00
/* We use non-NULL value to avoid useless run_time_cache allocation.
* The low bit must be zero , to not be interpreted as a MAP_PTR offset .
*/
static const void * dummy = ( void * ) ( intptr_t ) 2 ;
2020-04-06 18:46:52 +08:00
static const zend_arg_info arg_info [ 1 ] = { { 0 } } ;
2015-04-11 04:01:00 +08:00
ZEND_ASSERT ( fbc ) ;
if ( EXPECTED ( EG ( trampoline ) . common . function_name = = NULL ) ) {
func = & EG ( trampoline ) . op_array ;
} else {
func = ecalloc ( 1 , sizeof ( zend_op_array ) ) ;
}
func - > type = ZEND_USER_FUNCTION ;
2015-04-23 02:46:13 +08:00
func - > arg_flags [ 0 ] = 0 ;
func - > arg_flags [ 1 ] = 0 ;
func - > arg_flags [ 2 ] = 0 ;
2020-04-06 18:46:52 +08:00
func - > fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC ;
2015-04-11 04:01:00 +08:00
if ( is_static ) {
func - > fn_flags | = ZEND_ACC_STATIC ;
}
func - > opcodes = & EG ( call_trampoline_op ) ;
2021-10-14 17:16:18 +08:00
ZEND_MAP_PTR_INIT ( func - > run_time_cache , ( void * * ) dummy ) ;
2015-04-11 23:57:42 +08:00
func - > scope = fbc - > common . scope ;
2019-02-19 00:35:35 +08:00
/* reserve space for arguments, local and temporary variables */
2023-03-24 03:30:56 +08:00
/* EG(trampoline) is reused from other places, like FFI (e.g. zend_ffi_cdata_get_closure()) where
* it is used as an internal function . It may set fields that don ' t belong to common , thus
* modifying zend_op_array specific data , most significantly last_var . We need to reset this
* value so that it doesn ' t contain garbage when the engine allocates space for the next stack
* frame . This didn ' t cause any issues until now due to " lucky " structure layout . */
func - > last_var = 0 ;
2016-01-28 16:41:15 +08:00
func - > T = ( fbc - > type = = ZEND_USER_FUNCTION ) ? MAX ( fbc - > op_array . last_var + fbc - > op_array . T , 2 ) : 2 ;
2015-06-29 21:44:54 +08:00
func - > filename = ( fbc - > type = = ZEND_USER_FUNCTION ) ? fbc - > op_array . filename : ZSTR_EMPTY_ALLOC ( ) ;
2015-04-11 04:01:00 +08:00
func - > line_start = ( fbc - > type = = ZEND_USER_FUNCTION ) ? fbc - > op_array . line_start : 0 ;
func - > line_end = ( fbc - > type = = ZEND_USER_FUNCTION ) ? fbc - > op_array . line_end : 0 ;
2014-04-12 04:50:36 +08:00
//??? keep compatibility for "\0" characters
//??? see: Zend/tests/bug46238.phpt
2016-05-07 15:07:04 +08:00
if ( UNEXPECTED ( ( mname_len = strlen ( ZSTR_VAL ( method_name ) ) ) ! = ZSTR_LEN ( method_name ) ) ) {
func - > function_name = zend_string_init ( ZSTR_VAL ( method_name ) , mname_len , 0 ) ;
2014-04-12 04:50:36 +08:00
} else {
2015-04-11 04:01:00 +08:00
func - > function_name = zend_string_copy ( method_name ) ;
2014-04-12 04:50:36 +08:00
}
2009-01-08 06:12:39 +08:00
2019-02-04 18:57:03 +08:00
func - > prototype = NULL ;
func - > num_args = 0 ;
func - > required_num_args = 0 ;
2020-04-06 18:46:52 +08:00
func - > arg_info = ( zend_arg_info * ) arg_info ;
2019-02-04 18:57:03 +08:00
2015-04-11 04:01:00 +08:00
return ( zend_function * ) func ;
}
/* }}} */
static zend_always_inline zend_function * zend_get_user_call_function ( zend_class_entry * ce , zend_string * method_name ) /* { { { */
{
return zend_get_call_trampoline_func ( ce , method_name , 0 ) ;
2009-01-08 06:12:39 +08:00
}
/* }}} */
2018-09-13 15:31:49 +08:00
static ZEND_COLD zend_never_inline void zend_bad_method_call ( zend_function * fbc , zend_string * method_name , zend_class_entry * scope ) /* { { { */
{
2020-05-26 20:10:57 +08:00
zend_throw_error ( NULL , " Call to %s method %s::%s() from %s%s " ,
zend_visibility_string ( fbc - > common . fn_flags ) , ZEND_FN_SCOPE_NAME ( fbc ) , ZSTR_VAL ( method_name ) ,
scope ? " scope " : " global scope " ,
scope ? ZSTR_VAL ( scope - > name ) : " "
) ;
2018-09-13 15:31:49 +08:00
}
/* }}} */
2019-09-05 16:27:19 +08:00
static ZEND_COLD zend_never_inline void zend_abstract_method_call ( zend_function * fbc ) /* { { { */
{
zend_throw_error ( NULL , " Cannot call abstract method %s::%s() " ,
ZSTR_VAL ( fbc - > common . scope - > name ) , ZSTR_VAL ( fbc - > common . function_name ) ) ;
}
/* }}} */
2018-05-31 16:57:22 +08:00
ZEND_API zend_function * zend_std_get_method ( zend_object * * obj_ptr , zend_string * method_name , const zval * key ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2014-03-28 06:11:22 +08:00
zend_object * zobj = * obj_ptr ;
2014-02-10 14:04:30 +08:00
zval * func ;
2003-02-03 00:17:25 +08:00
zend_function * fbc ;
2014-02-10 14:04:30 +08:00
zend_string * lc_method_name ;
2018-09-12 23:59:12 +08:00
zend_class_entry * scope ;
2014-05-02 23:01:36 +08:00
ALLOCA_FLAG ( use_heap ) ;
2007-10-11 09:03:19 +08:00
2010-05-06 20:52:27 +08:00
if ( EXPECTED ( key ! = NULL ) ) {
2014-04-17 19:40:45 +08:00
lc_method_name = Z_STR_P ( key ) ;
2015-01-16 02:43:17 +08:00
# ifdef ZEND_ALLOCA_MAX_SIZE
2015-01-16 02:08:21 +08:00
use_heap = 0 ;
2015-01-16 02:43:17 +08:00
# endif
2010-04-20 18:57:45 +08:00
} else {
2015-06-30 18:59:27 +08:00
ZSTR_ALLOCA_ALLOC ( lc_method_name , ZSTR_LEN ( method_name ) , use_heap ) ;
zend_str_tolower_copy ( ZSTR_VAL ( lc_method_name ) , ZSTR_VAL ( method_name ) , ZSTR_LEN ( method_name ) ) ;
2010-04-20 18:57:45 +08:00
}
2007-10-11 09:03:19 +08:00
2014-02-10 14:04:30 +08:00
if ( UNEXPECTED ( ( func = zend_hash_find ( & zobj - > ce - > function_table , lc_method_name ) ) = = NULL ) ) {
2015-01-16 07:21:27 +08:00
if ( UNEXPECTED ( ! key ) ) {
2015-06-29 21:44:54 +08:00
ZSTR_ALLOCA_FREE ( lc_method_name , use_heap ) ;
2015-01-16 07:21:27 +08:00
}
2003-01-29 23:02:57 +08:00
if ( zobj - > ce - > __call ) {
2014-02-10 14:04:30 +08:00
return zend_get_user_call_function ( zobj - > ce , method_name ) ;
2003-01-29 23:02:57 +08:00
} else {
return NULL ;
2002-09-04 17:07:58 +08:00
}
2002-02-07 22:08:43 +08:00
}
2002-06-06 01:34:56 +08:00
2014-02-10 14:04:30 +08:00
fbc = Z_FUNC_P ( func ) ;
2018-09-12 23:59:12 +08:00
2003-02-03 00:17:25 +08:00
/* Check access level */
2018-09-12 23:59:12 +08:00
if ( fbc - > op_array . fn_flags & ( ZEND_ACC_CHANGED | ZEND_ACC_PRIVATE | ZEND_ACC_PROTECTED ) ) {
2018-07-31 18:05:57 +08:00
scope = zend_get_executed_scope ( ) ;
2018-09-12 23:59:12 +08:00
2018-09-13 14:31:38 +08:00
if ( fbc - > common . scope ! = scope ) {
if ( fbc - > op_array . fn_flags & ZEND_ACC_CHANGED ) {
zend_function * updated_fbc = zend_get_parent_private_method ( scope , zobj - > ce , lc_method_name ) ;
2018-09-12 17:16:50 +08:00
2018-09-13 14:31:38 +08:00
if ( EXPECTED ( updated_fbc ! = NULL ) ) {
fbc = updated_fbc ;
goto exit ;
} else if ( fbc - > op_array . fn_flags & ZEND_ACC_PUBLIC ) {
goto exit ;
}
}
if ( UNEXPECTED ( fbc - > op_array . fn_flags & ZEND_ACC_PRIVATE )
| | UNEXPECTED ( ! zend_check_protected ( zend_get_function_root_class ( fbc ) , scope ) ) ) {
2009-01-08 06:12:39 +08:00
if ( zobj - > ce - > __call ) {
2014-02-10 14:04:30 +08:00
fbc = zend_get_user_call_function ( zobj - > ce , method_name ) ;
2009-01-08 06:12:39 +08:00
} else {
2018-09-13 15:31:49 +08:00
zend_bad_method_call ( fbc , method_name , scope ) ;
2015-04-01 05:37:59 +08:00
fbc = NULL ;
2009-01-08 06:12:39 +08:00
}
2006-09-12 19:01:16 +08:00
}
2018-09-12 23:59:12 +08:00
}
2003-02-03 00:17:25 +08:00
}
2018-09-12 23:59:12 +08:00
exit :
2019-09-05 16:27:19 +08:00
if ( fbc & & UNEXPECTED ( fbc - > common . fn_flags & ZEND_ACC_ABSTRACT ) ) {
zend_abstract_method_call ( fbc ) ;
fbc = NULL ;
}
2015-01-16 07:21:27 +08:00
if ( UNEXPECTED ( ! key ) ) {
2015-06-29 21:44:54 +08:00
ZSTR_ALLOCA_FREE ( lc_method_name , use_heap ) ;
2015-01-16 07:21:27 +08:00
}
2003-02-03 00:17:25 +08:00
return fbc ;
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2015-04-11 04:01:00 +08:00
static zend_always_inline zend_function * zend_get_user_callstatic_function ( zend_class_entry * ce , zend_string * method_name ) /* { { { */
2009-06-12 09:05:25 +08:00
{
2015-04-11 04:01:00 +08:00
return zend_get_call_trampoline_func ( ce , method_name , 1 ) ;
2009-06-12 09:05:25 +08:00
}
/* }}} */
2021-02-18 21:16:48 +08:00
static zend_always_inline zend_function * get_static_method_fallback (
zend_class_entry * ce , zend_string * function_name )
{
zend_object * object ;
if ( ce - > __call & &
( object = zend_get_this_object ( EG ( current_execute_data ) ) ) ! = NULL & &
instanceof_function ( object - > ce , ce ) ) {
/* Call the top-level defined __call().
* see : tests / classes / __call_004 . phpt */
ZEND_ASSERT ( object - > ce - > __call ) ;
return zend_get_user_call_function ( object - > ce , function_name ) ;
} else if ( ce - > __callstatic ) {
return zend_get_user_callstatic_function ( ce , function_name ) ;
} else {
return NULL ;
}
}
2014-12-14 06:06:14 +08:00
ZEND_API zend_function * zend_std_get_static_method ( zend_class_entry * ce , zend_string * function_name , const zval * key ) /* { { { */
2003-02-03 00:17:25 +08:00
{
2014-02-10 14:04:30 +08:00
zend_string * lc_function_name ;
2010-05-06 20:52:27 +08:00
if ( EXPECTED ( key ! = NULL ) ) {
2014-04-17 19:40:45 +08:00
lc_function_name = Z_STR_P ( key ) ;
2010-04-20 18:57:45 +08:00
} else {
2014-12-24 20:04:51 +08:00
lc_function_name = zend_string_tolower ( function_name ) ;
2010-04-20 18:57:45 +08:00
}
2012-05-13 13:12:48 +08:00
2021-07-09 18:13:12 +08:00
zend_function * fbc ;
2021-07-09 18:18:35 +08:00
zval * func = zend_hash_find ( & ce - > function_table , lc_function_name ) ;
2021-07-09 18:13:12 +08:00
if ( EXPECTED ( func ) ) {
fbc = Z_FUNC_P ( func ) ;
if ( ! ( fbc - > op_array . fn_flags & ZEND_ACC_PUBLIC ) ) {
zend_class_entry * scope = zend_get_executed_scope ( ) ;
if ( UNEXPECTED ( fbc - > common . scope ! = scope ) ) {
if ( UNEXPECTED ( fbc - > op_array . fn_flags & ZEND_ACC_PRIVATE )
| | UNEXPECTED ( ! zend_check_protected ( zend_get_function_root_class ( fbc ) , scope ) ) ) {
zend_function * fallback_fbc = get_static_method_fallback ( ce , function_name ) ;
if ( ! fallback_fbc ) {
zend_bad_method_call ( fbc , function_name , scope ) ;
}
fbc = fallback_fbc ;
2018-09-13 06:41:19 +08:00
}
2009-06-12 09:05:25 +08:00
}
2003-02-03 00:17:25 +08:00
}
2021-07-09 18:13:12 +08:00
} else {
fbc = get_static_method_fallback ( ce , function_name ) ;
2003-02-03 00:17:25 +08:00
}
2021-09-28 15:32:54 +08:00
if ( UNEXPECTED ( ! key ) ) {
zend_string_release_ex ( lc_function_name , 0 ) ;
}
2021-07-09 18:13:12 +08:00
if ( EXPECTED ( fbc ) ) {
if ( UNEXPECTED ( fbc - > common . fn_flags & ZEND_ACC_ABSTRACT ) ) {
zend_abstract_method_call ( fbc ) ;
fbc = NULL ;
} else if ( UNEXPECTED ( fbc - > common . scope - > ce_flags & ZEND_ACC_TRAIT ) ) {
zend_error ( E_DEPRECATED ,
" Calling static trait method %s::%s is deprecated, "
" it should only be called on a class using the trait " ,
ZSTR_VAL ( fbc - > common . scope - > name ) , ZSTR_VAL ( fbc - > common . function_name ) ) ;
2021-09-28 15:32:54 +08:00
if ( EG ( exception ) ) {
return NULL ;
}
2021-07-09 18:13:12 +08:00
}
2019-09-05 16:27:19 +08:00
}
2003-02-03 00:17:25 +08:00
return fbc ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-03 00:17:25 +08:00
2019-01-25 18:00:35 +08:00
ZEND_API void zend_class_init_statics ( zend_class_entry * class_type ) /* { { { */
2018-06-27 17:33:20 +08:00
{
int i ;
zval * p ;
2019-02-20 07:33:21 +08:00
if ( class_type - > default_static_members_count & & ! CE_STATIC_MEMBERS ( class_type ) ) {
2018-06-27 17:33:20 +08:00
if ( class_type - > parent ) {
2019-01-25 18:00:35 +08:00
zend_class_init_statics ( class_type - > parent ) ;
2018-06-27 17:33:20 +08:00
}
2018-10-17 20:52:50 +08:00
ZEND_MAP_PTR_SET ( class_type - > static_members_table , emalloc ( sizeof ( zval ) * class_type - > default_static_members_count ) ) ;
2018-06-27 17:33:20 +08:00
for ( i = 0 ; i < class_type - > default_static_members_count ; i + + ) {
p = & class_type - > default_static_members_table [ i ] ;
if ( Z_TYPE_P ( p ) = = IS_INDIRECT ) {
zval * q = & CE_STATIC_MEMBERS ( class_type - > parent ) [ i ] ;
ZVAL_DEINDIRECT ( q ) ;
ZVAL_INDIRECT ( & CE_STATIC_MEMBERS ( class_type ) [ i ] , q ) ;
} else {
ZVAL_COPY_OR_DUP ( & CE_STATIC_MEMBERS ( class_type ) [ i ] , p ) ;
}
}
}
} /* }}} */
2019-01-07 19:28:51 +08:00
ZEND_API zval * zend_std_get_static_property_with_info ( zend_class_entry * ce , zend_string * property_name , int type , zend_property_info * * property_info_ptr ) /* { { { */
2003-02-05 21:35:52 +08:00
{
2015-01-13 08:32:51 +08:00
zval * ret ;
2019-01-07 19:28:51 +08:00
zend_class_entry * scope ;
zend_property_info * property_info = zend_hash_find_ptr ( & ce - > properties_info , property_name ) ;
* property_info_ptr = property_info ;
2012-05-13 13:12:48 +08:00
2015-01-13 14:33:15 +08:00
if ( UNEXPECTED ( property_info = = NULL ) ) {
goto undeclared_property ;
}
2003-02-05 21:35:52 +08:00
2018-09-13 14:31:38 +08:00
if ( ! ( property_info - > flags & ZEND_ACC_PUBLIC ) ) {
if ( UNEXPECTED ( EG ( fake_scope ) ) ) {
scope = EG ( fake_scope ) ;
} else {
scope = zend_get_executed_scope ( ) ;
}
if ( property_info - > ce ! = scope ) {
if ( UNEXPECTED ( property_info - > flags & ZEND_ACC_PRIVATE )
| | UNEXPECTED ( ! is_protected_compatible_scope ( property_info - > ce , scope ) ) ) {
2019-01-07 19:28:51 +08:00
if ( type ! = BP_VAR_IS ) {
2018-09-13 15:31:49 +08:00
zend_bad_property_access ( property_info , ce , property_name ) ;
2018-09-13 14:31:38 +08:00
}
return NULL ;
}
2010-05-24 22:11:39 +08:00
}
2015-01-13 14:33:15 +08:00
}
2005-09-01 18:05:32 +08:00
2015-01-13 14:33:15 +08:00
if ( UNEXPECTED ( ( property_info - > flags & ZEND_ACC_STATIC ) = = 0 ) ) {
2021-07-22 17:41:24 +08:00
undeclared_property :
if ( type ! = BP_VAR_IS ) {
zend_throw_error ( NULL , " Access to undeclared static property %s::$%s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( property_name ) ) ;
}
return NULL ;
2015-01-13 14:33:15 +08:00
}
2003-02-05 21:35:52 +08:00
2015-05-21 09:13:10 +08:00
if ( UNEXPECTED ( ! ( ce - > ce_flags & ZEND_ACC_CONSTANTS_UPDATED ) ) ) {
2023-01-18 05:26:34 +08:00
if ( UNEXPECTED ( zend_update_class_constants ( ce ) ! = SUCCESS ) ) {
2015-05-21 09:13:10 +08:00
return NULL ;
}
2015-04-02 07:05:25 +08:00
}
2006-05-10 07:53:23 +08:00
2021-07-22 17:41:24 +08:00
/* Ensure static properties are initialized. */
2015-01-13 14:33:15 +08:00
if ( UNEXPECTED ( CE_STATIC_MEMBERS ( ce ) = = NULL ) ) {
2021-07-22 17:41:24 +08:00
zend_class_init_statics ( ce ) ;
2013-05-21 13:58:11 +08:00
}
2015-01-03 17:22:58 +08:00
2018-06-22 18:58:48 +08:00
ret = CE_STATIC_MEMBERS ( ce ) + property_info - > offset ;
ZVAL_DEINDIRECT ( ret ) ;
2019-01-07 19:28:51 +08:00
if ( UNEXPECTED ( ( type = = BP_VAR_R | | type = = BP_VAR_RW )
2019-09-23 22:53:54 +08:00
& & Z_TYPE_P ( ret ) = = IS_UNDEF & & ZEND_TYPE_IS_SET ( property_info - > type ) ) ) {
2019-01-07 19:28:51 +08:00
zend_throw_error ( NULL , " Typed static property %s::$%s must not be accessed before initialization " ,
2021-07-09 18:34:35 +08:00
ZSTR_VAL ( property_info - > ce - > name ) , ZSTR_VAL ( property_name ) ) ;
2019-01-07 19:28:51 +08:00
return NULL ;
}
2021-07-09 18:13:12 +08:00
if ( UNEXPECTED ( ce - > ce_flags & ZEND_ACC_TRAIT ) ) {
zend_error ( E_DEPRECATED ,
" Accessing static trait property %s::$%s is deprecated, "
" it should only be accessed on a class using the trait " ,
ZSTR_VAL ( property_info - > ce - > name ) , ZSTR_VAL ( property_name ) ) ;
}
2015-01-13 08:32:51 +08:00
return ret ;
2003-02-05 21:35:52 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-05 21:35:52 +08:00
2019-01-07 19:28:51 +08:00
ZEND_API zval * zend_std_get_static_property ( zend_class_entry * ce , zend_string * property_name , int type ) /* { { { */
{
zend_property_info * prop_info ;
return zend_std_get_static_property_with_info ( ce , property_name , type , & prop_info ) ;
}
2021-01-15 19:30:54 +08:00
ZEND_API ZEND_COLD bool zend_std_unset_static_property ( zend_class_entry * ce , zend_string * property_name ) /* { { { */
2003-02-17 22:06:39 +08:00
{
2015-07-08 01:10:22 +08:00
zend_throw_error ( NULL , " Attempt to unset static property %s::$%s " , ZSTR_VAL ( ce - > name ) , ZSTR_VAL ( property_name ) ) ;
2003-02-17 22:06:39 +08:00
return 0 ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-17 22:06:39 +08:00
2018-09-13 15:31:49 +08:00
static ZEND_COLD zend_never_inline void zend_bad_constructor_call ( zend_function * constructor , zend_class_entry * scope ) /* { { { */
2018-09-13 06:41:19 +08:00
{
if ( scope ) {
2020-05-26 20:10:57 +08:00
zend_throw_error ( NULL , " Call to %s %s::%s() from scope %s " ,
zend_visibility_string ( constructor - > common . fn_flags ) , ZSTR_VAL ( constructor - > common . scope - > name ) ,
ZSTR_VAL ( constructor - > common . function_name ) , ZSTR_VAL ( scope - > name )
) ;
2018-09-13 06:41:19 +08:00
} else {
2020-05-26 20:10:57 +08:00
zend_throw_error ( NULL , " Call to %s %s::%s() from global scope " , zend_visibility_string ( constructor - > common . fn_flags ) , ZSTR_VAL ( constructor - > common . scope - > name ) , ZSTR_VAL ( constructor - > common . function_name ) ) ;
2018-09-13 06:41:19 +08:00
}
}
/* }}} */
2018-05-31 16:57:22 +08:00
ZEND_API zend_function * zend_std_get_constructor ( zend_object * zobj ) /* { { { */
2002-02-07 22:08:43 +08:00
{
2003-02-03 00:17:25 +08:00
zend_function * constructor = zobj - > ce - > constructor ;
2016-04-28 09:13:34 +08:00
zend_class_entry * scope ;
2003-02-03 00:17:25 +08:00
if ( constructor ) {
2018-09-13 06:41:19 +08:00
if ( UNEXPECTED ( ! ( constructor - > op_array . fn_flags & ZEND_ACC_PUBLIC ) ) ) {
if ( UNEXPECTED ( EG ( fake_scope ) ) ) {
2016-04-28 09:13:34 +08:00
scope = EG ( fake_scope ) ;
} else {
scope = zend_get_executed_scope ( ) ;
}
2018-09-13 14:31:38 +08:00
if ( UNEXPECTED ( constructor - > common . scope ! = scope ) ) {
if ( UNEXPECTED ( constructor - > op_array . fn_flags & ZEND_ACC_PRIVATE )
| | UNEXPECTED ( ! zend_check_protected ( zend_get_function_root_class ( constructor ) , scope ) ) ) {
2018-09-13 15:31:49 +08:00
zend_bad_constructor_call ( constructor , scope ) ;
2018-09-13 06:41:19 +08:00
constructor = NULL ;
2006-07-05 19:39:00 +08:00
}
2003-02-03 00:17:25 +08:00
}
}
}
return constructor ;
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-02-05 22:27:30 +08:00
2018-05-31 16:57:22 +08:00
ZEND_API int zend_std_compare_objects ( zval * o1 , zval * o2 ) /* { { { */
2002-02-07 22:08:43 +08:00
{
zend_object * zobj1 , * zobj2 ;
2006-05-10 07:53:23 +08:00
2019-10-07 22:57:49 +08:00
if ( Z_TYPE_P ( o1 ) ! = Z_TYPE_P ( o2 ) ) {
/* Object and non-object */
2022-06-08 00:07:26 +08:00
zval * object ;
zval * value ;
2019-10-07 22:57:49 +08:00
zval casted ;
2022-06-08 00:07:26 +08:00
bool object_lhs ;
2019-10-07 22:57:49 +08:00
if ( Z_TYPE_P ( o1 ) = = IS_OBJECT ) {
2022-06-08 00:07:26 +08:00
object = o1 ;
value = o2 ;
object_lhs = true ;
2019-10-07 22:57:49 +08:00
} else {
2022-06-08 00:07:26 +08:00
object = o2 ;
value = o1 ;
object_lhs = false ;
}
ZEND_ASSERT ( Z_TYPE_P ( value ) ! = IS_OBJECT ) ;
2023-02-23 22:56:54 +08:00
uint8_t target_type = ( Z_TYPE_P ( value ) = = IS_FALSE | | Z_TYPE_P ( value ) = = IS_TRUE )
2022-06-08 00:07:26 +08:00
? _IS_BOOL : Z_TYPE_P ( value ) ;
if ( Z_OBJ_HT_P ( object ) - > cast_object ( Z_OBJ_P ( object ) , & casted , target_type ) = = FAILURE ) {
// TODO: Less crazy.
if ( target_type = = IS_LONG | | target_type = = IS_DOUBLE ) {
zend_error ( E_NOTICE , " Object of class %s could not be converted to %s " ,
ZSTR_VAL ( Z_OBJCE_P ( object ) - > name ) , zend_get_type_by_const ( target_type ) ) ;
if ( target_type = = IS_LONG ) {
ZVAL_LONG ( & casted , 1 ) ;
2020-03-31 18:17:32 +08:00
} else {
2022-06-08 00:07:26 +08:00
ZVAL_DOUBLE ( & casted , 1.0 ) ;
2019-10-07 22:57:49 +08:00
}
2022-06-08 00:07:26 +08:00
} else {
return object_lhs ? 1 : - 1 ;
2019-10-07 22:57:49 +08:00
}
}
2022-06-08 00:07:26 +08:00
int ret = object_lhs ? zend_compare ( & casted , value ) : zend_compare ( value , & casted ) ;
zval_ptr_dtor ( & casted ) ;
return ret ;
2019-10-07 22:57:49 +08:00
}
2002-04-22 22:22:27 +08:00
zobj1 = Z_OBJ_P ( o1 ) ;
zobj2 = Z_OBJ_P ( o2 ) ;
2002-02-07 22:08:43 +08:00
2017-10-06 06:34:50 +08:00
if ( zobj1 = = zobj2 ) {
return 0 ; /* the same object */
}
2002-09-15 15:45:26 +08:00
if ( zobj1 - > ce ! = zobj2 - > ce ) {
2020-03-31 18:36:48 +08:00
return ZEND_UNCOMPARABLE ; /* different classes */
2002-02-07 22:08:43 +08:00
}
2010-05-24 22:11:39 +08:00
if ( ! zobj1 - > properties & & ! zobj2 - > properties ) {
2018-08-11 23:19:28 +08:00
zend_property_info * info ;
2021-02-27 00:27:55 +08:00
int i ;
2013-01-09 15:30:50 +08:00
2015-04-29 00:11:45 +08:00
if ( ! zobj1 - > ce - > default_properties_count ) {
return 0 ;
}
2017-10-06 06:34:50 +08:00
2017-10-06 16:47:11 +08:00
/* It's enough to protect only one of the objects.
* The second one may be referenced from the first and this may cause
* false recursion detection .
*/
2017-10-06 06:34:50 +08:00
/* use bitwise OR to make only one conditional jump */
2017-10-06 16:47:11 +08:00
if ( UNEXPECTED ( Z_IS_RECURSIVE_P ( o1 ) ) ) {
2017-10-06 06:34:50 +08:00
zend_error_noreturn ( E_ERROR , " Nesting level too deep - recursive dependency? " ) ;
}
Z_PROTECT_RECURSION_P ( o1 ) ;
2018-08-11 23:19:28 +08:00
2021-02-27 00:27:55 +08:00
for ( i = 0 ; i < zobj1 - > ce - > default_properties_count ; i + + ) {
zval * p1 , * p2 ;
info = zobj1 - > ce - > properties_info_table [ i ] ;
2018-08-11 23:19:28 +08:00
2021-02-27 00:27:55 +08:00
if ( ! info ) {
2018-08-11 23:19:28 +08:00
continue ;
}
2021-02-27 00:27:55 +08:00
p1 = OBJ_PROP ( zobj1 , info - > offset ) ;
p2 = OBJ_PROP ( zobj2 , info - > offset ) ;
2015-04-29 00:11:45 +08:00
if ( Z_TYPE_P ( p1 ) ! = IS_UNDEF ) {
if ( Z_TYPE_P ( p2 ) ! = IS_UNDEF ) {
2019-10-07 22:57:49 +08:00
int ret ;
2010-05-24 22:11:39 +08:00
2019-10-07 22:57:49 +08:00
ret = zend_compare ( p1 , p2 ) ;
if ( ret ! = 0 ) {
2017-10-06 06:34:50 +08:00
Z_UNPROTECT_RECURSION_P ( o1 ) ;
2019-10-07 22:57:49 +08:00
return ret ;
2010-05-24 22:11:39 +08:00
}
} else {
2017-10-06 06:34:50 +08:00
Z_UNPROTECT_RECURSION_P ( o1 ) ;
2010-05-24 22:11:39 +08:00
return 1 ;
}
} else {
2015-04-29 00:11:45 +08:00
if ( Z_TYPE_P ( p2 ) ! = IS_UNDEF ) {
2017-10-06 06:34:50 +08:00
Z_UNPROTECT_RECURSION_P ( o1 ) ;
2010-05-24 22:11:39 +08:00
return 1 ;
}
}
2021-02-27 00:27:55 +08:00
}
2018-08-11 23:19:28 +08:00
2017-10-06 06:34:50 +08:00
Z_UNPROTECT_RECURSION_P ( o1 ) ;
2010-05-24 22:11:39 +08:00
return 0 ;
} else {
if ( ! zobj1 - > properties ) {
rebuild_object_properties ( zobj1 ) ;
}
if ( ! zobj2 - > properties ) {
rebuild_object_properties ( zobj2 ) ;
}
2014-12-22 12:16:25 +08:00
return zend_compare_symbol_tables ( zobj1 - > properties , zobj2 - > properties ) ;
2010-05-24 22:11:39 +08:00
}
2002-02-07 22:08:43 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2021-02-16 19:32:43 +08:00
ZEND_API int zend_objects_not_comparable ( zval * o1 , zval * o2 )
{
return ZEND_UNCOMPARABLE ;
}
2019-01-31 23:47:58 +08:00
ZEND_API int zend_std_has_property ( zend_object * zobj , zend_string * name , int has_set_exists , void * * cache_slot ) /* { { { */
2002-02-07 22:08:43 +08:00
{
int result ;
2014-02-10 14:04:30 +08:00
zval * value = NULL ;
2017-10-25 16:45:17 +08:00
uintptr_t property_offset ;
2023-01-03 23:38:22 +08:00
const zend_property_info * prop_info = NULL ;
2019-01-31 23:47:58 +08:00
zend_string * tmp_name = NULL ;
2003-02-05 22:27:30 +08:00
2019-01-16 16:56:53 +08:00
property_offset = zend_get_property_offset ( zobj - > ce , name , 1 , cache_slot , & prop_info ) ;
2005-07-08 00:07:09 +08:00
2017-09-18 18:13:24 +08:00
if ( EXPECTED ( IS_VALID_PROPERTY_OFFSET ( property_offset ) ) ) {
value = OBJ_PROP ( zobj , property_offset ) ;
if ( Z_TYPE_P ( value ) ! = IS_UNDEF ) {
goto found ;
}
2023-03-01 05:54:38 +08:00
if ( UNEXPECTED ( Z_PROP_FLAG_P ( value ) & IS_PROP_UNINIT ) ) {
2019-10-28 20:34:33 +08:00
/* Skip __isset() for uninitialized typed properties */
result = 0 ;
goto exit ;
}
2017-09-18 18:13:24 +08:00
} else if ( EXPECTED ( IS_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) ) {
2017-10-23 23:29:13 +08:00
if ( EXPECTED ( zobj - > properties ! = NULL ) ) {
if ( ! IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET ( property_offset ) ) {
2017-10-25 16:45:17 +08:00
uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET ( property_offset ) ;
2017-10-23 23:29:13 +08:00
2017-10-24 15:28:19 +08:00
if ( EXPECTED ( idx < zobj - > properties - > nNumUsed * sizeof ( Bucket ) ) ) {
Bucket * p = ( Bucket * ) ( ( char * ) zobj - > properties - > arData + idx ) ;
2017-10-23 23:29:13 +08:00
2021-12-22 17:54:32 +08:00
if ( EXPECTED ( p - > key = = name ) | |
( EXPECTED ( p - > h = = ZSTR_H ( name ) ) & &
EXPECTED ( p - > key ! = NULL ) & &
EXPECTED ( zend_string_equal_content ( p - > key , name ) ) ) ) {
2017-10-23 23:29:13 +08:00
value = & p - > val ;
goto found ;
}
}
2017-10-24 15:28:19 +08:00
CACHE_PTR_EX ( cache_slot + 1 , ( void * ) ZEND_DYNAMIC_PROPERTY_OFFSET ) ;
2017-10-23 23:29:13 +08:00
}
2018-07-31 17:24:53 +08:00
value = zend_hash_find ( zobj - > properties , name ) ;
2017-10-23 23:29:13 +08:00
if ( value ) {
if ( cache_slot ) {
2017-10-25 16:45:17 +08:00
uintptr_t idx = ( char * ) value - ( char * ) zobj - > properties - > arData ;
2017-10-24 15:28:19 +08:00
CACHE_PTR_EX ( cache_slot + 1 , ( void * ) ZEND_ENCODE_DYN_PROP_OFFSET ( idx ) ) ;
2017-10-23 23:29:13 +08:00
}
2014-02-10 14:04:30 +08:00
found :
2018-07-31 17:24:53 +08:00
if ( has_set_exists = = ZEND_PROPERTY_NOT_EMPTY ) {
result = zend_is_true ( value ) ;
} else if ( has_set_exists < ZEND_PROPERTY_NOT_EMPTY ) {
ZEND_ASSERT ( has_set_exists = = ZEND_PROPERTY_ISSET ) ;
ZVAL_DEREF ( value ) ;
result = ( Z_TYPE_P ( value ) ! = IS_NULL ) ;
} else {
ZEND_ASSERT ( has_set_exists = = ZEND_PROPERTY_EXISTS ) ;
result = 1 ;
2017-10-23 23:29:13 +08:00
}
goto exit ;
2014-02-10 14:04:30 +08:00
}
}
2015-04-01 05:37:59 +08:00
} else if ( UNEXPECTED ( EG ( exception ) ) ) {
result = 0 ;
goto exit ;
2014-02-10 14:04:30 +08:00
}
result = 0 ;
2018-07-24 12:51:36 +08:00
if ( ( has_set_exists ! = ZEND_PROPERTY_EXISTS ) & & zobj - > ce - > __isset ) {
2018-07-31 17:24:53 +08:00
uint32_t * guard = zend_get_property_guard ( zobj , name ) ;
2014-02-10 14:04:30 +08:00
if ( ! ( ( * guard ) & IN_ISSET ) ) {
zval rv ;
2003-02-05 22:27:30 +08:00
2005-07-08 00:07:09 +08:00
/* have issetter - try with it! */
2018-07-31 17:24:53 +08:00
if ( ! tmp_name & & ! ZSTR_IS_INTERNED ( name ) ) {
tmp_name = zend_string_copy ( name ) ;
2017-10-26 18:05:23 +08:00
}
2018-07-10 18:17:33 +08:00
GC_ADDREF ( zobj ) ;
2014-02-10 14:04:30 +08:00
( * guard ) | = IN_ISSET ; /* prevent circular getting */
2018-07-31 17:24:53 +08:00
zend_std_call_issetter ( zobj , name , & rv ) ;
2018-07-31 17:23:46 +08:00
result = zend_is_true ( & rv ) ;
zval_ptr_dtor ( & rv ) ;
if ( has_set_exists = = ZEND_PROPERTY_NOT_EMPTY & & result ) {
if ( EXPECTED ( ! EG ( exception ) ) & & zobj - > ce - > __get & & ! ( ( * guard ) & IN_GET ) ) {
( * guard ) | = IN_GET ;
2018-07-31 17:24:53 +08:00
zend_std_call_getter ( zobj , name , & rv ) ;
2018-07-31 17:23:46 +08:00
( * guard ) & = ~ IN_GET ;
result = i_zend_is_true ( & rv ) ;
zval_ptr_dtor ( & rv ) ;
} else {
result = 0 ;
2005-07-08 00:07:09 +08:00
}
}
2014-02-10 14:04:30 +08:00
( * guard ) & = ~ IN_ISSET ;
2018-07-10 18:17:33 +08:00
OBJ_RELEASE ( zobj ) ;
2002-02-07 22:08:43 +08:00
}
}
2014-02-10 14:04:30 +08:00
exit :
2018-07-31 17:24:53 +08:00
zend_tmp_string_release ( tmp_name ) ;
2002-02-07 22:08:43 +08:00
return result ;
}
2007-11-03 03:40:39 +08:00
/* }}} */
2002-02-07 22:08:43 +08:00
2018-05-31 16:57:22 +08:00
ZEND_API zend_string * zend_std_get_class_name ( const zend_object * zobj ) /* { { { */
2003-01-12 21:56:51 +08:00
{
2014-10-10 01:15:07 +08:00
return zend_string_copy ( zobj - > ce - > name ) ;
2003-01-12 21:56:51 +08:00
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-01-12 21:56:51 +08:00
2022-06-26 07:00:19 +08:00
ZEND_API zend_result zend_std_cast_object_tostring ( zend_object * readobj , zval * writeobj , int type ) /* { { { */
2003-12-16 00:59:21 +08:00
{
switch ( type ) {
2020-03-31 18:04:59 +08:00
case IS_STRING : {
zend_class_entry * ce = readobj - > ce ;
2019-02-26 22:32:18 +08:00
if ( ce - > __tostring ) {
2020-03-31 18:04:59 +08:00
zval retval ;
2020-05-25 18:10:41 +08:00
GC_ADDREF ( readobj ) ;
Add zend_call_known_function() API family
This adds the following APIs:
void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope,
zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr, int param_count, zval *params);
void zend_call_known_instance_method_with_0_params(
zend_function *fn, zend_object *object, zval *retval_ptr);
void zend_call_known_instance_method_with_1_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param);
void zend_call_known_instance_method_with_2_params(
zend_function *fn, zend_object *object, zval *retval_ptr, zval *param1, zval *param2);
These are used to perform a call if you already have the
zend_function you want to call. zend_call_known_function()
is the base API, the rest are just really thin wrappers around
it for the common case of instance method calls.
Closes GH-5692.
2020-06-08 23:10:24 +08:00
zend_call_known_instance_method_with_0_params ( ce - > __tostring , readobj , & retval ) ;
2020-05-25 18:10:41 +08:00
zend_object_release ( readobj ) ;
2014-02-10 14:04:30 +08:00
if ( EXPECTED ( Z_TYPE ( retval ) = = IS_STRING ) ) {
2014-02-21 16:56:23 +08:00
ZVAL_COPY_VALUE ( writeobj , & retval ) ;
2006-05-10 07:53:23 +08:00
return SUCCESS ;
2019-02-26 22:32:18 +08:00
}
zval_ptr_dtor ( & retval ) ;
if ( ! EG ( exception ) ) {
zend_throw_error ( NULL , " Method %s::__toString() must return a string value " , ZSTR_VAL ( ce - > name ) ) ;
2003-12-16 00:59:21 +08:00
}
}
2006-05-10 07:53:23 +08:00
return FAILURE ;
2020-03-31 18:04:59 +08:00
}
2014-04-30 22:32:42 +08:00
case _IS_BOOL :
2017-12-26 04:19:45 +08:00
ZVAL_TRUE ( writeobj ) ;
2006-05-10 07:53:23 +08:00
return SUCCESS ;
2003-12-16 00:59:21 +08:00
default :
2020-03-31 18:04:59 +08:00
return FAILURE ;
2003-12-16 00:59:21 +08:00
}
}
2007-11-03 03:40:39 +08:00
/* }}} */
2003-09-18 19:38:33 +08:00
2022-06-26 07:00:19 +08:00
ZEND_API zend_result zend_std_get_closure ( zend_object * obj , zend_class_entry * * ce_ptr , zend_function * * fptr_ptr , zend_object * * obj_ptr , bool check_only ) /* { { { */
2008-08-15 05:36:56 +08:00
{
2019-01-31 23:47:58 +08:00
zend_class_entry * ce = obj - > ce ;
2021-07-21 07:07:17 +08:00
zval * func = zend_hash_find_known_hash ( & ce - > function_table , ZSTR_KNOWN ( ZEND_STR_MAGIC_INVOKE ) ) ;
2008-08-15 05:36:56 +08:00
2021-07-21 07:07:17 +08:00
if ( func = = NULL ) {
2008-08-15 05:36:56 +08:00
return FAILURE ;
}
2014-02-10 14:04:30 +08:00
* fptr_ptr = Z_FUNC_P ( func ) ;
2008-08-15 05:36:56 +08:00
* ce_ptr = ce ;
if ( ( * fptr_ptr ) - > common . fn_flags & ZEND_ACC_STATIC ) {
2014-03-28 06:11:22 +08:00
if ( obj_ptr ) {
* obj_ptr = NULL ;
2008-08-15 05:36:56 +08:00
}
} else {
2014-03-28 06:11:22 +08:00
if ( obj_ptr ) {
2019-01-31 23:47:58 +08:00
* obj_ptr = obj ;
2008-08-15 05:36:56 +08:00
}
}
return SUCCESS ;
}
/* }}} */
2019-01-31 23:47:58 +08:00
ZEND_API HashTable * zend_std_get_properties_for ( zend_object * obj , zend_prop_purpose purpose ) {
2018-10-04 19:58:35 +08:00
HashTable * ht ;
switch ( purpose ) {
case ZEND_PROP_PURPOSE_DEBUG :
2019-01-31 23:47:58 +08:00
if ( obj - > handlers - > get_debug_info ) {
2018-10-04 19:58:35 +08:00
int is_temp ;
2019-01-31 23:47:58 +08:00
ht = obj - > handlers - > get_debug_info ( obj , & is_temp ) ;
2020-09-15 17:38:40 +08:00
if ( ht & & ! is_temp ) {
GC_TRY_ADDREF ( ht ) ;
2018-10-04 19:58:35 +08:00
}
return ht ;
}
2020-09-30 06:25:49 +08:00
ZEND_FALLTHROUGH ;
2018-10-04 19:58:35 +08:00
case ZEND_PROP_PURPOSE_ARRAY_CAST :
case ZEND_PROP_PURPOSE_SERIALIZE :
case ZEND_PROP_PURPOSE_VAR_EXPORT :
case ZEND_PROP_PURPOSE_JSON :
2019-01-31 23:47:58 +08:00
ht = obj - > handlers - > get_properties ( obj ) ;
2020-09-15 17:38:40 +08:00
if ( ht ) {
GC_TRY_ADDREF ( ht ) ;
2018-10-04 19:58:35 +08:00
}
return ht ;
default :
2020-06-16 22:29:05 +08:00
ZEND_UNREACHABLE ( ) ;
2018-10-04 19:58:35 +08:00
return NULL ;
}
}
ZEND_API HashTable * zend_get_properties_for ( zval * obj , zend_prop_purpose purpose ) {
2019-01-31 23:47:58 +08:00
zend_object * zobj = Z_OBJ_P ( obj ) ;
if ( zobj - > handlers - > get_properties_for ) {
return zobj - > handlers - > get_properties_for ( zobj , purpose ) ;
2018-10-04 19:58:35 +08:00
}
2019-01-31 23:47:58 +08:00
return zend_std_get_properties_for ( zobj , purpose ) ;
2018-10-04 19:58:35 +08:00
}
2018-03-14 19:01:45 +08:00
ZEND_API const zend_object_handlers std_object_handlers = {
2014-04-09 05:50:15 +08:00
0 , /* offset */
zend_object_std_dtor , /* free_obj */
2014-02-21 19:48:56 +08:00
zend_objects_destroy_object , /* dtor_obj */
2003-07-07 17:12:15 +08:00
zend_objects_clone_obj , /* clone_obj */
2006-05-10 07:53:23 +08:00
2003-07-07 17:12:15 +08:00
zend_std_read_property , /* read_property */
zend_std_write_property , /* write_property */
2003-07-07 18:53:27 +08:00
zend_std_read_dimension , /* read_dimension */
2003-07-07 17:12:15 +08:00
zend_std_write_dimension , /* write_dimension */
2003-10-05 15:52:28 +08:00
zend_std_get_property_ptr_ptr , /* get_property_ptr_ptr */
2003-07-07 17:12:15 +08:00
zend_std_has_property , /* has_property */
zend_std_unset_property , /* unset_property */
2003-11-11 00:14:44 +08:00
zend_std_has_dimension , /* has_dimension */
2003-07-31 01:12:06 +08:00
zend_std_unset_dimension , /* unset_dimension */
2003-07-07 17:12:15 +08:00
zend_std_get_properties , /* get_properties */
zend_std_get_method , /* get_method */
zend_std_get_constructor , /* get_constructor */
2018-05-31 16:57:22 +08:00
zend_std_get_class_name , /* get_class_name */
2006-05-10 07:53:23 +08:00
zend_std_cast_object_tostring , /* cast_object */
2004-05-04 23:03:28 +08:00
NULL , /* count_elements */
2014-02-18 11:13:00 +08:00
zend_std_get_debug_info , /* get_debug_info */
2008-08-15 05:36:56 +08:00
zend_std_get_closure , /* get_closure */
2011-11-02 14:31:33 +08:00
zend_std_get_gc , /* get_gc */
2013-06-17 23:48:13 +08:00
NULL , /* do_operation */
2019-10-07 22:57:49 +08:00
zend_std_compare_objects , /* compare */
2018-10-04 19:58:35 +08:00
NULL , /* get_properties_for */
2002-02-07 22:08:43 +08:00
} ;