xref: /PHP-7.3/Zend/zend_objects.c (revision 18f2918a)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend license,     |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Dmitry Stogov <dmitry@php.net>                              |
18    +----------------------------------------------------------------------+
19 */
20 
21 #include "zend.h"
22 #include "zend_globals.h"
23 #include "zend_variables.h"
24 #include "zend_API.h"
25 #include "zend_interfaces.h"
26 #include "zend_exceptions.h"
27 
zend_object_std_init(zend_object * object,zend_class_entry * ce)28 ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
29 {
30 	GC_SET_REFCOUNT(object, 1);
31 	GC_TYPE_INFO(object) = IS_OBJECT | (GC_COLLECTABLE << GC_FLAGS_SHIFT);
32 	object->ce = ce;
33 	object->properties = NULL;
34 	zend_objects_store_put(object);
35 	if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
36 		ZVAL_UNDEF(object->properties_table + object->ce->default_properties_count);
37 	}
38 }
39 
zend_object_std_dtor(zend_object * object)40 ZEND_API void zend_object_std_dtor(zend_object *object)
41 {
42 	zval *p, *end;
43 
44 	if (object->properties) {
45 		if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) {
46 			if (EXPECTED(GC_DELREF(object->properties) == 0)
47 					&& EXPECTED(GC_TYPE(object->properties) != IS_NULL)) {
48 				zend_array_destroy(object->properties);
49 			}
50 		}
51 	}
52 	p = object->properties_table;
53 	if (EXPECTED(object->ce->default_properties_count)) {
54 		end = p + object->ce->default_properties_count;
55 		do {
56 			i_zval_ptr_dtor(p ZEND_FILE_LINE_CC);
57 			p++;
58 		} while (p != end);
59 	}
60 	if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
61 		if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) {
62 			zval_ptr_dtor_str(p);
63 		} else if (Z_TYPE_P(p) == IS_ARRAY) {
64 			HashTable *guards;
65 
66 			guards = Z_ARRVAL_P(p);
67 			ZEND_ASSERT(guards != NULL);
68 			zend_hash_destroy(guards);
69 			FREE_HASHTABLE(guards);
70 		}
71 	}
72 }
73 
zend_objects_destroy_object(zend_object * object)74 ZEND_API void zend_objects_destroy_object(zend_object *object)
75 {
76 	zend_function *destructor = object->ce->destructor;
77 
78 	if (destructor) {
79 		zend_object *old_exception;
80 		zend_class_entry *orig_fake_scope;
81 		zend_fcall_info fci;
82 		zend_fcall_info_cache fcic;
83 		zval ret;
84 
85 		if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
86 			if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
87 				/* Ensure that if we're calling a private function, we're allowed to do so.
88 				 */
89 				if (EG(current_execute_data)) {
90 					zend_class_entry *scope = zend_get_executed_scope();
91 
92 					if (object->ce != scope) {
93 						zend_throw_error(NULL,
94 							"Call to private %s::__destruct() from context '%s'",
95 							ZSTR_VAL(object->ce->name),
96 							scope ? ZSTR_VAL(scope->name) : "");
97 						return;
98 					}
99 				} else {
100 					zend_error(E_WARNING,
101 						"Call to private %s::__destruct() from context '' during shutdown ignored",
102 						ZSTR_VAL(object->ce->name));
103 					return;
104 				}
105 			} else {
106 				/* Ensure that if we're calling a protected function, we're allowed to do so.
107 				 */
108 				if (EG(current_execute_data)) {
109 					zend_class_entry *scope = zend_get_executed_scope();
110 
111 					if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) {
112 						zend_throw_error(NULL,
113 							"Call to protected %s::__destruct() from context '%s'",
114 							ZSTR_VAL(object->ce->name),
115 							scope ? ZSTR_VAL(scope->name) : "");
116 						return;
117 					}
118 				} else {
119 					zend_error(E_WARNING,
120 						"Call to protected %s::__destruct() from context '' during shutdown ignored",
121 						ZSTR_VAL(object->ce->name));
122 					return;
123 				}
124 			}
125 		}
126 
127 		GC_ADDREF(object);
128 
129 		/* Make sure that destructors are protected from previously thrown exceptions.
130 		 * For example, if an exception was thrown in a function and when the function's
131 		 * local variable destruction results in a destructor being called.
132 		 */
133 		old_exception = NULL;
134 		if (EG(exception)) {
135 			if (EG(exception) == object) {
136 				zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
137 			} else {
138 				old_exception = EG(exception);
139 				EG(exception) = NULL;
140 			}
141 		}
142 		orig_fake_scope = EG(fake_scope);
143 		EG(fake_scope) = NULL;
144 
145 		ZVAL_UNDEF(&ret);
146 
147 		fci.size = sizeof(fci);
148 		fci.object = object;
149 		fci.retval = &ret;
150 		fci.param_count = 0;
151 		fci.params = NULL;
152 		fci.no_separation = 1;
153 		ZVAL_UNDEF(&fci.function_name); /* Unused */
154 
155 		fcic.function_handler = destructor;
156 		fcic.called_scope = object->ce;
157 		fcic.object = object;
158 
159 		zend_call_function(&fci, &fcic);
160 		zval_ptr_dtor(&ret);
161 
162 		if (old_exception) {
163 			if (EG(exception)) {
164 				zend_exception_set_previous(EG(exception), old_exception);
165 			} else {
166 				EG(exception) = old_exception;
167 			}
168 		}
169 		OBJ_RELEASE(object);
170 		EG(fake_scope) = orig_fake_scope;
171 	}
172 }
173 
zend_objects_new(zend_class_entry * ce)174 ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
175 {
176 	zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce));
177 
178 	zend_object_std_init(object, ce);
179 	object->handlers = &std_object_handlers;
180 	return object;
181 }
182 
zend_objects_clone_members(zend_object * new_object,zend_object * old_object)183 ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
184 {
185 	if (old_object->ce->default_properties_count) {
186 		zval *src = old_object->properties_table;
187 		zval *dst = new_object->properties_table;
188 		zval *end = src + old_object->ce->default_properties_count;
189 
190 		do {
191 			i_zval_ptr_dtor(dst ZEND_FILE_LINE_CC);
192 			ZVAL_COPY_VALUE(dst, src);
193 			zval_add_ref(dst);
194 			src++;
195 			dst++;
196 		} while (src != end);
197 	} else if (old_object->properties && !old_object->ce->clone) {
198 		/* fast copy */
199 		if (EXPECTED(old_object->handlers == &std_object_handlers)) {
200 			if (EXPECTED(!(GC_FLAGS(old_object->properties) & IS_ARRAY_IMMUTABLE))) {
201 				GC_ADDREF(old_object->properties);
202 			}
203 			new_object->properties = old_object->properties;
204 			return;
205 		}
206 	}
207 
208 	if (old_object->properties &&
209 	    EXPECTED(zend_hash_num_elements(old_object->properties))) {
210 		zval *prop, new_prop;
211 		zend_ulong num_key;
212 		zend_string *key;
213 
214 		if (!new_object->properties) {
215 			new_object->properties = zend_new_array(zend_hash_num_elements(old_object->properties));
216 			zend_hash_real_init_mixed(new_object->properties);
217 		} else {
218 			zend_hash_extend(new_object->properties, new_object->properties->nNumUsed + zend_hash_num_elements(old_object->properties), 0);
219 		}
220 
221 		HT_FLAGS(new_object->properties) |=
222 			HT_FLAGS(old_object->properties) & HASH_FLAG_HAS_EMPTY_IND;
223 
224 		ZEND_HASH_FOREACH_KEY_VAL(old_object->properties, num_key, key, prop) {
225 			if (Z_TYPE_P(prop) == IS_INDIRECT) {
226 				ZVAL_INDIRECT(&new_prop, new_object->properties_table + (Z_INDIRECT_P(prop) - old_object->properties_table));
227 			} else {
228 				ZVAL_COPY_VALUE(&new_prop, prop);
229 				zval_add_ref(&new_prop);
230 			}
231 			if (EXPECTED(key)) {
232 				_zend_hash_append(new_object->properties, key, &new_prop);
233 			} else {
234 				zend_hash_index_add_new(new_object->properties, num_key, &new_prop);
235 			}
236 		} ZEND_HASH_FOREACH_END();
237 	}
238 
239 	if (old_object->ce->clone) {
240 		zend_fcall_info fci;
241 		zend_fcall_info_cache fcic;
242 		zval ret;
243 
244 		GC_ADDREF(new_object);
245 
246 		ZVAL_UNDEF(&ret);
247 
248 		fci.size = sizeof(fci);
249 		fci.object = new_object;
250 		fci.retval = &ret;
251 		fci.param_count = 0;
252 		fci.params = NULL;
253 		fci.no_separation = 1;
254 		ZVAL_UNDEF(&fci.function_name); /* Unused */
255 
256 		fcic.function_handler = new_object->ce->clone;
257 		fcic.called_scope = new_object->ce;
258 		fcic.object = new_object;
259 
260 		zend_call_function(&fci, &fcic);
261 		zval_ptr_dtor(&ret);
262 		OBJ_RELEASE(new_object);
263 	}
264 }
265 
zend_objects_clone_obj(zval * zobject)266 ZEND_API zend_object *zend_objects_clone_obj(zval *zobject)
267 {
268 	zend_object *old_object;
269 	zend_object *new_object;
270 
271 	/* assume that create isn't overwritten, so when clone depends on the
272 	 * overwritten one then it must itself be overwritten */
273 	old_object = Z_OBJ_P(zobject);
274 	new_object = zend_objects_new(old_object->ce);
275 
276 	/* zend_objects_clone_members() expect the properties to be initialized. */
277 	if (new_object->ce->default_properties_count) {
278 		zval *p = new_object->properties_table;
279 		zval *end = p + new_object->ce->default_properties_count;
280 		do {
281 			ZVAL_UNDEF(p);
282 			p++;
283 		} while (p != end);
284 	}
285 
286 	zend_objects_clone_members(new_object, old_object);
287 
288 	return new_object;
289 }
290 
291 /*
292  * Local variables:
293  * tab-width: 4
294  * c-basic-offset: 4
295  * indent-tabs-mode: t
296  * End:
297  * vim600: sw=4 ts=4 fdm=marker
298  * vim<600: sw=4 ts=4
299  */
300