zend_object_handlers.c 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 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. #include "zend.h"
  21. #include "zend_globals.h"
  22. #include "zend_variables.h"
  23. #include "zend_API.h"
  24. #include "zend_objects.h"
  25. #include "zend_objects_API.h"
  26. #include "zend_object_handlers.h"
  27. #include "zend_interfaces.h"
  28. #include "zend_exceptions.h"
  29. #include "zend_closures.h"
  30. #include "zend_compile.h"
  31. #include "zend_hash.h"
  32. #define DEBUG_OBJECT_HANDLERS 0
  33. #define ZEND_WRONG_PROPERTY_OFFSET 0
  34. /* guard flags */
  35. #define IN_GET (1<<0)
  36. #define IN_SET (1<<1)
  37. #define IN_UNSET (1<<2)
  38. #define IN_ISSET (1<<3)
  39. /*
  40. __X accessors explanation:
  41. if we have __get and property that is not part of the properties array is
  42. requested, we call __get handler. If it fails, we return uninitialized.
  43. if we have __set and property that is not part of the properties array is
  44. set, we call __set handler. If it fails, we do not change the array.
  45. for both handlers above, when we are inside __get/__set, no further calls for
  46. __get/__set for this property of this object will be made, to prevent endless
  47. recursion and enable accessors to change properties array.
  48. if we have __call and method which is not part of the class function table is
  49. called, we cal __call handler.
  50. */
  51. ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */
  52. {
  53. if (!zobj->properties) {
  54. zend_property_info *prop_info;
  55. zend_class_entry *ce = zobj->ce;
  56. int i;
  57. zobj->properties = zend_new_array(ce->default_properties_count);
  58. if (ce->default_properties_count) {
  59. zend_hash_real_init_mixed(zobj->properties);
  60. for (i = 0; i < ce->default_properties_count; i++) {
  61. prop_info = ce->properties_info_table[i];
  62. if (!prop_info) {
  63. continue;
  64. }
  65. if (UNEXPECTED(Z_TYPE_P(OBJ_PROP(zobj, prop_info->offset)) == IS_UNDEF)) {
  66. HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND;
  67. }
  68. _zend_hash_append_ind(zobj->properties, prop_info->name,
  69. OBJ_PROP(zobj, prop_info->offset));
  70. }
  71. }
  72. }
  73. }
  74. /* }}} */
  75. ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj) /* {{{ */
  76. {
  77. zend_property_info *prop_info;
  78. zend_class_entry *ce = zobj->ce;
  79. HashTable *ht;
  80. zval* prop;
  81. int i;
  82. ZEND_ASSERT(!zobj->properties);
  83. ht = zend_new_array(ce->default_properties_count);
  84. if (ce->default_properties_count) {
  85. zend_hash_real_init_mixed(ht);
  86. for (i = 0; i < ce->default_properties_count; i++) {
  87. prop_info = ce->properties_info_table[i];
  88. if (!prop_info) {
  89. continue;
  90. }
  91. prop = OBJ_PROP(zobj, prop_info->offset);
  92. if (UNEXPECTED(Z_TYPE_P(prop) == IS_UNDEF)) {
  93. continue;
  94. }
  95. if (Z_ISREF_P(prop) && Z_REFCOUNT_P(prop) == 1) {
  96. prop = Z_REFVAL_P(prop);
  97. }
  98. Z_TRY_ADDREF_P(prop);
  99. _zend_hash_append(ht, prop_info->name, prop);
  100. }
  101. }
  102. return ht;
  103. }
  104. /* }}} */
  105. ZEND_API HashTable *zend_std_get_properties(zend_object *zobj) /* {{{ */
  106. {
  107. if (!zobj->properties) {
  108. rebuild_object_properties(zobj);
  109. }
  110. return zobj->properties;
  111. }
  112. /* }}} */
  113. ZEND_API HashTable *zend_std_get_gc(zend_object *zobj, zval **table, int *n) /* {{{ */
  114. {
  115. if (zobj->handlers->get_properties != zend_std_get_properties) {
  116. *table = NULL;
  117. *n = 0;
  118. return zobj->handlers->get_properties(zobj);
  119. } else {
  120. if (zobj->properties) {
  121. *table = NULL;
  122. *n = 0;
  123. return zobj->properties;
  124. } else {
  125. *table = zobj->properties_table;
  126. *n = zobj->ce->default_properties_count;
  127. return NULL;
  128. }
  129. }
  130. }
  131. /* }}} */
  132. ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
  133. {
  134. zend_class_entry *ce = object->ce;
  135. zval retval;
  136. HashTable *ht;
  137. if (!ce->__debugInfo) {
  138. *is_temp = 0;
  139. return object->handlers->get_properties(object);
  140. }
  141. zend_call_known_instance_method_with_0_params(ce->__debugInfo, object, &retval);
  142. if (Z_TYPE(retval) == IS_ARRAY) {
  143. if (!Z_REFCOUNTED(retval)) {
  144. *is_temp = 1;
  145. return zend_array_dup(Z_ARRVAL(retval));
  146. } else if (Z_REFCOUNT(retval) <= 1) {
  147. *is_temp = 1;
  148. ht = Z_ARR(retval);
  149. return ht;
  150. } else {
  151. *is_temp = 0;
  152. zval_ptr_dtor(&retval);
  153. return Z_ARRVAL(retval);
  154. }
  155. } else if (Z_TYPE(retval) == IS_NULL) {
  156. *is_temp = 1;
  157. ht = zend_new_array(0);
  158. return ht;
  159. }
  160. zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array");
  161. return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */
  162. }
  163. /* }}} */
  164. static void zend_std_call_getter(zend_object *zobj, zend_string *prop_name, zval *retval) /* {{{ */
  165. {
  166. zval member;
  167. ZVAL_STR(&member, prop_name);
  168. zend_call_known_instance_method_with_1_params(zobj->ce->__get, zobj, retval, &member);
  169. }
  170. /* }}} */
  171. static void zend_std_call_setter(zend_object *zobj, zend_string *prop_name, zval *value) /* {{{ */
  172. {
  173. zval args[2];
  174. ZVAL_STR(&args[0], prop_name);
  175. ZVAL_COPY_VALUE(&args[1], value);
  176. zend_call_known_instance_method(zobj->ce->__set, zobj, NULL, 2, args);
  177. }
  178. /* }}} */
  179. static void zend_std_call_unsetter(zend_object *zobj, zend_string *prop_name) /* {{{ */
  180. {
  181. zval member;
  182. ZVAL_STR(&member, prop_name);
  183. zend_call_known_instance_method_with_1_params(zobj->ce->__unset, zobj, NULL, &member);
  184. }
  185. /* }}} */
  186. static void zend_std_call_issetter(zend_object *zobj, zend_string *prop_name, zval *retval) /* {{{ */
  187. {
  188. zval member;
  189. ZVAL_STR(&member, prop_name);
  190. zend_call_known_instance_method_with_1_params(zobj->ce->__isset, zobj, retval, &member);
  191. }
  192. /* }}} */
  193. static zend_always_inline bool is_derived_class(zend_class_entry *child_class, zend_class_entry *parent_class) /* {{{ */
  194. {
  195. child_class = child_class->parent;
  196. while (child_class) {
  197. if (child_class == parent_class) {
  198. return 1;
  199. }
  200. child_class = child_class->parent;
  201. }
  202. return 0;
  203. }
  204. /* }}} */
  205. static zend_never_inline int is_protected_compatible_scope(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
  206. {
  207. return scope &&
  208. (is_derived_class(ce, scope) || is_derived_class(scope, ce));
  209. }
  210. /* }}} */
  211. static zend_never_inline zend_property_info *zend_get_parent_private_property(zend_class_entry *scope, zend_class_entry *ce, zend_string *member) /* {{{ */
  212. {
  213. zval *zv;
  214. zend_property_info *prop_info;
  215. if (scope != ce && scope && is_derived_class(ce, scope)) {
  216. zv = zend_hash_find(&scope->properties_info, member);
  217. if (zv != NULL) {
  218. prop_info = (zend_property_info*)Z_PTR_P(zv);
  219. if ((prop_info->flags & ZEND_ACC_PRIVATE)
  220. && prop_info->ce == scope) {
  221. return prop_info;
  222. }
  223. }
  224. }
  225. return NULL;
  226. }
  227. /* }}} */
  228. static ZEND_COLD zend_never_inline void zend_bad_property_access(zend_property_info *property_info, zend_class_entry *ce, zend_string *member) /* {{{ */
  229. {
  230. zend_throw_error(NULL, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ZSTR_VAL(ce->name), ZSTR_VAL(member));
  231. }
  232. /* }}} */
  233. static ZEND_COLD zend_never_inline void zend_bad_property_name(void) /* {{{ */
  234. {
  235. zend_throw_error(NULL, "Cannot access property starting with \"\\0\"");
  236. }
  237. /* }}} */
  238. static ZEND_COLD zend_never_inline void zend_forbidden_dynamic_property(
  239. zend_class_entry *ce, zend_string *member) {
  240. zend_throw_error(NULL, "Cannot create dynamic property %s::$%s",
  241. ZSTR_VAL(ce->name), ZSTR_VAL(member));
  242. }
  243. static ZEND_COLD zend_never_inline void zend_readonly_property_modification_scope_error(
  244. zend_class_entry *ce, zend_string *member, zend_class_entry *scope, const char *operation) {
  245. zend_throw_error(NULL, "Cannot %s readonly property %s::$%s from %s%s",
  246. operation, ZSTR_VAL(ce->name), ZSTR_VAL(member),
  247. scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : "");
  248. }
  249. static ZEND_COLD zend_never_inline void zend_readonly_property_unset_error(
  250. zend_class_entry *ce, zend_string *member) {
  251. zend_throw_error(NULL, "Cannot unset readonly property %s::$%s",
  252. ZSTR_VAL(ce->name), ZSTR_VAL(member));
  253. }
  254. static zend_always_inline uintptr_t zend_get_property_offset(zend_class_entry *ce, zend_string *member, int silent, void **cache_slot, zend_property_info **info_ptr) /* {{{ */
  255. {
  256. zval *zv;
  257. zend_property_info *property_info;
  258. uint32_t flags;
  259. zend_class_entry *scope;
  260. uintptr_t offset;
  261. if (cache_slot && EXPECTED(ce == CACHED_PTR_EX(cache_slot))) {
  262. *info_ptr = CACHED_PTR_EX(cache_slot + 2);
  263. return (uintptr_t)CACHED_PTR_EX(cache_slot + 1);
  264. }
  265. if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)
  266. || UNEXPECTED((zv = zend_hash_find(&ce->properties_info, member)) == NULL)) {
  267. if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0') && ZSTR_LEN(member) != 0) {
  268. if (!silent) {
  269. zend_bad_property_name();
  270. }
  271. return ZEND_WRONG_PROPERTY_OFFSET;
  272. }
  273. dynamic:
  274. if (cache_slot) {
  275. CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
  276. CACHE_PTR_EX(cache_slot + 2, NULL);
  277. }
  278. return ZEND_DYNAMIC_PROPERTY_OFFSET;
  279. }
  280. property_info = (zend_property_info*)Z_PTR_P(zv);
  281. flags = property_info->flags;
  282. if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
  283. if (UNEXPECTED(EG(fake_scope))) {
  284. scope = EG(fake_scope);
  285. } else {
  286. scope = zend_get_executed_scope();
  287. }
  288. if (property_info->ce != scope) {
  289. if (flags & ZEND_ACC_CHANGED) {
  290. zend_property_info *p = zend_get_parent_private_property(scope, ce, member);
  291. /* If there is a public/protected instance property on ce, don't try to use a
  292. * private static property on scope. If both are static, prefer the static
  293. * property on scope. This will throw a static property notice, rather than
  294. * a visibility error. */
  295. if (p && (!(p->flags & ZEND_ACC_STATIC) || (flags & ZEND_ACC_STATIC))) {
  296. property_info = p;
  297. flags = property_info->flags;
  298. goto found;
  299. } else if (flags & ZEND_ACC_PUBLIC) {
  300. goto found;
  301. }
  302. }
  303. if (flags & ZEND_ACC_PRIVATE) {
  304. if (property_info->ce != ce) {
  305. goto dynamic;
  306. } else {
  307. wrong:
  308. /* Information was available, but we were denied access. Error out. */
  309. if (!silent) {
  310. zend_bad_property_access(property_info, ce, member);
  311. }
  312. return ZEND_WRONG_PROPERTY_OFFSET;
  313. }
  314. } else {
  315. ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
  316. if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
  317. goto wrong;
  318. }
  319. }
  320. }
  321. }
  322. found:
  323. if (UNEXPECTED(flags & ZEND_ACC_STATIC)) {
  324. if (!silent) {
  325. zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
  326. }
  327. return ZEND_DYNAMIC_PROPERTY_OFFSET;
  328. }
  329. offset = property_info->offset;
  330. if (EXPECTED(!ZEND_TYPE_IS_SET(property_info->type))) {
  331. property_info = NULL;
  332. } else {
  333. *info_ptr = property_info;
  334. }
  335. if (cache_slot) {
  336. CACHE_POLYMORPHIC_PTR_EX(cache_slot, ce, (void*)(uintptr_t)offset);
  337. CACHE_PTR_EX(cache_slot + 2, property_info);
  338. }
  339. return offset;
  340. }
  341. /* }}} */
  342. static ZEND_COLD void zend_wrong_offset(zend_class_entry *ce, zend_string *member) /* {{{ */
  343. {
  344. zend_property_info *dummy;
  345. /* Trigger the correct error */
  346. zend_get_property_offset(ce, member, 0, NULL, &dummy);
  347. }
  348. /* }}} */
  349. ZEND_API zend_property_info *zend_get_property_info(zend_class_entry *ce, zend_string *member, int silent) /* {{{ */
  350. {
  351. zval *zv;
  352. zend_property_info *property_info;
  353. uint32_t flags;
  354. zend_class_entry *scope;
  355. if (UNEXPECTED(zend_hash_num_elements(&ce->properties_info) == 0)
  356. || EXPECTED((zv = zend_hash_find(&ce->properties_info, member)) == NULL)) {
  357. if (UNEXPECTED(ZSTR_VAL(member)[0] == '\0') && ZSTR_LEN(member) != 0) {
  358. if (!silent) {
  359. zend_bad_property_name();
  360. }
  361. return ZEND_WRONG_PROPERTY_INFO;
  362. }
  363. dynamic:
  364. return NULL;
  365. }
  366. property_info = (zend_property_info*)Z_PTR_P(zv);
  367. flags = property_info->flags;
  368. if (flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
  369. if (UNEXPECTED(EG(fake_scope))) {
  370. scope = EG(fake_scope);
  371. } else {
  372. scope = zend_get_executed_scope();
  373. }
  374. if (property_info->ce != scope) {
  375. if (flags & ZEND_ACC_CHANGED) {
  376. zend_property_info *p = zend_get_parent_private_property(scope, ce, member);
  377. if (p) {
  378. property_info = p;
  379. flags = property_info->flags;
  380. goto found;
  381. } else if (flags & ZEND_ACC_PUBLIC) {
  382. goto found;
  383. }
  384. }
  385. if (flags & ZEND_ACC_PRIVATE) {
  386. if (property_info->ce != ce) {
  387. goto dynamic;
  388. } else {
  389. wrong:
  390. /* Information was available, but we were denied access. Error out. */
  391. if (!silent) {
  392. zend_bad_property_access(property_info, ce, member);
  393. }
  394. return ZEND_WRONG_PROPERTY_INFO;
  395. }
  396. } else {
  397. ZEND_ASSERT(flags & ZEND_ACC_PROTECTED);
  398. if (UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
  399. goto wrong;
  400. }
  401. }
  402. }
  403. }
  404. found:
  405. if (UNEXPECTED(flags & ZEND_ACC_STATIC)) {
  406. if (!silent) {
  407. zend_error(E_NOTICE, "Accessing static property %s::$%s as non static", ZSTR_VAL(ce->name), ZSTR_VAL(member));
  408. }
  409. }
  410. return property_info;
  411. }
  412. /* }}} */
  413. ZEND_API int zend_check_property_access(zend_object *zobj, zend_string *prop_info_name, bool is_dynamic) /* {{{ */
  414. {
  415. zend_property_info *property_info;
  416. const char *class_name = NULL;
  417. const char *prop_name;
  418. zend_string *member;
  419. size_t prop_name_len;
  420. if (ZSTR_VAL(prop_info_name)[0] == 0) {
  421. if (is_dynamic) {
  422. return SUCCESS;
  423. }
  424. zend_unmangle_property_name_ex(prop_info_name, &class_name, &prop_name, &prop_name_len);
  425. member = zend_string_init(prop_name, prop_name_len, 0);
  426. property_info = zend_get_property_info(zobj->ce, member, 1);
  427. zend_string_release_ex(member, 0);
  428. if (property_info == NULL || property_info == ZEND_WRONG_PROPERTY_INFO) {
  429. return FAILURE;
  430. }
  431. if (class_name[0] != '*') {
  432. if (!(property_info->flags & ZEND_ACC_PRIVATE)) {
  433. /* we we're looking for a private prop but found a non private one of the same name */
  434. return FAILURE;
  435. } else if (strcmp(ZSTR_VAL(prop_info_name)+1, ZSTR_VAL(property_info->name)+1)) {
  436. /* we we're looking for a private prop but found a private one of the same name but another class */
  437. return FAILURE;
  438. }
  439. } else {
  440. ZEND_ASSERT(property_info->flags & ZEND_ACC_PROTECTED);
  441. }
  442. return SUCCESS;
  443. } else {
  444. property_info = zend_get_property_info(zobj->ce, prop_info_name, 1);
  445. if (property_info == NULL) {
  446. ZEND_ASSERT(is_dynamic);
  447. return SUCCESS;
  448. } else if (property_info == ZEND_WRONG_PROPERTY_INFO) {
  449. return FAILURE;
  450. }
  451. return (property_info->flags & ZEND_ACC_PUBLIC) ? SUCCESS : FAILURE;
  452. }
  453. }
  454. /* }}} */
  455. static void zend_property_guard_dtor(zval *el) /* {{{ */ {
  456. uint32_t *ptr = (uint32_t*)Z_PTR_P(el);
  457. if (EXPECTED(!(((zend_uintptr_t)ptr) & 1))) {
  458. efree_size(ptr, sizeof(uint32_t));
  459. }
  460. }
  461. /* }}} */
  462. ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member) /* {{{ */
  463. {
  464. HashTable *guards;
  465. zval *zv;
  466. uint32_t *ptr;
  467. ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_USE_GUARDS);
  468. zv = zobj->properties_table + zobj->ce->default_properties_count;
  469. if (EXPECTED(Z_TYPE_P(zv) == IS_STRING)) {
  470. zend_string *str = Z_STR_P(zv);
  471. if (EXPECTED(str == member) ||
  472. /* "str" always has a pre-calculated hash value here */
  473. (EXPECTED(ZSTR_H(str) == zend_string_hash_val(member)) &&
  474. EXPECTED(zend_string_equal_content(str, member)))) {
  475. return &Z_PROPERTY_GUARD_P(zv);
  476. } else if (EXPECTED(Z_PROPERTY_GUARD_P(zv) == 0)) {
  477. zval_ptr_dtor_str(zv);
  478. ZVAL_STR_COPY(zv, member);
  479. return &Z_PROPERTY_GUARD_P(zv);
  480. } else {
  481. ALLOC_HASHTABLE(guards);
  482. zend_hash_init(guards, 8, NULL, zend_property_guard_dtor, 0);
  483. /* mark pointer as "special" using low bit */
  484. zend_hash_add_new_ptr(guards, str,
  485. (void*)(((zend_uintptr_t)&Z_PROPERTY_GUARD_P(zv)) | 1));
  486. zval_ptr_dtor_str(zv);
  487. ZVAL_ARR(zv, guards);
  488. }
  489. } else if (EXPECTED(Z_TYPE_P(zv) == IS_ARRAY)) {
  490. guards = Z_ARRVAL_P(zv);
  491. ZEND_ASSERT(guards != NULL);
  492. zv = zend_hash_find(guards, member);
  493. if (zv != NULL) {
  494. return (uint32_t*)(((zend_uintptr_t)Z_PTR_P(zv)) & ~1);
  495. }
  496. } else {
  497. ZEND_ASSERT(Z_TYPE_P(zv) == IS_UNDEF);
  498. ZVAL_STR_COPY(zv, member);
  499. Z_PROPERTY_GUARD_P(zv) = 0;
  500. return &Z_PROPERTY_GUARD_P(zv);
  501. }
  502. /* we have to allocate uint32_t separately because ht->arData may be reallocated */
  503. ptr = (uint32_t*)emalloc(sizeof(uint32_t));
  504. *ptr = 0;
  505. return (uint32_t*)zend_hash_add_new_ptr(guards, member, ptr);
  506. }
  507. /* }}} */
  508. ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
  509. {
  510. zval *retval;
  511. uintptr_t property_offset;
  512. zend_property_info *prop_info = NULL;
  513. uint32_t *guard = NULL;
  514. zend_string *tmp_name = NULL;
  515. #if DEBUG_OBJECT_HANDLERS
  516. fprintf(stderr, "Read object #%d property: %s\n", zobj->handle, ZSTR_VAL(name));
  517. #endif
  518. /* make zend_get_property_info silent if we have getter - we may want to use it */
  519. property_offset = zend_get_property_offset(zobj->ce, name, (type == BP_VAR_IS) || (zobj->ce->__get != NULL), cache_slot, &prop_info);
  520. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  521. retval = OBJ_PROP(zobj, property_offset);
  522. if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) {
  523. if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)
  524. && (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
  525. if (Z_TYPE_P(retval) == IS_OBJECT) {
  526. /* For objects, W/RW/UNSET fetch modes might not actually modify object.
  527. * Similar as with magic __get() allow them, but return the value as a copy
  528. * to make sure no actual modification is possible. */
  529. ZVAL_COPY(rv, retval);
  530. retval = rv;
  531. } else {
  532. zend_readonly_property_modification_error(prop_info);
  533. retval = &EG(uninitialized_zval);
  534. }
  535. }
  536. goto exit;
  537. } else {
  538. if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
  539. if (type == BP_VAR_W || type == BP_VAR_RW) {
  540. zend_readonly_property_indirect_modification_error(prop_info);
  541. retval = &EG(uninitialized_zval);
  542. goto exit;
  543. } else if (type == BP_VAR_UNSET) {
  544. retval = &EG(uninitialized_zval);
  545. goto exit;
  546. }
  547. }
  548. }
  549. if (UNEXPECTED(Z_PROP_FLAG_P(retval) == IS_PROP_UNINIT)) {
  550. /* Skip __get() for uninitialized typed properties */
  551. goto uninit_error;
  552. }
  553. } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
  554. if (EXPECTED(zobj->properties != NULL)) {
  555. if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
  556. uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
  557. if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
  558. Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
  559. if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
  560. (EXPECTED(p->key == name) ||
  561. (EXPECTED(p->h == ZSTR_H(name)) &&
  562. EXPECTED(p->key != NULL) &&
  563. EXPECTED(zend_string_equal_content(p->key, name))))) {
  564. retval = &p->val;
  565. goto exit;
  566. }
  567. }
  568. CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
  569. }
  570. retval = zend_hash_find(zobj->properties, name);
  571. if (EXPECTED(retval)) {
  572. if (cache_slot) {
  573. uintptr_t idx = (char*)retval - (char*)zobj->properties->arData;
  574. CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
  575. }
  576. goto exit;
  577. }
  578. }
  579. } else if (UNEXPECTED(EG(exception))) {
  580. retval = &EG(uninitialized_zval);
  581. goto exit;
  582. }
  583. /* magic isset */
  584. if ((type == BP_VAR_IS) && zobj->ce->__isset) {
  585. zval tmp_result;
  586. guard = zend_get_property_guard(zobj, name);
  587. if (!((*guard) & IN_ISSET)) {
  588. if (!tmp_name && !ZSTR_IS_INTERNED(name)) {
  589. tmp_name = zend_string_copy(name);
  590. }
  591. GC_ADDREF(zobj);
  592. ZVAL_UNDEF(&tmp_result);
  593. *guard |= IN_ISSET;
  594. zend_std_call_issetter(zobj, name, &tmp_result);
  595. *guard &= ~IN_ISSET;
  596. if (!zend_is_true(&tmp_result)) {
  597. retval = &EG(uninitialized_zval);
  598. OBJ_RELEASE(zobj);
  599. zval_ptr_dtor(&tmp_result);
  600. goto exit;
  601. }
  602. zval_ptr_dtor(&tmp_result);
  603. if (zobj->ce->__get && !((*guard) & IN_GET)) {
  604. goto call_getter;
  605. }
  606. OBJ_RELEASE(zobj);
  607. } else if (zobj->ce->__get && !((*guard) & IN_GET)) {
  608. goto call_getter_addref;
  609. }
  610. } else if (zobj->ce->__get) {
  611. /* magic get */
  612. guard = zend_get_property_guard(zobj, name);
  613. if (!((*guard) & IN_GET)) {
  614. /* have getter - try with it! */
  615. call_getter_addref:
  616. GC_ADDREF(zobj);
  617. call_getter:
  618. *guard |= IN_GET; /* prevent circular getting */
  619. zend_std_call_getter(zobj, name, rv);
  620. *guard &= ~IN_GET;
  621. if (Z_TYPE_P(rv) != IS_UNDEF) {
  622. retval = rv;
  623. if (!Z_ISREF_P(rv) &&
  624. (type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET)) {
  625. if (UNEXPECTED(Z_TYPE_P(rv) != IS_OBJECT)) {
  626. zend_error(E_NOTICE, "Indirect modification of overloaded property %s::$%s has no effect", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
  627. }
  628. }
  629. } else {
  630. retval = &EG(uninitialized_zval);
  631. }
  632. if (UNEXPECTED(prop_info)) {
  633. zend_verify_prop_assignable_by_ref(prop_info, retval, (zobj->ce->__get->common.fn_flags & ZEND_ACC_STRICT_TYPES) != 0);
  634. }
  635. OBJ_RELEASE(zobj);
  636. goto exit;
  637. } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) {
  638. /* Trigger the correct error */
  639. zend_get_property_offset(zobj->ce, name, 0, NULL, &prop_info);
  640. ZEND_ASSERT(EG(exception));
  641. retval = &EG(uninitialized_zval);
  642. goto exit;
  643. }
  644. }
  645. uninit_error:
  646. if (type != BP_VAR_IS) {
  647. if (UNEXPECTED(prop_info)) {
  648. zend_throw_error(NULL, "Typed property %s::$%s must not be accessed before initialization",
  649. ZSTR_VAL(prop_info->ce->name),
  650. ZSTR_VAL(name));
  651. } else {
  652. zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
  653. }
  654. }
  655. retval = &EG(uninitialized_zval);
  656. exit:
  657. zend_tmp_string_release(tmp_name);
  658. return retval;
  659. }
  660. /* }}} */
  661. static zend_always_inline bool property_uses_strict_types(void) {
  662. zend_execute_data *execute_data = EG(current_execute_data);
  663. return execute_data
  664. && execute_data->func
  665. && ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data));
  666. }
  667. static bool verify_readonly_initialization_access(
  668. zend_property_info *prop_info, zend_class_entry *ce,
  669. zend_string *name, const char *operation) {
  670. zend_class_entry *scope;
  671. if (UNEXPECTED(EG(fake_scope))) {
  672. scope = EG(fake_scope);
  673. } else {
  674. scope = zend_get_executed_scope();
  675. }
  676. if (prop_info->ce == scope) {
  677. return true;
  678. }
  679. /* We may have redeclared a parent property. In that case the parent should still be
  680. * allowed to initialize it. */
  681. if (scope && is_derived_class(ce, scope)) {
  682. zend_property_info *prop_info = zend_hash_find_ptr(&scope->properties_info, name);
  683. if (prop_info) {
  684. /* This should be ensured by inheritance. */
  685. ZEND_ASSERT(prop_info->flags & ZEND_ACC_READONLY);
  686. if (prop_info->ce == scope) {
  687. return true;
  688. }
  689. }
  690. }
  691. zend_readonly_property_modification_scope_error(prop_info->ce, name, scope, operation);
  692. return false;
  693. }
  694. ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot) /* {{{ */
  695. {
  696. zval *variable_ptr, tmp;
  697. uintptr_t property_offset;
  698. zend_property_info *prop_info = NULL;
  699. ZEND_ASSERT(!Z_ISREF_P(value));
  700. property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__set != NULL), cache_slot, &prop_info);
  701. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  702. variable_ptr = OBJ_PROP(zobj, property_offset);
  703. if (Z_TYPE_P(variable_ptr) != IS_UNDEF) {
  704. Z_TRY_ADDREF_P(value);
  705. if (UNEXPECTED(prop_info)) {
  706. if (UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
  707. Z_TRY_DELREF_P(value);
  708. zend_readonly_property_modification_error(prop_info);
  709. variable_ptr = &EG(error_zval);
  710. goto exit;
  711. }
  712. ZVAL_COPY_VALUE(&tmp, value);
  713. if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
  714. Z_TRY_DELREF_P(value);
  715. variable_ptr = &EG(error_zval);
  716. goto exit;
  717. }
  718. value = &tmp;
  719. }
  720. found:
  721. variable_ptr = zend_assign_to_variable(
  722. variable_ptr, value, IS_TMP_VAR, property_uses_strict_types());
  723. goto exit;
  724. }
  725. if (Z_PROP_FLAG_P(variable_ptr) == IS_PROP_UNINIT) {
  726. /* Writes to uninitialized typed properties bypass __set(). */
  727. goto write_std_property;
  728. }
  729. } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
  730. if (EXPECTED(zobj->properties != NULL)) {
  731. if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
  732. if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
  733. GC_DELREF(zobj->properties);
  734. }
  735. zobj->properties = zend_array_dup(zobj->properties);
  736. }
  737. if ((variable_ptr = zend_hash_find(zobj->properties, name)) != NULL) {
  738. Z_TRY_ADDREF_P(value);
  739. goto found;
  740. }
  741. }
  742. } else if (UNEXPECTED(EG(exception))) {
  743. variable_ptr = &EG(error_zval);
  744. goto exit;
  745. }
  746. /* magic set */
  747. if (zobj->ce->__set) {
  748. uint32_t *guard = zend_get_property_guard(zobj, name);
  749. if (!((*guard) & IN_SET)) {
  750. GC_ADDREF(zobj);
  751. (*guard) |= IN_SET; /* prevent circular setting */
  752. zend_std_call_setter(zobj, name, value);
  753. (*guard) &= ~IN_SET;
  754. OBJ_RELEASE(zobj);
  755. variable_ptr = value;
  756. } else if (EXPECTED(!IS_WRONG_PROPERTY_OFFSET(property_offset))) {
  757. goto write_std_property;
  758. } else {
  759. /* Trigger the correct error */
  760. zend_wrong_offset(zobj->ce, name);
  761. ZEND_ASSERT(EG(exception));
  762. variable_ptr = &EG(error_zval);
  763. goto exit;
  764. }
  765. } else {
  766. ZEND_ASSERT(!IS_WRONG_PROPERTY_OFFSET(property_offset));
  767. write_std_property:
  768. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  769. variable_ptr = OBJ_PROP(zobj, property_offset);
  770. Z_TRY_ADDREF_P(value);
  771. if (UNEXPECTED(prop_info)) {
  772. if (UNEXPECTED((prop_info->flags & ZEND_ACC_READONLY)
  773. && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "initialize"))) {
  774. Z_TRY_DELREF_P(value);
  775. variable_ptr = &EG(error_zval);
  776. goto exit;
  777. }
  778. ZVAL_COPY_VALUE(&tmp, value);
  779. if (UNEXPECTED(!zend_verify_property_type(prop_info, &tmp, property_uses_strict_types()))) {
  780. zval_ptr_dtor(value);
  781. goto exit;
  782. }
  783. value = &tmp;
  784. Z_PROP_FLAG_P(variable_ptr) = 0;
  785. goto found; /* might have been updated via e.g. __toString() */
  786. }
  787. ZVAL_COPY_VALUE(variable_ptr, value);
  788. } else {
  789. if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
  790. zend_forbidden_dynamic_property(zobj->ce, name);
  791. variable_ptr = &EG(error_zval);
  792. goto exit;
  793. }
  794. Z_TRY_ADDREF_P(value);
  795. if (!zobj->properties) {
  796. rebuild_object_properties(zobj);
  797. }
  798. variable_ptr = zend_hash_add_new(zobj->properties, name, value);
  799. }
  800. }
  801. exit:
  802. return variable_ptr;
  803. }
  804. /* }}} */
  805. static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry *ce) /* {{{ */
  806. {
  807. zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
  808. }
  809. /* }}} */
  810. ZEND_API zval *zend_std_read_dimension(zend_object *object, zval *offset, int type, zval *rv) /* {{{ */
  811. {
  812. zend_class_entry *ce = object->ce;
  813. zval tmp_offset;
  814. if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
  815. if (offset == NULL) {
  816. /* [] construct */
  817. ZVAL_NULL(&tmp_offset);
  818. } else {
  819. ZVAL_COPY_DEREF(&tmp_offset, offset);
  820. }
  821. GC_ADDREF(object);
  822. if (type == BP_VAR_IS) {
  823. zend_call_method_with_1_params(object, ce, NULL, "offsetexists", rv, &tmp_offset);
  824. if (UNEXPECTED(Z_ISUNDEF_P(rv))) {
  825. OBJ_RELEASE(object);
  826. zval_ptr_dtor(&tmp_offset);
  827. return NULL;
  828. }
  829. if (!i_zend_is_true(rv)) {
  830. OBJ_RELEASE(object);
  831. zval_ptr_dtor(&tmp_offset);
  832. zval_ptr_dtor(rv);
  833. return &EG(uninitialized_zval);
  834. }
  835. zval_ptr_dtor(rv);
  836. }
  837. zend_call_method_with_1_params(object, ce, NULL, "offsetget", rv, &tmp_offset);
  838. OBJ_RELEASE(object);
  839. zval_ptr_dtor(&tmp_offset);
  840. if (UNEXPECTED(Z_TYPE_P(rv) == IS_UNDEF)) {
  841. if (UNEXPECTED(!EG(exception))) {
  842. zend_throw_error(NULL, "Undefined offset for object of type %s used as array", ZSTR_VAL(ce->name));
  843. }
  844. return NULL;
  845. }
  846. return rv;
  847. } else {
  848. zend_bad_array_access(ce);
  849. return NULL;
  850. }
  851. }
  852. /* }}} */
  853. ZEND_API void zend_std_write_dimension(zend_object *object, zval *offset, zval *value) /* {{{ */
  854. {
  855. zend_class_entry *ce = object->ce;
  856. zval tmp_offset;
  857. if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
  858. if (!offset) {
  859. ZVAL_NULL(&tmp_offset);
  860. } else {
  861. ZVAL_COPY_DEREF(&tmp_offset, offset);
  862. }
  863. GC_ADDREF(object);
  864. zend_call_method_with_2_params(object, ce, NULL, "offsetset", NULL, &tmp_offset, value);
  865. OBJ_RELEASE(object);
  866. zval_ptr_dtor(&tmp_offset);
  867. } else {
  868. zend_bad_array_access(ce);
  869. }
  870. }
  871. /* }}} */
  872. ZEND_API int zend_std_has_dimension(zend_object *object, zval *offset, int check_empty) /* {{{ */
  873. {
  874. zend_class_entry *ce = object->ce;
  875. zval retval, tmp_offset;
  876. int result;
  877. if (EXPECTED(zend_class_implements_interface(ce, zend_ce_arrayaccess) != 0)) {
  878. ZVAL_COPY_DEREF(&tmp_offset, offset);
  879. GC_ADDREF(object);
  880. zend_call_method_with_1_params(object, ce, NULL, "offsetexists", &retval, &tmp_offset);
  881. result = i_zend_is_true(&retval);
  882. zval_ptr_dtor(&retval);
  883. if (check_empty && result && EXPECTED(!EG(exception))) {
  884. zend_call_method_with_1_params(object, ce, NULL, "offsetget", &retval, &tmp_offset);
  885. result = i_zend_is_true(&retval);
  886. zval_ptr_dtor(&retval);
  887. }
  888. OBJ_RELEASE(object);
  889. zval_ptr_dtor(&tmp_offset);
  890. } else {
  891. zend_bad_array_access(ce);
  892. return 0;
  893. }
  894. return result;
  895. }
  896. /* }}} */
  897. ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot) /* {{{ */
  898. {
  899. zval *retval = NULL;
  900. uintptr_t property_offset;
  901. zend_property_info *prop_info = NULL;
  902. #if DEBUG_OBJECT_HANDLERS
  903. fprintf(stderr, "Ptr object #%d property: %s\n", zobj->handle, ZSTR_VAL(name));
  904. #endif
  905. property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__get != NULL), cache_slot, &prop_info);
  906. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  907. retval = OBJ_PROP(zobj, property_offset);
  908. if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) {
  909. if (EXPECTED(!zobj->ce->__get) ||
  910. UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET) ||
  911. UNEXPECTED(prop_info && Z_PROP_FLAG_P(retval) == IS_PROP_UNINIT)) {
  912. if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
  913. if (UNEXPECTED(prop_info)) {
  914. zend_throw_error(NULL,
  915. "Typed property %s::$%s must not be accessed before initialization",
  916. ZSTR_VAL(prop_info->ce->name),
  917. ZSTR_VAL(name));
  918. retval = &EG(error_zval);
  919. } else {
  920. ZVAL_NULL(retval);
  921. zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
  922. }
  923. } else if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
  924. /* Readonly property, delegate to read_property + write_property. */
  925. retval = NULL;
  926. }
  927. } else {
  928. /* we do have getter - fail and let it try again with usual get/set */
  929. retval = NULL;
  930. }
  931. } else if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) {
  932. /* Readonly property, delegate to read_property + write_property. */
  933. retval = NULL;
  934. }
  935. } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
  936. if (EXPECTED(zobj->properties)) {
  937. if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
  938. if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
  939. GC_DELREF(zobj->properties);
  940. }
  941. zobj->properties = zend_array_dup(zobj->properties);
  942. }
  943. if (EXPECTED((retval = zend_hash_find(zobj->properties, name)) != NULL)) {
  944. return retval;
  945. }
  946. }
  947. if (EXPECTED(!zobj->ce->__get) ||
  948. UNEXPECTED((*zend_get_property_guard(zobj, name)) & IN_GET)) {
  949. if (UNEXPECTED(zobj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
  950. zend_forbidden_dynamic_property(zobj->ce, name);
  951. return &EG(error_zval);
  952. }
  953. if (UNEXPECTED(!zobj->properties)) {
  954. rebuild_object_properties(zobj);
  955. }
  956. retval = zend_hash_update(zobj->properties, name, &EG(uninitialized_zval));
  957. /* Notice is thrown after creation of the property, to avoid EG(std_property_info)
  958. * being overwritten in an error handler. */
  959. if (UNEXPECTED(type == BP_VAR_RW || type == BP_VAR_R)) {
  960. zend_error(E_WARNING, "Undefined property: %s::$%s", ZSTR_VAL(zobj->ce->name), ZSTR_VAL(name));
  961. }
  962. }
  963. } else if (zobj->ce->__get == NULL) {
  964. retval = &EG(error_zval);
  965. }
  966. return retval;
  967. }
  968. /* }}} */
  969. ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void **cache_slot) /* {{{ */
  970. {
  971. uintptr_t property_offset;
  972. zend_property_info *prop_info = NULL;
  973. property_offset = zend_get_property_offset(zobj->ce, name, (zobj->ce->__unset != NULL), cache_slot, &prop_info);
  974. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  975. zval *slot = OBJ_PROP(zobj, property_offset);
  976. if (Z_TYPE_P(slot) != IS_UNDEF) {
  977. if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY))) {
  978. zend_readonly_property_unset_error(prop_info->ce, name);
  979. return;
  980. }
  981. if (UNEXPECTED(Z_ISREF_P(slot)) &&
  982. (ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(slot)))) {
  983. if (prop_info) {
  984. ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(slot), prop_info);
  985. }
  986. }
  987. zval tmp;
  988. ZVAL_COPY_VALUE(&tmp, slot);
  989. ZVAL_UNDEF(slot);
  990. zval_ptr_dtor(&tmp);
  991. if (zobj->properties) {
  992. HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND;
  993. }
  994. return;
  995. }
  996. if (UNEXPECTED(Z_PROP_FLAG_P(slot) == IS_PROP_UNINIT)) {
  997. if (UNEXPECTED(prop_info && (prop_info->flags & ZEND_ACC_READONLY)
  998. && !verify_readonly_initialization_access(prop_info, zobj->ce, name, "unset"))) {
  999. return;
  1000. }
  1001. /* Reset the IS_PROP_UNINIT flag, if it exists and bypass __unset(). */
  1002. Z_PROP_FLAG_P(slot) = 0;
  1003. return;
  1004. }
  1005. } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))
  1006. && EXPECTED(zobj->properties != NULL)) {
  1007. if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
  1008. if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
  1009. GC_DELREF(zobj->properties);
  1010. }
  1011. zobj->properties = zend_array_dup(zobj->properties);
  1012. }
  1013. if (EXPECTED(zend_hash_del(zobj->properties, name) != FAILURE)) {
  1014. return;
  1015. }
  1016. } else if (UNEXPECTED(EG(exception))) {
  1017. return;
  1018. }
  1019. /* magic unset */
  1020. if (zobj->ce->__unset) {
  1021. uint32_t *guard = zend_get_property_guard(zobj, name);
  1022. if (!((*guard) & IN_UNSET)) {
  1023. /* have unseter - try with it! */
  1024. (*guard) |= IN_UNSET; /* prevent circular unsetting */
  1025. zend_std_call_unsetter(zobj, name);
  1026. (*guard) &= ~IN_UNSET;
  1027. } else if (UNEXPECTED(IS_WRONG_PROPERTY_OFFSET(property_offset))) {
  1028. /* Trigger the correct error */
  1029. zend_wrong_offset(zobj->ce, name);
  1030. ZEND_ASSERT(EG(exception));
  1031. return;
  1032. } else {
  1033. /* Nothing to do: The property already does not exist. */
  1034. }
  1035. }
  1036. }
  1037. /* }}} */
  1038. ZEND_API void zend_std_unset_dimension(zend_object *object, zval *offset) /* {{{ */
  1039. {
  1040. zend_class_entry *ce = object->ce;
  1041. zval tmp_offset;
  1042. if (zend_class_implements_interface(ce, zend_ce_arrayaccess)) {
  1043. ZVAL_COPY_DEREF(&tmp_offset, offset);
  1044. GC_ADDREF(object);
  1045. zend_call_method_with_1_params(object, ce, NULL, "offsetunset", NULL, &tmp_offset);
  1046. OBJ_RELEASE(object);
  1047. zval_ptr_dtor(&tmp_offset);
  1048. } else {
  1049. zend_bad_array_access(ce);
  1050. }
  1051. }
  1052. /* }}} */
  1053. static zend_never_inline zend_function *zend_get_parent_private_method(zend_class_entry *scope, zend_class_entry *ce, zend_string *function_name) /* {{{ */
  1054. {
  1055. zval *func;
  1056. zend_function *fbc;
  1057. if (scope != ce && scope && is_derived_class(ce, scope)) {
  1058. func = zend_hash_find(&scope->function_table, function_name);
  1059. if (func != NULL) {
  1060. fbc = Z_FUNC_P(func);
  1061. if ((fbc->common.fn_flags & ZEND_ACC_PRIVATE)
  1062. && fbc->common.scope == scope) {
  1063. return fbc;
  1064. }
  1065. }
  1066. }
  1067. return NULL;
  1068. }
  1069. /* }}} */
  1070. /* Ensures that we're allowed to call a protected method.
  1071. */
  1072. ZEND_API int zend_check_protected(zend_class_entry *ce, zend_class_entry *scope) /* {{{ */
  1073. {
  1074. zend_class_entry *fbc_scope = ce;
  1075. /* Is the context that's calling the function, the same as one of
  1076. * the function's parents?
  1077. */
  1078. while (fbc_scope) {
  1079. if (fbc_scope==scope) {
  1080. return 1;
  1081. }
  1082. fbc_scope = fbc_scope->parent;
  1083. }
  1084. /* Is the function's scope the same as our current object context,
  1085. * or any of the parents of our context?
  1086. */
  1087. while (scope) {
  1088. if (scope==ce) {
  1089. return 1;
  1090. }
  1091. scope = scope->parent;
  1092. }
  1093. return 0;
  1094. }
  1095. /* }}} */
  1096. ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend_string *method_name, int is_static) /* {{{ */
  1097. {
  1098. size_t mname_len;
  1099. zend_op_array *func;
  1100. zend_function *fbc = is_static ? ce->__callstatic : ce->__call;
  1101. /* We use non-NULL value to avoid useless run_time_cache allocation.
  1102. * The low bit must be zero, to not be interpreted as a MAP_PTR offset.
  1103. */
  1104. static const void *dummy = (void*)(intptr_t)2;
  1105. static const zend_arg_info arg_info[1] = {{0}};
  1106. ZEND_ASSERT(fbc);
  1107. if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
  1108. func = &EG(trampoline).op_array;
  1109. } else {
  1110. func = ecalloc(1, sizeof(zend_op_array));
  1111. }
  1112. func->type = ZEND_USER_FUNCTION;
  1113. func->arg_flags[0] = 0;
  1114. func->arg_flags[1] = 0;
  1115. func->arg_flags[2] = 0;
  1116. func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC;
  1117. if (is_static) {
  1118. func->fn_flags |= ZEND_ACC_STATIC;
  1119. }
  1120. func->opcodes = &EG(call_trampoline_op);
  1121. ZEND_MAP_PTR_INIT(func->run_time_cache, (void***)&dummy);
  1122. func->scope = fbc->common.scope;
  1123. /* reserve space for arguments, local and temporary variables */
  1124. func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2;
  1125. func->filename = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.filename : ZSTR_EMPTY_ALLOC();
  1126. func->line_start = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_start : 0;
  1127. func->line_end = (fbc->type == ZEND_USER_FUNCTION)? fbc->op_array.line_end : 0;
  1128. //??? keep compatibility for "\0" characters
  1129. //??? see: Zend/tests/bug46238.phpt
  1130. if (UNEXPECTED((mname_len = strlen(ZSTR_VAL(method_name))) != ZSTR_LEN(method_name))) {
  1131. func->function_name = zend_string_init(ZSTR_VAL(method_name), mname_len, 0);
  1132. } else {
  1133. func->function_name = zend_string_copy(method_name);
  1134. }
  1135. func->prototype = NULL;
  1136. func->num_args = 0;
  1137. func->required_num_args = 0;
  1138. func->arg_info = (zend_arg_info *) arg_info;
  1139. return (zend_function*)func;
  1140. }
  1141. /* }}} */
  1142. static zend_always_inline zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
  1143. {
  1144. return zend_get_call_trampoline_func(ce, method_name, 0);
  1145. }
  1146. /* }}} */
  1147. static ZEND_COLD zend_never_inline void zend_bad_method_call(zend_function *fbc, zend_string *method_name, zend_class_entry *scope) /* {{{ */
  1148. {
  1149. zend_throw_error(NULL, "Call to %s method %s::%s() from %s%s",
  1150. zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), ZSTR_VAL(method_name),
  1151. scope ? "scope " : "global scope",
  1152. scope ? ZSTR_VAL(scope->name) : ""
  1153. );
  1154. }
  1155. /* }}} */
  1156. static ZEND_COLD zend_never_inline void zend_abstract_method_call(zend_function *fbc) /* {{{ */
  1157. {
  1158. zend_throw_error(NULL, "Cannot call abstract method %s::%s()",
  1159. ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
  1160. }
  1161. /* }}} */
  1162. ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
  1163. {
  1164. zend_object *zobj = *obj_ptr;
  1165. zval *func;
  1166. zend_function *fbc;
  1167. zend_string *lc_method_name;
  1168. zend_class_entry *scope;
  1169. ALLOCA_FLAG(use_heap);
  1170. if (EXPECTED(key != NULL)) {
  1171. lc_method_name = Z_STR_P(key);
  1172. #ifdef ZEND_ALLOCA_MAX_SIZE
  1173. use_heap = 0;
  1174. #endif
  1175. } else {
  1176. ZSTR_ALLOCA_ALLOC(lc_method_name, ZSTR_LEN(method_name), use_heap);
  1177. zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
  1178. }
  1179. if (UNEXPECTED((func = zend_hash_find(&zobj->ce->function_table, lc_method_name)) == NULL)) {
  1180. if (UNEXPECTED(!key)) {
  1181. ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
  1182. }
  1183. if (zobj->ce->__call) {
  1184. return zend_get_user_call_function(zobj->ce, method_name);
  1185. } else {
  1186. return NULL;
  1187. }
  1188. }
  1189. fbc = Z_FUNC_P(func);
  1190. /* Check access level */
  1191. if (fbc->op_array.fn_flags & (ZEND_ACC_CHANGED|ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
  1192. scope = zend_get_executed_scope();
  1193. if (fbc->common.scope != scope) {
  1194. if (fbc->op_array.fn_flags & ZEND_ACC_CHANGED) {
  1195. zend_function *updated_fbc = zend_get_parent_private_method(scope, zobj->ce, lc_method_name);
  1196. if (EXPECTED(updated_fbc != NULL)) {
  1197. fbc = updated_fbc;
  1198. goto exit;
  1199. } else if (fbc->op_array.fn_flags & ZEND_ACC_PUBLIC) {
  1200. goto exit;
  1201. }
  1202. }
  1203. if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE)
  1204. || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
  1205. if (zobj->ce->__call) {
  1206. fbc = zend_get_user_call_function(zobj->ce, method_name);
  1207. } else {
  1208. zend_bad_method_call(fbc, method_name, scope);
  1209. fbc = NULL;
  1210. }
  1211. }
  1212. }
  1213. }
  1214. exit:
  1215. if (fbc && UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) {
  1216. zend_abstract_method_call(fbc);
  1217. fbc = NULL;
  1218. }
  1219. if (UNEXPECTED(!key)) {
  1220. ZSTR_ALLOCA_FREE(lc_method_name, use_heap);
  1221. }
  1222. return fbc;
  1223. }
  1224. /* }}} */
  1225. static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */
  1226. {
  1227. return zend_get_call_trampoline_func(ce, method_name, 1);
  1228. }
  1229. /* }}} */
  1230. static zend_always_inline zend_function *get_static_method_fallback(
  1231. zend_class_entry *ce, zend_string *function_name)
  1232. {
  1233. zend_object *object;
  1234. if (ce->__call &&
  1235. (object = zend_get_this_object(EG(current_execute_data))) != NULL &&
  1236. instanceof_function(object->ce, ce)) {
  1237. /* Call the top-level defined __call().
  1238. * see: tests/classes/__call_004.phpt */
  1239. ZEND_ASSERT(object->ce->__call);
  1240. return zend_get_user_call_function(object->ce, function_name);
  1241. } else if (ce->__callstatic) {
  1242. return zend_get_user_callstatic_function(ce, function_name);
  1243. } else {
  1244. return NULL;
  1245. }
  1246. }
  1247. ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name, const zval *key) /* {{{ */
  1248. {
  1249. zend_string *lc_function_name;
  1250. if (EXPECTED(key != NULL)) {
  1251. lc_function_name = Z_STR_P(key);
  1252. } else {
  1253. lc_function_name = zend_string_tolower(function_name);
  1254. }
  1255. zend_function *fbc;
  1256. zval *func = zend_hash_find(&ce->function_table, lc_function_name);
  1257. if (EXPECTED(func)) {
  1258. fbc = Z_FUNC_P(func);
  1259. if (!(fbc->op_array.fn_flags & ZEND_ACC_PUBLIC)) {
  1260. zend_class_entry *scope = zend_get_executed_scope();
  1261. if (UNEXPECTED(fbc->common.scope != scope)) {
  1262. if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE)
  1263. || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) {
  1264. zend_function *fallback_fbc = get_static_method_fallback(ce, function_name);
  1265. if (!fallback_fbc) {
  1266. zend_bad_method_call(fbc, function_name, scope);
  1267. }
  1268. fbc = fallback_fbc;
  1269. }
  1270. }
  1271. }
  1272. } else {
  1273. fbc = get_static_method_fallback(ce, function_name);
  1274. }
  1275. if (UNEXPECTED(!key)) {
  1276. zend_string_release_ex(lc_function_name, 0);
  1277. }
  1278. if (EXPECTED(fbc)) {
  1279. if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) {
  1280. zend_abstract_method_call(fbc);
  1281. fbc = NULL;
  1282. } else if (UNEXPECTED(fbc->common.scope->ce_flags & ZEND_ACC_TRAIT)) {
  1283. zend_error(E_DEPRECATED,
  1284. "Calling static trait method %s::%s is deprecated, "
  1285. "it should only be called on a class using the trait",
  1286. ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
  1287. if (EG(exception)) {
  1288. return NULL;
  1289. }
  1290. }
  1291. }
  1292. return fbc;
  1293. }
  1294. /* }}} */
  1295. ZEND_API void zend_class_init_statics(zend_class_entry *class_type) /* {{{ */
  1296. {
  1297. int i;
  1298. zval *p;
  1299. if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) {
  1300. if (class_type->parent) {
  1301. zend_class_init_statics(class_type->parent);
  1302. }
  1303. ZEND_MAP_PTR_SET(class_type->static_members_table, emalloc(sizeof(zval) * class_type->default_static_members_count));
  1304. for (i = 0; i < class_type->default_static_members_count; i++) {
  1305. p = &class_type->default_static_members_table[i];
  1306. if (Z_TYPE_P(p) == IS_INDIRECT) {
  1307. zval *q = &CE_STATIC_MEMBERS(class_type->parent)[i];
  1308. ZVAL_DEINDIRECT(q);
  1309. ZVAL_INDIRECT(&CE_STATIC_MEMBERS(class_type)[i], q);
  1310. } else {
  1311. ZVAL_COPY_OR_DUP(&CE_STATIC_MEMBERS(class_type)[i], p);
  1312. }
  1313. }
  1314. }
  1315. } /* }}} */
  1316. 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) /* {{{ */
  1317. {
  1318. zval *ret;
  1319. zend_class_entry *scope;
  1320. zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property_name);
  1321. *property_info_ptr = property_info;
  1322. if (UNEXPECTED(property_info == NULL)) {
  1323. goto undeclared_property;
  1324. }
  1325. if (!(property_info->flags & ZEND_ACC_PUBLIC)) {
  1326. if (UNEXPECTED(EG(fake_scope))) {
  1327. scope = EG(fake_scope);
  1328. } else {
  1329. scope = zend_get_executed_scope();
  1330. }
  1331. if (property_info->ce != scope) {
  1332. if (UNEXPECTED(property_info->flags & ZEND_ACC_PRIVATE)
  1333. || UNEXPECTED(!is_protected_compatible_scope(property_info->ce, scope))) {
  1334. if (type != BP_VAR_IS) {
  1335. zend_bad_property_access(property_info, ce, property_name);
  1336. }
  1337. return NULL;
  1338. }
  1339. }
  1340. }
  1341. if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) {
  1342. undeclared_property:
  1343. if (type != BP_VAR_IS) {
  1344. zend_throw_error(NULL, "Access to undeclared static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
  1345. }
  1346. return NULL;
  1347. }
  1348. if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
  1349. if (UNEXPECTED(zend_update_class_constants(ce)) != SUCCESS) {
  1350. return NULL;
  1351. }
  1352. }
  1353. /* Ensure static properties are initialized. */
  1354. if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) {
  1355. zend_class_init_statics(ce);
  1356. }
  1357. ret = CE_STATIC_MEMBERS(ce) + property_info->offset;
  1358. ZVAL_DEINDIRECT(ret);
  1359. if (UNEXPECTED((type == BP_VAR_R || type == BP_VAR_RW)
  1360. && Z_TYPE_P(ret) == IS_UNDEF && ZEND_TYPE_IS_SET(property_info->type))) {
  1361. zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization",
  1362. ZSTR_VAL(property_info->ce->name), ZSTR_VAL(property_name));
  1363. return NULL;
  1364. }
  1365. if (UNEXPECTED(ce->ce_flags & ZEND_ACC_TRAIT)) {
  1366. zend_error(E_DEPRECATED,
  1367. "Accessing static trait property %s::$%s is deprecated, "
  1368. "it should only be accessed on a class using the trait",
  1369. ZSTR_VAL(property_info->ce->name), ZSTR_VAL(property_name));
  1370. }
  1371. return ret;
  1372. }
  1373. /* }}} */
  1374. ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type) /* {{{ */
  1375. {
  1376. zend_property_info *prop_info;
  1377. return zend_std_get_static_property_with_info(ce, property_name, type, &prop_info);
  1378. }
  1379. ZEND_API ZEND_COLD bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name) /* {{{ */
  1380. {
  1381. zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
  1382. return 0;
  1383. }
  1384. /* }}} */
  1385. static ZEND_COLD zend_never_inline void zend_bad_constructor_call(zend_function *constructor, zend_class_entry *scope) /* {{{ */
  1386. {
  1387. if (scope) {
  1388. zend_throw_error(NULL, "Call to %s %s::%s() from scope %s",
  1389. zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name),
  1390. ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name)
  1391. );
  1392. } else {
  1393. 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));
  1394. }
  1395. }
  1396. /* }}} */
  1397. ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */
  1398. {
  1399. zend_function *constructor = zobj->ce->constructor;
  1400. zend_class_entry *scope;
  1401. if (constructor) {
  1402. if (UNEXPECTED(!(constructor->op_array.fn_flags & ZEND_ACC_PUBLIC))) {
  1403. if (UNEXPECTED(EG(fake_scope))) {
  1404. scope = EG(fake_scope);
  1405. } else {
  1406. scope = zend_get_executed_scope();
  1407. }
  1408. if (UNEXPECTED(constructor->common.scope != scope)) {
  1409. if (UNEXPECTED(constructor->op_array.fn_flags & ZEND_ACC_PRIVATE)
  1410. || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(constructor), scope))) {
  1411. zend_bad_constructor_call(constructor, scope);
  1412. constructor = NULL;
  1413. }
  1414. }
  1415. }
  1416. }
  1417. return constructor;
  1418. }
  1419. /* }}} */
  1420. ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
  1421. {
  1422. zend_object *zobj1, *zobj2;
  1423. if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) {
  1424. /* Object and non-object */
  1425. zval casted;
  1426. if (Z_TYPE_P(o1) == IS_OBJECT) {
  1427. ZEND_ASSERT(Z_TYPE_P(o2) != IS_OBJECT);
  1428. zend_uchar target_type = (Z_TYPE_P(o2) == IS_FALSE || Z_TYPE_P(o2) == IS_TRUE)
  1429. ? _IS_BOOL : Z_TYPE_P(o2);
  1430. if (Z_OBJ_HT_P(o1)->cast_object(Z_OBJ_P(o1), &casted, target_type) == FAILURE) {
  1431. // TODO: Less crazy.
  1432. if (target_type == IS_LONG || target_type == IS_DOUBLE) {
  1433. zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
  1434. ZSTR_VAL(Z_OBJCE_P(o1)->name), zend_get_type_by_const(target_type));
  1435. if (target_type == IS_LONG) {
  1436. ZVAL_LONG(&casted, 1);
  1437. } else {
  1438. ZVAL_DOUBLE(&casted, 1.0);
  1439. }
  1440. } else {
  1441. return 1;
  1442. }
  1443. }
  1444. int ret = zend_compare(&casted, o2);
  1445. zval_ptr_dtor(&casted);
  1446. return ret;
  1447. } else {
  1448. ZEND_ASSERT(Z_TYPE_P(o2) == IS_OBJECT);
  1449. zend_uchar target_type = (Z_TYPE_P(o1) == IS_FALSE || Z_TYPE_P(o1) == IS_TRUE)
  1450. ? _IS_BOOL : Z_TYPE_P(o1);
  1451. if (Z_OBJ_HT_P(o2)->cast_object(Z_OBJ_P(o2), &casted, target_type) == FAILURE) {
  1452. // TODO: Less crazy.
  1453. if (target_type == IS_LONG || target_type == IS_DOUBLE) {
  1454. zend_error(E_NOTICE, "Object of class %s could not be converted to %s",
  1455. ZSTR_VAL(Z_OBJCE_P(o2)->name), zend_get_type_by_const(target_type));
  1456. if (target_type == IS_LONG) {
  1457. ZVAL_LONG(&casted, 1);
  1458. } else {
  1459. ZVAL_DOUBLE(&casted, 1.0);
  1460. }
  1461. } else {
  1462. return -1;
  1463. }
  1464. }
  1465. int ret = zend_compare(o1, &casted);
  1466. zval_ptr_dtor(&casted);
  1467. return ret;
  1468. }
  1469. return ZEND_UNCOMPARABLE;
  1470. }
  1471. zobj1 = Z_OBJ_P(o1);
  1472. zobj2 = Z_OBJ_P(o2);
  1473. if (zobj1 == zobj2) {
  1474. return 0; /* the same object */
  1475. }
  1476. if (zobj1->ce != zobj2->ce) {
  1477. return ZEND_UNCOMPARABLE; /* different classes */
  1478. }
  1479. if (!zobj1->properties && !zobj2->properties) {
  1480. zend_property_info *info;
  1481. int i;
  1482. if (!zobj1->ce->default_properties_count) {
  1483. return 0;
  1484. }
  1485. /* It's enough to protect only one of the objects.
  1486. * The second one may be referenced from the first and this may cause
  1487. * false recursion detection.
  1488. */
  1489. /* use bitwise OR to make only one conditional jump */
  1490. if (UNEXPECTED(Z_IS_RECURSIVE_P(o1))) {
  1491. zend_error_noreturn(E_ERROR, "Nesting level too deep - recursive dependency?");
  1492. }
  1493. Z_PROTECT_RECURSION_P(o1);
  1494. for (i = 0; i < zobj1->ce->default_properties_count; i++) {
  1495. zval *p1, *p2;
  1496. info = zobj1->ce->properties_info_table[i];
  1497. if (!info) {
  1498. continue;
  1499. }
  1500. p1 = OBJ_PROP(zobj1, info->offset);
  1501. p2 = OBJ_PROP(zobj2, info->offset);
  1502. if (Z_TYPE_P(p1) != IS_UNDEF) {
  1503. if (Z_TYPE_P(p2) != IS_UNDEF) {
  1504. int ret;
  1505. ret = zend_compare(p1, p2);
  1506. if (ret != 0) {
  1507. Z_UNPROTECT_RECURSION_P(o1);
  1508. return ret;
  1509. }
  1510. } else {
  1511. Z_UNPROTECT_RECURSION_P(o1);
  1512. return 1;
  1513. }
  1514. } else {
  1515. if (Z_TYPE_P(p2) != IS_UNDEF) {
  1516. Z_UNPROTECT_RECURSION_P(o1);
  1517. return 1;
  1518. }
  1519. }
  1520. }
  1521. Z_UNPROTECT_RECURSION_P(o1);
  1522. return 0;
  1523. } else {
  1524. if (!zobj1->properties) {
  1525. rebuild_object_properties(zobj1);
  1526. }
  1527. if (!zobj2->properties) {
  1528. rebuild_object_properties(zobj2);
  1529. }
  1530. return zend_compare_symbol_tables(zobj1->properties, zobj2->properties);
  1531. }
  1532. }
  1533. /* }}} */
  1534. ZEND_API int zend_objects_not_comparable(zval *o1, zval *o2)
  1535. {
  1536. return ZEND_UNCOMPARABLE;
  1537. }
  1538. ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has_set_exists, void **cache_slot) /* {{{ */
  1539. {
  1540. int result;
  1541. zval *value = NULL;
  1542. uintptr_t property_offset;
  1543. zend_property_info *prop_info = NULL;
  1544. zend_string *tmp_name = NULL;
  1545. property_offset = zend_get_property_offset(zobj->ce, name, 1, cache_slot, &prop_info);
  1546. if (EXPECTED(IS_VALID_PROPERTY_OFFSET(property_offset))) {
  1547. value = OBJ_PROP(zobj, property_offset);
  1548. if (Z_TYPE_P(value) != IS_UNDEF) {
  1549. goto found;
  1550. }
  1551. if (UNEXPECTED(Z_PROP_FLAG_P(value) == IS_PROP_UNINIT)) {
  1552. /* Skip __isset() for uninitialized typed properties */
  1553. result = 0;
  1554. goto exit;
  1555. }
  1556. } else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
  1557. if (EXPECTED(zobj->properties != NULL)) {
  1558. if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
  1559. uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
  1560. if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
  1561. Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
  1562. if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) &&
  1563. (EXPECTED(p->key == name) ||
  1564. (EXPECTED(p->h == ZSTR_H(name)) &&
  1565. EXPECTED(p->key != NULL) &&
  1566. EXPECTED(zend_string_equal_content(p->key, name))))) {
  1567. value = &p->val;
  1568. goto found;
  1569. }
  1570. }
  1571. CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
  1572. }
  1573. value = zend_hash_find(zobj->properties, name);
  1574. if (value) {
  1575. if (cache_slot) {
  1576. uintptr_t idx = (char*)value - (char*)zobj->properties->arData;
  1577. CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
  1578. }
  1579. found:
  1580. if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) {
  1581. result = zend_is_true(value);
  1582. } else if (has_set_exists < ZEND_PROPERTY_NOT_EMPTY) {
  1583. ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_ISSET);
  1584. ZVAL_DEREF(value);
  1585. result = (Z_TYPE_P(value) != IS_NULL);
  1586. } else {
  1587. ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_EXISTS);
  1588. result = 1;
  1589. }
  1590. goto exit;
  1591. }
  1592. }
  1593. } else if (UNEXPECTED(EG(exception))) {
  1594. result = 0;
  1595. goto exit;
  1596. }
  1597. result = 0;
  1598. if ((has_set_exists != ZEND_PROPERTY_EXISTS) && zobj->ce->__isset) {
  1599. uint32_t *guard = zend_get_property_guard(zobj, name);
  1600. if (!((*guard) & IN_ISSET)) {
  1601. zval rv;
  1602. /* have issetter - try with it! */
  1603. if (!tmp_name && !ZSTR_IS_INTERNED(name)) {
  1604. tmp_name = zend_string_copy(name);
  1605. }
  1606. GC_ADDREF(zobj);
  1607. (*guard) |= IN_ISSET; /* prevent circular getting */
  1608. zend_std_call_issetter(zobj, name, &rv);
  1609. result = zend_is_true(&rv);
  1610. zval_ptr_dtor(&rv);
  1611. if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY && result) {
  1612. if (EXPECTED(!EG(exception)) && zobj->ce->__get && !((*guard) & IN_GET)) {
  1613. (*guard) |= IN_GET;
  1614. zend_std_call_getter(zobj, name, &rv);
  1615. (*guard) &= ~IN_GET;
  1616. result = i_zend_is_true(&rv);
  1617. zval_ptr_dtor(&rv);
  1618. } else {
  1619. result = 0;
  1620. }
  1621. }
  1622. (*guard) &= ~IN_ISSET;
  1623. OBJ_RELEASE(zobj);
  1624. }
  1625. }
  1626. exit:
  1627. zend_tmp_string_release(tmp_name);
  1628. return result;
  1629. }
  1630. /* }}} */
  1631. ZEND_API zend_string *zend_std_get_class_name(const zend_object *zobj) /* {{{ */
  1632. {
  1633. return zend_string_copy(zobj->ce->name);
  1634. }
  1635. /* }}} */
  1636. ZEND_API int zend_std_cast_object_tostring(zend_object *readobj, zval *writeobj, int type) /* {{{ */
  1637. {
  1638. switch (type) {
  1639. case IS_STRING: {
  1640. zend_class_entry *ce = readobj->ce;
  1641. if (ce->__tostring) {
  1642. zval retval;
  1643. GC_ADDREF(readobj);
  1644. zend_call_known_instance_method_with_0_params(ce->__tostring, readobj, &retval);
  1645. zend_object_release(readobj);
  1646. if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
  1647. ZVAL_COPY_VALUE(writeobj, &retval);
  1648. return SUCCESS;
  1649. }
  1650. zval_ptr_dtor(&retval);
  1651. if (!EG(exception)) {
  1652. zend_throw_error(NULL, "Method %s::__toString() must return a string value", ZSTR_VAL(ce->name));
  1653. }
  1654. }
  1655. return FAILURE;
  1656. }
  1657. case _IS_BOOL:
  1658. ZVAL_TRUE(writeobj);
  1659. return SUCCESS;
  1660. default:
  1661. return FAILURE;
  1662. }
  1663. }
  1664. /* }}} */
  1665. ZEND_API int zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
  1666. {
  1667. zend_class_entry *ce = obj->ce;
  1668. zval *func = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE));
  1669. if (func == NULL) {
  1670. return FAILURE;
  1671. }
  1672. *fptr_ptr = Z_FUNC_P(func);
  1673. *ce_ptr = ce;
  1674. if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) {
  1675. if (obj_ptr) {
  1676. *obj_ptr = NULL;
  1677. }
  1678. } else {
  1679. if (obj_ptr) {
  1680. *obj_ptr = obj;
  1681. }
  1682. }
  1683. return SUCCESS;
  1684. }
  1685. /* }}} */
  1686. ZEND_API HashTable *zend_std_get_properties_for(zend_object *obj, zend_prop_purpose purpose) {
  1687. HashTable *ht;
  1688. switch (purpose) {
  1689. case ZEND_PROP_PURPOSE_DEBUG:
  1690. if (obj->handlers->get_debug_info) {
  1691. int is_temp;
  1692. ht = obj->handlers->get_debug_info(obj, &is_temp);
  1693. if (ht && !is_temp) {
  1694. GC_TRY_ADDREF(ht);
  1695. }
  1696. return ht;
  1697. }
  1698. ZEND_FALLTHROUGH;
  1699. case ZEND_PROP_PURPOSE_ARRAY_CAST:
  1700. case ZEND_PROP_PURPOSE_SERIALIZE:
  1701. case ZEND_PROP_PURPOSE_VAR_EXPORT:
  1702. case ZEND_PROP_PURPOSE_JSON:
  1703. ht = obj->handlers->get_properties(obj);
  1704. if (ht) {
  1705. GC_TRY_ADDREF(ht);
  1706. }
  1707. return ht;
  1708. default:
  1709. ZEND_UNREACHABLE();
  1710. return NULL;
  1711. }
  1712. }
  1713. ZEND_API HashTable *zend_get_properties_for(zval *obj, zend_prop_purpose purpose) {
  1714. zend_object *zobj = Z_OBJ_P(obj);
  1715. if (zobj->handlers->get_properties_for) {
  1716. return zobj->handlers->get_properties_for(zobj, purpose);
  1717. }
  1718. return zend_std_get_properties_for(zobj, purpose);
  1719. }
  1720. ZEND_API const zend_object_handlers std_object_handlers = {
  1721. 0, /* offset */
  1722. zend_object_std_dtor, /* free_obj */
  1723. zend_objects_destroy_object, /* dtor_obj */
  1724. zend_objects_clone_obj, /* clone_obj */
  1725. zend_std_read_property, /* read_property */
  1726. zend_std_write_property, /* write_property */
  1727. zend_std_read_dimension, /* read_dimension */
  1728. zend_std_write_dimension, /* write_dimension */
  1729. zend_std_get_property_ptr_ptr, /* get_property_ptr_ptr */
  1730. zend_std_has_property, /* has_property */
  1731. zend_std_unset_property, /* unset_property */
  1732. zend_std_has_dimension, /* has_dimension */
  1733. zend_std_unset_dimension, /* unset_dimension */
  1734. zend_std_get_properties, /* get_properties */
  1735. zend_std_get_method, /* get_method */
  1736. zend_std_get_constructor, /* get_constructor */
  1737. zend_std_get_class_name, /* get_class_name */
  1738. zend_std_cast_object_tostring, /* cast_object */
  1739. NULL, /* count_elements */
  1740. zend_std_get_debug_info, /* get_debug_info */
  1741. zend_std_get_closure, /* get_closure */
  1742. zend_std_get_gc, /* get_gc */
  1743. NULL, /* do_operation */
  1744. zend_std_compare_objects, /* compare */
  1745. NULL, /* get_properties_for */
  1746. };