spl_fixedarray.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Antony Dovgal <tony@daylessday.org> |
  14. | Etienne Kneuss <colder@php.net> |
  15. +----------------------------------------------------------------------+
  16. */
  17. #ifdef HAVE_CONFIG_H
  18. #include "config.h"
  19. #endif
  20. #include "php.h"
  21. #include "php_ini.h"
  22. #include "ext/standard/info.h"
  23. #include "zend_exceptions.h"
  24. #include "php_spl.h"
  25. #include "spl_fixedarray_arginfo.h"
  26. #include "spl_functions.h"
  27. #include "spl_engine.h"
  28. #include "spl_fixedarray.h"
  29. #include "spl_exceptions.h"
  30. #include "spl_iterators.h"
  31. #include "ext/json/php_json.h"
  32. zend_object_handlers spl_handler_SplFixedArray;
  33. PHPAPI zend_class_entry *spl_ce_SplFixedArray;
  34. #ifdef COMPILE_DL_SPL_FIXEDARRAY
  35. ZEND_GET_MODULE(spl_fixedarray)
  36. #endif
  37. typedef struct _spl_fixedarray {
  38. zend_long size;
  39. /* It is possible to resize this, so this can't be combined with the object */
  40. zval *elements;
  41. /* True if this was modified after the last call to get_properties or the hash table wasn't rebuilt. */
  42. bool should_rebuild_properties;
  43. } spl_fixedarray;
  44. typedef struct _spl_fixedarray_methods {
  45. zend_function *fptr_offset_get;
  46. zend_function *fptr_offset_set;
  47. zend_function *fptr_offset_has;
  48. zend_function *fptr_offset_del;
  49. zend_function *fptr_count;
  50. } spl_fixedarray_methods;
  51. typedef struct _spl_fixedarray_object {
  52. spl_fixedarray array;
  53. spl_fixedarray_methods *methods;
  54. zend_object std;
  55. } spl_fixedarray_object;
  56. typedef struct _spl_fixedarray_it {
  57. zend_object_iterator intern;
  58. zend_long current;
  59. } spl_fixedarray_it;
  60. static spl_fixedarray_object *spl_fixed_array_from_obj(zend_object *obj)
  61. {
  62. return (spl_fixedarray_object*)((char*)(obj) - XtOffsetOf(spl_fixedarray_object, std));
  63. }
  64. #define Z_SPLFIXEDARRAY_P(zv) spl_fixed_array_from_obj(Z_OBJ_P((zv)))
  65. /* Helps enforce the invariants in debug mode:
  66. * - if size == 0, then elements == NULL
  67. * - if size > 0, then elements != NULL
  68. * - size is not less than 0
  69. */
  70. static bool spl_fixedarray_empty(spl_fixedarray *array)
  71. {
  72. if (array->elements) {
  73. ZEND_ASSERT(array->size > 0);
  74. return false;
  75. }
  76. ZEND_ASSERT(array->size == 0);
  77. return true;
  78. }
  79. static void spl_fixedarray_default_ctor(spl_fixedarray *array)
  80. {
  81. array->size = 0;
  82. array->elements = NULL;
  83. }
  84. /* Initializes the range [from, to) to null. Does not dtor existing elements. */
  85. static void spl_fixedarray_init_elems(spl_fixedarray *array, zend_long from, zend_long to)
  86. {
  87. ZEND_ASSERT(from <= to);
  88. zval *begin = array->elements + from, *end = array->elements + to;
  89. while (begin != end) {
  90. ZVAL_NULL(begin++);
  91. }
  92. }
  93. static void spl_fixedarray_init(spl_fixedarray *array, zend_long size)
  94. {
  95. if (size > 0) {
  96. array->size = 0; /* reset size in case ecalloc() fails */
  97. array->elements = safe_emalloc(size, sizeof(zval), 0);
  98. array->size = size;
  99. array->should_rebuild_properties = true;
  100. spl_fixedarray_init_elems(array, 0, size);
  101. } else {
  102. spl_fixedarray_default_ctor(array);
  103. }
  104. }
  105. /* Copies the range [begin, end) into the fixedarray, beginning at `offset`.
  106. * Does not dtor the existing elements.
  107. */
  108. static void spl_fixedarray_copy_range(spl_fixedarray *array, zend_long offset, zval *begin, zval *end)
  109. {
  110. ZEND_ASSERT(offset >= 0);
  111. ZEND_ASSERT(array->size - offset >= end - begin);
  112. zval *to = &array->elements[offset];
  113. while (begin != end) {
  114. ZVAL_COPY(to++, begin++);
  115. }
  116. }
  117. static void spl_fixedarray_copy_ctor(spl_fixedarray *to, spl_fixedarray *from)
  118. {
  119. zend_long size = from->size;
  120. spl_fixedarray_init(to, size);
  121. if (size != 0) {
  122. zval *begin = from->elements, *end = from->elements + size;
  123. spl_fixedarray_copy_range(to, 0, begin, end);
  124. }
  125. }
  126. /* Destructs the elements in the range [from, to).
  127. * Caller is expected to bounds check.
  128. */
  129. static void spl_fixedarray_dtor_range(spl_fixedarray *array, zend_long from, zend_long to)
  130. {
  131. zval *begin = array->elements + from, *end = array->elements + to;
  132. while (begin != end) {
  133. zval_ptr_dtor(begin++);
  134. }
  135. }
  136. /* Destructs and frees contents but not the array itself.
  137. * If you want to re-use the array then you need to re-initialize it.
  138. */
  139. static void spl_fixedarray_dtor(spl_fixedarray *array)
  140. {
  141. if (!spl_fixedarray_empty(array)) {
  142. zval *begin = array->elements, *end = array->elements + array->size;
  143. array->elements = NULL;
  144. array->size = 0;
  145. while (begin != end) {
  146. zval_ptr_dtor(--end);
  147. }
  148. efree(begin);
  149. }
  150. }
  151. static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size)
  152. {
  153. if (size == array->size) {
  154. /* nothing to do */
  155. return;
  156. }
  157. array->should_rebuild_properties = true;
  158. /* first initialization */
  159. if (array->size == 0) {
  160. spl_fixedarray_init(array, size);
  161. return;
  162. }
  163. /* clearing the array */
  164. if (size == 0) {
  165. spl_fixedarray_dtor(array);
  166. array->elements = NULL;
  167. } else if (size > array->size) {
  168. array->elements = safe_erealloc(array->elements, size, sizeof(zval), 0);
  169. spl_fixedarray_init_elems(array, array->size, size);
  170. } else { /* size < array->size */
  171. spl_fixedarray_dtor_range(array, size, array->size);
  172. array->elements = erealloc(array->elements, sizeof(zval) * size);
  173. }
  174. array->size = size;
  175. }
  176. static HashTable* spl_fixedarray_object_get_gc(zend_object *obj, zval **table, int *n)
  177. {
  178. spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
  179. HashTable *ht = zend_std_get_properties(obj);
  180. *table = intern->array.elements;
  181. *n = (int)intern->array.size;
  182. return ht;
  183. }
  184. static HashTable* spl_fixedarray_object_get_properties(zend_object *obj)
  185. {
  186. spl_fixedarray_object *intern = spl_fixed_array_from_obj(obj);
  187. HashTable *ht = zend_std_get_properties(obj);
  188. if (!spl_fixedarray_empty(&intern->array)) {
  189. /*
  190. * Usually, the reference count of the hash table is 1,
  191. * except during cyclic reference cycles.
  192. *
  193. * Maintain the DEBUG invariant that a hash table isn't modified during iteration,
  194. * and avoid unnecessary work rebuilding a hash table for unmodified properties.
  195. *
  196. * See https://github.com/php/php-src/issues/8079 and ext/spl/tests/fixedarray_022.phpt
  197. * Also see https://github.com/php/php-src/issues/8044 for alternate considered approaches.
  198. */
  199. if (!intern->array.should_rebuild_properties) {
  200. /* Return the same hash table so that recursion cycle detection works in internal functions. */
  201. return ht;
  202. }
  203. intern->array.should_rebuild_properties = false;
  204. zend_long j = zend_hash_num_elements(ht);
  205. if (GC_REFCOUNT(ht) > 1) {
  206. intern->std.properties = zend_array_dup(ht);
  207. GC_TRY_DELREF(ht);
  208. }
  209. for (zend_long i = 0; i < intern->array.size; i++) {
  210. zend_hash_index_update(ht, i, &intern->array.elements[i]);
  211. Z_TRY_ADDREF(intern->array.elements[i]);
  212. }
  213. if (j > intern->array.size) {
  214. for (zend_long i = intern->array.size; i < j; ++i) {
  215. zend_hash_index_del(ht, i);
  216. }
  217. }
  218. }
  219. return ht;
  220. }
  221. static void spl_fixedarray_object_free_storage(zend_object *object)
  222. {
  223. spl_fixedarray_object *intern = spl_fixed_array_from_obj(object);
  224. spl_fixedarray_dtor(&intern->array);
  225. zend_object_std_dtor(&intern->std);
  226. if (intern->methods) {
  227. efree(intern->methods);
  228. }
  229. }
  230. static zend_object *spl_fixedarray_object_new_ex(zend_class_entry *class_type, zend_object *orig, bool clone_orig)
  231. {
  232. spl_fixedarray_object *intern;
  233. zend_class_entry *parent = class_type;
  234. bool inherited = false;
  235. intern = zend_object_alloc(sizeof(spl_fixedarray_object), parent);
  236. zend_object_std_init(&intern->std, class_type);
  237. object_properties_init(&intern->std, class_type);
  238. if (orig && clone_orig) {
  239. spl_fixedarray_object *other = spl_fixed_array_from_obj(orig);
  240. spl_fixedarray_copy_ctor(&intern->array, &other->array);
  241. }
  242. while (parent) {
  243. if (parent == spl_ce_SplFixedArray) {
  244. intern->std.handlers = &spl_handler_SplFixedArray;
  245. break;
  246. }
  247. parent = parent->parent;
  248. inherited = true;
  249. }
  250. ZEND_ASSERT(parent);
  251. if (UNEXPECTED(inherited)) {
  252. spl_fixedarray_methods methods;
  253. methods.fptr_offset_get = zend_hash_str_find_ptr(&class_type->function_table, "offsetget", sizeof("offsetget") - 1);
  254. if (methods.fptr_offset_get->common.scope == parent) {
  255. methods.fptr_offset_get = NULL;
  256. }
  257. methods.fptr_offset_set = zend_hash_str_find_ptr(&class_type->function_table, "offsetset", sizeof("offsetset") - 1);
  258. if (methods.fptr_offset_set->common.scope == parent) {
  259. methods.fptr_offset_set = NULL;
  260. }
  261. methods.fptr_offset_has = zend_hash_str_find_ptr(&class_type->function_table, "offsetexists", sizeof("offsetexists") - 1);
  262. if (methods.fptr_offset_has->common.scope == parent) {
  263. methods.fptr_offset_has = NULL;
  264. }
  265. methods.fptr_offset_del = zend_hash_str_find_ptr(&class_type->function_table, "offsetunset", sizeof("offsetunset") - 1);
  266. if (methods.fptr_offset_del->common.scope == parent) {
  267. methods.fptr_offset_del = NULL;
  268. }
  269. methods.fptr_count = zend_hash_str_find_ptr(&class_type->function_table, "count", sizeof("count") - 1);
  270. if (methods.fptr_count->common.scope == parent) {
  271. methods.fptr_count = NULL;
  272. }
  273. /* Assume that most of the time in performance-sensitive code, SplFixedArray won't be subclassed with overrides for these ArrayAccess methods. */
  274. /* Save 32 bytes per object on 64-bit systems by combining the 5 null pointers into 1 null pointer */
  275. /* (This is already looking up 5 functions when any subclass of SplFixedArray is instantiated, which is inefficient) */
  276. if (methods.fptr_offset_get || methods.fptr_offset_set || methods.fptr_offset_del || methods.fptr_offset_has || methods.fptr_count) {
  277. intern->methods = emalloc(sizeof(spl_fixedarray_methods));
  278. *intern->methods = methods;
  279. }
  280. }
  281. return &intern->std;
  282. }
  283. static zend_object *spl_fixedarray_new(zend_class_entry *class_type)
  284. {
  285. return spl_fixedarray_object_new_ex(class_type, NULL, 0);
  286. }
  287. static zend_object *spl_fixedarray_object_clone(zend_object *old_object)
  288. {
  289. zend_object *new_object = spl_fixedarray_object_new_ex(old_object->ce, old_object, 1);
  290. zend_objects_clone_members(new_object, old_object);
  291. return new_object;
  292. }
  293. static zend_long spl_offset_convert_to_long(zval *offset) /* {{{ */
  294. {
  295. try_again:
  296. switch (Z_TYPE_P(offset)) {
  297. case IS_STRING: {
  298. zend_ulong index;
  299. if (ZEND_HANDLE_NUMERIC(Z_STR_P(offset), index)) {
  300. return (zend_long) index;
  301. }
  302. break;
  303. }
  304. case IS_DOUBLE:
  305. return zend_dval_to_lval_safe(Z_DVAL_P(offset));
  306. case IS_LONG:
  307. return Z_LVAL_P(offset);
  308. case IS_FALSE:
  309. return 0;
  310. case IS_TRUE:
  311. return 1;
  312. case IS_REFERENCE:
  313. offset = Z_REFVAL_P(offset);
  314. goto try_again;
  315. case IS_RESOURCE:
  316. zend_use_resource_as_offset(offset);
  317. return Z_RES_HANDLE_P(offset);
  318. }
  319. zend_type_error("Illegal offset type");
  320. return 0;
  321. }
  322. static zval *spl_fixedarray_object_read_dimension_helper(spl_fixedarray_object *intern, zval *offset)
  323. {
  324. zend_long index;
  325. /* we have to return NULL on error here to avoid memleak because of
  326. * ZE duplicating uninitialized_zval_ptr */
  327. if (!offset) {
  328. zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
  329. return NULL;
  330. }
  331. index = spl_offset_convert_to_long(offset);
  332. if (EG(exception)) {
  333. return NULL;
  334. }
  335. if (index < 0 || index >= intern->array.size) {
  336. // TODO Change error message and use OutOfBound SPL Exception?
  337. zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
  338. return NULL;
  339. } else {
  340. return &intern->array.elements[index];
  341. }
  342. }
  343. static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty);
  344. static zval *spl_fixedarray_object_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
  345. {
  346. spl_fixedarray_object *intern;
  347. intern = spl_fixed_array_from_obj(object);
  348. if (type == BP_VAR_IS && !spl_fixedarray_object_has_dimension(object, offset, 0)) {
  349. return &EG(uninitialized_zval);
  350. }
  351. if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_get)) {
  352. zval tmp;
  353. if (!offset) {
  354. ZVAL_NULL(&tmp);
  355. offset = &tmp;
  356. }
  357. zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_get, "offsetGet", rv, offset);
  358. if (!Z_ISUNDEF_P(rv)) {
  359. return rv;
  360. }
  361. return &EG(uninitialized_zval);
  362. }
  363. if (type != BP_VAR_IS && type != BP_VAR_R) {
  364. intern->array.should_rebuild_properties = true;
  365. }
  366. return spl_fixedarray_object_read_dimension_helper(intern, offset);
  367. }
  368. static void spl_fixedarray_object_write_dimension_helper(spl_fixedarray_object *intern, zval *offset, zval *value)
  369. {
  370. zend_long index;
  371. if (!offset) {
  372. /* '$array[] = value' syntax is not supported */
  373. zend_throw_error(NULL, "[] operator not supported for SplFixedArray");
  374. return;
  375. }
  376. index = spl_offset_convert_to_long(offset);
  377. if (EG(exception)) {
  378. return;
  379. }
  380. if (index < 0 || index >= intern->array.size) {
  381. // TODO Change error message and use OutOfBound SPL Exception?
  382. zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
  383. return;
  384. } else {
  385. intern->array.should_rebuild_properties = true;
  386. /* Fix #81429 */
  387. zval *ptr = &(intern->array.elements[index]);
  388. zval tmp;
  389. ZVAL_COPY_VALUE(&tmp, ptr);
  390. ZVAL_COPY_DEREF(ptr, value);
  391. zval_ptr_dtor(&tmp);
  392. }
  393. }
  394. static void spl_fixedarray_object_write_dimension(zend_object *object, zval *offset, zval *value)
  395. {
  396. spl_fixedarray_object *intern;
  397. zval tmp;
  398. intern = spl_fixed_array_from_obj(object);
  399. if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_set)) {
  400. if (!offset) {
  401. ZVAL_NULL(&tmp);
  402. offset = &tmp;
  403. }
  404. zend_call_method_with_2_params(object, intern->std.ce, &intern->methods->fptr_offset_set, "offsetSet", NULL, offset, value);
  405. return;
  406. }
  407. spl_fixedarray_object_write_dimension_helper(intern, offset, value);
  408. }
  409. static void spl_fixedarray_object_unset_dimension_helper(spl_fixedarray_object *intern, zval *offset)
  410. {
  411. zend_long index;
  412. index = spl_offset_convert_to_long(offset);
  413. if (EG(exception)) {
  414. return;
  415. }
  416. if (index < 0 || index >= intern->array.size) {
  417. // TODO Change error message and use OutOfBound SPL Exception?
  418. zend_throw_exception(spl_ce_RuntimeException, "Index invalid or out of range", 0);
  419. return;
  420. } else {
  421. intern->array.should_rebuild_properties = true;
  422. zval_ptr_dtor(&(intern->array.elements[index]));
  423. ZVAL_NULL(&intern->array.elements[index]);
  424. }
  425. }
  426. static void spl_fixedarray_object_unset_dimension(zend_object *object, zval *offset)
  427. {
  428. spl_fixedarray_object *intern;
  429. intern = spl_fixed_array_from_obj(object);
  430. if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_del)) {
  431. zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_del, "offsetUnset", NULL, offset);
  432. return;
  433. }
  434. spl_fixedarray_object_unset_dimension_helper(intern, offset);
  435. }
  436. static bool spl_fixedarray_object_has_dimension_helper(spl_fixedarray_object *intern, zval *offset, bool check_empty)
  437. {
  438. zend_long index;
  439. index = spl_offset_convert_to_long(offset);
  440. if (EG(exception)) {
  441. return false;
  442. }
  443. if (index < 0 || index >= intern->array.size) {
  444. return false;
  445. }
  446. if (check_empty) {
  447. return zend_is_true(&intern->array.elements[index]);
  448. }
  449. return Z_TYPE(intern->array.elements[index]) != IS_NULL;
  450. }
  451. static int spl_fixedarray_object_has_dimension(zend_object *object, zval *offset, int check_empty)
  452. {
  453. spl_fixedarray_object *intern;
  454. intern = spl_fixed_array_from_obj(object);
  455. if (UNEXPECTED(intern->methods && intern->methods->fptr_offset_has)) {
  456. zval rv;
  457. bool result;
  458. zend_call_method_with_1_params(object, intern->std.ce, &intern->methods->fptr_offset_has, "offsetExists", &rv, offset);
  459. result = zend_is_true(&rv);
  460. zval_ptr_dtor(&rv);
  461. return result;
  462. }
  463. return spl_fixedarray_object_has_dimension_helper(intern, offset, check_empty);
  464. }
  465. static int spl_fixedarray_object_count_elements(zend_object *object, zend_long *count)
  466. {
  467. spl_fixedarray_object *intern;
  468. intern = spl_fixed_array_from_obj(object);
  469. if (UNEXPECTED(intern->methods && intern->methods->fptr_count)) {
  470. zval rv;
  471. zend_call_method_with_0_params(object, intern->std.ce, &intern->methods->fptr_count, "count", &rv);
  472. if (!Z_ISUNDEF(rv)) {
  473. *count = zval_get_long(&rv);
  474. zval_ptr_dtor(&rv);
  475. } else {
  476. *count = 0;
  477. }
  478. } else {
  479. *count = intern->array.size;
  480. }
  481. return SUCCESS;
  482. }
  483. PHP_METHOD(SplFixedArray, __construct)
  484. {
  485. zval *object = ZEND_THIS;
  486. spl_fixedarray_object *intern;
  487. zend_long size = 0;
  488. if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &size) == FAILURE) {
  489. RETURN_THROWS();
  490. }
  491. if (size < 0) {
  492. zend_argument_value_error(1, "must be greater than or equal to 0");
  493. RETURN_THROWS();
  494. }
  495. intern = Z_SPLFIXEDARRAY_P(object);
  496. if (!spl_fixedarray_empty(&intern->array)) {
  497. /* called __construct() twice, bail out */
  498. return;
  499. }
  500. spl_fixedarray_init(&intern->array, size);
  501. }
  502. PHP_METHOD(SplFixedArray, __wakeup)
  503. {
  504. spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  505. HashTable *intern_ht = zend_std_get_properties(Z_OBJ_P(ZEND_THIS));
  506. zval *data;
  507. if (zend_parse_parameters_none() == FAILURE) {
  508. RETURN_THROWS();
  509. }
  510. if (intern->array.size == 0) {
  511. int index = 0;
  512. int size = zend_hash_num_elements(intern_ht);
  513. spl_fixedarray_init(&intern->array, size);
  514. ZEND_HASH_FOREACH_VAL(intern_ht, data) {
  515. ZVAL_COPY(&intern->array.elements[index], data);
  516. index++;
  517. } ZEND_HASH_FOREACH_END();
  518. /* Remove the unserialised properties, since we now have the elements
  519. * within the spl_fixedarray_object structure. */
  520. zend_hash_clean(intern_ht);
  521. }
  522. }
  523. PHP_METHOD(SplFixedArray, count)
  524. {
  525. zval *object = ZEND_THIS;
  526. spl_fixedarray_object *intern;
  527. if (zend_parse_parameters_none() == FAILURE) {
  528. RETURN_THROWS();
  529. }
  530. intern = Z_SPLFIXEDARRAY_P(object);
  531. RETURN_LONG(intern->array.size);
  532. }
  533. PHP_METHOD(SplFixedArray, toArray)
  534. {
  535. spl_fixedarray_object *intern;
  536. if (zend_parse_parameters_none() == FAILURE) {
  537. RETURN_THROWS();
  538. }
  539. intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  540. if (!spl_fixedarray_empty(&intern->array)) {
  541. array_init(return_value);
  542. for (zend_long i = 0; i < intern->array.size; i++) {
  543. zend_hash_index_update(Z_ARRVAL_P(return_value), i, &intern->array.elements[i]);
  544. Z_TRY_ADDREF(intern->array.elements[i]);
  545. }
  546. } else {
  547. RETURN_EMPTY_ARRAY();
  548. }
  549. }
  550. PHP_METHOD(SplFixedArray, fromArray)
  551. {
  552. zval *data;
  553. spl_fixedarray array;
  554. spl_fixedarray_object *intern;
  555. int num;
  556. bool save_indexes = 1;
  557. if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|b", &data, &save_indexes) == FAILURE) {
  558. RETURN_THROWS();
  559. }
  560. num = zend_hash_num_elements(Z_ARRVAL_P(data));
  561. if (num > 0 && save_indexes) {
  562. zval *element;
  563. zend_string *str_index;
  564. zend_ulong num_index, max_index = 0;
  565. zend_long tmp;
  566. ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) {
  567. if (str_index != NULL || (zend_long)num_index < 0) {
  568. zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys");
  569. RETURN_THROWS();
  570. }
  571. if (num_index > max_index) {
  572. max_index = num_index;
  573. }
  574. } ZEND_HASH_FOREACH_END();
  575. tmp = max_index + 1;
  576. if (tmp <= 0) {
  577. zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected");
  578. RETURN_THROWS();
  579. }
  580. spl_fixedarray_init(&array, tmp);
  581. ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(data), num_index, str_index, element) {
  582. ZVAL_COPY_DEREF(&array.elements[num_index], element);
  583. } ZEND_HASH_FOREACH_END();
  584. } else if (num > 0 && !save_indexes) {
  585. zval *element;
  586. zend_long i = 0;
  587. spl_fixedarray_init(&array, num);
  588. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), element) {
  589. ZVAL_COPY_DEREF(&array.elements[i], element);
  590. i++;
  591. } ZEND_HASH_FOREACH_END();
  592. } else {
  593. spl_fixedarray_init(&array, 0);
  594. }
  595. object_init_ex(return_value, spl_ce_SplFixedArray);
  596. intern = Z_SPLFIXEDARRAY_P(return_value);
  597. intern->array = array;
  598. }
  599. PHP_METHOD(SplFixedArray, getSize)
  600. {
  601. zval *object = ZEND_THIS;
  602. spl_fixedarray_object *intern;
  603. if (zend_parse_parameters_none() == FAILURE) {
  604. RETURN_THROWS();
  605. }
  606. intern = Z_SPLFIXEDARRAY_P(object);
  607. RETURN_LONG(intern->array.size);
  608. }
  609. PHP_METHOD(SplFixedArray, setSize)
  610. {
  611. zval *object = ZEND_THIS;
  612. spl_fixedarray_object *intern;
  613. zend_long size;
  614. if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
  615. RETURN_THROWS();
  616. }
  617. if (size < 0) {
  618. zend_argument_value_error(1, "must be greater than or equal to 0");
  619. RETURN_THROWS();
  620. }
  621. intern = Z_SPLFIXEDARRAY_P(object);
  622. spl_fixedarray_resize(&intern->array, size);
  623. RETURN_TRUE;
  624. }
  625. /* Returns whether the requested $index exists. */
  626. PHP_METHOD(SplFixedArray, offsetExists)
  627. {
  628. zval *zindex;
  629. spl_fixedarray_object *intern;
  630. if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
  631. RETURN_THROWS();
  632. }
  633. intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  634. RETURN_BOOL(spl_fixedarray_object_has_dimension_helper(intern, zindex, 0));
  635. }
  636. /* Returns the value at the specified $index. */
  637. PHP_METHOD(SplFixedArray, offsetGet)
  638. {
  639. zval *zindex, *value;
  640. spl_fixedarray_object *intern;
  641. if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
  642. RETURN_THROWS();
  643. }
  644. intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  645. value = spl_fixedarray_object_read_dimension_helper(intern, zindex);
  646. if (value) {
  647. RETURN_COPY_DEREF(value);
  648. } else {
  649. RETURN_NULL();
  650. }
  651. }
  652. /* Sets the value at the specified $index to $newval. */
  653. PHP_METHOD(SplFixedArray, offsetSet)
  654. {
  655. zval *zindex, *value;
  656. spl_fixedarray_object *intern;
  657. if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &zindex, &value) == FAILURE) {
  658. RETURN_THROWS();
  659. }
  660. intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  661. spl_fixedarray_object_write_dimension_helper(intern, zindex, value);
  662. }
  663. /* Unsets the value at the specified $index. */
  664. PHP_METHOD(SplFixedArray, offsetUnset)
  665. {
  666. zval *zindex;
  667. spl_fixedarray_object *intern;
  668. if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zindex) == FAILURE) {
  669. RETURN_THROWS();
  670. }
  671. intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  672. spl_fixedarray_object_unset_dimension_helper(intern, zindex);
  673. }
  674. /* Create a new iterator from a SplFixedArray instance. */
  675. PHP_METHOD(SplFixedArray, getIterator)
  676. {
  677. if (zend_parse_parameters_none() == FAILURE) {
  678. return;
  679. }
  680. zend_create_internal_iterator_zval(return_value, ZEND_THIS);
  681. }
  682. PHP_METHOD(SplFixedArray, jsonSerialize)
  683. {
  684. ZEND_PARSE_PARAMETERS_NONE();
  685. spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS);
  686. array_init_size(return_value, intern->array.size);
  687. for (zend_long i = 0; i < intern->array.size; i++) {
  688. zend_hash_next_index_insert_new(Z_ARR_P(return_value), &intern->array.elements[i]);
  689. Z_TRY_ADDREF(intern->array.elements[i]);
  690. }
  691. }
  692. static void spl_fixedarray_it_dtor(zend_object_iterator *iter)
  693. {
  694. zval_ptr_dtor(&iter->data);
  695. }
  696. static void spl_fixedarray_it_rewind(zend_object_iterator *iter)
  697. {
  698. ((spl_fixedarray_it*)iter)->current = 0;
  699. }
  700. static int spl_fixedarray_it_valid(zend_object_iterator *iter)
  701. {
  702. spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
  703. spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
  704. if (iterator->current >= 0 && iterator->current < object->array.size) {
  705. return SUCCESS;
  706. }
  707. return FAILURE;
  708. }
  709. static zval *spl_fixedarray_it_get_current_data(zend_object_iterator *iter)
  710. {
  711. zval zindex, *data;
  712. spl_fixedarray_it *iterator = (spl_fixedarray_it*)iter;
  713. spl_fixedarray_object *object = Z_SPLFIXEDARRAY_P(&iter->data);
  714. ZVAL_LONG(&zindex, iterator->current);
  715. data = spl_fixedarray_object_read_dimension_helper(object, &zindex);
  716. if (data == NULL) {
  717. data = &EG(uninitialized_zval);
  718. }
  719. return data;
  720. }
  721. static void spl_fixedarray_it_get_current_key(zend_object_iterator *iter, zval *key)
  722. {
  723. ZVAL_LONG(key, ((spl_fixedarray_it*)iter)->current);
  724. }
  725. static void spl_fixedarray_it_move_forward(zend_object_iterator *iter)
  726. {
  727. ((spl_fixedarray_it*)iter)->current++;
  728. }
  729. /* iterator handler table */
  730. static const zend_object_iterator_funcs spl_fixedarray_it_funcs = {
  731. spl_fixedarray_it_dtor,
  732. spl_fixedarray_it_valid,
  733. spl_fixedarray_it_get_current_data,
  734. spl_fixedarray_it_get_current_key,
  735. spl_fixedarray_it_move_forward,
  736. spl_fixedarray_it_rewind,
  737. NULL,
  738. NULL, /* get_gc */
  739. };
  740. zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
  741. {
  742. spl_fixedarray_it *iterator;
  743. if (by_ref) {
  744. zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
  745. return NULL;
  746. }
  747. iterator = emalloc(sizeof(spl_fixedarray_it));
  748. zend_iterator_init((zend_object_iterator*)iterator);
  749. ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
  750. iterator->intern.funcs = &spl_fixedarray_it_funcs;
  751. return &iterator->intern;
  752. }
  753. PHP_MINIT_FUNCTION(spl_fixedarray)
  754. {
  755. spl_ce_SplFixedArray = register_class_SplFixedArray(
  756. zend_ce_aggregate, zend_ce_arrayaccess, zend_ce_countable, php_json_serializable_ce);
  757. spl_ce_SplFixedArray->create_object = spl_fixedarray_new;
  758. spl_ce_SplFixedArray->get_iterator = spl_fixedarray_get_iterator;
  759. spl_ce_SplFixedArray->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
  760. memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers));
  761. spl_handler_SplFixedArray.offset = XtOffsetOf(spl_fixedarray_object, std);
  762. spl_handler_SplFixedArray.clone_obj = spl_fixedarray_object_clone;
  763. spl_handler_SplFixedArray.read_dimension = spl_fixedarray_object_read_dimension;
  764. spl_handler_SplFixedArray.write_dimension = spl_fixedarray_object_write_dimension;
  765. spl_handler_SplFixedArray.unset_dimension = spl_fixedarray_object_unset_dimension;
  766. spl_handler_SplFixedArray.has_dimension = spl_fixedarray_object_has_dimension;
  767. spl_handler_SplFixedArray.count_elements = spl_fixedarray_object_count_elements;
  768. spl_handler_SplFixedArray.get_properties = spl_fixedarray_object_get_properties;
  769. spl_handler_SplFixedArray.get_gc = spl_fixedarray_object_get_gc;
  770. spl_handler_SplFixedArray.free_obj = spl_fixedarray_object_free_storage;
  771. return SUCCESS;
  772. }