You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1939 lines
66 KiB

23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
23 years ago
18 years ago
23 years ago
21 years ago
21 years ago
22 years ago
22 years ago
22 years ago
21 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
21 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
21 years ago
21 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
21 years ago
21 years ago
21 years ago
23 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
22 years ago
21 years ago
22 years ago
19 years ago
22 years ago
22 years ago
22 years ago
20 years ago
20 years ago
20 years ago
21 years ago
22 years ago
21 years ago
22 years ago
18 years ago
21 years ago
21 years ago
19 years ago
19 years ago
22 years ago
22 years ago
23 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 6 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2009 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Marcus Boerger <helly@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id$ */
  19. #ifdef HAVE_CONFIG_H
  20. # include "config.h"
  21. #endif
  22. #include "php.h"
  23. #include "php_ini.h"
  24. #include "ext/standard/info.h"
  25. #include "ext/standard/php_var.h"
  26. #include "ext/standard/php_smart_str.h"
  27. #include "zend_interfaces.h"
  28. #include "zend_API.h"
  29. #include "zend_exceptions.h"
  30. #include "php_spl.h"
  31. #include "spl_functions.h"
  32. #include "spl_engine.h"
  33. #include "spl_iterators.h"
  34. #include "spl_array.h"
  35. #include "spl_exceptions.h"
  36. zend_object_handlers spl_handler_ArrayObject;
  37. PHPAPI zend_class_entry *spl_ce_ArrayObject;
  38. zend_object_handlers spl_handler_ArrayIterator;
  39. PHPAPI zend_class_entry *spl_ce_ArrayIterator;
  40. PHPAPI zend_class_entry *spl_ce_RecursiveArrayIterator;
  41. #define SPL_ARRAY_STD_PROP_LIST 0x00000001
  42. #define SPL_ARRAY_ARRAY_AS_PROPS 0x00000002
  43. #define SPL_ARRAY_CHILD_ARRAYS_ONLY 0x00000004
  44. #define SPL_ARRAY_OVERLOADED_REWIND 0x00010000
  45. #define SPL_ARRAY_OVERLOADED_VALID 0x00020000
  46. #define SPL_ARRAY_OVERLOADED_KEY 0x00040000
  47. #define SPL_ARRAY_OVERLOADED_CURRENT 0x00080000
  48. #define SPL_ARRAY_OVERLOADED_NEXT 0x00100000
  49. #define SPL_ARRAY_IS_REF 0x01000000
  50. #define SPL_ARRAY_IS_SELF 0x02000000
  51. #define SPL_ARRAY_USE_OTHER 0x04000000
  52. #define SPL_ARRAY_INT_MASK 0xFFFF0000
  53. #define SPL_ARRAY_CLONE_MASK 0x0300FFFF
  54. typedef struct _spl_array_object {
  55. zend_object std;
  56. zval *array;
  57. zval *retval;
  58. HashPosition pos;
  59. ulong pos_h;
  60. int ar_flags;
  61. int is_self;
  62. zend_function *fptr_offset_get;
  63. zend_function *fptr_offset_set;
  64. zend_function *fptr_offset_has;
  65. zend_function *fptr_offset_del;
  66. zend_function *fptr_count;
  67. zend_function *fptr_serialize;
  68. zend_function *fptr_unserialize;
  69. zend_class_entry *ce_get_iterator;
  70. php_serialize_data_t *serialize_data;
  71. php_unserialize_data_t *unserialize_data;
  72. } spl_array_object;
  73. static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { /* {{{ */
  74. if ((intern->ar_flags & SPL_ARRAY_IS_SELF) != 0) {
  75. return intern->std.properties;
  76. } else if ((intern->ar_flags & SPL_ARRAY_USE_OTHER) && (check_std_props == 0 || (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) == 0) && Z_TYPE_P(intern->array) == IS_OBJECT) {
  77. spl_array_object *other = (spl_array_object*)zend_object_store_get_object(intern->array TSRMLS_CC);
  78. return spl_array_get_hash_table(other, check_std_props TSRMLS_CC);
  79. } else if ((intern->ar_flags & ((check_std_props ? SPL_ARRAY_STD_PROP_LIST : 0) | SPL_ARRAY_IS_SELF)) != 0) {
  80. return intern->std.properties;
  81. } else {
  82. return HASH_OF(intern->array);
  83. }
  84. } /* }}} */
  85. static void spl_array_rewind(spl_array_object *intern TSRMLS_DC);
  86. static void spl_array_update_pos(spl_array_object* intern) /* {{{ */
  87. {
  88. Bucket *pos = intern->pos;
  89. if (pos != NULL) {
  90. intern->pos_h = pos->h;
  91. }
  92. } /* }}} */
  93. static void spl_array_set_pos(spl_array_object* intern, HashPosition pos) /* {{{ */
  94. {
  95. intern->pos = pos;
  96. spl_array_update_pos(intern);
  97. } /* }}} */
  98. SPL_API int spl_hash_verify_pos_ex(spl_array_object * intern, HashTable * ht TSRMLS_DC) /* {{{ */
  99. {
  100. Bucket *p;
  101. /* IS_CONSISTENT(ht);*/
  102. /* HASH_PROTECT_RECURSION(ht);*/
  103. p = ht->arBuckets[intern->pos_h & ht->nTableMask];
  104. while (p != NULL) {
  105. if (p == intern->pos) {
  106. return SUCCESS;
  107. }
  108. p = p->pNext;
  109. }
  110. /* HASH_UNPROTECT_RECURSION(ht); */
  111. spl_array_rewind(intern TSRMLS_CC);
  112. return FAILURE;
  113. } /* }}} */
  114. SPL_API int spl_hash_verify_pos(spl_array_object * intern TSRMLS_DC) /* {{{ */
  115. {
  116. HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  117. return spl_hash_verify_pos_ex(intern, ht TSRMLS_CC);
  118. }
  119. /* }}} */
  120. /* {{{ spl_array_object_free_storage */
  121. static void spl_array_object_free_storage(void *object TSRMLS_DC)
  122. {
  123. spl_array_object *intern = (spl_array_object *)object;
  124. zend_object_std_dtor(&intern->std TSRMLS_CC);
  125. zval_ptr_dtor(&intern->array);
  126. zval_ptr_dtor(&intern->retval);
  127. efree(object);
  128. }
  129. /* }}} */
  130. zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC);
  131. int spl_array_serialize(zval *object, int *type, zstr *buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC);
  132. int spl_array_unserialize(zval **object, zend_class_entry *ce, int type, const zstr buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
  133. /* {{{ spl_array_object_new_ex */
  134. static zend_object_value spl_array_object_new_ex(zend_class_entry *class_type, spl_array_object **obj, zval *orig, int clone_orig TSRMLS_DC)
  135. {
  136. zend_object_value retval;
  137. spl_array_object *intern;
  138. zval *tmp;
  139. zend_class_entry * parent = class_type;
  140. int inherited = 0;
  141. intern = emalloc(sizeof(spl_array_object));
  142. memset(intern, 0, sizeof(spl_array_object));
  143. *obj = intern;
  144. ALLOC_INIT_ZVAL(intern->retval);
  145. zend_object_std_init(&intern->std, class_type TSRMLS_CC);
  146. zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
  147. intern->ar_flags = 0;
  148. intern->serialize_data = NULL;
  149. intern->unserialize_data = NULL;
  150. intern->ce_get_iterator = spl_ce_ArrayIterator;
  151. if (orig) {
  152. spl_array_object *other = (spl_array_object*)zend_object_store_get_object(orig TSRMLS_CC);
  153. intern->ar_flags &= ~ SPL_ARRAY_CLONE_MASK;
  154. intern->ar_flags |= (other->ar_flags & SPL_ARRAY_CLONE_MASK);
  155. intern->ce_get_iterator = other->ce_get_iterator;
  156. if (clone_orig) {
  157. intern->array = other->array;
  158. if (Z_OBJ_HT_P(orig) == &spl_handler_ArrayObject) {
  159. MAKE_STD_ZVAL(intern->array);
  160. array_init(intern->array);
  161. zend_hash_copy(HASH_OF(intern->array), HASH_OF(other->array), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
  162. }
  163. if (Z_OBJ_HT_P(orig) == &spl_handler_ArrayIterator) {
  164. Z_ADDREF_P(other->array);
  165. }
  166. } else {
  167. intern->array = orig;
  168. Z_ADDREF_P(intern->array);
  169. intern->ar_flags |= SPL_ARRAY_IS_REF | SPL_ARRAY_USE_OTHER;
  170. }
  171. } else {
  172. MAKE_STD_ZVAL(intern->array);
  173. array_init(intern->array);
  174. intern->ar_flags &= ~SPL_ARRAY_IS_REF;
  175. }
  176. retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) spl_array_object_free_storage, NULL TSRMLS_CC);
  177. while (parent) {
  178. if (parent == spl_ce_ArrayIterator || parent == spl_ce_RecursiveArrayIterator) {
  179. retval.handlers = &spl_handler_ArrayIterator;
  180. class_type->get_iterator = spl_array_get_iterator;
  181. break;
  182. } else if (parent == spl_ce_ArrayObject) {
  183. retval.handlers = &spl_handler_ArrayObject;
  184. break;
  185. }
  186. parent = parent->parent;
  187. inherited = 1;
  188. }
  189. if (!parent) { /* this must never happen */
  190. php_error_docref(NULL TSRMLS_CC, E_COMPILE_ERROR, "Internal compiler error, Class is not child of ArrayObject or ArrayIterator");
  191. }
  192. if (inherited) {
  193. zend_hash_find(&class_type->function_table, "offsetget", sizeof("offsetget"), (void **) &intern->fptr_offset_get);
  194. if (intern->fptr_offset_get->common.scope == parent) {
  195. intern->fptr_offset_get = NULL;
  196. }
  197. zend_hash_find(&class_type->function_table, "offsetset", sizeof("offsetset"), (void **) &intern->fptr_offset_set);
  198. if (intern->fptr_offset_set->common.scope == parent) {
  199. intern->fptr_offset_set = NULL;
  200. }
  201. zend_hash_find(&class_type->function_table, "offsetexists", sizeof("offsetexists"), (void **) &intern->fptr_offset_has);
  202. if (intern->fptr_offset_has->common.scope == parent) {
  203. intern->fptr_offset_has = NULL;
  204. }
  205. zend_hash_find(&class_type->function_table, "offsetunset", sizeof("offsetunset"), (void **) &intern->fptr_offset_del);
  206. if (intern->fptr_offset_del->common.scope == parent) {
  207. intern->fptr_offset_del = NULL;
  208. }
  209. zend_hash_find(&class_type->function_table, "count", sizeof("count"), (void **) &intern->fptr_count);
  210. if (intern->fptr_count->common.scope == parent) {
  211. intern->fptr_count = NULL;
  212. }
  213. zend_hash_find(&class_type->function_table, "serialize", sizeof("serialize"), (void **) &intern->fptr_serialize);
  214. if (intern->fptr_serialize->common.scope == parent) {
  215. intern->fptr_serialize = NULL;
  216. }
  217. zend_hash_find(&class_type->function_table, "unserialize", sizeof("unserialize"), (void **) &intern->fptr_unserialize);
  218. if (intern->fptr_unserialize->common.scope == parent) {
  219. intern->fptr_unserialize = NULL;
  220. }
  221. }
  222. /* Cache iterator functions if ArrayIterator or derived. Check current's */
  223. /* cache since only current is always required */
  224. if (retval.handlers == &spl_handler_ArrayIterator) {
  225. if (!class_type->iterator_funcs.zf_current) {
  226. zend_hash_find(&class_type->function_table, "rewind", sizeof("rewind"), (void **) &class_type->iterator_funcs.zf_rewind);
  227. zend_hash_find(&class_type->function_table, "valid", sizeof("valid"), (void **) &class_type->iterator_funcs.zf_valid);
  228. zend_hash_find(&class_type->function_table, "key", sizeof("key"), (void **) &class_type->iterator_funcs.zf_key);
  229. zend_hash_find(&class_type->function_table, "current", sizeof("current"), (void **) &class_type->iterator_funcs.zf_current);
  230. zend_hash_find(&class_type->function_table, "next", sizeof("next"), (void **) &class_type->iterator_funcs.zf_next);
  231. }
  232. if (inherited) {
  233. if (class_type->iterator_funcs.zf_rewind->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_REWIND;
  234. if (class_type->iterator_funcs.zf_valid->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_VALID;
  235. if (class_type->iterator_funcs.zf_key->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_KEY;
  236. if (class_type->iterator_funcs.zf_current->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_CURRENT;
  237. if (class_type->iterator_funcs.zf_next->common.scope != parent) intern->ar_flags |= SPL_ARRAY_OVERLOADED_NEXT;
  238. }
  239. }
  240. spl_array_rewind(intern TSRMLS_CC);
  241. return retval;
  242. }
  243. /* }}} */
  244. /* {{{ spl_array_object_new */
  245. static zend_object_value spl_array_object_new(zend_class_entry *class_type TSRMLS_DC)
  246. {
  247. spl_array_object *tmp;
  248. return spl_array_object_new_ex(class_type, &tmp, NULL, 0 TSRMLS_CC);
  249. }
  250. /* }}} */
  251. /* {{{ spl_array_object_clone */
  252. static zend_object_value spl_array_object_clone(zval *zobject TSRMLS_DC)
  253. {
  254. zend_object_value new_obj_val;
  255. zend_object *old_object;
  256. zend_object *new_object;
  257. zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
  258. spl_array_object *intern;
  259. old_object = zend_objects_get_address(zobject TSRMLS_CC);
  260. new_obj_val = spl_array_object_new_ex(old_object->ce, &intern, zobject, 1 TSRMLS_CC);
  261. new_object = &intern->std;
  262. zend_objects_clone_members(new_object, new_obj_val, old_object, handle TSRMLS_CC);
  263. return new_obj_val;
  264. }
  265. /* }}} */
  266. static zval **spl_array_get_dimension_ptr_ptr(int check_inherited, zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
  267. {
  268. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  269. zval **retval;
  270. long index;
  271. HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  272. /* We cannot get the pointer pointer so we don't allow it here for now
  273. if (check_inherited && intern->fptr_offset_get) {
  274. return zend_call_method_with_1_params(&object, intern->std.ce, &intern->fptr_offset_get, "offsetGet", NULL, offset);
  275. }*/
  276. if (!offset) {
  277. return &EG(uninitialized_zval_ptr);
  278. }
  279. switch(Z_TYPE_P(offset)) {
  280. case IS_STRING:
  281. case IS_UNICODE:
  282. if (zend_u_symtable_find(ht, Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1, (void **) &retval) == FAILURE) {
  283. if (type == BP_VAR_W || type == BP_VAR_RW) {
  284. zval *value;
  285. ALLOC_INIT_ZVAL(value);
  286. zend_u_symtable_update(ht, Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1, (void**)&value, sizeof(void*), NULL);
  287. zend_u_symtable_find(ht, Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1, (void **) &retval);
  288. return retval;
  289. } else {
  290. zend_error(E_NOTICE, "Undefined index: %R", Z_TYPE_P(offset), Z_STRVAL_P(offset));
  291. return &EG(uninitialized_zval_ptr);
  292. }
  293. } else {
  294. return retval;
  295. }
  296. case IS_DOUBLE:
  297. case IS_RESOURCE:
  298. case IS_BOOL:
  299. case IS_LONG:
  300. if (offset->type == IS_DOUBLE) {
  301. index = (long)Z_DVAL_P(offset);
  302. } else {
  303. index = Z_LVAL_P(offset);
  304. }
  305. if (zend_hash_index_find(ht, index, (void **) &retval) == FAILURE) {
  306. if (type == BP_VAR_W || type == BP_VAR_RW) {
  307. zval *value;
  308. ALLOC_INIT_ZVAL(value);
  309. zend_hash_index_update(ht, index, (void**)&value, sizeof(void*), NULL);
  310. zend_hash_index_find(ht, index, (void **) &retval);
  311. return retval;
  312. } else {
  313. zend_error(E_NOTICE, "Undefined offset: %ld", index);
  314. return &EG(uninitialized_zval_ptr);
  315. }
  316. } else {
  317. return retval;
  318. }
  319. break;
  320. default:
  321. zend_error(E_WARNING, "Illegal offset type");
  322. return &EG(uninitialized_zval_ptr);
  323. }
  324. } /* }}} */
  325. static zval *spl_array_read_dimension_ex(int check_inherited, zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
  326. {
  327. zval **ret;
  328. if (check_inherited) {
  329. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  330. if (intern->fptr_offset_get) {
  331. zval *rv;
  332. SEPARATE_ARG_IF_REF(offset);
  333. zend_call_method_with_1_params(&object, intern->std.ce, &intern->fptr_offset_get, "offsetGet", &rv, offset);
  334. zval_ptr_dtor(&offset);
  335. if (rv) {
  336. zval_ptr_dtor(&intern->retval);
  337. MAKE_STD_ZVAL(intern->retval);
  338. ZVAL_ZVAL(intern->retval, rv, 1, 1);
  339. return intern->retval;
  340. }
  341. return EG(uninitialized_zval_ptr);
  342. }
  343. }
  344. ret = spl_array_get_dimension_ptr_ptr(check_inherited, object, offset, type TSRMLS_CC);
  345. /* When in a write context,
  346. * ZE has to be fooled into thinking this is in a reference set
  347. * by separating (if necessary) and returning as an is_ref=1 zval (even if refcount == 1) */
  348. if ((type == BP_VAR_W || type == BP_VAR_RW) && !Z_ISREF_PP(ret)) {
  349. if (Z_REFCOUNT_PP(ret) > 1) {
  350. zval *newval;
  351. /* Separate */
  352. MAKE_STD_ZVAL(newval);
  353. *newval = **ret;
  354. zval_copy_ctor(newval);
  355. Z_SET_REFCOUNT_P(newval, 1);
  356. /* Replace */
  357. Z_DELREF_PP(ret);
  358. *ret = newval;
  359. }
  360. Z_SET_ISREF_PP(ret);
  361. }
  362. return *ret;
  363. } /* }}} */
  364. static zval *spl_array_read_dimension(zval *object, zval *offset, int type TSRMLS_DC) /* {{{ */
  365. {
  366. return spl_array_read_dimension_ex(1, object, offset, type TSRMLS_CC);
  367. } /* }}} */
  368. static void spl_array_write_dimension_ex(int check_inherited, zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
  369. {
  370. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  371. long index;
  372. if (check_inherited && intern->fptr_offset_set) {
  373. if (!offset) {
  374. ALLOC_INIT_ZVAL(offset);
  375. } else {
  376. SEPARATE_ARG_IF_REF(offset);
  377. }
  378. zend_call_method_with_2_params(&object, intern->std.ce, &intern->fptr_offset_set, "offsetSet", NULL, offset, value);
  379. zval_ptr_dtor(&offset);
  380. return;
  381. }
  382. if (!offset) {
  383. Z_ADDREF_P(value);
  384. zend_hash_next_index_insert(spl_array_get_hash_table(intern, 0 TSRMLS_CC), (void**)&value, sizeof(void*), NULL);
  385. return;
  386. }
  387. switch(Z_TYPE_P(offset)) {
  388. case IS_STRING:
  389. case IS_UNICODE:
  390. Z_ADDREF_P(value);
  391. zend_u_symtable_update(spl_array_get_hash_table(intern, 0 TSRMLS_CC), Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1, (void**)&value, sizeof(void*), NULL);
  392. return;
  393. case IS_DOUBLE:
  394. case IS_RESOURCE:
  395. case IS_BOOL:
  396. case IS_LONG:
  397. if (offset->type == IS_DOUBLE) {
  398. index = (long)Z_DVAL_P(offset);
  399. } else {
  400. index = Z_LVAL_P(offset);
  401. }
  402. Z_ADDREF_P(value);
  403. zend_hash_index_update(spl_array_get_hash_table(intern, 0 TSRMLS_CC), index, (void**)&value, sizeof(void*), NULL);
  404. return;
  405. case IS_NULL:
  406. Z_ADDREF_P(value);
  407. zend_hash_next_index_insert(spl_array_get_hash_table(intern, 0 TSRMLS_CC), (void**)&value, sizeof(void*), NULL);
  408. return;
  409. default:
  410. zend_error(E_WARNING, "Illegal offset type");
  411. return;
  412. }
  413. } /* }}} */
  414. static void spl_array_write_dimension(zval *object, zval *offset, zval *value TSRMLS_DC) /* {{{ */
  415. {
  416. spl_array_write_dimension_ex(1, object, offset, value TSRMLS_CC);
  417. } /* }}} */
  418. static void spl_array_unset_dimension_ex(int check_inherited, zval *object, zval *offset TSRMLS_DC) /* {{{ */
  419. {
  420. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  421. long index;
  422. if (check_inherited && intern->fptr_offset_del) {
  423. SEPARATE_ARG_IF_REF(offset);
  424. zend_call_method_with_1_params(&object, intern->std.ce, &intern->fptr_offset_del, "offsetUnset", NULL, offset);
  425. zval_ptr_dtor(&offset);
  426. return;
  427. }
  428. switch(Z_TYPE_P(offset)) {
  429. case IS_STRING:
  430. case IS_UNICODE:
  431. if (spl_array_get_hash_table(intern, 0 TSRMLS_CC) == &EG(symbol_table)) {
  432. if (zend_u_delete_global_variable(Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset) TSRMLS_CC)) {
  433. zend_error(E_NOTICE,"Undefined index: %s", Z_STRVAL_P(offset));
  434. }
  435. } else {
  436. if (zend_u_symtable_del(spl_array_get_hash_table(intern, 0 TSRMLS_CC), Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1) == FAILURE) {
  437. zend_error(E_NOTICE,"Undefined index: %R", Z_TYPE_P(offset), Z_UNIVAL_P(offset));
  438. }
  439. }
  440. break;
  441. case IS_DOUBLE:
  442. case IS_RESOURCE:
  443. case IS_BOOL:
  444. case IS_LONG:
  445. if (offset->type == IS_DOUBLE) {
  446. index = (long)Z_DVAL_P(offset);
  447. } else {
  448. index = Z_LVAL_P(offset);
  449. }
  450. if (zend_hash_index_del(spl_array_get_hash_table(intern, 0 TSRMLS_CC), index) == FAILURE) {
  451. zend_error(E_NOTICE,"Undefined offset: %ld", Z_LVAL_P(offset));
  452. }
  453. break;
  454. default:
  455. zend_error(E_WARNING, "Illegal offset type");
  456. return;
  457. }
  458. spl_hash_verify_pos(intern TSRMLS_CC); /* call rewind on FAILURE */
  459. } /* }}} */
  460. static void spl_array_unset_dimension(zval *object, zval *offset TSRMLS_DC) /* {{{ */
  461. {
  462. spl_array_unset_dimension_ex(1, object, offset TSRMLS_CC);
  463. } /* }}} */
  464. static int spl_array_has_dimension_ex(int check_inherited, zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
  465. {
  466. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  467. long index;
  468. zval *rv, **tmp;
  469. if (check_inherited && intern->fptr_offset_has) {
  470. SEPARATE_ARG_IF_REF(offset);
  471. zend_call_method_with_1_params(&object, intern->std.ce, &intern->fptr_offset_has, "offsetExists", &rv, offset);
  472. zval_ptr_dtor(&offset);
  473. if (rv && zend_is_true(rv)) {
  474. zval_ptr_dtor(&rv);
  475. return 1;
  476. }
  477. if (rv) {
  478. zval_ptr_dtor(&rv);
  479. }
  480. return 0;
  481. }
  482. switch(Z_TYPE_P(offset)) {
  483. case IS_STRING:
  484. case IS_UNICODE:
  485. if (check_empty) {
  486. if (zend_u_symtable_find(spl_array_get_hash_table(intern, 0 TSRMLS_CC), Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1, (void **) &tmp) != FAILURE && zend_is_true(*tmp)) {
  487. return 1;
  488. }
  489. return 0;
  490. } else {
  491. return zend_u_symtable_exists(spl_array_get_hash_table(intern, 0 TSRMLS_CC), Z_TYPE_P(offset), Z_UNIVAL_P(offset), Z_UNILEN_P(offset)+1);
  492. }
  493. case IS_DOUBLE:
  494. case IS_RESOURCE:
  495. case IS_BOOL:
  496. case IS_LONG:
  497. if (offset->type == IS_DOUBLE) {
  498. index = (long)Z_DVAL_P(offset);
  499. } else {
  500. index = Z_LVAL_P(offset);
  501. }
  502. if (check_empty) {
  503. HashTable *ht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  504. if (zend_hash_index_find(ht, index, (void **)&tmp) != FAILURE && zend_is_true(*tmp)) {
  505. return 1;
  506. }
  507. return 0;
  508. } else {
  509. return zend_hash_index_exists(spl_array_get_hash_table(intern, 0 TSRMLS_CC), index);
  510. }
  511. default:
  512. zend_error(E_WARNING, "Illegal offset type");
  513. }
  514. return 0;
  515. } /* }}} */
  516. static int spl_array_has_dimension(zval *object, zval *offset, int check_empty TSRMLS_DC) /* {{{ */
  517. {
  518. return spl_array_has_dimension_ex(1, object, offset, check_empty TSRMLS_CC);
  519. } /* }}} */
  520. /* {{{ proto bool ArrayObject::offsetExists(mixed $index) U
  521. proto bool ArrayIterator::offsetExists(mixed $index) U
  522. Returns whether the requested $index exists. */
  523. SPL_METHOD(Array, offsetExists)
  524. {
  525. zval *index;
  526. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
  527. return;
  528. }
  529. RETURN_BOOL(spl_array_has_dimension_ex(0, getThis(), index, 0 TSRMLS_CC));
  530. } /* }}} */
  531. /* {{{ proto mixed ArrayObject::offsetGet(mixed $index) U
  532. proto mixed ArrayIterator::offsetGet(mixed $index) U
  533. Returns the value at the specified $index. */
  534. SPL_METHOD(Array, offsetGet)
  535. {
  536. zval *index, *value;
  537. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
  538. return;
  539. }
  540. value = spl_array_read_dimension_ex(0, getThis(), index, BP_VAR_R TSRMLS_CC);
  541. RETURN_ZVAL(value, 1, 0);
  542. } /* }}} */
  543. /* {{{ proto void ArrayObject::offsetSet(mixed $index, mixed $newval) U
  544. proto void ArrayIterator::offsetSet(mixed $index, mixed $newval) U
  545. Sets the value at the specified $index to $newval. */
  546. SPL_METHOD(Array, offsetSet)
  547. {
  548. zval *index, *value;
  549. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) == FAILURE) {
  550. return;
  551. }
  552. spl_array_write_dimension_ex(0, getThis(), index, value TSRMLS_CC);
  553. } /* }}} */
  554. void spl_array_iterator_append(zval *object, zval *append_value TSRMLS_DC) /* {{{ */
  555. {
  556. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  557. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  558. if (!aht) {
  559. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  560. return;
  561. }
  562. if (Z_TYPE_P(intern->array) == IS_OBJECT) {
  563. php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Cannot append properties to objects, use %v::offsetSet() instead", intern->std.ce->name);
  564. return;
  565. }
  566. spl_array_write_dimension(object, NULL, append_value TSRMLS_CC);
  567. if (!intern->pos) {
  568. spl_array_set_pos(intern, aht->pListTail);
  569. }
  570. } /* }}} */
  571. /* {{{ proto void ArrayObject::append(mixed $newval) U
  572. proto void ArrayIterator::append(mixed $newval) U
  573. Appends the value (cannot be called for objects). */
  574. SPL_METHOD(Array, append)
  575. {
  576. zval *value;
  577. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  578. return;
  579. }
  580. spl_array_iterator_append(getThis(), value TSRMLS_CC);
  581. } /* }}} */
  582. /* {{{ proto void ArrayObject::offsetUnset(mixed $index) U
  583. proto void ArrayIterator::offsetUnset(mixed $index) U
  584. Unsets the value at the specified $index. */
  585. SPL_METHOD(Array, offsetUnset)
  586. {
  587. zval *index;
  588. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) == FAILURE) {
  589. return;
  590. }
  591. spl_array_unset_dimension_ex(0, getThis(), index TSRMLS_CC);
  592. } /* }}} */
  593. /* {{{ proto array ArrayObject::getArrayCopy() U
  594. proto array ArrayIterator::getArrayCopy() U
  595. Return a copy of the contained array */
  596. SPL_METHOD(Array, getArrayCopy)
  597. {
  598. zval *object = getThis(), *tmp;
  599. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  600. array_init(return_value);
  601. zend_hash_copy(HASH_OF(return_value), spl_array_get_hash_table(intern, 0 TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
  602. } /* }}} */
  603. static HashTable *spl_array_get_properties(zval *object TSRMLS_DC) /* {{{ */
  604. {
  605. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  606. return spl_array_get_hash_table(intern, 1 TSRMLS_CC);
  607. } /* }}} */
  608. static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
  609. {
  610. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(obj TSRMLS_CC);
  611. HashTable *rv;
  612. zval *tmp, *storage;
  613. int name_len;
  614. zstr zname;
  615. zend_class_entry *base;
  616. if (HASH_OF(intern->array) == intern->std.properties) {
  617. *is_temp = 0;
  618. return intern->std.properties;
  619. } else {
  620. *is_temp = 1;
  621. ALLOC_HASHTABLE(rv);
  622. ZEND_INIT_SYMTABLE_EX(rv, zend_hash_num_elements(intern->std.properties) + 1, 0);
  623. zend_hash_copy(rv, intern->std.properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
  624. storage = intern->array;
  625. zval_add_ref(&storage);
  626. base = (Z_OBJ_HT_P(obj) == &spl_handler_ArrayIterator) ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
  627. zname = spl_gen_private_prop_name(base, "storage", sizeof("storage")-1, &name_len TSRMLS_CC);
  628. zend_u_symtable_update(rv, IS_UNICODE, zname, name_len+1, &storage, sizeof(zval *), NULL);
  629. efree(zname.v);
  630. return rv;
  631. }
  632. }
  633. /* }}} */
  634. static zval *spl_array_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */
  635. {
  636. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  637. if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
  638. && !std_object_handlers.has_property(object, member, 2 TSRMLS_CC)) {
  639. return spl_array_read_dimension(object, member, type TSRMLS_CC);
  640. }
  641. return std_object_handlers.read_property(object, member, type TSRMLS_CC);
  642. } /* }}} */
  643. static void spl_array_write_property(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
  644. {
  645. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  646. if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
  647. && !std_object_handlers.has_property(object, member, 2 TSRMLS_CC)) {
  648. spl_array_write_dimension(object, member, value TSRMLS_CC);
  649. return;
  650. }
  651. std_object_handlers.write_property(object, member, value TSRMLS_CC);
  652. } /* }}} */
  653. static zval **spl_array_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC) /* {{{ */
  654. {
  655. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  656. if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
  657. && !std_object_handlers.has_property(object, member, 2 TSRMLS_CC)) {
  658. return spl_array_get_dimension_ptr_ptr(1, object, member, 0 TSRMLS_CC);
  659. }
  660. return std_object_handlers.get_property_ptr_ptr(object, member TSRMLS_CC);
  661. } /* }}} */
  662. static int spl_array_has_property(zval *object, zval *member, int has_set_exists TSRMLS_DC) /* {{{ */
  663. {
  664. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  665. if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
  666. && !std_object_handlers.has_property(object, member, 2 TSRMLS_CC)) {
  667. return spl_array_has_dimension(object, member, has_set_exists TSRMLS_CC);
  668. }
  669. return std_object_handlers.has_property(object, member, has_set_exists TSRMLS_CC);
  670. } /* }}} */
  671. static void spl_array_unset_property(zval *object, zval *member TSRMLS_DC) /* {{{ */
  672. {
  673. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  674. if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
  675. && !std_object_handlers.has_property(object, member, 2 TSRMLS_CC)) {
  676. spl_array_unset_dimension(object, member TSRMLS_CC);
  677. spl_array_rewind(intern TSRMLS_CC); /* because deletion might invalidate position */
  678. return;
  679. }
  680. std_object_handlers.unset_property(object, member TSRMLS_CC);
  681. } /* }}} */
  682. static int spl_array_skip_protected(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
  683. {
  684. zstr string_key;
  685. uint string_length;
  686. ulong num_key;
  687. if (Z_TYPE_P(intern->array) == IS_OBJECT) {
  688. do {
  689. if (zend_hash_get_current_key_ex(aht, &string_key, &string_length, &num_key, 0, &intern->pos) == HASH_KEY_IS_UNICODE) {
  690. if (!string_length || string_key.u[0]) {
  691. return SUCCESS;
  692. }
  693. } else {
  694. return SUCCESS;
  695. }
  696. if (zend_hash_has_more_elements_ex(aht, &intern->pos) != SUCCESS) {
  697. return FAILURE;
  698. }
  699. zend_hash_move_forward_ex(aht, &intern->pos);
  700. spl_array_update_pos(intern);
  701. } while (1);
  702. }
  703. return FAILURE;
  704. } /* }}} */
  705. static int spl_array_next_no_verify(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
  706. {
  707. zend_hash_move_forward_ex(aht, &intern->pos);
  708. spl_array_update_pos(intern);
  709. if (Z_TYPE_P(intern->array) == IS_OBJECT) {
  710. return spl_array_skip_protected(intern, aht TSRMLS_CC);
  711. } else {
  712. return zend_hash_has_more_elements_ex(aht, &intern->pos);
  713. }
  714. } /* }}} */
  715. static int spl_array_next_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
  716. {
  717. if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  718. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  719. return FAILURE;
  720. }
  721. return spl_array_next_no_verify(intern, aht TSRMLS_CC);
  722. } /* }}} */
  723. static int spl_array_next(spl_array_object *intern TSRMLS_DC) /* {{{ */
  724. {
  725. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  726. return spl_array_next_ex(intern, aht TSRMLS_CC);
  727. } /* }}} */
  728. /* {{{ define an overloaded iterator structure */
  729. typedef struct {
  730. zend_user_iterator intern;
  731. spl_array_object *object;
  732. } spl_array_it; /* }}} */
  733. static void spl_array_it_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
  734. {
  735. spl_array_it *iterator = (spl_array_it *)iter;
  736. zend_user_it_invalidate_current(iter TSRMLS_CC);
  737. zval_ptr_dtor((zval**)&iterator->intern.it.data);
  738. efree(iterator);
  739. }
  740. /* }}} */
  741. static int spl_array_it_valid(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
  742. {
  743. spl_array_it *iterator = (spl_array_it *)iter;
  744. spl_array_object *object = iterator->object;
  745. HashTable *aht = spl_array_get_hash_table(object, 0 TSRMLS_CC);
  746. if (object->ar_flags & SPL_ARRAY_OVERLOADED_VALID) {
  747. return zend_user_it_valid(iter TSRMLS_CC);
  748. } else {
  749. if (!aht) {
  750. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::valid(): Array was modified outside object and is no longer an array");
  751. return FAILURE;
  752. }
  753. if (object->pos && (object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, aht TSRMLS_CC) == FAILURE) {
  754. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::valid(): Array was modified outside object and internal position is no longer valid");
  755. return FAILURE;
  756. } else {
  757. return zend_hash_has_more_elements_ex(aht, &object->pos);
  758. }
  759. }
  760. }
  761. /* }}} */
  762. static void spl_array_it_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) /* {{{ */
  763. {
  764. spl_array_it *iterator = (spl_array_it *)iter;
  765. spl_array_object *object = iterator->object;
  766. HashTable *aht = spl_array_get_hash_table(object, 0 TSRMLS_CC);
  767. if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
  768. zend_user_it_get_current_data(iter, data TSRMLS_CC);
  769. } else {
  770. if (zend_hash_get_current_data_ex(aht, (void**)data, &object->pos) == FAILURE) {
  771. *data = NULL;
  772. }
  773. }
  774. }
  775. /* }}} */
  776. static int spl_array_it_get_current_key(zend_object_iterator *iter, zstr *str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */
  777. {
  778. spl_array_it *iterator = (spl_array_it *)iter;
  779. spl_array_object *object = iterator->object;
  780. HashTable *aht = spl_array_get_hash_table(object, 0 TSRMLS_CC);
  781. if (object->ar_flags & SPL_ARRAY_OVERLOADED_KEY) {
  782. return zend_user_it_get_current_key(iter, str_key, str_key_len, int_key TSRMLS_CC);
  783. } else {
  784. if (!aht) {
  785. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and is no longer an array");
  786. return HASH_KEY_NON_EXISTANT;
  787. }
  788. if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, aht TSRMLS_CC) == FAILURE) {
  789. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and internal position is no longer valid");
  790. return HASH_KEY_NON_EXISTANT;
  791. }
  792. return zend_hash_get_current_key_ex(aht, str_key, str_key_len, int_key, 1, &object->pos);
  793. }
  794. }
  795. /* }}} */
  796. static void spl_array_it_move_forward(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
  797. {
  798. spl_array_it *iterator = (spl_array_it *)iter;
  799. spl_array_object *object = iterator->object;
  800. HashTable *aht = spl_array_get_hash_table(object, 0 TSRMLS_CC);
  801. if (object->ar_flags & SPL_ARRAY_OVERLOADED_NEXT) {
  802. zend_user_it_move_forward(iter TSRMLS_CC);
  803. } else {
  804. zend_user_it_invalidate_current(iter TSRMLS_CC);
  805. if (!aht) {
  806. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::current(): Array was modified outside object and is no longer an array");
  807. return;
  808. }
  809. if ((object->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(object, aht TSRMLS_CC) == FAILURE) {
  810. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::next(): Array was modified outside object and internal position is no longer valid");
  811. } else {
  812. spl_array_next_no_verify(object, aht TSRMLS_CC);
  813. }
  814. }
  815. }
  816. /* }}} */
  817. static void spl_array_rewind_ex(spl_array_object *intern, HashTable *aht TSRMLS_DC) /* {{{ */
  818. {
  819. zend_hash_internal_pointer_reset_ex(aht, &intern->pos);
  820. spl_array_update_pos(intern);
  821. spl_array_skip_protected(intern, aht TSRMLS_CC);
  822. } /* }}} */
  823. static void spl_array_rewind(spl_array_object *intern TSRMLS_DC) /* {{{ */
  824. {
  825. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  826. if (!aht) {
  827. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "ArrayIterator::rewind(): Array was modified outside object and is no longer an array");
  828. return;
  829. }
  830. spl_array_rewind_ex(intern, aht TSRMLS_CC);
  831. }
  832. /* }}} */
  833. static void spl_array_it_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
  834. {
  835. spl_array_it *iterator = (spl_array_it *)iter;
  836. spl_array_object *object = iterator->object;
  837. if (object->ar_flags & SPL_ARRAY_OVERLOADED_REWIND) {
  838. zend_user_it_rewind(iter TSRMLS_CC);
  839. } else {
  840. zend_user_it_invalidate_current(iter TSRMLS_CC);
  841. spl_array_rewind(object TSRMLS_CC);
  842. }
  843. }
  844. /* }}} */
  845. /* {{{ spl_array_set_array */
  846. static void spl_array_set_array(zval *object, spl_array_object *intern, zval **array, long ar_flags, int just_array TSRMLS_DC) {
  847. if (Z_TYPE_PP(array) == IS_ARRAY) {
  848. SEPARATE_ZVAL_IF_NOT_REF(array);
  849. }
  850. if (Z_TYPE_PP(array) == IS_OBJECT && (Z_OBJ_HT_PP(array) == &spl_handler_ArrayObject || Z_OBJ_HT_PP(array) == &spl_handler_ArrayIterator)) {
  851. zval_ptr_dtor(&intern->array);
  852. if (just_array) {
  853. spl_array_object *other = (spl_array_object*)zend_object_store_get_object(*array TSRMLS_CC);
  854. ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;
  855. }
  856. ar_flags |= SPL_ARRAY_USE_OTHER;
  857. intern->array = *array;
  858. } else {
  859. if (Z_TYPE_PP(array) != IS_OBJECT && Z_TYPE_PP(array) != IS_ARRAY) {
  860. zend_throw_exception(spl_ce_InvalidArgumentException, "Passed variable is not an array or object, using empty array instead", 0 TSRMLS_CC);
  861. return;
  862. }
  863. zval_ptr_dtor(&intern->array);
  864. intern->array = *array;
  865. }
  866. if (object == *array) {
  867. intern->ar_flags |= SPL_ARRAY_IS_SELF;
  868. intern->ar_flags &= ~SPL_ARRAY_USE_OTHER;
  869. } else {
  870. intern->ar_flags &= ~SPL_ARRAY_IS_SELF;
  871. }
  872. intern->ar_flags |= ar_flags;
  873. Z_ADDREF_P(intern->array);
  874. if (Z_TYPE_PP(array) == IS_OBJECT) {
  875. zend_object_get_properties_t handler = Z_OBJ_HANDLER_PP(array, get_properties);
  876. if ((handler != std_object_handlers.get_properties && handler != spl_array_get_properties)
  877. || !spl_array_get_hash_table(intern, 0 TSRMLS_CC)) {
  878. zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Overloaded object of type %v is not compatible with %v", Z_OBJCE_PP(array)->name, intern->std.ce->name);
  879. }
  880. }
  881. spl_array_rewind(intern TSRMLS_CC);
  882. }
  883. /* }}} */
  884. /* {{{ iterator handler table */
  885. zend_object_iterator_funcs spl_array_it_funcs = {
  886. spl_array_it_dtor,
  887. spl_array_it_valid,
  888. spl_array_it_get_current_data,
  889. spl_array_it_get_current_key,
  890. spl_array_it_move_forward,
  891. spl_array_it_rewind
  892. }; /* }}} */
  893. zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
  894. {
  895. spl_array_it *iterator;
  896. spl_array_object *array_object = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  897. if (by_ref && (array_object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT)) {
  898. zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
  899. }
  900. iterator = emalloc(sizeof(spl_array_it));
  901. Z_ADDREF_P(object);
  902. iterator->intern.it.data = (void*)object;
  903. iterator->intern.it.funcs = &spl_array_it_funcs;
  904. iterator->intern.ce = ce;
  905. iterator->intern.value = NULL;
  906. iterator->object = array_object;
  907. return (zend_object_iterator*)iterator;
  908. }
  909. /* }}} */
  910. /* {{{ proto void ArrayObject::__construct(array|object ar = array() [, int flags = 0 [, string iterator_class = "ArrayIterator"]]) U
  911. proto void ArrayIterator::__construct(array|object ar = array() [, int flags = 0]) U
  912. Constructs a new array iterator from a path. */
  913. SPL_METHOD(Array, __construct)
  914. {
  915. zval *object = getThis();
  916. spl_array_object *intern;
  917. zval **array;
  918. long ar_flags = 0;
  919. zend_class_entry *ce_get_iterator = spl_ce_Iterator;
  920. zend_error_handling error_handling;
  921. if (ZEND_NUM_ARGS() == 0) {
  922. return; /* nothing to do */
  923. }
  924. zend_replace_error_handling(EH_THROW, spl_ce_InvalidArgumentException, &error_handling TSRMLS_CC);
  925. intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  926. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|lC", &array, &ar_flags, &ce_get_iterator) == FAILURE) {
  927. zend_restore_error_handling(&error_handling TSRMLS_CC);
  928. return;
  929. }
  930. if (ZEND_NUM_ARGS() > 2) {
  931. intern->ce_get_iterator = ce_get_iterator;
  932. }
  933. ar_flags &= ~SPL_ARRAY_INT_MASK;
  934. spl_array_set_array(object, intern, array, ar_flags, ZEND_NUM_ARGS() == 1 TSRMLS_CC);
  935. zend_restore_error_handling(&error_handling TSRMLS_CC);
  936. }
  937. /* }}} */
  938. /* {{{ proto void ArrayObject::setIteratorClass(string iterator_class) U
  939. Set the class used in getIterator. */
  940. SPL_METHOD(Array, setIteratorClass)
  941. {
  942. zval *object = getThis();
  943. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  944. zend_class_entry *ce_get_iterator = spl_ce_Iterator;
  945. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &ce_get_iterator) == FAILURE) {
  946. return;
  947. }
  948. intern->ce_get_iterator = ce_get_iterator;
  949. }
  950. /* }}} */
  951. /* {{{ proto string ArrayObject::getIteratorClass() U
  952. Get the class used in getIterator. */
  953. SPL_METHOD(Array, getIteratorClass)
  954. {
  955. zval *object = getThis();
  956. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  957. RETURN_UNICODEL(intern->ce_get_iterator->name.u, intern->ce_get_iterator->name_length, 1);
  958. }
  959. /* }}} */
  960. /* {{{ proto int ArrayObject::getFlags() U
  961. Get flags */
  962. SPL_METHOD(Array, getFlags)
  963. {
  964. zval *object = getThis();
  965. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  966. RETURN_LONG(intern->ar_flags & ~SPL_ARRAY_INT_MASK);
  967. }
  968. /* }}} */
  969. /* {{{ proto void ArrayObject::setFlags(int flags) U
  970. Set flags */
  971. SPL_METHOD(Array, setFlags)
  972. {
  973. zval *object = getThis();
  974. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  975. long ar_flags = 0;
  976. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ar_flags) == FAILURE) {
  977. return;
  978. }
  979. intern->ar_flags = (intern->ar_flags & SPL_ARRAY_INT_MASK) | (ar_flags & ~SPL_ARRAY_INT_MASK);
  980. }
  981. /* }}} */
  982. /* {{{ proto Array|Object ArrayObject::exchangeArray(Array|Object ar = array()) U
  983. Replace the referenced array or object with a new one and return the old one (right now copy - to be changed) */
  984. SPL_METHOD(Array, exchangeArray)
  985. {
  986. zval *object = getThis(), *tmp, **array;
  987. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  988. array_init(return_value);
  989. zend_hash_copy(HASH_OF(return_value), spl_array_get_hash_table(intern, 0 TSRMLS_CC), (copy_ctor_func_t) zval_add_ref, &tmp, sizeof(zval*));
  990. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &array) == FAILURE) {
  991. return;
  992. }
  993. spl_array_set_array(object, intern, array, 0L, 1 TSRMLS_CC);
  994. }
  995. /* }}} */
  996. /* {{{ proto ArrayIterator ArrayObject::getIterator() U
  997. Create a new iterator from a ArrayObject instance */
  998. SPL_METHOD(Array, getIterator)
  999. {
  1000. zval *object = getThis();
  1001. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1002. spl_array_object *iterator;
  1003. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1004. if (!aht) {
  1005. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1006. return;
  1007. }
  1008. return_value->type = IS_OBJECT;
  1009. return_value->value.obj = spl_array_object_new_ex(intern->ce_get_iterator, &iterator, object, 0 TSRMLS_CC);
  1010. Z_SET_REFCOUNT_P(return_value, 1);
  1011. Z_SET_ISREF_P(return_value);
  1012. }
  1013. /* }}} */
  1014. /* {{{ proto void ArrayIterator::rewind() U
  1015. Rewind array back to the start */
  1016. SPL_METHOD(Array, rewind)
  1017. {
  1018. zval *object = getThis();
  1019. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1020. spl_array_rewind(intern TSRMLS_CC);
  1021. }
  1022. /* }}} */
  1023. /* {{{ proto void ArrayIterator::seek(int $position) U
  1024. Seek to position. */
  1025. SPL_METHOD(Array, seek)
  1026. {
  1027. long opos, position;
  1028. zval *object = getThis();
  1029. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1030. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1031. int result;
  1032. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &position) == FAILURE) {
  1033. return;
  1034. }
  1035. if (!aht) {
  1036. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1037. return;
  1038. }
  1039. opos = position;
  1040. if (position >= 0) { /* negative values are not supported */
  1041. spl_array_rewind(intern TSRMLS_CC);
  1042. result = SUCCESS;
  1043. while (position-- > 0 && (result = spl_array_next(intern TSRMLS_CC)) == SUCCESS);
  1044. if (result == SUCCESS && zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS) {
  1045. return; /* ok */
  1046. }
  1047. }
  1048. zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0 TSRMLS_CC, "Seek position %ld is out of range", opos);
  1049. } /* }}} */
  1050. int static spl_array_object_count_elements_helper(spl_array_object *intern, long *count TSRMLS_DC) /* {{{ */
  1051. {
  1052. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1053. HashPosition pos;
  1054. if (!aht) {
  1055. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1056. *count = 0;
  1057. return FAILURE;
  1058. }
  1059. if (Z_TYPE_P(intern->array) == IS_OBJECT) {
  1060. /* We need to store the 'pos' since we'll modify it in the functions
  1061. * we're going to call and which do not support 'pos' as parameter. */
  1062. pos = intern->pos;
  1063. *count = 0;
  1064. spl_array_rewind(intern TSRMLS_CC);
  1065. while(intern->pos && spl_array_next(intern TSRMLS_CC) == SUCCESS) {
  1066. (*count)++;
  1067. }
  1068. spl_array_set_pos(intern, pos);
  1069. return SUCCESS;
  1070. } else {
  1071. *count = zend_hash_num_elements(aht);
  1072. return SUCCESS;
  1073. }
  1074. } /* }}} */
  1075. int spl_array_object_count_elements(zval *object, long *count TSRMLS_DC) /* {{{ */
  1076. {
  1077. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1078. if (intern->fptr_count) {
  1079. zval *rv;
  1080. zend_call_method_with_0_params(&object, intern->std.ce, &intern->fptr_count, "count", &rv);
  1081. if (rv) {
  1082. zval_ptr_dtor(&intern->retval);
  1083. MAKE_STD_ZVAL(intern->retval);
  1084. ZVAL_ZVAL(intern->retval, rv, 1, 1);
  1085. convert_to_long(intern->retval);
  1086. *count = (long) Z_LVAL_P(intern->retval);
  1087. return SUCCESS;
  1088. }
  1089. *count = 0;
  1090. return FAILURE;
  1091. }
  1092. return spl_array_object_count_elements_helper(intern, count TSRMLS_CC);
  1093. } /* }}} */
  1094. /* {{{ proto int ArrayObject::count() U
  1095. proto int ArrayIterator::count() U
  1096. Return the number of elements in the Iterator. */
  1097. SPL_METHOD(Array, count)
  1098. {
  1099. long count;
  1100. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1101. spl_array_object_count_elements_helper(intern, &count TSRMLS_CC);
  1102. RETURN_LONG(count);
  1103. } /* }}} */
  1104. /* {{{ static void spl_array_method
  1105. */
  1106. static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg)
  1107. {
  1108. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1109. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1110. zval *tmp, *arg;
  1111. zval *retval_ptr = NULL;
  1112. MAKE_STD_ZVAL(tmp);
  1113. Z_TYPE_P(tmp) = IS_ARRAY;
  1114. Z_ARRVAL_P(tmp) = aht;
  1115. if (use_arg) {
  1116. if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
  1117. Z_TYPE_P(tmp) = IS_NULL;
  1118. zval_ptr_dtor(&tmp);
  1119. zend_throw_exception(spl_ce_BadMethodCallException, "Function expects exactly one argument", 0 TSRMLS_CC);
  1120. return;
  1121. }
  1122. zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 2, tmp, arg TSRMLS_CC);
  1123. } else {
  1124. zend_call_method(NULL, NULL, NULL, fname, fname_len, &retval_ptr, 1, tmp, NULL TSRMLS_CC);
  1125. }
  1126. Z_TYPE_P(tmp) = IS_NULL; /* we want to destroy the zval, not the hashtable */
  1127. zval_ptr_dtor(&tmp);
  1128. if (retval_ptr) {
  1129. COPY_PZVAL_TO_ZVAL(*return_value, retval_ptr);
  1130. }
  1131. } /* }}} */
  1132. /* {{{ SPL_ARRAY_METHOD */
  1133. #define SPL_ARRAY_METHOD(cname, fname, use_arg) \
  1134. SPL_METHOD(cname, fname) \
  1135. { \
  1136. spl_array_method(INTERNAL_FUNCTION_PARAM_PASSTHRU, #fname, sizeof(#fname)-1, use_arg); \
  1137. } /* }}} */
  1138. /* {{{ proto int ArrayObject::asort() U
  1139. proto int ArrayIterator::asort() U
  1140. Sort the entries by values. */
  1141. SPL_ARRAY_METHOD(Array, asort, 0) /* }}} */
  1142. /* {{{ proto int ArrayObject::ksort() U
  1143. proto int ArrayIterator::ksort() U
  1144. Sort the entries by key. */
  1145. SPL_ARRAY_METHOD(Array, ksort, 0) /* }}} */
  1146. /* {{{ proto int ArrayObject::uasort(callback cmp_function) U
  1147. proto int ArrayIterator::uasort(callback cmp_function) U
  1148. Sort the entries by values user defined function. */
  1149. SPL_ARRAY_METHOD(Array, uasort, 1) /* }}} */
  1150. /* {{{ proto int ArrayObject::uksort(callback cmp_function) U
  1151. proto int ArrayIterator::uksort(callback cmp_function) U
  1152. Sort the entries by key using user defined function. */
  1153. SPL_ARRAY_METHOD(Array, uksort, 1) /* }}} */
  1154. /* {{{ proto int ArrayObject::natsort() U
  1155. proto int ArrayIterator::natsort() U
  1156. Sort the entries by values using "natural order" algorithm. */
  1157. SPL_ARRAY_METHOD(Array, natsort, 0) /* }}} */
  1158. /* {{{ proto int ArrayObject::natcasesort() U
  1159. proto int ArrayIterator::natcasesort() U
  1160. Sort the entries by key using case insensitive "natural order" algorithm. */
  1161. SPL_ARRAY_METHOD(Array, natcasesort, 0) /* }}} */
  1162. /* {{{ proto mixed|NULL ArrayIterator::current() U
  1163. Return current array entry */
  1164. SPL_METHOD(Array, current)
  1165. {
  1166. zval *object = getThis();
  1167. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1168. zval **entry;
  1169. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1170. if (!aht) {
  1171. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1172. return;
  1173. }
  1174. if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  1175. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  1176. return;
  1177. }
  1178. if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
  1179. return;
  1180. }
  1181. RETVAL_ZVAL(*entry, 1, 0);
  1182. }
  1183. /* }}} */
  1184. void spl_array_iterator_key(zval *object, zval *return_value TSRMLS_DC) /* {{{ */
  1185. {
  1186. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1187. zstr string_key;
  1188. uint string_length;
  1189. ulong num_key;
  1190. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1191. if (!aht) {
  1192. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1193. return;
  1194. }
  1195. if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  1196. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  1197. return;
  1198. }
  1199. switch (zend_hash_get_current_key_ex(aht, &string_key, &string_length, &num_key, 1, &intern->pos)) {
  1200. case HASH_KEY_IS_STRING:
  1201. RETVAL_STRINGL(string_key.s, string_length - 1, 0);
  1202. break;
  1203. case HASH_KEY_IS_UNICODE:
  1204. RETVAL_UNICODEL(string_key.u, string_length - 1, 0);
  1205. break;
  1206. case HASH_KEY_IS_LONG:
  1207. RETVAL_LONG(num_key);
  1208. break;
  1209. case HASH_KEY_NON_EXISTANT:
  1210. return;
  1211. }
  1212. }
  1213. /* }}} */
  1214. /* {{{ proto mixed|NULL ArrayIterator::key() U
  1215. Return current array key */
  1216. SPL_METHOD(Array, key)
  1217. {
  1218. spl_array_iterator_key(getThis(), return_value TSRMLS_CC);
  1219. }
  1220. /* }}} */
  1221. /* {{{ proto void ArrayIterator::next() U
  1222. Move to next entry */
  1223. SPL_METHOD(Array, next)
  1224. {
  1225. zval *object = getThis();
  1226. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1227. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1228. if (!aht) {
  1229. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1230. return;
  1231. }
  1232. spl_array_next_ex(intern, aht TSRMLS_CC);
  1233. }
  1234. /* }}} */
  1235. /* {{{ proto bool ArrayIterator::valid() U
  1236. Check whether array contains more entries */
  1237. SPL_METHOD(Array, valid)
  1238. {
  1239. zval *object = getThis();
  1240. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1241. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1242. if (!aht) {
  1243. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1244. return;
  1245. }
  1246. if (intern->pos && (intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  1247. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  1248. RETURN_FALSE;
  1249. } else {
  1250. RETURN_BOOL(zend_hash_has_more_elements_ex(aht, &intern->pos) == SUCCESS);
  1251. }
  1252. }
  1253. /* }}} */
  1254. /* {{{ proto bool RecursiveArrayIterator::hasChildren() U
  1255. Check whether current element has children (e.g. is an array) */
  1256. SPL_METHOD(Array, hasChildren)
  1257. {
  1258. zval *object = getThis(), **entry;
  1259. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1260. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1261. if (!aht) {
  1262. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1263. RETURN_FALSE;
  1264. }
  1265. if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  1266. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  1267. RETURN_FALSE;
  1268. }
  1269. if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
  1270. RETURN_FALSE;
  1271. }
  1272. RETURN_BOOL(Z_TYPE_PP(entry) == IS_ARRAY || (Z_TYPE_PP(entry) == IS_OBJECT && (intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) == 0));
  1273. }
  1274. /* }}} */
  1275. /* {{{ proto object RecursiveArrayIterator::getChildren() U
  1276. Create a sub iterator for the current element (same class as $this) */
  1277. SPL_METHOD(Array, getChildren)
  1278. {
  1279. zval *object = getThis(), **entry, *flags;
  1280. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1281. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1282. if (!aht) {
  1283. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1284. return;
  1285. }
  1286. if ((intern->ar_flags & SPL_ARRAY_IS_REF) && spl_hash_verify_pos_ex(intern, aht TSRMLS_CC) == FAILURE) {
  1287. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and internal position is no longer valid");
  1288. return;
  1289. }
  1290. if (zend_hash_get_current_data_ex(aht, (void **) &entry, &intern->pos) == FAILURE) {
  1291. return;
  1292. }
  1293. if (Z_TYPE_PP(entry) == IS_OBJECT) {
  1294. if ((intern->ar_flags & SPL_ARRAY_CHILD_ARRAYS_ONLY) != 0) {
  1295. return;
  1296. }
  1297. if (instanceof_function(Z_OBJCE_PP(entry), Z_OBJCE_P(getThis()) TSRMLS_CC)) {
  1298. RETURN_ZVAL(*entry, 0, 0);
  1299. }
  1300. }
  1301. MAKE_STD_ZVAL(flags);
  1302. ZVAL_LONG(flags, SPL_ARRAY_USE_OTHER | intern->ar_flags);
  1303. spl_instantiate_arg_ex2(intern->std.ce, &return_value, 0, *entry, flags TSRMLS_CC);
  1304. zval_ptr_dtor(&flags);
  1305. }
  1306. /* }}} */
  1307. smart_str spl_array_serialize_helper(spl_array_object *intern, php_serialize_data_t *var_hash_p TSRMLS_DC) /* {{{ */
  1308. {
  1309. HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC);
  1310. zval members, *pmembers;
  1311. smart_str buf = {0};
  1312. zval *flags;
  1313. if (!aht) {
  1314. php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array was modified outside object and is no longer an array");
  1315. return buf;
  1316. }
  1317. MAKE_STD_ZVAL(flags);
  1318. ZVAL_LONG(flags, (intern->ar_flags & SPL_ARRAY_CLONE_MASK));
  1319. /* storage */
  1320. smart_str_appendl(&buf, "x:", 2);
  1321. php_var_serialize(&buf, &flags, var_hash_p TSRMLS_CC);
  1322. zval_ptr_dtor(&flags);
  1323. if (!(intern->ar_flags & SPL_ARRAY_IS_SELF)) {
  1324. php_var_serialize(&buf, &intern->array, var_hash_p TSRMLS_CC);
  1325. smart_str_appendc(&buf, ';');
  1326. }
  1327. /* members */
  1328. smart_str_appendl(&buf, "m:", 2);
  1329. INIT_PZVAL(&members);
  1330. Z_ARRVAL(members) = intern->std.properties;
  1331. Z_TYPE(members) = IS_ARRAY;
  1332. pmembers = &members;
  1333. php_var_serialize(&buf, &pmembers, var_hash_p TSRMLS_CC); /* finishes the string */
  1334. return buf;
  1335. }
  1336. /* }}} */
  1337. /* {{{ proto string ArrayObject::serialize()
  1338. Serialize the object */
  1339. SPL_METHOD(Array, serialize)
  1340. {
  1341. zval *object = getThis();
  1342. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1343. int was_in_serialize = intern->serialize_data != NULL;
  1344. smart_str buf;
  1345. if (!was_in_serialize) {
  1346. intern->serialize_data = emalloc(sizeof(php_serialize_data_t));
  1347. PHP_VAR_SERIALIZE_INIT(*intern->serialize_data);
  1348. }
  1349. buf = spl_array_serialize_helper(intern, intern->serialize_data TSRMLS_CC);
  1350. if (!was_in_serialize) {
  1351. PHP_VAR_SERIALIZE_DESTROY(*intern->serialize_data);
  1352. efree(intern->serialize_data);
  1353. intern->serialize_data = NULL;
  1354. }
  1355. if (buf.c) {
  1356. RETURN_STRINGL(buf.c, buf.len, 0);
  1357. }
  1358. RETURN_NULL();
  1359. } /* }}} */
  1360. int spl_array_serialize(zval *object, int *type, zstr *buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) /* {{{ */
  1361. {
  1362. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);
  1363. if (intern->fptr_serialize) {
  1364. int retval;
  1365. php_serialize_data_t *before;
  1366. before = intern->serialize_data;
  1367. intern->serialize_data = (php_serialize_data_t *)data;
  1368. retval = zend_user_serialize(object, type, buffer, buf_len, data TSRMLS_CC);
  1369. intern->serialize_data = before;
  1370. return retval;
  1371. } else {
  1372. smart_str buf;
  1373. buf = spl_array_serialize_helper(intern, (php_serialize_data_t *)data TSRMLS_CC);
  1374. if (buf.c) {
  1375. buffer->s = estrndup(buf.c, buf.len);
  1376. *buf_len = buf.len;
  1377. *type = IS_STRING;
  1378. efree(buf.c);
  1379. return SUCCESS;
  1380. } else {
  1381. return FAILURE;
  1382. }
  1383. }
  1384. }
  1385. /* }}} */
  1386. void spl_array_unserialize_helper(spl_array_object *intern, const unsigned char *buf, zend_uint buf_len, php_unserialize_data_t *var_hash_p TSRMLS_DC) /* {{{ */
  1387. {
  1388. const unsigned char *p, *s;
  1389. zval *pmembers, *pflags = NULL;
  1390. long flags;
  1391. s = p = buf;
  1392. if (*p!= 'x' || *++p != ':') {
  1393. goto outexcept;
  1394. }
  1395. ++p;
  1396. ALLOC_INIT_ZVAL(pflags);
  1397. if (!php_var_unserialize(&pflags, &p, s + buf_len, var_hash_p TSRMLS_CC) || Z_TYPE_P(pflags) != IS_LONG) {
  1398. zval_ptr_dtor(&pflags);
  1399. goto outexcept;
  1400. }
  1401. --p; /* for ';' */
  1402. flags = Z_LVAL_P(pflags);
  1403. zval_ptr_dtor(&pflags);
  1404. /* flags needs to be verified and we also need to verify whether the next
  1405. * thing we get is ';'. After that we require an 'm' or somethign else
  1406. * where 'm' stands for members and anything else should be an array. If
  1407. * neither 'a' or 'm' follows we have an error. */
  1408. if (*p != ';') {
  1409. goto outexcept;
  1410. }
  1411. ++p;
  1412. if (*p!='m') {
  1413. if (*p!='a' && *p!='O' && *p!='C') {
  1414. goto outexcept;
  1415. }
  1416. intern->ar_flags &= ~SPL_ARRAY_CLONE_MASK;
  1417. intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
  1418. zval_ptr_dtor(&intern->array);
  1419. ALLOC_INIT_ZVAL(intern->array);
  1420. if (!php_var_unserialize(&intern->array, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
  1421. goto outexcept;
  1422. }
  1423. }
  1424. if (*p != ';') {
  1425. goto outexcept;
  1426. }
  1427. ++p;
  1428. /* members */
  1429. if (*p!= 'm' || *++p != ':') {
  1430. goto outexcept;
  1431. }
  1432. ++p;
  1433. ALLOC_INIT_ZVAL(pmembers);
  1434. if (!php_var_unserialize(&pmembers, &p, s + buf_len, var_hash_p TSRMLS_CC)) {
  1435. zval_ptr_dtor(&pmembers);
  1436. goto outexcept;
  1437. }
  1438. /* copy members */
  1439. zend_hash_copy(intern->std.properties, Z_ARRVAL_P(pmembers), (copy_ctor_func_t) zval_add_ref, (void *) NULL, sizeof(zval *));
  1440. zval_ptr_dtor(&pmembers);
  1441. /* done reading $serialized */
  1442. return;
  1443. outexcept:
  1444. zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - (char *)buf), buf_len);
  1445. return;
  1446. }
  1447. /* }}} */
  1448. /* {{{ proto void ArrayObject::unserialize(string serialized)
  1449. Unserialize the object */
  1450. SPL_METHOD(Array, unserialize)
  1451. {
  1452. char *buf;
  1453. int buf_len;
  1454. spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1455. int was_in_unserialize = intern->unserialize_data != NULL;
  1456. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
  1457. return;
  1458. }
  1459. if (buf_len == 0) {
  1460. zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Serialized string cannot be empty");
  1461. return;
  1462. }
  1463. if (!was_in_unserialize) {
  1464. intern->unserialize_data = emalloc(sizeof(php_unserialize_data_t));
  1465. PHP_VAR_UNSERIALIZE_INIT(*intern->unserialize_data);
  1466. }
  1467. spl_array_unserialize_helper(intern, (const unsigned char *)buf, buf_len, intern->unserialize_data TSRMLS_CC);
  1468. if (!was_in_unserialize) {
  1469. PHP_VAR_UNSERIALIZE_DESTROY(*intern->unserialize_data);
  1470. efree(intern->unserialize_data);
  1471. intern->unserialize_data = NULL;
  1472. }
  1473. }
  1474. /* }}} */
  1475. int spl_array_unserialize(zval **object, zend_class_entry *ce, int type, const zstr buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) /* {{{ */
  1476. {
  1477. spl_array_object *intern;
  1478. object_init_ex(*object, ce);
  1479. intern = (spl_array_object*)zend_object_store_get_object(*object TSRMLS_CC);
  1480. if (intern->fptr_unserialize) {
  1481. zval *zdata;
  1482. php_unserialize_data_t *before;
  1483. MAKE_STD_ZVAL(zdata);
  1484. ZVAL_ZSTRL(zdata, type, buf, buf_len, 1);
  1485. before = intern->unserialize_data;
  1486. intern->unserialize_data = (php_unserialize_data_t *)data;
  1487. zend_call_method_with_1_params(object, ce, &ce->unserialize_func, "unserialize", NULL, zdata);
  1488. intern->unserialize_data = before;
  1489. zval_ptr_dtor(&zdata);
  1490. } else {
  1491. if (type == IS_STRING) {
  1492. spl_array_unserialize_helper(intern, (unsigned char *)buf.s, buf_len, (php_unserialize_data_t *)data TSRMLS_CC);
  1493. } else {
  1494. unsigned char *bufc = (unsigned char*)zend_unicode_to_ascii(buf.u, buf_len TSRMLS_CC);
  1495. if (!bufc) {
  1496. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Binary or ASCII-Unicode string expected, non-ASCII-Unicode string received");
  1497. return FAILURE;
  1498. }
  1499. spl_array_unserialize_helper(intern, bufc, buf_len, (php_unserialize_data_t *)data TSRMLS_CC);
  1500. efree(bufc);
  1501. }
  1502. }
  1503. if (EG(exception)) {
  1504. return FAILURE;
  1505. } else {
  1506. return SUCCESS;
  1507. }
  1508. }
  1509. /* }}} */
  1510. /* {{{ arginfo */
  1511. ZEND_BEGIN_ARG_INFO(arginfo_array___construct, 0)
  1512. ZEND_ARG_INFO(0, array)
  1513. ZEND_END_ARG_INFO()
  1514. ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetGet, 0, 0, 1)
  1515. ZEND_ARG_INFO(0, index)
  1516. ZEND_END_ARG_INFO()
  1517. ZEND_BEGIN_ARG_INFO_EX(arginfo_array_offsetSet, 0, 0, 2)
  1518. ZEND_ARG_INFO(0, index)
  1519. ZEND_ARG_INFO(0, newval)
  1520. ZEND_END_ARG_INFO()
  1521. ZEND_BEGIN_ARG_INFO(arginfo_array_append, 0)
  1522. ZEND_ARG_INFO(0, value)
  1523. ZEND_END_ARG_INFO()
  1524. ZEND_BEGIN_ARG_INFO(arginfo_array_seek, 0)
  1525. ZEND_ARG_INFO(0, position)
  1526. ZEND_END_ARG_INFO()
  1527. ZEND_BEGIN_ARG_INFO(arginfo_array_exchangeArray, 0)
  1528. ZEND_ARG_INFO(0, array)
  1529. ZEND_END_ARG_INFO()
  1530. ZEND_BEGIN_ARG_INFO(arginfo_array_setFlags, 0)
  1531. ZEND_ARG_INFO(0, flags)
  1532. ZEND_END_ARG_INFO()
  1533. ZEND_BEGIN_ARG_INFO(arginfo_array_setIteratorClass, 0)
  1534. ZEND_ARG_INFO(0, iteratorClass)
  1535. ZEND_END_ARG_INFO()
  1536. ZEND_BEGIN_ARG_INFO(arginfo_array_uXsort, 0)
  1537. ZEND_ARG_INFO(0, cmp_function)
  1538. ZEND_END_ARG_INFO();
  1539. ZEND_BEGIN_ARG_INFO(arginfo_array_unserialize, 0)
  1540. ZEND_ARG_INFO(0, serialized)
  1541. ZEND_END_ARG_INFO();
  1542. /* }}} */
  1543. static const zend_function_entry spl_funcs_ArrayObject[] = { /* {{{ */
  1544. SPL_ME(Array, __construct, arginfo_array___construct, ZEND_ACC_PUBLIC)
  1545. SPL_ME(Array, offsetExists, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1546. SPL_ME(Array, offsetGet, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1547. SPL_ME(Array, offsetSet, arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
  1548. SPL_ME(Array, offsetUnset, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1549. SPL_ME(Array, append, arginfo_array_append, ZEND_ACC_PUBLIC)
  1550. SPL_ME(Array, getArrayCopy, NULL, ZEND_ACC_PUBLIC)
  1551. SPL_ME(Array, count, NULL, ZEND_ACC_PUBLIC)
  1552. SPL_ME(Array, getFlags, NULL, ZEND_ACC_PUBLIC)
  1553. SPL_ME(Array, setFlags, arginfo_array_setFlags, ZEND_ACC_PUBLIC)
  1554. SPL_ME(Array, asort, NULL, ZEND_ACC_PUBLIC)
  1555. SPL_ME(Array, ksort, NULL, ZEND_ACC_PUBLIC)
  1556. SPL_ME(Array, uasort, arginfo_array_uXsort, ZEND_ACC_PUBLIC)
  1557. SPL_ME(Array, uksort, arginfo_array_uXsort, ZEND_ACC_PUBLIC)
  1558. SPL_ME(Array, natsort, NULL, ZEND_ACC_PUBLIC)
  1559. SPL_ME(Array, natcasesort, NULL, ZEND_ACC_PUBLIC)
  1560. SPL_ME(Array, unserialize, arginfo_array_unserialize, ZEND_ACC_PUBLIC)
  1561. SPL_ME(Array, serialize, NULL, ZEND_ACC_PUBLIC)
  1562. /* ArrayObject specific */
  1563. SPL_ME(Array, getIterator, NULL, ZEND_ACC_PUBLIC)
  1564. SPL_ME(Array, exchangeArray, arginfo_array_exchangeArray, ZEND_ACC_PUBLIC)
  1565. SPL_ME(Array, setIteratorClass, arginfo_array_setIteratorClass, ZEND_ACC_PUBLIC)
  1566. SPL_ME(Array, getIteratorClass, NULL, ZEND_ACC_PUBLIC)
  1567. {NULL, NULL, NULL}
  1568. }; /* }}} */
  1569. static const zend_function_entry spl_funcs_ArrayIterator[] = { /* {{{ */
  1570. SPL_ME(Array, __construct, arginfo_array___construct, ZEND_ACC_PUBLIC)
  1571. SPL_ME(Array, offsetExists, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1572. SPL_ME(Array, offsetGet, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1573. SPL_ME(Array, offsetSet, arginfo_array_offsetSet, ZEND_ACC_PUBLIC)
  1574. SPL_ME(Array, offsetUnset, arginfo_array_offsetGet, ZEND_ACC_PUBLIC)
  1575. SPL_ME(Array, append, arginfo_array_append, ZEND_ACC_PUBLIC)
  1576. SPL_ME(Array, getArrayCopy, NULL, ZEND_ACC_PUBLIC)
  1577. SPL_ME(Array, count, NULL, ZEND_ACC_PUBLIC)
  1578. SPL_ME(Array, getFlags, NULL, ZEND_ACC_PUBLIC)
  1579. SPL_ME(Array, setFlags, arginfo_array_setFlags, ZEND_ACC_PUBLIC)
  1580. SPL_ME(Array, asort, NULL, ZEND_ACC_PUBLIC)
  1581. SPL_ME(Array, ksort, NULL, ZEND_ACC_PUBLIC)
  1582. SPL_ME(Array, uasort, arginfo_array_uXsort, ZEND_ACC_PUBLIC)
  1583. SPL_ME(Array, uksort, arginfo_array_uXsort, ZEND_ACC_PUBLIC)
  1584. SPL_ME(Array, natsort, NULL, ZEND_ACC_PUBLIC)
  1585. SPL_ME(Array, natcasesort, NULL, ZEND_ACC_PUBLIC)
  1586. SPL_ME(Array, unserialize, arginfo_array_unserialize, ZEND_ACC_PUBLIC)
  1587. SPL_ME(Array, serialize, NULL, ZEND_ACC_PUBLIC)
  1588. /* ArrayIterator specific */
  1589. SPL_ME(Array, rewind, NULL, ZEND_ACC_PUBLIC)
  1590. SPL_ME(Array, current, NULL, ZEND_ACC_PUBLIC)
  1591. SPL_ME(Array, key, NULL, ZEND_ACC_PUBLIC)
  1592. SPL_ME(Array, next, NULL, ZEND_ACC_PUBLIC)
  1593. SPL_ME(Array, valid, NULL, ZEND_ACC_PUBLIC)
  1594. SPL_ME(Array, seek, arginfo_array_seek, ZEND_ACC_PUBLIC)
  1595. {NULL, NULL, NULL}
  1596. }; /* }}} */
  1597. static const zend_function_entry spl_funcs_RecursiveArrayIterator[] = { /* {{{ */
  1598. SPL_ME(Array, hasChildren, NULL, ZEND_ACC_PUBLIC)
  1599. SPL_ME(Array, getChildren, NULL, ZEND_ACC_PUBLIC)
  1600. {NULL, NULL, NULL}
  1601. }; /* }}} */
  1602. /* {{{ PHP_MINIT_FUNCTION(spl_array) */
  1603. PHP_MINIT_FUNCTION(spl_array)
  1604. {
  1605. REGISTER_SPL_STD_CLASS_EX(ArrayObject, spl_array_object_new, spl_funcs_ArrayObject);
  1606. REGISTER_SPL_IMPLEMENTS(ArrayObject, Aggregate);
  1607. REGISTER_SPL_IMPLEMENTS(ArrayObject, ArrayAccess);
  1608. REGISTER_SPL_IMPLEMENTS(ArrayObject, Serializable);
  1609. spl_ce_ArrayObject->serialize = spl_array_serialize;
  1610. spl_ce_ArrayObject->unserialize = spl_array_unserialize;
  1611. memcpy(&spl_handler_ArrayObject, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
  1612. spl_handler_ArrayObject.clone_obj = spl_array_object_clone;
  1613. spl_handler_ArrayObject.read_dimension = spl_array_read_dimension;
  1614. spl_handler_ArrayObject.write_dimension = spl_array_write_dimension;
  1615. spl_handler_ArrayObject.unset_dimension = spl_array_unset_dimension;
  1616. spl_handler_ArrayObject.has_dimension = spl_array_has_dimension;
  1617. spl_handler_ArrayObject.count_elements = spl_array_object_count_elements;
  1618. spl_handler_ArrayObject.get_properties = spl_array_get_properties;
  1619. spl_handler_ArrayObject.get_debug_info = spl_array_get_debug_info;
  1620. spl_handler_ArrayObject.read_property = spl_array_read_property;
  1621. spl_handler_ArrayObject.write_property = spl_array_write_property;
  1622. spl_handler_ArrayObject.get_property_ptr_ptr = spl_array_get_property_ptr_ptr;
  1623. spl_handler_ArrayObject.has_property = spl_array_has_property;
  1624. spl_handler_ArrayObject.unset_property = spl_array_unset_property;
  1625. REGISTER_SPL_STD_CLASS_EX(ArrayIterator, spl_array_object_new, spl_funcs_ArrayIterator);
  1626. REGISTER_SPL_IMPLEMENTS(ArrayIterator, Iterator);
  1627. REGISTER_SPL_IMPLEMENTS(ArrayIterator, ArrayAccess);
  1628. REGISTER_SPL_IMPLEMENTS(ArrayIterator, SeekableIterator);
  1629. REGISTER_SPL_IMPLEMENTS(ArrayIterator, Serializable);
  1630. spl_ce_ArrayIterator->serialize = spl_array_serialize;
  1631. spl_ce_ArrayIterator->unserialize = spl_array_unserialize;
  1632. memcpy(&spl_handler_ArrayIterator, &spl_handler_ArrayObject, sizeof(zend_object_handlers));
  1633. spl_ce_ArrayIterator->get_iterator = spl_array_get_iterator;
  1634. REGISTER_SPL_SUB_CLASS_EX(RecursiveArrayIterator, ArrayIterator, spl_array_object_new, spl_funcs_RecursiveArrayIterator);
  1635. REGISTER_SPL_IMPLEMENTS(RecursiveArrayIterator, RecursiveIterator);
  1636. spl_ce_RecursiveArrayIterator->get_iterator = spl_array_get_iterator;
  1637. REGISTER_SPL_IMPLEMENTS(ArrayObject, Countable);
  1638. REGISTER_SPL_IMPLEMENTS(ArrayIterator, Countable);
  1639. REGISTER_SPL_CLASS_CONST_LONG(ArrayObject, "STD_PROP_LIST", SPL_ARRAY_STD_PROP_LIST);
  1640. REGISTER_SPL_CLASS_CONST_LONG(ArrayObject, "ARRAY_AS_PROPS", SPL_ARRAY_ARRAY_AS_PROPS);
  1641. REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "STD_PROP_LIST", SPL_ARRAY_STD_PROP_LIST);
  1642. REGISTER_SPL_CLASS_CONST_LONG(ArrayIterator, "ARRAY_AS_PROPS", SPL_ARRAY_ARRAY_AS_PROPS);
  1643. REGISTER_SPL_CLASS_CONST_LONG(RecursiveArrayIterator, "CHILD_ARRAYS_ONLY", SPL_ARRAY_CHILD_ARRAYS_ONLY);
  1644. return SUCCESS;
  1645. }
  1646. /* }}} */
  1647. /*
  1648. * Local variables:
  1649. * tab-width: 4
  1650. * c-basic-offset: 4
  1651. * End:
  1652. * vim600: fdm=marker
  1653. * vim: noet sw=4 ts=4
  1654. */