PageRenderTime 88ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/standard/array.c

http://github.com/php/php-src
C | 6360 lines | 5077 code | 710 blank | 573 comment | 1527 complexity | 1050632557f69c6cefadd35cb7616cef MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  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. | http://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. | Authors: Andi Gutmans <andi@php.net> |
  14. | Zeev Suraski <zeev@php.net> |
  15. | Rasmus Lerdorf <rasmus@php.net> |
  16. | Andrei Zmievski <andrei@php.net> |
  17. | Stig Venaas <venaas@php.net> |
  18. | Jason Greene <jason@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. #include "php.h"
  22. #include "php_ini.h"
  23. #include <stdarg.h>
  24. #include <stdlib.h>
  25. #include <math.h>
  26. #include <time.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #ifdef PHP_WIN32
  30. #include "win32/unistd.h"
  31. #endif
  32. #include "zend_globals.h"
  33. #include "zend_interfaces.h"
  34. #include "php_globals.h"
  35. #include "php_array.h"
  36. #include "basic_functions.h"
  37. #include "php_string.h"
  38. #include "php_rand.h"
  39. #include "php_math.h"
  40. #include "zend_smart_str.h"
  41. #include "zend_bitset.h"
  42. #include "zend_exceptions.h"
  43. #include "ext/spl/spl_array.h"
  44. /* {{{ defines */
  45. #define EXTR_OVERWRITE 0
  46. #define EXTR_SKIP 1
  47. #define EXTR_PREFIX_SAME 2
  48. #define EXTR_PREFIX_ALL 3
  49. #define EXTR_PREFIX_INVALID 4
  50. #define EXTR_PREFIX_IF_EXISTS 5
  51. #define EXTR_IF_EXISTS 6
  52. #define EXTR_REFS 0x100
  53. #define CASE_LOWER 0
  54. #define CASE_UPPER 1
  55. #define DIFF_NORMAL 1
  56. #define DIFF_KEY 2
  57. #define DIFF_ASSOC 6
  58. #define DIFF_COMP_DATA_NONE -1
  59. #define DIFF_COMP_DATA_INTERNAL 0
  60. #define DIFF_COMP_DATA_USER 1
  61. #define DIFF_COMP_KEY_INTERNAL 0
  62. #define DIFF_COMP_KEY_USER 1
  63. #define INTERSECT_NORMAL 1
  64. #define INTERSECT_KEY 2
  65. #define INTERSECT_ASSOC 6
  66. #define INTERSECT_COMP_DATA_NONE -1
  67. #define INTERSECT_COMP_DATA_INTERNAL 0
  68. #define INTERSECT_COMP_DATA_USER 1
  69. #define INTERSECT_COMP_KEY_INTERNAL 0
  70. #define INTERSECT_COMP_KEY_USER 1
  71. /* }}} */
  72. ZEND_DECLARE_MODULE_GLOBALS(array)
  73. /* {{{ php_array_init_globals
  74. */
  75. static void php_array_init_globals(zend_array_globals *array_globals)
  76. {
  77. memset(array_globals, 0, sizeof(zend_array_globals));
  78. }
  79. /* }}} */
  80. PHP_MINIT_FUNCTION(array) /* {{{ */
  81. {
  82. ZEND_INIT_MODULE_GLOBALS(array, php_array_init_globals, NULL);
  83. REGISTER_LONG_CONSTANT("EXTR_OVERWRITE", EXTR_OVERWRITE, CONST_CS | CONST_PERSISTENT);
  84. REGISTER_LONG_CONSTANT("EXTR_SKIP", EXTR_SKIP, CONST_CS | CONST_PERSISTENT);
  85. REGISTER_LONG_CONSTANT("EXTR_PREFIX_SAME", EXTR_PREFIX_SAME, CONST_CS | CONST_PERSISTENT);
  86. REGISTER_LONG_CONSTANT("EXTR_PREFIX_ALL", EXTR_PREFIX_ALL, CONST_CS | CONST_PERSISTENT);
  87. REGISTER_LONG_CONSTANT("EXTR_PREFIX_INVALID", EXTR_PREFIX_INVALID, CONST_CS | CONST_PERSISTENT);
  88. REGISTER_LONG_CONSTANT("EXTR_PREFIX_IF_EXISTS", EXTR_PREFIX_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
  89. REGISTER_LONG_CONSTANT("EXTR_IF_EXISTS", EXTR_IF_EXISTS, CONST_CS | CONST_PERSISTENT);
  90. REGISTER_LONG_CONSTANT("EXTR_REFS", EXTR_REFS, CONST_CS | CONST_PERSISTENT);
  91. REGISTER_LONG_CONSTANT("SORT_ASC", PHP_SORT_ASC, CONST_CS | CONST_PERSISTENT);
  92. REGISTER_LONG_CONSTANT("SORT_DESC", PHP_SORT_DESC, CONST_CS | CONST_PERSISTENT);
  93. REGISTER_LONG_CONSTANT("SORT_REGULAR", PHP_SORT_REGULAR, CONST_CS | CONST_PERSISTENT);
  94. REGISTER_LONG_CONSTANT("SORT_NUMERIC", PHP_SORT_NUMERIC, CONST_CS | CONST_PERSISTENT);
  95. REGISTER_LONG_CONSTANT("SORT_STRING", PHP_SORT_STRING, CONST_CS | CONST_PERSISTENT);
  96. REGISTER_LONG_CONSTANT("SORT_LOCALE_STRING", PHP_SORT_LOCALE_STRING, CONST_CS | CONST_PERSISTENT);
  97. REGISTER_LONG_CONSTANT("SORT_NATURAL", PHP_SORT_NATURAL, CONST_CS | CONST_PERSISTENT);
  98. REGISTER_LONG_CONSTANT("SORT_FLAG_CASE", PHP_SORT_FLAG_CASE, CONST_CS | CONST_PERSISTENT);
  99. REGISTER_LONG_CONSTANT("CASE_LOWER", CASE_LOWER, CONST_CS | CONST_PERSISTENT);
  100. REGISTER_LONG_CONSTANT("CASE_UPPER", CASE_UPPER, CONST_CS | CONST_PERSISTENT);
  101. REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
  102. REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
  103. REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
  104. REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
  105. return SUCCESS;
  106. }
  107. /* }}} */
  108. PHP_MSHUTDOWN_FUNCTION(array) /* {{{ */
  109. {
  110. #ifdef ZTS
  111. ts_free_id(array_globals_id);
  112. #endif
  113. return SUCCESS;
  114. }
  115. /* }}} */
  116. static int php_array_key_compare(Bucket *f, Bucket *s) /* {{{ */
  117. {
  118. zend_uchar t;
  119. zend_long l1, l2;
  120. double d;
  121. if (f->key == NULL) {
  122. if (s->key == NULL) {
  123. return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
  124. } else {
  125. l1 = (zend_long)f->h;
  126. t = is_numeric_string(s->key->val, s->key->len, &l2, &d, 1);
  127. if (t == IS_LONG) {
  128. /* pass */
  129. } else if (t == IS_DOUBLE) {
  130. return ZEND_NORMALIZE_BOOL((double)l1 - d);
  131. } else {
  132. l2 = 0;
  133. }
  134. }
  135. } else {
  136. if (s->key) {
  137. return zendi_smart_strcmp(f->key, s->key);
  138. } else {
  139. l2 = (zend_long)s->h;
  140. t = is_numeric_string(f->key->val, f->key->len, &l1, &d, 1);
  141. if (t == IS_LONG) {
  142. /* pass */
  143. } else if (t == IS_DOUBLE) {
  144. return ZEND_NORMALIZE_BOOL(d - (double)l2);
  145. } else {
  146. l1 = 0;
  147. }
  148. }
  149. }
  150. return ZEND_NORMALIZE_BOOL(l1 - l2);
  151. }
  152. /* }}} */
  153. static int php_array_reverse_key_compare(Bucket *a, Bucket *b) /* {{{ */
  154. {
  155. return php_array_key_compare(b, a);
  156. }
  157. /* }}} */
  158. static int php_array_key_compare_numeric(Bucket *f, Bucket *s) /* {{{ */
  159. {
  160. if (f->key == NULL && s->key == NULL) {
  161. return (zend_long)f->h > (zend_long)s->h ? 1 : -1;
  162. } else {
  163. double d1, d2;
  164. if (f->key) {
  165. d1 = zend_strtod(f->key->val, NULL);
  166. } else {
  167. d1 = (double)(zend_long)f->h;
  168. }
  169. if (s->key) {
  170. d2 = zend_strtod(s->key->val, NULL);
  171. } else {
  172. d2 = (double)(zend_long)s->h;
  173. }
  174. return ZEND_NORMALIZE_BOOL(d1 - d2);
  175. }
  176. }
  177. /* }}} */
  178. static int php_array_reverse_key_compare_numeric(Bucket *a, Bucket *b) /* {{{ */
  179. {
  180. return php_array_key_compare_numeric(b, a);
  181. }
  182. /* }}} */
  183. static int php_array_key_compare_string_case(Bucket *f, Bucket *s) /* {{{ */
  184. {
  185. const char *s1, *s2;
  186. size_t l1, l2;
  187. char buf1[MAX_LENGTH_OF_LONG + 1];
  188. char buf2[MAX_LENGTH_OF_LONG + 1];
  189. if (f->key) {
  190. s1 = f->key->val;
  191. l1 = f->key->len;
  192. } else {
  193. s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
  194. l1 = buf1 + sizeof(buf1) - 1 - s1;
  195. }
  196. if (s->key) {
  197. s2 = s->key->val;
  198. l2 = s->key->len;
  199. } else {
  200. s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
  201. l2 = buf2 + sizeof(buf2) - 1 - s1;
  202. }
  203. return zend_binary_strcasecmp_l(s1, l1, s2, l2);
  204. }
  205. /* }}} */
  206. static int php_array_reverse_key_compare_string_case(Bucket *a, Bucket *b) /* {{{ */
  207. {
  208. return php_array_key_compare_string_case(b, a);
  209. }
  210. /* }}} */
  211. static int php_array_key_compare_string(Bucket *f, Bucket *s) /* {{{ */
  212. {
  213. const char *s1, *s2;
  214. size_t l1, l2;
  215. char buf1[MAX_LENGTH_OF_LONG + 1];
  216. char buf2[MAX_LENGTH_OF_LONG + 1];
  217. if (f->key) {
  218. s1 = f->key->val;
  219. l1 = f->key->len;
  220. } else {
  221. s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
  222. l1 = buf1 + sizeof(buf1) - 1 - s1;
  223. }
  224. if (s->key) {
  225. s2 = s->key->val;
  226. l2 = s->key->len;
  227. } else {
  228. s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
  229. l2 = buf2 + sizeof(buf2) - 1 - s2;
  230. }
  231. return zend_binary_strcmp(s1, l1, s2, l2);
  232. }
  233. /* }}} */
  234. static int php_array_reverse_key_compare_string(Bucket *a, Bucket *b) /* {{{ */
  235. {
  236. return php_array_key_compare_string(b, a);
  237. }
  238. /* }}} */
  239. static int php_array_key_compare_string_natural_general(Bucket *f, Bucket *s, int fold_case) /* {{{ */
  240. {
  241. const char *s1, *s2;
  242. size_t l1, l2;
  243. char buf1[MAX_LENGTH_OF_LONG + 1];
  244. char buf2[MAX_LENGTH_OF_LONG + 1];
  245. if (f->key) {
  246. s1 = f->key->val;
  247. l1 = f->key->len;
  248. } else {
  249. s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
  250. l1 = buf1 + sizeof(buf1) - 1 - s1;
  251. }
  252. if (s->key) {
  253. s2 = s->key->val;
  254. l2 = s->key->len;
  255. } else {
  256. s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
  257. l2 = buf2 + sizeof(buf2) - 1 - s1;
  258. }
  259. return strnatcmp_ex(s1, l1, s2, l2, fold_case);
  260. }
  261. /* }}} */
  262. static int php_array_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
  263. {
  264. return php_array_key_compare_string_natural_general(a, b, 1);
  265. }
  266. /* }}} */
  267. static int php_array_reverse_key_compare_string_natural_case(Bucket *a, Bucket *b) /* {{{ */
  268. {
  269. return php_array_key_compare_string_natural_general(b, a, 1);
  270. }
  271. /* }}} */
  272. static int php_array_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
  273. {
  274. return php_array_key_compare_string_natural_general(a, b, 0);
  275. }
  276. /* }}} */
  277. static int php_array_reverse_key_compare_string_natural(Bucket *a, Bucket *b) /* {{{ */
  278. {
  279. return php_array_key_compare_string_natural_general(b, a, 0);
  280. }
  281. /* }}} */
  282. static int php_array_key_compare_string_locale(Bucket *f, Bucket *s) /* {{{ */
  283. {
  284. const char *s1, *s2;
  285. char buf1[MAX_LENGTH_OF_LONG + 1];
  286. char buf2[MAX_LENGTH_OF_LONG + 1];
  287. if (f->key) {
  288. s1 = f->key->val;
  289. } else {
  290. s1 = zend_print_long_to_buf(buf1 + sizeof(buf1) - 1, f->h);
  291. }
  292. if (s->key) {
  293. s2 = s->key->val;
  294. } else {
  295. s2 = zend_print_long_to_buf(buf2 + sizeof(buf2) - 1, s->h);
  296. }
  297. return strcoll(s1, s2);
  298. }
  299. /* }}} */
  300. static int php_array_reverse_key_compare_string_locale(Bucket *a, Bucket *b) /* {{{ */
  301. {
  302. return php_array_key_compare_string_locale(b, a);
  303. }
  304. /* }}} */
  305. static int php_array_data_compare(Bucket *f, Bucket *s) /* {{{ */
  306. {
  307. zval *first = &f->val;
  308. zval *second = &s->val;
  309. if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
  310. first = Z_INDIRECT_P(first);
  311. }
  312. if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
  313. second = Z_INDIRECT_P(second);
  314. }
  315. return zend_compare(first, second);
  316. }
  317. /* }}} */
  318. static int php_array_reverse_data_compare(Bucket *a, Bucket *b) /* {{{ */
  319. {
  320. return php_array_data_compare(a, b) * -1;
  321. }
  322. /* }}} */
  323. static int php_array_data_compare_numeric(Bucket *f, Bucket *s) /* {{{ */
  324. {
  325. zval *first = &f->val;
  326. zval *second = &s->val;
  327. if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
  328. first = Z_INDIRECT_P(first);
  329. }
  330. if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
  331. second = Z_INDIRECT_P(second);
  332. }
  333. return numeric_compare_function(first, second);
  334. }
  335. /* }}} */
  336. static int php_array_reverse_data_compare_numeric(Bucket *a, Bucket *b) /* {{{ */
  337. {
  338. return php_array_data_compare_numeric(b, a);
  339. }
  340. /* }}} */
  341. static int php_array_data_compare_string_case(Bucket *f, Bucket *s) /* {{{ */
  342. {
  343. zval *first = &f->val;
  344. zval *second = &s->val;
  345. if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
  346. first = Z_INDIRECT_P(first);
  347. }
  348. if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
  349. second = Z_INDIRECT_P(second);
  350. }
  351. return string_case_compare_function(first, second);
  352. }
  353. /* }}} */
  354. static int php_array_reverse_data_compare_string_case(Bucket *a, Bucket *b) /* {{{ */
  355. {
  356. return php_array_data_compare_string_case(b, a);
  357. }
  358. /* }}} */
  359. static int php_array_data_compare_string(Bucket *f, Bucket *s) /* {{{ */
  360. {
  361. zval *first = &f->val;
  362. zval *second = &s->val;
  363. if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
  364. first = Z_INDIRECT_P(first);
  365. }
  366. if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
  367. second = Z_INDIRECT_P(second);
  368. }
  369. return string_compare_function(first, second);
  370. }
  371. /* }}} */
  372. static int php_array_reverse_data_compare_string(Bucket *a, Bucket *b) /* {{{ */
  373. {
  374. return php_array_data_compare_string(b, a);
  375. }
  376. /* }}} */
  377. static int php_array_natural_general_compare(Bucket *f, Bucket *s, int fold_case) /* {{{ */
  378. {
  379. zend_string *tmp_str1, *tmp_str2;
  380. zend_string *str1 = zval_get_tmp_string(&f->val, &tmp_str1);
  381. zend_string *str2 = zval_get_tmp_string(&s->val, &tmp_str2);
  382. int result = strnatcmp_ex(ZSTR_VAL(str1), ZSTR_LEN(str1), ZSTR_VAL(str2), ZSTR_LEN(str2), fold_case);
  383. zend_tmp_string_release(tmp_str1);
  384. zend_tmp_string_release(tmp_str2);
  385. return result;
  386. }
  387. /* }}} */
  388. static int php_array_natural_compare(Bucket *a, Bucket *b) /* {{{ */
  389. {
  390. return php_array_natural_general_compare(a, b, 0);
  391. }
  392. /* }}} */
  393. static int php_array_reverse_natural_compare(Bucket *a, Bucket *b) /* {{{ */
  394. {
  395. return php_array_natural_general_compare(b, a, 0);
  396. }
  397. /* }}} */
  398. static int php_array_natural_case_compare(Bucket *a, Bucket *b) /* {{{ */
  399. {
  400. return php_array_natural_general_compare(a, b, 1);
  401. }
  402. /* }}} */
  403. static int php_array_reverse_natural_case_compare(Bucket *a, Bucket *b) /* {{{ */
  404. {
  405. return php_array_natural_general_compare(b, a, 1);
  406. }
  407. /* }}} */
  408. static int php_array_data_compare_string_locale(Bucket *f, Bucket *s) /* {{{ */
  409. {
  410. zval *first = &f->val;
  411. zval *second = &s->val;
  412. if (UNEXPECTED(Z_TYPE_P(first) == IS_INDIRECT)) {
  413. first = Z_INDIRECT_P(first);
  414. }
  415. if (UNEXPECTED(Z_TYPE_P(second) == IS_INDIRECT)) {
  416. second = Z_INDIRECT_P(second);
  417. }
  418. return string_locale_compare_function(first, second);
  419. }
  420. /* }}} */
  421. static int php_array_reverse_data_compare_string_locale(Bucket *a, Bucket *b) /* {{{ */
  422. {
  423. return php_array_data_compare_string_locale(b, a);
  424. }
  425. /* }}} */
  426. static bucket_compare_func_t php_get_key_compare_func(zend_long sort_type, int reverse) /* {{{ */
  427. {
  428. switch (sort_type & ~PHP_SORT_FLAG_CASE) {
  429. case PHP_SORT_NUMERIC:
  430. if (reverse) {
  431. return php_array_reverse_key_compare_numeric;
  432. } else {
  433. return php_array_key_compare_numeric;
  434. }
  435. break;
  436. case PHP_SORT_STRING:
  437. if (sort_type & PHP_SORT_FLAG_CASE) {
  438. if (reverse) {
  439. return php_array_reverse_key_compare_string_case;
  440. } else {
  441. return php_array_key_compare_string_case;
  442. }
  443. } else {
  444. if (reverse) {
  445. return php_array_reverse_key_compare_string;
  446. } else {
  447. return php_array_key_compare_string;
  448. }
  449. }
  450. break;
  451. case PHP_SORT_NATURAL:
  452. if (sort_type & PHP_SORT_FLAG_CASE) {
  453. if (reverse) {
  454. return php_array_reverse_key_compare_string_natural_case;
  455. } else {
  456. return php_array_key_compare_string_natural_case;
  457. }
  458. } else {
  459. if (reverse) {
  460. return php_array_reverse_key_compare_string_natural;
  461. } else {
  462. return php_array_key_compare_string_natural;
  463. }
  464. }
  465. break;
  466. case PHP_SORT_LOCALE_STRING:
  467. if (reverse) {
  468. return php_array_reverse_key_compare_string_locale;
  469. } else {
  470. return php_array_key_compare_string_locale;
  471. }
  472. break;
  473. case PHP_SORT_REGULAR:
  474. default:
  475. if (reverse) {
  476. return php_array_reverse_key_compare;
  477. } else {
  478. return php_array_key_compare;
  479. }
  480. break;
  481. }
  482. return NULL;
  483. }
  484. /* }}} */
  485. static bucket_compare_func_t php_get_data_compare_func(zend_long sort_type, int reverse) /* {{{ */
  486. {
  487. switch (sort_type & ~PHP_SORT_FLAG_CASE) {
  488. case PHP_SORT_NUMERIC:
  489. if (reverse) {
  490. return php_array_reverse_data_compare_numeric;
  491. } else {
  492. return php_array_data_compare_numeric;
  493. }
  494. break;
  495. case PHP_SORT_STRING:
  496. if (sort_type & PHP_SORT_FLAG_CASE) {
  497. if (reverse) {
  498. return php_array_reverse_data_compare_string_case;
  499. } else {
  500. return php_array_data_compare_string_case;
  501. }
  502. } else {
  503. if (reverse) {
  504. return php_array_reverse_data_compare_string;
  505. } else {
  506. return php_array_data_compare_string;
  507. }
  508. }
  509. break;
  510. case PHP_SORT_NATURAL:
  511. if (sort_type & PHP_SORT_FLAG_CASE) {
  512. if (reverse) {
  513. return php_array_reverse_natural_case_compare;
  514. } else {
  515. return php_array_natural_case_compare;
  516. }
  517. } else {
  518. if (reverse) {
  519. return php_array_reverse_natural_compare;
  520. } else {
  521. return php_array_natural_compare;
  522. }
  523. }
  524. break;
  525. case PHP_SORT_LOCALE_STRING:
  526. if (reverse) {
  527. return php_array_reverse_data_compare_string_locale;
  528. } else {
  529. return php_array_data_compare_string_locale;
  530. }
  531. break;
  532. case PHP_SORT_REGULAR:
  533. default:
  534. if (reverse) {
  535. return php_array_reverse_data_compare;
  536. } else {
  537. return php_array_data_compare;
  538. }
  539. break;
  540. }
  541. return NULL;
  542. }
  543. /* }}} */
  544. /* {{{ proto bool krsort(array &array_arg [, int sort_flags])
  545. Sort an array by key value in reverse order */
  546. PHP_FUNCTION(krsort)
  547. {
  548. zval *array;
  549. zend_long sort_type = PHP_SORT_REGULAR;
  550. bucket_compare_func_t cmp;
  551. ZEND_PARSE_PARAMETERS_START(1, 2)
  552. Z_PARAM_ARRAY_EX(array, 0, 1)
  553. Z_PARAM_OPTIONAL
  554. Z_PARAM_LONG(sort_type)
  555. ZEND_PARSE_PARAMETERS_END();
  556. cmp = php_get_key_compare_func(sort_type, 1);
  557. zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
  558. RETURN_TRUE;
  559. }
  560. /* }}} */
  561. /* {{{ proto bool ksort(array &array_arg [, int sort_flags])
  562. Sort an array by key */
  563. PHP_FUNCTION(ksort)
  564. {
  565. zval *array;
  566. zend_long sort_type = PHP_SORT_REGULAR;
  567. bucket_compare_func_t cmp;
  568. ZEND_PARSE_PARAMETERS_START(1, 2)
  569. Z_PARAM_ARRAY_EX(array, 0, 1)
  570. Z_PARAM_OPTIONAL
  571. Z_PARAM_LONG(sort_type)
  572. ZEND_PARSE_PARAMETERS_END();
  573. cmp = php_get_key_compare_func(sort_type, 0);
  574. zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
  575. RETURN_TRUE;
  576. }
  577. /* }}} */
  578. PHPAPI zend_long php_count_recursive(HashTable *ht) /* {{{ */
  579. {
  580. zend_long cnt = 0;
  581. zval *element;
  582. if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
  583. if (GC_IS_RECURSIVE(ht)) {
  584. php_error_docref(NULL, E_WARNING, "Recursion detected");
  585. return 0;
  586. }
  587. GC_PROTECT_RECURSION(ht);
  588. }
  589. cnt = zend_array_count(ht);
  590. ZEND_HASH_FOREACH_VAL(ht, element) {
  591. ZVAL_DEREF(element);
  592. if (Z_TYPE_P(element) == IS_ARRAY) {
  593. cnt += php_count_recursive(Z_ARRVAL_P(element));
  594. }
  595. } ZEND_HASH_FOREACH_END();
  596. if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
  597. GC_UNPROTECT_RECURSION(ht);
  598. }
  599. return cnt;
  600. }
  601. /* }}} */
  602. /* {{{ proto int count(mixed var [, int mode])
  603. Count the number of elements in a variable (usually an array) */
  604. PHP_FUNCTION(count)
  605. {
  606. zval *array;
  607. zend_long mode = COUNT_NORMAL;
  608. zend_long cnt;
  609. ZEND_PARSE_PARAMETERS_START(1, 2)
  610. Z_PARAM_ZVAL(array)
  611. Z_PARAM_OPTIONAL
  612. Z_PARAM_LONG(mode)
  613. ZEND_PARSE_PARAMETERS_END();
  614. if (mode != COUNT_NORMAL && mode != COUNT_RECURSIVE) {
  615. zend_argument_value_error(2, "must be either COUNT_NORMAL or COUNT_RECURSIVE");
  616. RETURN_THROWS();
  617. }
  618. switch (Z_TYPE_P(array)) {
  619. case IS_NULL:
  620. /* Intentionally not converted to an exception */
  621. php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
  622. RETURN_LONG(0);
  623. break;
  624. case IS_ARRAY:
  625. if (mode != COUNT_RECURSIVE) {
  626. cnt = zend_array_count(Z_ARRVAL_P(array));
  627. } else {
  628. cnt = php_count_recursive(Z_ARRVAL_P(array));
  629. }
  630. RETURN_LONG(cnt);
  631. break;
  632. case IS_OBJECT: {
  633. zval retval;
  634. /* first, we check if the handler is defined */
  635. if (Z_OBJ_HT_P(array)->count_elements) {
  636. RETVAL_LONG(1);
  637. if (SUCCESS == Z_OBJ_HT(*array)->count_elements(Z_OBJ_P(array), &Z_LVAL_P(return_value))) {
  638. return;
  639. }
  640. if (EG(exception)) {
  641. RETURN_THROWS();
  642. }
  643. }
  644. /* if not and the object implements Countable we call its count() method */
  645. if (instanceof_function(Z_OBJCE_P(array), zend_ce_countable)) {
  646. zend_call_method_with_0_params(Z_OBJ_P(array), NULL, NULL, "count", &retval);
  647. if (Z_TYPE(retval) != IS_UNDEF) {
  648. RETVAL_LONG(zval_get_long(&retval));
  649. zval_ptr_dtor(&retval);
  650. }
  651. return;
  652. }
  653. /* If There's no handler and it doesn't implement Countable then add a warning */
  654. /* Intentionally not converted to an exception */
  655. php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
  656. RETURN_LONG(1);
  657. break;
  658. }
  659. default:
  660. /* Intentionally not converted to an exception */
  661. php_error_docref(NULL, E_WARNING, "Parameter must be an array or an object that implements Countable");
  662. RETURN_LONG(1);
  663. break;
  664. }
  665. }
  666. /* }}} */
  667. static void php_natsort(INTERNAL_FUNCTION_PARAMETERS, int fold_case) /* {{{ */
  668. {
  669. zval *array;
  670. ZEND_PARSE_PARAMETERS_START(1, 1)
  671. Z_PARAM_ARRAY_EX(array, 0, 1)
  672. ZEND_PARSE_PARAMETERS_END();
  673. if (fold_case) {
  674. zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_case_compare, 0);
  675. } else {
  676. zend_hash_sort(Z_ARRVAL_P(array), php_array_natural_compare, 0);
  677. }
  678. RETURN_TRUE;
  679. }
  680. /* }}} */
  681. /* {{{ proto void natsort(array &array_arg)
  682. Sort an array using natural sort */
  683. PHP_FUNCTION(natsort)
  684. {
  685. php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  686. }
  687. /* }}} */
  688. /* {{{ proto void natcasesort(array &array_arg)
  689. Sort an array using case-insensitive natural sort */
  690. PHP_FUNCTION(natcasesort)
  691. {
  692. php_natsort(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  693. }
  694. /* }}} */
  695. /* {{{ proto bool asort(array &array_arg [, int sort_flags])
  696. Sort an array and maintain index association */
  697. PHP_FUNCTION(asort)
  698. {
  699. zval *array;
  700. zend_long sort_type = PHP_SORT_REGULAR;
  701. bucket_compare_func_t cmp;
  702. ZEND_PARSE_PARAMETERS_START(1, 2)
  703. Z_PARAM_ARRAY_EX(array, 0, 1)
  704. Z_PARAM_OPTIONAL
  705. Z_PARAM_LONG(sort_type)
  706. ZEND_PARSE_PARAMETERS_END();
  707. cmp = php_get_data_compare_func(sort_type, 0);
  708. zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
  709. RETURN_TRUE;
  710. }
  711. /* }}} */
  712. /* {{{ proto bool arsort(array &array_arg [, int sort_flags])
  713. Sort an array in reverse order and maintain index association */
  714. PHP_FUNCTION(arsort)
  715. {
  716. zval *array;
  717. zend_long sort_type = PHP_SORT_REGULAR;
  718. bucket_compare_func_t cmp;
  719. ZEND_PARSE_PARAMETERS_START(1, 2)
  720. Z_PARAM_ARRAY_EX(array, 0, 1)
  721. Z_PARAM_OPTIONAL
  722. Z_PARAM_LONG(sort_type)
  723. ZEND_PARSE_PARAMETERS_END();
  724. cmp = php_get_data_compare_func(sort_type, 1);
  725. zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);
  726. RETURN_TRUE;
  727. }
  728. /* }}} */
  729. /* {{{ proto bool sort(array &array_arg [, int sort_flags])
  730. Sort an array */
  731. PHP_FUNCTION(sort)
  732. {
  733. zval *array;
  734. zend_long sort_type = PHP_SORT_REGULAR;
  735. bucket_compare_func_t cmp;
  736. ZEND_PARSE_PARAMETERS_START(1, 2)
  737. Z_PARAM_ARRAY_EX(array, 0, 1)
  738. Z_PARAM_OPTIONAL
  739. Z_PARAM_LONG(sort_type)
  740. ZEND_PARSE_PARAMETERS_END();
  741. cmp = php_get_data_compare_func(sort_type, 0);
  742. zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
  743. RETURN_TRUE;
  744. }
  745. /* }}} */
  746. /* {{{ proto bool rsort(array &array_arg [, int sort_flags])
  747. Sort an array in reverse order */
  748. PHP_FUNCTION(rsort)
  749. {
  750. zval *array;
  751. zend_long sort_type = PHP_SORT_REGULAR;
  752. bucket_compare_func_t cmp;
  753. ZEND_PARSE_PARAMETERS_START(1, 2)
  754. Z_PARAM_ARRAY_EX(array, 0, 1)
  755. Z_PARAM_OPTIONAL
  756. Z_PARAM_LONG(sort_type)
  757. ZEND_PARSE_PARAMETERS_END();
  758. cmp = php_get_data_compare_func(sort_type, 1);
  759. zend_hash_sort(Z_ARRVAL_P(array), cmp, 1);
  760. RETURN_TRUE;
  761. }
  762. /* }}} */
  763. static int php_array_user_compare(Bucket *f, Bucket *s) /* {{{ */
  764. {
  765. zval args[2];
  766. zval retval;
  767. ZVAL_COPY(&args[0], &f->val);
  768. ZVAL_COPY(&args[1], &s->val);
  769. BG(user_compare_fci).param_count = 2;
  770. BG(user_compare_fci).params = args;
  771. BG(user_compare_fci).retval = &retval;
  772. BG(user_compare_fci).no_separation = 0;
  773. if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
  774. zend_long ret = zval_get_long(&retval);
  775. zval_ptr_dtor(&retval);
  776. zval_ptr_dtor(&args[1]);
  777. zval_ptr_dtor(&args[0]);
  778. return ZEND_NORMALIZE_BOOL(ret);
  779. } else {
  780. zval_ptr_dtor(&args[1]);
  781. zval_ptr_dtor(&args[0]);
  782. return 0;
  783. }
  784. }
  785. /* }}} */
  786. /* check if comparison function is valid */
  787. #define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
  788. if (!zend_is_callable(*func_name, 0, NULL)) { \
  789. php_error_docref(NULL, E_WARNING, "Invalid comparison function"); \
  790. BG(user_compare_fci) = old_user_compare_fci; \
  791. BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
  792. RETURN_FALSE; \
  793. } \
  794. /* Clear FCI cache otherwise : for example the same or other array with
  795. * (partly) the same key values has been sorted with uasort() or
  796. * other sorting function the comparison is cached, however the name
  797. * of the function for comparison is not respected. see bug #28739 AND #33295
  798. *
  799. * Following defines will assist in backup / restore values. */
  800. #define PHP_ARRAY_CMP_FUNC_VARS \
  801. zend_fcall_info old_user_compare_fci; \
  802. zend_fcall_info_cache old_user_compare_fci_cache \
  803. #define PHP_ARRAY_CMP_FUNC_BACKUP() \
  804. old_user_compare_fci = BG(user_compare_fci); \
  805. old_user_compare_fci_cache = BG(user_compare_fci_cache); \
  806. BG(user_compare_fci_cache) = empty_fcall_info_cache; \
  807. #define PHP_ARRAY_CMP_FUNC_RESTORE() \
  808. zend_release_fcall_info_cache(&BG(user_compare_fci_cache)); \
  809. BG(user_compare_fci) = old_user_compare_fci; \
  810. BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
  811. static void php_usort(INTERNAL_FUNCTION_PARAMETERS, bucket_compare_func_t compare_func, zend_bool renumber) /* {{{ */
  812. {
  813. zval *array;
  814. zend_array *arr;
  815. PHP_ARRAY_CMP_FUNC_VARS;
  816. PHP_ARRAY_CMP_FUNC_BACKUP();
  817. ZEND_PARSE_PARAMETERS_START(2, 2)
  818. Z_PARAM_ARRAY_EX2(array, 0, 1, 0)
  819. Z_PARAM_FUNC(BG(user_compare_fci), BG(user_compare_fci_cache))
  820. ZEND_PARSE_PARAMETERS_END_EX( PHP_ARRAY_CMP_FUNC_RESTORE(); return );
  821. arr = Z_ARR_P(array);
  822. if (zend_hash_num_elements(arr) == 0) {
  823. PHP_ARRAY_CMP_FUNC_RESTORE();
  824. RETURN_TRUE;
  825. }
  826. /* Copy array, so the in-place modifications will not be visible to the callback function */
  827. arr = zend_array_dup(arr);
  828. zend_hash_sort(arr, compare_func, renumber);
  829. zval_ptr_dtor(array);
  830. ZVAL_ARR(array, arr);
  831. PHP_ARRAY_CMP_FUNC_RESTORE();
  832. RETURN_TRUE;
  833. }
  834. /* }}} */
  835. /* {{{ proto bool usort(array array_arg, string cmp_function)
  836. Sort an array by values using a user-defined comparison function */
  837. PHP_FUNCTION(usort)
  838. {
  839. php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 1);
  840. }
  841. /* }}} */
  842. /* {{{ proto bool uasort(array array_arg, string cmp_function)
  843. Sort an array with a user-defined comparison function and maintain index association */
  844. PHP_FUNCTION(uasort)
  845. {
  846. php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_compare, 0);
  847. }
  848. /* }}} */
  849. static int php_array_user_key_compare(Bucket *f, Bucket *s) /* {{{ */
  850. {
  851. zval args[2];
  852. zval retval;
  853. zend_long result;
  854. if (f->key == NULL) {
  855. ZVAL_LONG(&args[0], f->h);
  856. } else {
  857. ZVAL_STR_COPY(&args[0], f->key);
  858. }
  859. if (s->key == NULL) {
  860. ZVAL_LONG(&args[1], s->h);
  861. } else {
  862. ZVAL_STR_COPY(&args[1], s->key);
  863. }
  864. BG(user_compare_fci).param_count = 2;
  865. BG(user_compare_fci).params = args;
  866. BG(user_compare_fci).retval = &retval;
  867. BG(user_compare_fci).no_separation = 0;
  868. if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
  869. result = zval_get_long(&retval);
  870. zval_ptr_dtor(&retval);
  871. } else {
  872. result = 0;
  873. }
  874. zval_ptr_dtor(&args[0]);
  875. zval_ptr_dtor(&args[1]);
  876. return ZEND_NORMALIZE_BOOL(result);
  877. }
  878. /* }}} */
  879. /* {{{ proto bool uksort(array array_arg, string cmp_function)
  880. Sort an array by keys using a user-defined comparison function */
  881. PHP_FUNCTION(uksort)
  882. {
  883. php_usort(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_array_user_key_compare, 0);
  884. }
  885. /* }}} */
  886. /* {{{ proto mixed end(array array_arg)
  887. Advances array argument's internal pointer to the last element and return it */
  888. PHP_FUNCTION(end)
  889. {
  890. HashTable *array;
  891. zval *entry;
  892. ZEND_PARSE_PARAMETERS_START(1, 1)
  893. Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
  894. ZEND_PARSE_PARAMETERS_END();
  895. zend_hash_internal_pointer_end(array);
  896. if (USED_RET()) {
  897. if ((entry = zend_hash_get_current_data(array)) == NULL) {
  898. RETURN_FALSE;
  899. }
  900. if (Z_TYPE_P(entry) == IS_INDIRECT) {
  901. entry = Z_INDIRECT_P(entry);
  902. }
  903. ZVAL_COPY_DEREF(return_value, entry);
  904. }
  905. }
  906. /* }}} */
  907. /* {{{ proto mixed prev(array array_arg)
  908. Move array argument's internal pointer to the previous element and return it */
  909. PHP_FUNCTION(prev)
  910. {
  911. HashTable *array;
  912. zval *entry;
  913. ZEND_PARSE_PARAMETERS_START(1, 1)
  914. Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
  915. ZEND_PARSE_PARAMETERS_END();
  916. zend_hash_move_backwards(array);
  917. if (USED_RET()) {
  918. if ((entry = zend_hash_get_current_data(array)) == NULL) {
  919. RETURN_FALSE;
  920. }
  921. if (Z_TYPE_P(entry) == IS_INDIRECT) {
  922. entry = Z_INDIRECT_P(entry);
  923. }
  924. ZVAL_COPY_DEREF(return_value, entry);
  925. }
  926. }
  927. /* }}} */
  928. /* {{{ proto mixed next(array array_arg)
  929. Move array argument's internal pointer to the next element and return it */
  930. PHP_FUNCTION(next)
  931. {
  932. HashTable *array;
  933. zval *entry;
  934. ZEND_PARSE_PARAMETERS_START(1, 1)
  935. Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
  936. ZEND_PARSE_PARAMETERS_END();
  937. zend_hash_move_forward(array);
  938. if (USED_RET()) {
  939. if ((entry = zend_hash_get_current_data(array)) == NULL) {
  940. RETURN_FALSE;
  941. }
  942. if (Z_TYPE_P(entry) == IS_INDIRECT) {
  943. entry = Z_INDIRECT_P(entry);
  944. }
  945. ZVAL_COPY_DEREF(return_value, entry);
  946. }
  947. }
  948. /* }}} */
  949. /* {{{ proto mixed reset(array array_arg)
  950. Set array argument's internal pointer to the first element and return it */
  951. PHP_FUNCTION(reset)
  952. {
  953. HashTable *array;
  954. zval *entry;
  955. ZEND_PARSE_PARAMETERS_START(1, 1)
  956. Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
  957. ZEND_PARSE_PARAMETERS_END();
  958. zend_hash_internal_pointer_reset(array);
  959. if (USED_RET()) {
  960. if ((entry = zend_hash_get_current_data(array)) == NULL) {
  961. RETURN_FALSE;
  962. }
  963. if (Z_TYPE_P(entry) == IS_INDIRECT) {
  964. entry = Z_INDIRECT_P(entry);
  965. }
  966. ZVAL_COPY_DEREF(return_value, entry);
  967. }
  968. }
  969. /* }}} */
  970. /* {{{ proto mixed current(array array_arg)
  971. Return the element currently pointed to by the internal array pointer */
  972. PHP_FUNCTION(current)
  973. {
  974. HashTable *array;
  975. zval *entry;
  976. ZEND_PARSE_PARAMETERS_START(1, 1)
  977. Z_PARAM_ARRAY_OR_OBJECT_HT(array)
  978. ZEND_PARSE_PARAMETERS_END();
  979. if ((entry = zend_hash_get_current_data(array)) == NULL) {
  980. RETURN_FALSE;
  981. }
  982. if (Z_TYPE_P(entry) == IS_INDIRECT) {
  983. entry = Z_INDIRECT_P(entry);
  984. }
  985. ZVAL_COPY_DEREF(return_value, entry);
  986. }
  987. /* }}} */
  988. /* {{{ proto mixed key(array array_arg)
  989. Return the key of the element currently pointed to by the internal array pointer */
  990. PHP_FUNCTION(key)
  991. {
  992. HashTable *array;
  993. ZEND_PARSE_PARAMETERS_START(1, 1)
  994. Z_PARAM_ARRAY_OR_OBJECT_HT(array)
  995. ZEND_PARSE_PARAMETERS_END();
  996. zend_hash_get_current_key_zval(array, return_value);
  997. }
  998. /* }}} */
  999. /* {{{
  1000. * proto mixed min(array values)
  1001. * proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
  1002. Return the lowest value in an array or a series of arguments */
  1003. PHP_FUNCTION(min)
  1004. {
  1005. int argc;
  1006. zval *args = NULL;
  1007. ZEND_PARSE_PARAMETERS_START(1, -1)
  1008. Z_PARAM_VARIADIC('+', args, argc)
  1009. ZEND_PARSE_PARAMETERS_END();
  1010. /* mixed min ( array $values ) */
  1011. if (argc == 1) {
  1012. zval *result;
  1013. if (Z_TYPE(args[0]) != IS_ARRAY) {
  1014. zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
  1015. RETURN_THROWS();
  1016. } else {
  1017. if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) {
  1018. ZVAL_COPY_DEREF(return_value, result);
  1019. } else {
  1020. zend_argument_value_error(1, "must contain at least one element");
  1021. RETURN_THROWS();
  1022. }
  1023. }
  1024. } else {
  1025. /* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
  1026. zval *min, result;
  1027. int i;
  1028. min = &args[0];
  1029. for (i = 1; i < argc; i++) {
  1030. is_smaller_function(&result, &args[i], min);
  1031. if (Z_TYPE(result) == IS_TRUE) {
  1032. min = &args[i];
  1033. }
  1034. }
  1035. ZVAL_COPY(return_value, min);
  1036. }
  1037. }
  1038. /* }}} */
  1039. /* {{{
  1040. * proto mixed max(array values)
  1041. * proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
  1042. Return the highest value in an array or a series of arguments */
  1043. PHP_FUNCTION(max)
  1044. {
  1045. zval *args = NULL;
  1046. int argc;
  1047. ZEND_PARSE_PARAMETERS_START(1, -1)
  1048. Z_PARAM_VARIADIC('+', args, argc)
  1049. ZEND_PARSE_PARAMETERS_END();
  1050. /* mixed max ( array $values ) */
  1051. if (argc == 1) {
  1052. zval *result;
  1053. if (Z_TYPE(args[0]) != IS_ARRAY) {
  1054. zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
  1055. RETURN_THROWS();
  1056. } else {
  1057. if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) {
  1058. ZVAL_COPY_DEREF(return_value, result);
  1059. } else {
  1060. zend_argument_value_error(1, "must contain at least one element");
  1061. RETURN_THROWS();
  1062. }
  1063. }
  1064. } else {
  1065. /* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
  1066. zval *max, result;
  1067. int i;
  1068. max = &args[0];
  1069. for (i = 1; i < argc; i++) {
  1070. is_smaller_or_equal_function(&result, &args[i], max);
  1071. if (Z_TYPE(result) == IS_FALSE) {
  1072. max = &args[i];
  1073. }
  1074. }
  1075. ZVAL_COPY(return_value, max);
  1076. }
  1077. }
  1078. /* }}} */
  1079. static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
  1080. {
  1081. zval args[3], /* Arguments to userland function */
  1082. retval, /* Return value - unused */
  1083. *zv;
  1084. HashTable *target_hash = HASH_OF(array);
  1085. HashPosition pos;
  1086. uint32_t ht_iter;
  1087. int result = SUCCESS;
  1088. /* Set up known arguments */
  1089. ZVAL_UNDEF(&args[1]);
  1090. if (userdata) {
  1091. ZVAL_COPY(&args[2], userdata);
  1092. }
  1093. BG(array_walk_fci).retval = &retval;
  1094. BG(array_walk_fci).param_count = userdata ? 3 : 2;
  1095. BG(array_walk_fci).params = args;
  1096. BG(array_walk_fci).no_separation = 0;
  1097. zend_hash_internal_pointer_reset_ex(target_hash, &pos);
  1098. ht_iter = zend_hash_iterator_add(target_hash, pos);
  1099. /* Iterate through hash */
  1100. do {
  1101. /* Retrieve value */
  1102. zv = zend_hash_get_current_data_ex(target_hash, &pos);
  1103. if (zv == NULL) {
  1104. break;
  1105. }
  1106. /* Skip undefined indirect elements */
  1107. if (Z_TYPE_P(zv) == IS_INDIRECT) {
  1108. zv = Z_INDIRECT_P(zv);
  1109. if (Z_TYPE_P(zv) == IS_UNDEF) {
  1110. zend_hash_move_forward_ex(target_hash, &pos);
  1111. continue;
  1112. }
  1113. }
  1114. /* Ensure the value is a reference. Otherwise the location of the value may be freed. */
  1115. ZVAL_MAKE_REF(zv);
  1116. /* Retrieve key */
  1117. zend_hash_get_current_key_zval_ex(target_hash, &args[1], &pos);
  1118. /* Move to next element already now -- this mirrors the approach used by foreach
  1119. * and ensures proper behavior with regard to modifications. */
  1120. zend_hash_move_forward_ex(target_hash, &pos);
  1121. /* Back up hash position, as it may change */
  1122. EG(ht_iterators)[ht_iter].pos = pos;
  1123. if (recursive && Z_TYPE_P(Z_REFVAL_P(zv)) == IS_ARRAY) {
  1124. HashTable *thash;
  1125. zend_fcall_info orig_array_walk_fci;
  1126. zend_fcall_info_cache orig_array_walk_fci_cache;
  1127. zval ref;
  1128. ZVAL_COPY_VALUE(&ref, zv);
  1129. ZVAL_DEREF(zv);
  1130. SEPARATE_ARRAY(zv);
  1131. thash = Z_ARRVAL_P(zv);
  1132. if (GC_IS_RECURSIVE(thash)) {
  1133. zend_throw_error(NULL, "Recursion detected");
  1134. result = FAILURE;
  1135. break;
  1136. }
  1137. /* backup the fcall info and cache */
  1138. orig_array_walk_fci = BG(array_walk_fci);
  1139. orig_array_walk_fci_cache = BG(array_walk_fci_cache);
  1140. Z_ADDREF(ref);
  1141. GC_PROTECT_RECURSION(thash);
  1142. result = php_array_walk(zv, userdata, recursive);
  1143. if (Z_TYPE_P(Z_REFVAL(ref)) == IS_ARRAY && thash == Z_ARRVAL_P(Z_REFVAL(ref))) {
  1144. /* If the hashtable changed in the meantime, we'll "leak" this apply count
  1145. * increment -- our reference to thash is no longer valid. */
  1146. GC_UNPROTECT_RECURSION(thash);
  1147. }
  1148. zval_ptr_dtor(&ref);
  1149. /* restore the fcall info and cache */
  1150. BG(array_walk_fci) = orig_array_walk_fci;
  1151. BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
  1152. } else {
  1153. ZVAL_COPY(&args[0], zv);
  1154. /* Call the userland function */
  1155. result = zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache));
  1156. if (result == SUCCESS) {
  1157. zval_ptr_dtor(&retval);
  1158. }
  1159. zval_ptr_dtor(&args[0]);
  1160. }
  1161. if (Z_TYPE(args[1]) != IS_UNDEF) {
  1162. zval_ptr_dtor(&args[1]);
  1163. ZVAL_UNDEF(&args[1]);
  1164. }
  1165. if (result == FAILURE) {
  1166. break;
  1167. }
  1168. /* Reload array and position -- both may have changed */
  1169. if (Z_TYPE_P(array) == IS_ARRAY) {
  1170. pos = zend_hash_iterator_pos_ex(ht_iter, array);
  1171. target_hash = Z_ARRVAL_P(array);
  1172. } else if (Z_TYPE_P(array) == IS_OBJECT) {
  1173. target_hash = Z_OBJPROP_P(array);
  1174. pos = zend_hash_iterator_pos(ht_iter, target_hash);
  1175. } else {
  1176. zend_type_error("Iterated value is no longer an array or object");
  1177. result = FAILURE;
  1178. break;
  1179. }
  1180. } while (!EG(exception));
  1181. if (userdata) {
  1182. zval_ptr_dtor(&args[2]);
  1183. }
  1184. zend_hash_iterator_del(ht_iter);
  1185. return result;
  1186. }
  1187. /* }}} */
  1188. /* {{{ proto bool array_walk(array input, string funcname [, mixed userdata])
  1189. Apply a user function to every member of an array */
  1190. PHP_FUNCTION(array_walk)
  1191. {
  1192. zval *array;
  1193. zval *userdata = NULL;
  1194. zend_fcall_info orig_array_walk_fci;
  1195. zend_fcall_info_cache orig_array_walk_fci_cache;
  1196. orig_array_walk_fci = BG(array_walk_fci);
  1197. orig_array_walk_fci_cache = BG(array_walk_fci_cache);
  1198. ZEND_PARSE_PARAMETERS_START(2, 3)
  1199. Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
  1200. Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
  1201. Z_PARAM_OPTIONAL
  1202. Z_PARAM_ZVAL(userdata)
  1203. ZEND_PARSE_PARAMETERS_END_EX(
  1204. BG(array_walk_fci) = orig_array_walk_fci;
  1205. BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
  1206. return
  1207. );
  1208. php_array_walk(array, userdata, 0);
  1209. zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
  1210. BG(array_walk_fci) = orig_array_walk_fci;
  1211. BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
  1212. RETURN_TRUE;
  1213. }
  1214. /* }}} */
  1215. /* {{{ proto bool array_walk_recursive(array input, string funcname [, mixed userdata])
  1216. Apply a user function recursively to every member of an array */
  1217. PHP_FUNCTION(array_walk_recursive)
  1218. {
  1219. zval *array;
  1220. zval *userdata = NULL;
  1221. zend_fcall_info orig_array_walk_fci;
  1222. zend_fcall_info_cache orig_array_walk_fci_cache;
  1223. orig_array_walk_fci = BG(array_walk_fci);
  1224. orig_array_walk_fci_cache = BG(array_walk_fci_cache);
  1225. ZEND_PARSE_PARAMETERS_START(2, 3)
  1226. Z_PARAM_ARRAY_OR_OBJECT_EX(array, 0, 1)
  1227. Z_PARAM_FUNC(BG(array_walk_fci), BG(array_walk_fci_cache))
  1228. Z_PARAM_OPTIONAL
  1229. Z_PARAM_ZVAL(userdata)
  1230. ZEND_PARSE_PARAMETERS_END_EX(
  1231. BG(array_walk_fci) = orig_array_walk_fci;
  1232. BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
  1233. return
  1234. );
  1235. php_array_walk(array, userdata, 1);
  1236. zend_release_fcall_info_cache(&BG(array_walk_fci_cache));
  1237. BG(array_walk_fci) = orig_array_walk_fci;
  1238. BG(array_walk_fci_cache) = orig_array_walk_fci_cache;
  1239. RETURN_TRUE;
  1240. }
  1241. /* }}} */
  1242. /* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
  1243. * 0 = return boolean
  1244. * 1 = return key
  1245. */
  1246. static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
  1247. {
  1248. zval *value, /* value to check for */
  1249. *array, /* array to check in */
  1250. *entry; /* pointer to array entry */
  1251. zend_ulong num_idx;
  1252. zend_string *str_idx;
  1253. zend_bool strict = 0; /* strict comparison or not */
  1254. ZEND_PARSE_PARAMETERS_START(2, 3)
  1255. Z_PARAM_ZVAL(value)
  1256. Z_PARAM_ARRAY(array)
  1257. Z_PARAM_OPTIONAL
  1258. Z_PARAM_BOOL(strict)
  1259. ZEND_PARSE_PARAMETERS_END();
  1260. if (strict) {
  1261. if (Z_TYPE_P(value) == IS_LONG) {
  1262. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  1263. ZVAL_DEREF(entry);
  1264. if (Z_TYPE_P(entry) == IS_LONG && Z_LVAL_P(entry) == Z_LVAL_P(value)) {
  1265. if (behavior == 0) {
  1266. RETURN_TRUE;
  1267. } else {
  1268. if (str_idx) {
  1269. RETVAL_STR_COPY(str_idx);
  1270. } else {
  1271. RETVAL_LONG(num_idx);
  1272. }
  1273. return;
  1274. }
  1275. }
  1276. } ZEND_HASH_FOREACH_END();
  1277. } else {
  1278. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  1279. ZVAL_DEREF(entry);
  1280. if (fast_is_identical_function(value, entry)) {
  1281. if (behavior == 0) {
  1282. RETURN_TRUE;
  1283. } else {
  1284. if (str_idx) {
  1285. RETVAL_STR_COPY(str_idx);
  1286. } else {
  1287. RETVAL_LONG(num_idx);
  1288. }
  1289. return;
  1290. }
  1291. }
  1292. } ZEND_HASH_FOREACH_END();
  1293. }
  1294. } else {
  1295. if (Z_TYPE_P(value) == IS_LONG) {
  1296. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  1297. if (fast_equal_check_long(value, entry)) {
  1298. if (behavior == 0) {
  1299. RETURN_TRUE;
  1300. } else {
  1301. if (str_idx) {
  1302. RETVAL_STR_COPY(str_idx);
  1303. } else {
  1304. RETVAL_LONG(num_idx);
  1305. }
  1306. return;
  1307. }
  1308. }
  1309. } ZEND_HASH_FOREACH_END();
  1310. } else if (Z_TYPE_P(value) == IS_STRING) {
  1311. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  1312. if (fast_equal_check_string(value, entry)) {
  1313. if (behavior == 0) {
  1314. RETURN_TRUE;
  1315. } else {
  1316. if (str_idx) {
  1317. RETVAL_STR_COPY(str_idx);
  1318. } else {
  1319. RETVAL_LONG(num_idx);
  1320. }
  1321. return;
  1322. }
  1323. }
  1324. } ZEND_HASH_FOREACH_END();
  1325. } else {
  1326. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  1327. if (fast_equal_check_function(value, entry)) {
  1328. if (behavior == 0) {
  1329. RETURN_TRUE;
  1330. } else {
  1331. if (str_idx) {
  1332. RETVAL_STR_COPY(str_idx);
  1333. } else {
  1334. RETVAL_LONG(num_idx);
  1335. }
  1336. return;
  1337. }
  1338. }
  1339. } ZEND_HASH_FOREACH_END();
  1340. }
  1341. }
  1342. RETURN_FALSE;
  1343. }
  1344. /* }}} */
  1345. /* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
  1346. Checks if the given value exists in the array */
  1347. PHP_FUNCTION(in_array)
  1348. {
  1349. php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  1350. }
  1351. /* }}} */
  1352. /* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
  1353. Searches the array for a given value and returns the corresponding key if successful */
  1354. PHP_FUNCTION(array_search)
  1355. {
  1356. php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  1357. }
  1358. /* }}} */
  1359. static zend_always_inline int php_valid_var_name(const char *var_name, size_t var_name_len) /* {{{ */
  1360. {
  1361. #if 1
  1362. /* first 256 bits for first character, and second 256 bits for the next */
  1363. static const uint32_t charset[8] = {
  1364. /* 31 0 63 32 95 64 127 96 */
  1365. 0x00000000, 0x00000000, 0x87fffffe, 0x07fffffe,
  1366. 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
  1367. static const uint32_t charset2[8] = {
  1368. /* 31 0 63 32 95 64 127 96 */
  1369. 0x00000000, 0x03ff0000, 0x87fffffe, 0x07fffffe,
  1370. 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
  1371. #endif
  1372. size_t i;
  1373. uint32_t ch;
  1374. if (UNEXPECTED(!var_name_len)) {
  1375. return 0;
  1376. }
  1377. /* These are allowed as first char: [a-zA-Z_\x7f-\xff] */
  1378. ch = (uint32_t)((unsigned char *)var_name)[0];
  1379. #if 1
  1380. if (UNEXPECTED(!ZEND_BIT_TEST(charset, ch))) {
  1381. #else
  1382. if (var_name[0] != '_' &&
  1383. (ch < 65 /* A */ || /* Z */ ch > 90) &&
  1384. (ch < 97 /* a */ || /* z */ ch > 122) &&
  1385. (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
  1386. ) {
  1387. #endif
  1388. return 0;
  1389. }
  1390. /* And these as the rest: [a-zA-Z0-9_\x7f-\xff] */
  1391. if (var_name_len > 1) {
  1392. i = 1;
  1393. do {
  1394. ch = (uint32_t)((unsigned char *)var_name)[i];
  1395. #if 1
  1396. if (UNEXPECTED(!ZEND_BIT_TEST(charset2, ch))) {
  1397. #else
  1398. if (var_name[i] != '_' &&
  1399. (ch < 48 /* 0 */ || /* 9 */ ch > 57) &&
  1400. (ch < 65 /* A */ || /* Z */ ch > 90) &&
  1401. (ch < 97 /* a */ || /* z */ ch > 122) &&
  1402. (ch < 127 /* 0x7f */ || /* 0xff */ ch > 255)
  1403. ) {
  1404. #endif
  1405. return 0;
  1406. }
  1407. } while (++i < var_name_len);
  1408. }
  1409. return 1;
  1410. }
  1411. /* }}} */
  1412. PHPAPI int php_prefix_varname(zval *result, zend_string *prefix, const char *var_name, size_t var_name_len, zend_bool add_underscore) /* {{{ */
  1413. {
  1414. ZVAL_NEW_STR(result, zend_string_alloc(ZSTR_LEN(prefix) + (add_underscore ? 1 : 0) + var_name_len, 0));
  1415. memcpy(Z_STRVAL_P(result), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
  1416. if (add_underscore) {
  1417. Z_STRVAL_P(result)[ZSTR_LEN(prefix)] = '_';
  1418. }
  1419. memcpy(Z_STRVAL_P(result) + ZSTR_LEN(prefix) + (add_underscore ? 1 : 0), var_name, var_name_len + 1);
  1420. return SUCCESS;
  1421. }
  1422. /* }}} */
  1423. static zend_long php_extract_ref_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
  1424. {
  1425. zend_long count = 0;
  1426. zend_string *var_name;
  1427. zval *entry, *orig_var;
  1428. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1429. if (!var_name) {
  1430. continue;
  1431. }
  1432. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1433. if (orig_var) {
  1434. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1435. orig_var = Z_INDIRECT_P(orig_var);
  1436. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1437. continue;
  1438. }
  1439. }
  1440. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1441. continue;
  1442. }
  1443. if (zend_string_equals_literal(var_name, "GLOBALS")) {
  1444. continue;
  1445. }
  1446. if (zend_string_equals_literal(var_name, "this")) {
  1447. zend_throw_error(NULL, "Cannot re-assign $this");
  1448. return -1;
  1449. }
  1450. if (Z_ISREF_P(entry)) {
  1451. Z_ADDREF_P(entry);
  1452. } else {
  1453. ZVAL_MAKE_REF_EX(entry, 2);
  1454. }
  1455. zval_ptr_dtor(orig_var);
  1456. ZVAL_REF(orig_var, Z_REF_P(entry));
  1457. count++;
  1458. }
  1459. } ZEND_HASH_FOREACH_END();
  1460. return count;
  1461. }
  1462. /* }}} */
  1463. static zend_long php_extract_if_exists(zend_array *arr, zend_array *symbol_table) /* {{{ */
  1464. {
  1465. zend_long count = 0;
  1466. zend_string *var_name;
  1467. zval *entry, *orig_var;
  1468. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1469. if (!var_name) {
  1470. continue;
  1471. }
  1472. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1473. if (orig_var) {
  1474. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1475. orig_var = Z_INDIRECT_P(orig_var);
  1476. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1477. continue;
  1478. }
  1479. }
  1480. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1481. continue;
  1482. }
  1483. if (zend_string_equals_literal(var_name, "GLOBALS")) {
  1484. continue;
  1485. }
  1486. if (zend_string_equals_literal(var_name, "this")) {
  1487. zend_throw_error(NULL, "Cannot re-assign $this");
  1488. return -1;
  1489. }
  1490. ZVAL_DEREF(entry);
  1491. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1492. if (UNEXPECTED(EG(exception))) {
  1493. return -1;
  1494. }
  1495. count++;
  1496. }
  1497. } ZEND_HASH_FOREACH_END();
  1498. return count;
  1499. }
  1500. /* }}} */
  1501. static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
  1502. {
  1503. zend_long count = 0;
  1504. zend_string *var_name;
  1505. zval *entry, *orig_var;
  1506. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1507. if (!var_name) {
  1508. continue;
  1509. }
  1510. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1511. continue;
  1512. }
  1513. if (zend_string_equals_literal(var_name, "this")) {
  1514. zend_throw_error(NULL, "Cannot re-assign $this");
  1515. return -1;
  1516. }
  1517. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1518. if (orig_var) {
  1519. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1520. orig_var = Z_INDIRECT_P(orig_var);
  1521. }
  1522. if (zend_string_equals_literal(var_name, "GLOBALS")) {
  1523. continue;
  1524. }
  1525. if (Z_ISREF_P(entry)) {
  1526. Z_ADDREF_P(entry);
  1527. } else {
  1528. ZVAL_MAKE_REF_EX(entry, 2);
  1529. }
  1530. zval_ptr_dtor(orig_var);
  1531. ZVAL_REF(orig_var, Z_REF_P(entry));
  1532. } else {
  1533. if (Z_ISREF_P(entry)) {
  1534. Z_ADDREF_P(entry);
  1535. } else {
  1536. ZVAL_MAKE_REF_EX(entry, 2);
  1537. }
  1538. zend_hash_add_new(symbol_table, var_name, entry);
  1539. }
  1540. count++;
  1541. } ZEND_HASH_FOREACH_END();
  1542. return count;
  1543. }
  1544. /* }}} */
  1545. static zend_long php_extract_overwrite(zend_array *arr, zend_array *symbol_table) /* {{{ */
  1546. {
  1547. zend_long count = 0;
  1548. zend_string *var_name;
  1549. zval *entry, *orig_var;
  1550. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1551. if (!var_name) {
  1552. continue;
  1553. }
  1554. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1555. continue;
  1556. }
  1557. if (zend_string_equals_literal(var_name, "this")) {
  1558. zend_throw_error(NULL, "Cannot re-assign $this");
  1559. return -1;
  1560. }
  1561. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1562. if (orig_var) {
  1563. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1564. orig_var = Z_INDIRECT_P(orig_var);
  1565. }
  1566. if (zend_string_equals_literal(var_name, "GLOBALS")) {
  1567. continue;
  1568. }
  1569. ZVAL_DEREF(entry);
  1570. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1571. if (UNEXPECTED(EG(exception))) {
  1572. return -1;
  1573. }
  1574. } else {
  1575. ZVAL_DEREF(entry);
  1576. Z_TRY_ADDREF_P(entry);
  1577. zend_hash_add_new(symbol_table, var_name, entry);
  1578. }
  1579. count++;
  1580. } ZEND_HASH_FOREACH_END();
  1581. return count;
  1582. }
  1583. /* }}} */
  1584. static zend_long php_extract_ref_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1585. {
  1586. zend_long count = 0;
  1587. zend_string *var_name;
  1588. zval *entry, *orig_var, final_name;
  1589. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1590. if (!var_name) {
  1591. continue;
  1592. }
  1593. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1594. if (orig_var) {
  1595. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1596. orig_var = Z_INDIRECT_P(orig_var);
  1597. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1598. if (Z_ISREF_P(entry)) {
  1599. Z_ADDREF_P(entry);
  1600. } else {
  1601. ZVAL_MAKE_REF_EX(entry, 2);
  1602. }
  1603. ZVAL_REF(orig_var, Z_REF_P(entry));
  1604. count++;
  1605. continue;
  1606. }
  1607. }
  1608. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1609. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1610. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1611. zend_throw_error(NULL, "Cannot re-assign $this");
  1612. return -1;
  1613. } else {
  1614. if (Z_ISREF_P(entry)) {
  1615. Z_ADDREF_P(entry);
  1616. } else {
  1617. ZVAL_MAKE_REF_EX(entry, 2);
  1618. }
  1619. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1620. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1621. orig_var = Z_INDIRECT_P(orig_var);
  1622. }
  1623. zval_ptr_dtor(orig_var);
  1624. ZVAL_REF(orig_var, Z_REF_P(entry));
  1625. } else {
  1626. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1627. }
  1628. count++;
  1629. }
  1630. }
  1631. zval_ptr_dtor_str(&final_name);
  1632. }
  1633. } ZEND_HASH_FOREACH_END();
  1634. return count;
  1635. }
  1636. /* }}} */
  1637. static zend_long php_extract_prefix_if_exists(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1638. {
  1639. zend_long count = 0;
  1640. zend_string *var_name;
  1641. zval *entry, *orig_var, final_name;
  1642. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1643. if (!var_name) {
  1644. continue;
  1645. }
  1646. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1647. if (orig_var) {
  1648. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1649. orig_var = Z_INDIRECT_P(orig_var);
  1650. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1651. ZVAL_COPY_DEREF(orig_var, entry);
  1652. count++;
  1653. continue;
  1654. }
  1655. }
  1656. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1657. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1658. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1659. zend_throw_error(NULL, "Cannot re-assign $this");
  1660. return -1;
  1661. } else {
  1662. ZVAL_DEREF(entry);
  1663. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1664. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1665. orig_var = Z_INDIRECT_P(orig_var);
  1666. }
  1667. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1668. if (UNEXPECTED(EG(exception))) {
  1669. zend_string_release_ex(Z_STR(final_name), 0);
  1670. return -1;
  1671. }
  1672. } else {
  1673. Z_TRY_ADDREF_P(entry);
  1674. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1675. }
  1676. count++;
  1677. }
  1678. }
  1679. zval_ptr_dtor_str(&final_name);
  1680. }
  1681. } ZEND_HASH_FOREACH_END();
  1682. return count;
  1683. }
  1684. /* }}} */
  1685. static zend_long php_extract_ref_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1686. {
  1687. zend_long count = 0;
  1688. zend_string *var_name;
  1689. zval *entry, *orig_var, final_name;
  1690. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1691. if (!var_name) {
  1692. continue;
  1693. }
  1694. if (ZSTR_LEN(var_name) == 0) {
  1695. continue;
  1696. }
  1697. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1698. if (orig_var) {
  1699. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1700. orig_var = Z_INDIRECT_P(orig_var);
  1701. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1702. if (Z_ISREF_P(entry)) {
  1703. Z_ADDREF_P(entry);
  1704. } else {
  1705. ZVAL_MAKE_REF_EX(entry, 2);
  1706. }
  1707. ZVAL_REF(orig_var, Z_REF_P(entry));
  1708. count++;
  1709. continue;
  1710. }
  1711. }
  1712. prefix:
  1713. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1714. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1715. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1716. zend_throw_error(NULL, "Cannot re-assign $this");
  1717. return -1;
  1718. } else {
  1719. if (Z_ISREF_P(entry)) {
  1720. Z_ADDREF_P(entry);
  1721. } else {
  1722. ZVAL_MAKE_REF_EX(entry, 2);
  1723. }
  1724. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1725. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1726. orig_var = Z_INDIRECT_P(orig_var);
  1727. }
  1728. zval_ptr_dtor(orig_var);
  1729. ZVAL_REF(orig_var, Z_REF_P(entry));
  1730. } else {
  1731. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1732. }
  1733. count++;
  1734. }
  1735. }
  1736. zval_ptr_dtor_str(&final_name);
  1737. } else {
  1738. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1739. continue;
  1740. }
  1741. if (zend_string_equals_literal(var_name, "this")) {
  1742. goto prefix;
  1743. }
  1744. if (Z_ISREF_P(entry)) {
  1745. Z_ADDREF_P(entry);
  1746. } else {
  1747. ZVAL_MAKE_REF_EX(entry, 2);
  1748. }
  1749. zend_hash_add_new(symbol_table, var_name, entry);
  1750. count++;
  1751. }
  1752. } ZEND_HASH_FOREACH_END();
  1753. return count;
  1754. }
  1755. /* }}} */
  1756. static zend_long php_extract_prefix_same(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1757. {
  1758. zend_long count = 0;
  1759. zend_string *var_name;
  1760. zval *entry, *orig_var, final_name;
  1761. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  1762. if (!var_name) {
  1763. continue;
  1764. }
  1765. if (ZSTR_LEN(var_name) == 0) {
  1766. continue;
  1767. }
  1768. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  1769. if (orig_var) {
  1770. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1771. orig_var = Z_INDIRECT_P(orig_var);
  1772. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  1773. ZVAL_COPY_DEREF(orig_var, entry);
  1774. count++;
  1775. continue;
  1776. }
  1777. }
  1778. prefix:
  1779. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1780. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1781. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1782. zend_throw_error(NULL, "Cannot re-assign $this");
  1783. return -1;
  1784. } else {
  1785. ZVAL_DEREF(entry);
  1786. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1787. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1788. orig_var = Z_INDIRECT_P(orig_var);
  1789. }
  1790. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1791. if (UNEXPECTED(EG(exception))) {
  1792. zend_string_release_ex(Z_STR(final_name), 0);
  1793. return -1;
  1794. }
  1795. } else {
  1796. Z_TRY_ADDREF_P(entry);
  1797. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1798. }
  1799. count++;
  1800. }
  1801. }
  1802. zval_ptr_dtor_str(&final_name);
  1803. } else {
  1804. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  1805. continue;
  1806. }
  1807. if (zend_string_equals_literal(var_name, "this")) {
  1808. goto prefix;
  1809. }
  1810. ZVAL_DEREF(entry);
  1811. Z_TRY_ADDREF_P(entry);
  1812. zend_hash_add_new(symbol_table, var_name, entry);
  1813. count++;
  1814. }
  1815. } ZEND_HASH_FOREACH_END();
  1816. return count;
  1817. }
  1818. /* }}} */
  1819. static zend_long php_extract_ref_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1820. {
  1821. zend_long count = 0;
  1822. zend_string *var_name;
  1823. zend_ulong num_key;
  1824. zval *entry, *orig_var, final_name;
  1825. ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
  1826. if (var_name) {
  1827. if (ZSTR_LEN(var_name) == 0) {
  1828. continue;
  1829. }
  1830. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1831. } else {
  1832. zend_string *str = zend_long_to_str(num_key);
  1833. php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
  1834. zend_string_release_ex(str, 0);
  1835. }
  1836. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1837. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1838. zend_throw_error(NULL, "Cannot re-assign $this");
  1839. return -1;
  1840. } else {
  1841. if (Z_ISREF_P(entry)) {
  1842. Z_ADDREF_P(entry);
  1843. } else {
  1844. ZVAL_MAKE_REF_EX(entry, 2);
  1845. }
  1846. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1847. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1848. orig_var = Z_INDIRECT_P(orig_var);
  1849. }
  1850. zval_ptr_dtor(orig_var);
  1851. ZVAL_REF(orig_var, Z_REF_P(entry));
  1852. } else {
  1853. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1854. }
  1855. count++;
  1856. }
  1857. }
  1858. zval_ptr_dtor_str(&final_name);
  1859. } ZEND_HASH_FOREACH_END();
  1860. return count;
  1861. }
  1862. /* }}} */
  1863. static zend_long php_extract_prefix_all(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1864. {
  1865. zend_long count = 0;
  1866. zend_string *var_name;
  1867. zend_ulong num_key;
  1868. zval *entry, *orig_var, final_name;
  1869. ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
  1870. if (var_name) {
  1871. if (ZSTR_LEN(var_name) == 0) {
  1872. continue;
  1873. }
  1874. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1875. } else {
  1876. zend_string *str = zend_long_to_str(num_key);
  1877. php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
  1878. zend_string_release_ex(str, 0);
  1879. }
  1880. if (php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1881. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1882. zend_throw_error(NULL, "Cannot re-assign $this");
  1883. return -1;
  1884. } else {
  1885. ZVAL_DEREF(entry);
  1886. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1887. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1888. orig_var = Z_INDIRECT_P(orig_var);
  1889. }
  1890. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1891. if (UNEXPECTED(EG(exception))) {
  1892. zend_string_release_ex(Z_STR(final_name), 0);
  1893. return -1;
  1894. }
  1895. } else {
  1896. Z_TRY_ADDREF_P(entry);
  1897. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1898. }
  1899. count++;
  1900. }
  1901. }
  1902. zval_ptr_dtor_str(&final_name);
  1903. } ZEND_HASH_FOREACH_END();
  1904. return count;
  1905. }
  1906. /* }}} */
  1907. static zend_long php_extract_ref_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1908. {
  1909. zend_long count = 0;
  1910. zend_string *var_name;
  1911. zend_ulong num_key;
  1912. zval *entry, *orig_var, final_name;
  1913. ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
  1914. if (var_name) {
  1915. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
  1916. || zend_string_equals_literal(var_name, "this")) {
  1917. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1918. if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1919. zval_ptr_dtor_str(&final_name);
  1920. continue;
  1921. }
  1922. } else {
  1923. ZVAL_STR_COPY(&final_name, var_name);
  1924. }
  1925. } else {
  1926. zend_string *str = zend_long_to_str(num_key);
  1927. php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
  1928. zend_string_release_ex(str, 0);
  1929. if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1930. zval_ptr_dtor_str(&final_name);
  1931. continue;
  1932. }
  1933. }
  1934. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1935. zend_throw_error(NULL, "Cannot re-assign $this");
  1936. return -1;
  1937. } else {
  1938. if (Z_ISREF_P(entry)) {
  1939. Z_ADDREF_P(entry);
  1940. } else {
  1941. ZVAL_MAKE_REF_EX(entry, 2);
  1942. }
  1943. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1944. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1945. orig_var = Z_INDIRECT_P(orig_var);
  1946. }
  1947. zval_ptr_dtor(orig_var);
  1948. ZVAL_REF(orig_var, Z_REF_P(entry));
  1949. } else {
  1950. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  1951. }
  1952. count++;
  1953. }
  1954. zval_ptr_dtor_str(&final_name);
  1955. } ZEND_HASH_FOREACH_END();
  1956. return count;
  1957. }
  1958. /* }}} */
  1959. static zend_long php_extract_prefix_invalid(zend_array *arr, zend_array *symbol_table, zend_string *prefix) /* {{{ */
  1960. {
  1961. zend_long count = 0;
  1962. zend_string *var_name;
  1963. zend_ulong num_key;
  1964. zval *entry, *orig_var, final_name;
  1965. ZEND_HASH_FOREACH_KEY_VAL_IND(arr, num_key, var_name, entry) {
  1966. if (var_name) {
  1967. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))
  1968. || zend_string_equals_literal(var_name, "this")) {
  1969. php_prefix_varname(&final_name, prefix, ZSTR_VAL(var_name), ZSTR_LEN(var_name), 1);
  1970. if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1971. zval_ptr_dtor_str(&final_name);
  1972. continue;
  1973. }
  1974. } else {
  1975. ZVAL_STR_COPY(&final_name, var_name);
  1976. }
  1977. } else {
  1978. zend_string *str = zend_long_to_str(num_key);
  1979. php_prefix_varname(&final_name, prefix, ZSTR_VAL(str), ZSTR_LEN(str), 1);
  1980. zend_string_release_ex(str, 0);
  1981. if (!php_valid_var_name(Z_STRVAL(final_name), Z_STRLEN(final_name))) {
  1982. zval_ptr_dtor_str(&final_name);
  1983. continue;
  1984. }
  1985. }
  1986. if (zend_string_equals_literal(Z_STR(final_name), "this")) {
  1987. zend_throw_error(NULL, "Cannot re-assign $this");
  1988. return -1;
  1989. } else {
  1990. ZVAL_DEREF(entry);
  1991. if ((orig_var = zend_hash_find(symbol_table, Z_STR(final_name))) != NULL) {
  1992. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  1993. orig_var = Z_INDIRECT_P(orig_var);
  1994. }
  1995. ZEND_TRY_ASSIGN_COPY_EX(orig_var, entry, 0);
  1996. if (UNEXPECTED(EG(exception))) {
  1997. zend_string_release_ex(Z_STR(final_name), 0);
  1998. return -1;
  1999. }
  2000. } else {
  2001. Z_TRY_ADDREF_P(entry);
  2002. zend_hash_add_new(symbol_table, Z_STR(final_name), entry);
  2003. }
  2004. count++;
  2005. }
  2006. zval_ptr_dtor_str(&final_name);
  2007. } ZEND_HASH_FOREACH_END();
  2008. return count;
  2009. }
  2010. /* }}} */
  2011. static zend_long php_extract_ref_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
  2012. {
  2013. zend_long count = 0;
  2014. zend_string *var_name;
  2015. zval *entry, *orig_var;
  2016. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  2017. if (!var_name) {
  2018. continue;
  2019. }
  2020. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  2021. continue;
  2022. }
  2023. if (zend_string_equals_literal(var_name, "this")) {
  2024. continue;
  2025. }
  2026. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  2027. if (orig_var) {
  2028. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  2029. orig_var = Z_INDIRECT_P(orig_var);
  2030. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  2031. if (Z_ISREF_P(entry)) {
  2032. Z_ADDREF_P(entry);
  2033. } else {
  2034. ZVAL_MAKE_REF_EX(entry, 2);
  2035. }
  2036. ZVAL_REF(orig_var, Z_REF_P(entry));
  2037. count++;
  2038. }
  2039. }
  2040. } else {
  2041. if (Z_ISREF_P(entry)) {
  2042. Z_ADDREF_P(entry);
  2043. } else {
  2044. ZVAL_MAKE_REF_EX(entry, 2);
  2045. }
  2046. zend_hash_add_new(symbol_table, var_name, entry);
  2047. count++;
  2048. }
  2049. } ZEND_HASH_FOREACH_END();
  2050. return count;
  2051. }
  2052. /* }}} */
  2053. static zend_long php_extract_skip(zend_array *arr, zend_array *symbol_table) /* {{{ */
  2054. {
  2055. zend_long count = 0;
  2056. zend_string *var_name;
  2057. zval *entry, *orig_var;
  2058. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(arr, var_name, entry) {
  2059. if (!var_name) {
  2060. continue;
  2061. }
  2062. if (!php_valid_var_name(ZSTR_VAL(var_name), ZSTR_LEN(var_name))) {
  2063. continue;
  2064. }
  2065. if (zend_string_equals_literal(var_name, "this")) {
  2066. continue;
  2067. }
  2068. orig_var = zend_hash_find_ex(symbol_table, var_name, 1);
  2069. if (orig_var) {
  2070. if (Z_TYPE_P(orig_var) == IS_INDIRECT) {
  2071. orig_var = Z_INDIRECT_P(orig_var);
  2072. if (Z_TYPE_P(orig_var) == IS_UNDEF) {
  2073. ZVAL_COPY_DEREF(orig_var, entry);
  2074. count++;
  2075. }
  2076. }
  2077. } else {
  2078. ZVAL_DEREF(entry);
  2079. Z_TRY_ADDREF_P(entry);
  2080. zend_hash_add_new(symbol_table, var_name, entry);
  2081. count++;
  2082. }
  2083. } ZEND_HASH_FOREACH_END();
  2084. return count;
  2085. }
  2086. /* }}} */
  2087. /* {{{ proto int extract(array var_array [, int extract_type [, string prefix]])
  2088. Imports variables into symbol table from an array */
  2089. PHP_FUNCTION(extract)
  2090. {
  2091. zval *var_array_param;
  2092. zend_long extract_refs;
  2093. zend_long extract_type = EXTR_OVERWRITE;
  2094. zend_string *prefix = NULL;
  2095. zend_long count;
  2096. zend_array *symbol_table;
  2097. ZEND_PARSE_PARAMETERS_START(1, 3)
  2098. Z_PARAM_ARRAY_EX2(var_array_param, 0, 1, 0)
  2099. Z_PARAM_OPTIONAL
  2100. Z_PARAM_LONG(extract_type)
  2101. Z_PARAM_STR(prefix)
  2102. ZEND_PARSE_PARAMETERS_END();
  2103. extract_refs = (extract_type & EXTR_REFS);
  2104. if (extract_refs) {
  2105. SEPARATE_ARRAY(var_array_param);
  2106. }
  2107. extract_type &= 0xff;
  2108. if (extract_type < EXTR_OVERWRITE || extract_type > EXTR_IF_EXISTS) {
  2109. zend_argument_value_error(2, "must be a valid extract type");
  2110. RETURN_THROWS();
  2111. }
  2112. if (extract_type > EXTR_SKIP && extract_type <= EXTR_PREFIX_IF_EXISTS && ZEND_NUM_ARGS() < 3) {
  2113. zend_argument_value_error(3, "is required when using this extract type");
  2114. RETURN_THROWS();
  2115. }
  2116. if (prefix) {
  2117. if (ZSTR_LEN(prefix) && !php_valid_var_name(ZSTR_VAL(prefix), ZSTR_LEN(prefix))) {
  2118. zend_argument_value_error(3, "must be a valid identifier");
  2119. RETURN_THROWS();
  2120. }
  2121. }
  2122. if (zend_forbid_dynamic_call("extract()") == FAILURE) {
  2123. return;
  2124. }
  2125. symbol_table = zend_rebuild_symbol_table();
  2126. ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
  2127. if (extract_refs) {
  2128. switch (extract_type) {
  2129. case EXTR_IF_EXISTS:
  2130. count = php_extract_ref_if_exists(Z_ARRVAL_P(var_array_param), symbol_table);
  2131. break;
  2132. case EXTR_OVERWRITE:
  2133. count = php_extract_ref_overwrite(Z_ARRVAL_P(var_array_param), symbol_table);
  2134. break;
  2135. case EXTR_PREFIX_IF_EXISTS:
  2136. count = php_extract_ref_prefix_if_exists(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
  2137. break;
  2138. case EXTR_PREFIX_SAME:
  2139. count = php_extract_ref_prefix_same(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
  2140. break;
  2141. case EXTR_PREFIX_ALL:
  2142. count = php_extract_ref_prefix_all(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
  2143. break;
  2144. case EXTR_PREFIX_INVALID:
  2145. count = php_extract_ref_prefix_invalid(Z_ARRVAL_P(var_array_param), symbol_table, prefix);
  2146. break;
  2147. default:
  2148. count = php_extract_ref_skip(Z_ARRVAL_P(var_array_param), symbol_table);
  2149. break;
  2150. }
  2151. } else {
  2152. /* The array might be stored in a local variable that will be overwritten */
  2153. zval array_copy;
  2154. ZVAL_COPY(&array_copy, var_array_param);
  2155. switch (extract_type) {
  2156. case EXTR_IF_EXISTS:
  2157. count = php_extract_if_exists(Z_ARRVAL(array_copy), symbol_table);
  2158. break;
  2159. case EXTR_OVERWRITE:
  2160. count = php_extract_overwrite(Z_ARRVAL(array_copy), symbol_table);
  2161. break;
  2162. case EXTR_PREFIX_IF_EXISTS:
  2163. count = php_extract_prefix_if_exists(Z_ARRVAL(array_copy), symbol_table, prefix);
  2164. break;
  2165. case EXTR_PREFIX_SAME:
  2166. count = php_extract_prefix_same(Z_ARRVAL(array_copy), symbol_table, prefix);
  2167. break;
  2168. case EXTR_PREFIX_ALL:
  2169. count = php_extract_prefix_all(Z_ARRVAL(array_copy), symbol_table, prefix);
  2170. break;
  2171. case EXTR_PREFIX_INVALID:
  2172. count = php_extract_prefix_invalid(Z_ARRVAL(array_copy), symbol_table, prefix);
  2173. break;
  2174. default:
  2175. count = php_extract_skip(Z_ARRVAL(array_copy), symbol_table);
  2176. break;
  2177. }
  2178. zval_ptr_dtor(&array_copy);
  2179. }
  2180. RETURN_LONG(count);
  2181. }
  2182. /* }}} */
  2183. static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry) /* {{{ */
  2184. {
  2185. zval *value_ptr, data;
  2186. ZVAL_DEREF(entry);
  2187. if (Z_TYPE_P(entry) == IS_STRING) {
  2188. if ((value_ptr = zend_hash_find_ind(eg_active_symbol_table, Z_STR_P(entry))) != NULL) {
  2189. ZVAL_DEREF(value_ptr);
  2190. Z_TRY_ADDREF_P(value_ptr);
  2191. zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), value_ptr);
  2192. } else if (zend_string_equals_literal(Z_STR_P(entry), "this")) {
  2193. zend_object *object = zend_get_this_object(EG(current_execute_data));
  2194. if (object) {
  2195. GC_ADDREF(object);
  2196. ZVAL_OBJ(&data, object);
  2197. zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
  2198. }
  2199. } else {
  2200. php_error_docref(NULL, E_NOTICE, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
  2201. }
  2202. } else if (Z_TYPE_P(entry) == IS_ARRAY) {
  2203. if (Z_REFCOUNTED_P(entry)) {
  2204. if (Z_IS_RECURSIVE_P(entry)) {
  2205. zend_throw_error(NULL, "Recursion detected");
  2206. return;
  2207. }
  2208. Z_PROTECT_RECURSION_P(entry);
  2209. }
  2210. ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) {
  2211. php_compact_var(eg_active_symbol_table, return_value, value_ptr);
  2212. } ZEND_HASH_FOREACH_END();
  2213. if (Z_REFCOUNTED_P(entry)) {
  2214. Z_UNPROTECT_RECURSION_P(entry);
  2215. }
  2216. }
  2217. }
  2218. /* }}} */
  2219. /* {{{ proto array compact(mixed var_names [, mixed ...])
  2220. Creates a hash containing variables and their values */
  2221. PHP_FUNCTION(compact)
  2222. {
  2223. zval *args = NULL; /* function arguments array */
  2224. uint32_t num_args, i;
  2225. zend_array *symbol_table;
  2226. ZEND_PARSE_PARAMETERS_START(1, -1)
  2227. Z_PARAM_VARIADIC('+', args, num_args)
  2228. ZEND_PARSE_PARAMETERS_END();
  2229. if (zend_forbid_dynamic_call("compact()") == FAILURE) {
  2230. return;
  2231. }
  2232. symbol_table = zend_rebuild_symbol_table();
  2233. ZEND_ASSERT(symbol_table && "A symbol table should always be available here");
  2234. /* compact() is probably most used with a single array of var_names
  2235. or multiple string names, rather than a combination of both.
  2236. So quickly guess a minimum result size based on that */
  2237. if (num_args && Z_TYPE(args[0]) == IS_ARRAY) {
  2238. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
  2239. } else {
  2240. array_init_size(return_value, num_args);
  2241. }
  2242. for (i = 0; i < num_args; i++) {
  2243. php_compact_var(symbol_table, return_value, &args[i]);
  2244. }
  2245. }
  2246. /* }}} */
  2247. /* {{{ proto array array_fill(int start_key, int num, mixed val)
  2248. Create an array containing num elements starting with index start_key each initialized to val */
  2249. PHP_FUNCTION(array_fill)
  2250. {
  2251. zval *val;
  2252. zend_long start_key, num;
  2253. ZEND_PARSE_PARAMETERS_START(3, 3)
  2254. Z_PARAM_LONG(start_key)
  2255. Z_PARAM_LONG(num)
  2256. Z_PARAM_ZVAL(val)
  2257. ZEND_PARSE_PARAMETERS_END();
  2258. if (EXPECTED(num > 0)) {
  2259. if (sizeof(num) > 4 && UNEXPECTED(EXPECTED(num > 0x7fffffff))) {
  2260. zend_argument_value_error(2, "is too large");
  2261. RETURN_THROWS();
  2262. } else if (UNEXPECTED(start_key > ZEND_LONG_MAX - num + 1)) {
  2263. zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
  2264. RETURN_THROWS();
  2265. } else if (EXPECTED(start_key >= 0) && EXPECTED(start_key < num)) {
  2266. /* create packed array */
  2267. Bucket *p;
  2268. zend_long n;
  2269. array_init_size(return_value, (uint32_t)(start_key + num));
  2270. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  2271. Z_ARRVAL_P(return_value)->nNumUsed = (uint32_t)(start_key + num);
  2272. Z_ARRVAL_P(return_value)->nNumOfElements = (uint32_t)num;
  2273. Z_ARRVAL_P(return_value)->nNextFreeElement = (zend_long)(start_key + num);
  2274. if (Z_REFCOUNTED_P(val)) {
  2275. GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
  2276. }
  2277. p = Z_ARRVAL_P(return_value)->arData;
  2278. n = start_key;
  2279. while (start_key--) {
  2280. ZVAL_UNDEF(&p->val);
  2281. p++;
  2282. }
  2283. while (num--) {
  2284. ZVAL_COPY_VALUE(&p->val, val);
  2285. p->h = n++;
  2286. p->key = NULL;
  2287. p++;
  2288. }
  2289. } else {
  2290. /* create hash */
  2291. array_init_size(return_value, (uint32_t)num);
  2292. zend_hash_real_init_mixed(Z_ARRVAL_P(return_value));
  2293. if (Z_REFCOUNTED_P(val)) {
  2294. GC_ADDREF_EX(Z_COUNTED_P(val), (uint32_t)num);
  2295. }
  2296. zend_hash_index_add_new(Z_ARRVAL_P(return_value), start_key, val);
  2297. while (--num) {
  2298. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), val);
  2299. start_key++;
  2300. }
  2301. }
  2302. } else if (EXPECTED(num == 0)) {
  2303. RETURN_EMPTY_ARRAY();
  2304. } else {
  2305. zend_argument_value_error(2, "must be greater than or equal to 0");
  2306. RETURN_THROWS();
  2307. }
  2308. }
  2309. /* }}} */
  2310. /* {{{ proto array array_fill_keys(array keys, mixed val)
  2311. Create an array using the elements of the first parameter as keys each initialized to val */
  2312. PHP_FUNCTION(array_fill_keys)
  2313. {
  2314. zval *keys, *val, *entry;
  2315. ZEND_PARSE_PARAMETERS_START(2, 2)
  2316. Z_PARAM_ARRAY(keys)
  2317. Z_PARAM_ZVAL(val)
  2318. ZEND_PARSE_PARAMETERS_END();
  2319. /* Initialize return array */
  2320. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(keys)));
  2321. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry) {
  2322. ZVAL_DEREF(entry);
  2323. Z_TRY_ADDREF_P(val);
  2324. if (Z_TYPE_P(entry) == IS_LONG) {
  2325. zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), val);
  2326. } else {
  2327. zend_string *tmp_key;
  2328. zend_string *key = zval_get_tmp_string(entry, &tmp_key);
  2329. zend_symtable_update(Z_ARRVAL_P(return_value), key, val);
  2330. zend_tmp_string_release(tmp_key);
  2331. }
  2332. } ZEND_HASH_FOREACH_END();
  2333. }
  2334. /* }}} */
  2335. #define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
  2336. double __calc_size = ((start - end) / step) + 1; \
  2337. if (__calc_size >= (double)HT_MAX_SIZE) { \
  2338. zend_value_error(\
  2339. "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
  2340. RETURN_THROWS(); \
  2341. } \
  2342. size = (uint32_t)_php_math_round(__calc_size, 0, PHP_ROUND_HALF_UP); \
  2343. array_init_size(return_value, size); \
  2344. zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
  2345. } while (0)
  2346. #define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
  2347. zend_ulong __calc_size = ((zend_ulong) start - end) / lstep; \
  2348. if (__calc_size >= HT_MAX_SIZE - 1) { \
  2349. zend_value_error(\
  2350. "The supplied range exceeds the maximum array size: start=" ZEND_LONG_FMT " end=" ZEND_LONG_FMT, end, start); \
  2351. RETURN_THROWS(); \
  2352. } \
  2353. size = (uint32_t)(__calc_size + 1); \
  2354. array_init_size(return_value, size); \
  2355. zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
  2356. } while (0)
  2357. /* {{{ proto array range(mixed low, mixed high[, int step])
  2358. Create an array containing the range of integers or characters from low to high (inclusive) */
  2359. PHP_FUNCTION(range)
  2360. {
  2361. zval *zlow, *zhigh, *zstep = NULL, tmp;
  2362. int err = 0, is_step_double = 0;
  2363. double step = 1.0;
  2364. ZEND_PARSE_PARAMETERS_START(2, 3)
  2365. Z_PARAM_ZVAL(zlow)
  2366. Z_PARAM_ZVAL(zhigh)
  2367. Z_PARAM_OPTIONAL
  2368. Z_PARAM_NUMBER(zstep)
  2369. ZEND_PARSE_PARAMETERS_END();
  2370. if (zstep) {
  2371. is_step_double = Z_TYPE_P(zstep) == IS_DOUBLE;
  2372. step = zval_get_double(zstep);
  2373. /* We only want positive step values. */
  2374. if (step < 0.0) {
  2375. step *= -1;
  2376. }
  2377. }
  2378. /* If the range is given as strings, generate an array of characters. */
  2379. if (Z_TYPE_P(zlow) == IS_STRING && Z_TYPE_P(zhigh) == IS_STRING && Z_STRLEN_P(zlow) >= 1 && Z_STRLEN_P(zhigh) >= 1) {
  2380. int type1, type2;
  2381. unsigned char low, high;
  2382. zend_long lstep = (zend_long) step;
  2383. type1 = is_numeric_string(Z_STRVAL_P(zlow), Z_STRLEN_P(zlow), NULL, NULL, 0);
  2384. type2 = is_numeric_string(Z_STRVAL_P(zhigh), Z_STRLEN_P(zhigh), NULL, NULL, 0);
  2385. if (type1 == IS_DOUBLE || type2 == IS_DOUBLE || is_step_double) {
  2386. goto double_str;
  2387. } else if (type1 == IS_LONG || type2 == IS_LONG) {
  2388. goto long_str;
  2389. }
  2390. low = (unsigned char)Z_STRVAL_P(zlow)[0];
  2391. high = (unsigned char)Z_STRVAL_P(zhigh)[0];
  2392. if (low > high) { /* Negative Steps */
  2393. if (lstep <= 0) {
  2394. err = 1;
  2395. goto err;
  2396. }
  2397. /* Initialize the return_value as an array. */
  2398. array_init_size(return_value, (uint32_t)(((low - high) / lstep) + 1));
  2399. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  2400. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2401. for (; low >= high; low -= (unsigned int)lstep) {
  2402. ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
  2403. ZEND_HASH_FILL_NEXT();
  2404. if (((signed int)low - lstep) < 0) {
  2405. break;
  2406. }
  2407. }
  2408. } ZEND_HASH_FILL_END();
  2409. } else if (high > low) { /* Positive Steps */
  2410. if (lstep <= 0) {
  2411. err = 1;
  2412. goto err;
  2413. }
  2414. array_init_size(return_value, (uint32_t)(((high - low) / lstep) + 1));
  2415. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  2416. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2417. for (; low <= high; low += (unsigned int)lstep) {
  2418. ZEND_HASH_FILL_SET_INTERNED_STR(ZSTR_CHAR(low));
  2419. ZEND_HASH_FILL_NEXT();
  2420. if (((signed int)low + lstep) > 255) {
  2421. break;
  2422. }
  2423. }
  2424. } ZEND_HASH_FILL_END();
  2425. } else {
  2426. array_init(return_value);
  2427. ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR(low));
  2428. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  2429. }
  2430. } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
  2431. double low, high, element;
  2432. uint32_t i, size;
  2433. double_str:
  2434. low = zval_get_double(zlow);
  2435. high = zval_get_double(zhigh);
  2436. if (zend_isinf(high) || zend_isinf(low)) {
  2437. zend_value_error("Invalid range supplied: start=%0.0f end=%0.0f", low, high);
  2438. RETURN_THROWS();
  2439. }
  2440. if (low > high) { /* Negative steps */
  2441. if (low - high < step || step <= 0) {
  2442. err = 1;
  2443. goto err;
  2444. }
  2445. RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
  2446. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2447. for (i = 0, element = low; i < size && element >= high; ++i, element = low - (i * step)) {
  2448. ZEND_HASH_FILL_SET_DOUBLE(element);
  2449. ZEND_HASH_FILL_NEXT();
  2450. }
  2451. } ZEND_HASH_FILL_END();
  2452. } else if (high > low) { /* Positive steps */
  2453. if (high - low < step || step <= 0) {
  2454. err = 1;
  2455. goto err;
  2456. }
  2457. RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
  2458. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2459. for (i = 0, element = low; i < size && element <= high; ++i, element = low + (i * step)) {
  2460. ZEND_HASH_FILL_SET_DOUBLE(element);
  2461. ZEND_HASH_FILL_NEXT();
  2462. }
  2463. } ZEND_HASH_FILL_END();
  2464. } else {
  2465. array_init(return_value);
  2466. ZVAL_DOUBLE(&tmp, low);
  2467. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  2468. }
  2469. } else {
  2470. zend_long low, high;
  2471. /* lstep is a zend_ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
  2472. zend_ulong lstep;
  2473. uint32_t i, size;
  2474. long_str:
  2475. low = zval_get_long(zlow);
  2476. high = zval_get_long(zhigh);
  2477. if (step <= 0) {
  2478. err = 1;
  2479. goto err;
  2480. }
  2481. lstep = (zend_ulong)step;
  2482. if (step <= 0) {
  2483. err = 1;
  2484. goto err;
  2485. }
  2486. if (low > high) { /* Negative steps */
  2487. if ((zend_ulong)low - high < lstep) {
  2488. err = 1;
  2489. goto err;
  2490. }
  2491. RANGE_CHECK_LONG_INIT_ARRAY(low, high);
  2492. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2493. for (i = 0; i < size; ++i) {
  2494. ZEND_HASH_FILL_SET_LONG(low - (i * lstep));
  2495. ZEND_HASH_FILL_NEXT();
  2496. }
  2497. } ZEND_HASH_FILL_END();
  2498. } else if (high > low) { /* Positive steps */
  2499. if ((zend_ulong)high - low < lstep) {
  2500. err = 1;
  2501. goto err;
  2502. }
  2503. RANGE_CHECK_LONG_INIT_ARRAY(high, low);
  2504. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  2505. for (i = 0; i < size; ++i) {
  2506. ZEND_HASH_FILL_SET_LONG(low + (i * lstep));
  2507. ZEND_HASH_FILL_NEXT();
  2508. }
  2509. } ZEND_HASH_FILL_END();
  2510. } else {
  2511. array_init(return_value);
  2512. ZVAL_LONG(&tmp, low);
  2513. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  2514. }
  2515. }
  2516. err:
  2517. if (err) {
  2518. zend_argument_value_error(3, "must not exceed the specified range");
  2519. RETURN_THROWS();
  2520. }
  2521. }
  2522. /* }}} */
  2523. #undef RANGE_CHECK_DOUBLE_INIT_ARRAY
  2524. #undef RANGE_CHECK_LONG_INIT_ARRAY
  2525. static void php_array_data_shuffle(zval *array) /* {{{ */
  2526. {
  2527. uint32_t idx, j, n_elems;
  2528. Bucket *p, temp;
  2529. HashTable *hash;
  2530. zend_long rnd_idx;
  2531. uint32_t n_left;
  2532. n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
  2533. if (n_elems < 1) {
  2534. return;
  2535. }
  2536. hash = Z_ARRVAL_P(array);
  2537. n_left = n_elems;
  2538. if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
  2539. if (hash->nNumUsed != hash->nNumOfElements) {
  2540. for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
  2541. p = hash->arData + idx;
  2542. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2543. if (j != idx) {
  2544. hash->arData[j] = *p;
  2545. }
  2546. j++;
  2547. }
  2548. }
  2549. while (--n_left) {
  2550. rnd_idx = php_mt_rand_range(0, n_left);
  2551. if (rnd_idx != n_left) {
  2552. temp = hash->arData[n_left];
  2553. hash->arData[n_left] = hash->arData[rnd_idx];
  2554. hash->arData[rnd_idx] = temp;
  2555. }
  2556. }
  2557. } else {
  2558. uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
  2559. if (hash->nNumUsed != hash->nNumOfElements) {
  2560. for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
  2561. p = hash->arData + idx;
  2562. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2563. if (j != idx) {
  2564. hash->arData[j] = *p;
  2565. if (idx == iter_pos) {
  2566. zend_hash_iterators_update(hash, idx, j);
  2567. iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
  2568. }
  2569. }
  2570. j++;
  2571. }
  2572. }
  2573. while (--n_left) {
  2574. rnd_idx = php_mt_rand_range(0, n_left);
  2575. if (rnd_idx != n_left) {
  2576. temp = hash->arData[n_left];
  2577. hash->arData[n_left] = hash->arData[rnd_idx];
  2578. hash->arData[rnd_idx] = temp;
  2579. zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
  2580. }
  2581. }
  2582. }
  2583. hash->nNumUsed = n_elems;
  2584. hash->nInternalPointer = 0;
  2585. for (j = 0; j < n_elems; j++) {
  2586. p = hash->arData + j;
  2587. if (p->key) {
  2588. zend_string_release_ex(p->key, 0);
  2589. }
  2590. p->h = j;
  2591. p->key = NULL;
  2592. }
  2593. hash->nNextFreeElement = n_elems;
  2594. if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
  2595. zend_hash_to_packed(hash);
  2596. }
  2597. }
  2598. /* }}} */
  2599. /* {{{ proto bool shuffle(array array_arg)
  2600. Randomly shuffle the contents of an array */
  2601. PHP_FUNCTION(shuffle)
  2602. {
  2603. zval *array;
  2604. ZEND_PARSE_PARAMETERS_START(1, 1)
  2605. Z_PARAM_ARRAY_EX(array, 0, 1)
  2606. ZEND_PARSE_PARAMETERS_END();
  2607. php_array_data_shuffle(array);
  2608. RETURN_TRUE;
  2609. }
  2610. /* }}} */
  2611. static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, HashTable *replace, HashTable *removed) /* {{{ */
  2612. {
  2613. HashTable out_hash; /* Output hashtable */
  2614. zend_long num_in; /* Number of entries in the input hashtable */
  2615. zend_long pos; /* Current position in the hashtable */
  2616. uint32_t idx;
  2617. Bucket *p; /* Pointer to hash bucket */
  2618. zval *entry; /* Hash entry */
  2619. uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0);
  2620. /* Get number of entries in the input hash */
  2621. num_in = zend_hash_num_elements(in_hash);
  2622. /* Clamp the offset.. */
  2623. if (offset > num_in) {
  2624. offset = num_in;
  2625. } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
  2626. offset = 0;
  2627. }
  2628. /* ..and the length */
  2629. if (length < 0) {
  2630. length = num_in - offset + length;
  2631. } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
  2632. length = num_in - offset;
  2633. }
  2634. /* Create and initialize output hash */
  2635. zend_hash_init(&out_hash, (length > 0 ? num_in - length : 0) + (replace ? zend_hash_num_elements(replace) : 0), NULL, ZVAL_PTR_DTOR, 0);
  2636. /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
  2637. for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) {
  2638. p = in_hash->arData + idx;
  2639. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2640. /* Get entry and increase reference count */
  2641. entry = &p->val;
  2642. /* Update output hash depending on key type */
  2643. if (p->key == NULL) {
  2644. zend_hash_next_index_insert_new(&out_hash, entry);
  2645. } else {
  2646. zend_hash_add_new(&out_hash, p->key, entry);
  2647. }
  2648. if (idx == iter_pos) {
  2649. if ((zend_long)idx != pos) {
  2650. zend_hash_iterators_update(in_hash, idx, pos);
  2651. }
  2652. iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
  2653. }
  2654. pos++;
  2655. }
  2656. /* If hash for removed entries exists, go until offset+length and copy the entries to it */
  2657. if (removed != NULL) {
  2658. for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) {
  2659. p = in_hash->arData + idx;
  2660. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2661. pos++;
  2662. entry = &p->val;
  2663. Z_TRY_ADDREF_P(entry);
  2664. if (p->key == NULL) {
  2665. zend_hash_next_index_insert_new(removed, entry);
  2666. zend_hash_del_bucket(in_hash, p);
  2667. } else {
  2668. zend_hash_add_new(removed, p->key, entry);
  2669. if (in_hash == &EG(symbol_table)) {
  2670. zend_delete_global_variable(p->key);
  2671. } else {
  2672. zend_hash_del_bucket(in_hash, p);
  2673. }
  2674. }
  2675. }
  2676. } else { /* otherwise just skip those entries */
  2677. int pos2 = pos;
  2678. for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) {
  2679. p = in_hash->arData + idx;
  2680. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2681. pos2++;
  2682. if (p->key && in_hash == &EG(symbol_table)) {
  2683. zend_delete_global_variable(p->key);
  2684. } else {
  2685. zend_hash_del_bucket(in_hash, p);
  2686. }
  2687. }
  2688. }
  2689. iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos);
  2690. /* If there are entries to insert.. */
  2691. if (replace) {
  2692. ZEND_HASH_FOREACH_VAL_IND(replace, entry) {
  2693. Z_TRY_ADDREF_P(entry);
  2694. zend_hash_next_index_insert_new(&out_hash, entry);
  2695. pos++;
  2696. } ZEND_HASH_FOREACH_END();
  2697. }
  2698. /* Copy the remaining input hash entries to the output hash */
  2699. for ( ; idx < in_hash->nNumUsed ; idx++) {
  2700. p = in_hash->arData + idx;
  2701. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2702. entry = &p->val;
  2703. if (p->key == NULL) {
  2704. zend_hash_next_index_insert_new(&out_hash, entry);
  2705. } else {
  2706. zend_hash_add_new(&out_hash, p->key, entry);
  2707. }
  2708. if (idx == iter_pos) {
  2709. if ((zend_long)idx != pos) {
  2710. zend_hash_iterators_update(in_hash, idx, pos);
  2711. }
  2712. iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1);
  2713. }
  2714. pos++;
  2715. }
  2716. /* replace HashTable data */
  2717. HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash));
  2718. HT_SET_ITERATORS_COUNT(in_hash, 0);
  2719. in_hash->pDestructor = NULL;
  2720. zend_hash_destroy(in_hash);
  2721. HT_FLAGS(in_hash) = HT_FLAGS(&out_hash);
  2722. in_hash->nTableSize = out_hash.nTableSize;
  2723. in_hash->nTableMask = out_hash.nTableMask;
  2724. in_hash->nNumUsed = out_hash.nNumUsed;
  2725. in_hash->nNumOfElements = out_hash.nNumOfElements;
  2726. in_hash->nNextFreeElement = out_hash.nNextFreeElement;
  2727. in_hash->arData = out_hash.arData;
  2728. in_hash->pDestructor = out_hash.pDestructor;
  2729. zend_hash_internal_pointer_reset(in_hash);
  2730. }
  2731. /* }}} */
  2732. /* {{{ proto int array_push(array stack, mixed var [, mixed ...])
  2733. Pushes elements onto the end of the array */
  2734. PHP_FUNCTION(array_push)
  2735. {
  2736. zval *args, /* Function arguments array */
  2737. *stack, /* Input array */
  2738. new_var; /* Variable to be pushed */
  2739. int i, /* Loop counter */
  2740. argc; /* Number of function arguments */
  2741. ZEND_PARSE_PARAMETERS_START(1, -1)
  2742. Z_PARAM_ARRAY_EX(stack, 0, 1)
  2743. Z_PARAM_VARIADIC('+', args, argc)
  2744. ZEND_PARSE_PARAMETERS_END();
  2745. /* For each subsequent argument, make it a reference, increase refcount, and add it to the end of the array */
  2746. for (i = 0; i < argc; i++) {
  2747. ZVAL_COPY(&new_var, &args[i]);
  2748. if (zend_hash_next_index_insert(Z_ARRVAL_P(stack), &new_var) == NULL) {
  2749. Z_TRY_DELREF(new_var);
  2750. zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied");
  2751. RETURN_THROWS();
  2752. }
  2753. }
  2754. /* Clean up and return the number of values in the stack */
  2755. RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
  2756. }
  2757. /* }}} */
  2758. /* {{{ proto mixed array_pop(array stack)
  2759. Pops an element off the end of the array */
  2760. PHP_FUNCTION(array_pop)
  2761. {
  2762. zval *stack, /* Input stack */
  2763. *val; /* Value to be popped */
  2764. uint32_t idx;
  2765. Bucket *p;
  2766. ZEND_PARSE_PARAMETERS_START(1, 1)
  2767. Z_PARAM_ARRAY_EX(stack, 0, 1)
  2768. ZEND_PARSE_PARAMETERS_END();
  2769. if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
  2770. return;
  2771. }
  2772. /* Get the last value and copy it into the return value */
  2773. idx = Z_ARRVAL_P(stack)->nNumUsed;
  2774. while (1) {
  2775. if (idx == 0) {
  2776. return;
  2777. }
  2778. idx--;
  2779. p = Z_ARRVAL_P(stack)->arData + idx;
  2780. val = &p->val;
  2781. if (Z_TYPE_P(val) == IS_INDIRECT) {
  2782. val = Z_INDIRECT_P(val);
  2783. }
  2784. if (Z_TYPE_P(val) != IS_UNDEF) {
  2785. break;
  2786. }
  2787. }
  2788. ZVAL_COPY_DEREF(return_value, val);
  2789. if (!p->key && (zend_long)p->h == (Z_ARRVAL_P(stack)->nNextFreeElement - 1)) {
  2790. Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
  2791. }
  2792. /* Delete the last value */
  2793. if (p->key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
  2794. zend_delete_global_variable(p->key);
  2795. } else {
  2796. zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
  2797. }
  2798. zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
  2799. }
  2800. /* }}} */
  2801. /* {{{ proto mixed array_shift(array stack)
  2802. Pops an element off the beginning of the array */
  2803. PHP_FUNCTION(array_shift)
  2804. {
  2805. zval *stack, /* Input stack */
  2806. *val; /* Value to be popped */
  2807. uint32_t idx;
  2808. Bucket *p;
  2809. ZEND_PARSE_PARAMETERS_START(1, 1)
  2810. Z_PARAM_ARRAY_EX(stack, 0, 1)
  2811. ZEND_PARSE_PARAMETERS_END();
  2812. if (zend_hash_num_elements(Z_ARRVAL_P(stack)) == 0) {
  2813. return;
  2814. }
  2815. /* Get the first value and copy it into the return value */
  2816. idx = 0;
  2817. while (1) {
  2818. if (idx == Z_ARRVAL_P(stack)->nNumUsed) {
  2819. return;
  2820. }
  2821. p = Z_ARRVAL_P(stack)->arData + idx;
  2822. val = &p->val;
  2823. if (Z_TYPE_P(val) == IS_INDIRECT) {
  2824. val = Z_INDIRECT_P(val);
  2825. }
  2826. if (Z_TYPE_P(val) != IS_UNDEF) {
  2827. break;
  2828. }
  2829. idx++;
  2830. }
  2831. ZVAL_COPY_DEREF(return_value, val);
  2832. /* Delete the first value */
  2833. if (p->key && Z_ARRVAL_P(stack) == &EG(symbol_table)) {
  2834. zend_delete_global_variable(p->key);
  2835. } else {
  2836. zend_hash_del_bucket(Z_ARRVAL_P(stack), p);
  2837. }
  2838. /* re-index like it did before */
  2839. if (HT_FLAGS(Z_ARRVAL_P(stack)) & HASH_FLAG_PACKED) {
  2840. uint32_t k = 0;
  2841. if (EXPECTED(!HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
  2842. for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
  2843. p = Z_ARRVAL_P(stack)->arData + idx;
  2844. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2845. if (idx != k) {
  2846. Bucket *q = Z_ARRVAL_P(stack)->arData + k;
  2847. q->h = k;
  2848. q->key = NULL;
  2849. ZVAL_COPY_VALUE(&q->val, &p->val);
  2850. ZVAL_UNDEF(&p->val);
  2851. }
  2852. k++;
  2853. }
  2854. } else {
  2855. uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0);
  2856. for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
  2857. p = Z_ARRVAL_P(stack)->arData + idx;
  2858. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2859. if (idx != k) {
  2860. Bucket *q = Z_ARRVAL_P(stack)->arData + k;
  2861. q->h = k;
  2862. q->key = NULL;
  2863. ZVAL_COPY_VALUE(&q->val, &p->val);
  2864. ZVAL_UNDEF(&p->val);
  2865. if (idx == iter_pos) {
  2866. zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k);
  2867. iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1);
  2868. }
  2869. }
  2870. k++;
  2871. }
  2872. }
  2873. Z_ARRVAL_P(stack)->nNumUsed = k;
  2874. Z_ARRVAL_P(stack)->nNextFreeElement = k;
  2875. } else {
  2876. uint32_t k = 0;
  2877. int should_rehash = 0;
  2878. for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) {
  2879. p = Z_ARRVAL_P(stack)->arData + idx;
  2880. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  2881. if (p->key == NULL) {
  2882. if (p->h != k) {
  2883. p->h = k++;
  2884. should_rehash = 1;
  2885. } else {
  2886. k++;
  2887. }
  2888. }
  2889. }
  2890. Z_ARRVAL_P(stack)->nNextFreeElement = k;
  2891. if (should_rehash) {
  2892. zend_hash_rehash(Z_ARRVAL_P(stack));
  2893. }
  2894. }
  2895. zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
  2896. }
  2897. /* }}} */
  2898. /* {{{ proto int array_unshift(array stack, mixed var [, mixed ...])
  2899. Pushes elements onto the beginning of the array */
  2900. PHP_FUNCTION(array_unshift)
  2901. {
  2902. zval *args, /* Function arguments array */
  2903. *stack; /* Input stack */
  2904. HashTable new_hash; /* New hashtable for the stack */
  2905. int argc; /* Number of function arguments */
  2906. int i;
  2907. zend_string *key;
  2908. zval *value;
  2909. ZEND_PARSE_PARAMETERS_START(1, -1)
  2910. Z_PARAM_ARRAY_EX(stack, 0, 1)
  2911. Z_PARAM_VARIADIC('+', args, argc)
  2912. ZEND_PARSE_PARAMETERS_END();
  2913. zend_hash_init(&new_hash, zend_hash_num_elements(Z_ARRVAL_P(stack)) + argc, NULL, ZVAL_PTR_DTOR, 0);
  2914. for (i = 0; i < argc; i++) {
  2915. Z_TRY_ADDREF(args[i]);
  2916. zend_hash_next_index_insert_new(&new_hash, &args[i]);
  2917. }
  2918. ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) {
  2919. if (key) {
  2920. zend_hash_add_new(&new_hash, key, value);
  2921. } else {
  2922. zend_hash_next_index_insert_new(&new_hash, value);
  2923. }
  2924. } ZEND_HASH_FOREACH_END();
  2925. if (UNEXPECTED(HT_HAS_ITERATORS(Z_ARRVAL_P(stack)))) {
  2926. zend_hash_iterators_advance(Z_ARRVAL_P(stack), argc);
  2927. HT_SET_ITERATORS_COUNT(&new_hash, HT_ITERATORS_COUNT(Z_ARRVAL_P(stack)));
  2928. HT_SET_ITERATORS_COUNT(Z_ARRVAL_P(stack), 0);
  2929. }
  2930. /* replace HashTable data */
  2931. Z_ARRVAL_P(stack)->pDestructor = NULL;
  2932. zend_hash_destroy(Z_ARRVAL_P(stack));
  2933. HT_FLAGS(Z_ARRVAL_P(stack)) = HT_FLAGS(&new_hash);
  2934. Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize;
  2935. Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask;
  2936. Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed;
  2937. Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements;
  2938. Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement;
  2939. Z_ARRVAL_P(stack)->arData = new_hash.arData;
  2940. Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor;
  2941. zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
  2942. /* Clean up and return the number of elements in the stack */
  2943. RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack)));
  2944. }
  2945. /* }}} */
  2946. /* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
  2947. Removes the elements designated by offset and length and replace them with supplied array */
  2948. PHP_FUNCTION(array_splice)
  2949. {
  2950. zval *array, /* Input array */
  2951. *repl_array = NULL; /* Replacement array */
  2952. HashTable *rem_hash = NULL;
  2953. zend_long offset,
  2954. length = 0;
  2955. zend_bool length_is_null = 1;
  2956. int num_in; /* Number of elements in the input array */
  2957. ZEND_PARSE_PARAMETERS_START(2, 4)
  2958. Z_PARAM_ARRAY_EX(array, 0, 1)
  2959. Z_PARAM_LONG(offset)
  2960. Z_PARAM_OPTIONAL
  2961. Z_PARAM_LONG_OR_NULL(length, length_is_null)
  2962. Z_PARAM_ZVAL(repl_array)
  2963. ZEND_PARSE_PARAMETERS_END();
  2964. num_in = zend_hash_num_elements(Z_ARRVAL_P(array));
  2965. if (length_is_null) {
  2966. length = num_in;
  2967. }
  2968. if (ZEND_NUM_ARGS() == 4) {
  2969. /* Make sure the last argument, if passed, is an array */
  2970. convert_to_array_ex(repl_array);
  2971. }
  2972. /* Don't create the array of removed elements if it's not going
  2973. * to be used; e.g. only removing and/or replacing elements */
  2974. if (USED_RET()) {
  2975. zend_long size = length;
  2976. /* Clamp the offset.. */
  2977. if (offset > num_in) {
  2978. offset = num_in;
  2979. } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
  2980. offset = 0;
  2981. }
  2982. /* ..and the length */
  2983. if (length < 0) {
  2984. size = num_in - offset + length;
  2985. } else if (((zend_ulong) offset + (zend_ulong) length) > (uint32_t) num_in) {
  2986. size = num_in - offset;
  2987. }
  2988. /* Initialize return value */
  2989. array_init_size(return_value, size > 0 ? (uint32_t)size : 0);
  2990. rem_hash = Z_ARRVAL_P(return_value);
  2991. } else {
  2992. /* The return value will not be used, but make sure it still has the correct type. */
  2993. RETVAL_EMPTY_ARRAY();
  2994. }
  2995. /* Perform splice */
  2996. php_splice(Z_ARRVAL_P(array), offset, length, repl_array ? Z_ARRVAL_P(repl_array) : NULL, rem_hash);
  2997. }
  2998. /* }}} */
  2999. /* {{{ find_bucket_at_offset(HashTable* ht, zend_long offset)
  3000. Finds the bucket at the given valid offset */
  3001. static inline Bucket* find_bucket_at_offset(HashTable* ht, zend_long offset)
  3002. {
  3003. zend_long pos;
  3004. Bucket *bucket;
  3005. ZEND_ASSERT(offset >= 0 && offset <= ht->nNumOfElements);
  3006. if (HT_IS_WITHOUT_HOLES(ht)) {
  3007. /* There's no need to iterate over the array to filter out holes if there are no holes */
  3008. /* This properly handles both packed and unpacked arrays. */
  3009. return ht->arData + offset;
  3010. }
  3011. /* Otherwise, this code has to iterate over the HashTable and skip holes in the array. */
  3012. pos = 0;
  3013. ZEND_HASH_FOREACH_BUCKET(ht, bucket) {
  3014. if (pos >= offset) {
  3015. /* This is the bucket of the array element at the requested offset */
  3016. return bucket;
  3017. }
  3018. ++pos;
  3019. } ZEND_HASH_FOREACH_END();
  3020. /* Return a pointer to the end of the bucket array. */
  3021. return ht->arData + ht->nNumUsed;
  3022. }
  3023. /* }}} */
  3024. /* {{{ proto array array_slice(array input, int offset [, int length [, bool preserve_keys]])
  3025. Returns elements specified by offset and length */
  3026. PHP_FUNCTION(array_slice)
  3027. {
  3028. zval *input; /* Input array */
  3029. zval *entry; /* An array entry */
  3030. zend_long offset; /* Offset to get elements from */
  3031. zend_long length = 0; /* How many elements to get */
  3032. zend_bool length_is_null = 1; /* Whether an explicit length has been omitted */
  3033. zend_bool preserve_keys = 0; /* Whether to preserve keys while copying to the new array */
  3034. uint32_t num_in; /* Number of elements in the input array */
  3035. zend_string *string_key;
  3036. zend_ulong num_key;
  3037. ZEND_PARSE_PARAMETERS_START(2, 4)
  3038. Z_PARAM_ARRAY(input)
  3039. Z_PARAM_LONG(offset)
  3040. Z_PARAM_OPTIONAL
  3041. Z_PARAM_LONG_OR_NULL(length, length_is_null)
  3042. Z_PARAM_BOOL(preserve_keys)
  3043. ZEND_PARSE_PARAMETERS_END();
  3044. /* Get number of entries in the input hash */
  3045. num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
  3046. /* We want all entries from offset to the end if length is not passed or is null */
  3047. if (length_is_null) {
  3048. length = num_in;
  3049. }
  3050. /* Clamp the offset.. */
  3051. if (offset > (zend_long) num_in) {
  3052. RETURN_EMPTY_ARRAY();
  3053. } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
  3054. offset = 0;
  3055. }
  3056. /* ..and the length */
  3057. if (length < 0) {
  3058. length = num_in - offset + length;
  3059. } else if (((zend_ulong) offset + (zend_ulong) length) > (unsigned) num_in) {
  3060. length = num_in - offset;
  3061. }
  3062. if (length <= 0) {
  3063. RETURN_EMPTY_ARRAY();
  3064. }
  3065. /* Initialize returned array */
  3066. array_init_size(return_value, (uint32_t)length);
  3067. // Contains modified variants of ZEND_HASH_FOREACH_VAL
  3068. {
  3069. HashTable *ht = Z_ARRVAL_P(input);
  3070. Bucket *p = find_bucket_at_offset(ht, offset);
  3071. Bucket *end = ht->arData + ht->nNumUsed;
  3072. /* Start at the beginning and go until we hit offset */
  3073. if (HT_IS_PACKED(Z_ARRVAL_P(input)) &&
  3074. (!preserve_keys ||
  3075. (offset == 0 && HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(input))))) {
  3076. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3077. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3078. for (; p != end; p++) {
  3079. if (__fill_idx >= length) {
  3080. break;
  3081. }
  3082. entry = &p->val;
  3083. if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
  3084. continue;
  3085. }
  3086. if (UNEXPECTED(Z_ISREF_P(entry)) &&
  3087. UNEXPECTED(Z_REFCOUNT_P(entry) == 1)) {
  3088. entry = Z_REFVAL_P(entry);
  3089. }
  3090. Z_TRY_ADDREF_P(entry);
  3091. ZEND_HASH_FILL_ADD(entry);
  3092. }
  3093. } ZEND_HASH_FILL_END();
  3094. } else {
  3095. zend_long n = 0; /* Current number of elements */
  3096. for (; p != end; p++) {
  3097. entry = &p->val;
  3098. if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) {
  3099. continue;
  3100. }
  3101. if (n >= length) {
  3102. break;
  3103. }
  3104. n++;
  3105. num_key = p->h;
  3106. string_key = p->key;
  3107. if (string_key) {
  3108. entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
  3109. } else {
  3110. if (preserve_keys) {
  3111. entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
  3112. } else {
  3113. entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
  3114. }
  3115. }
  3116. zval_add_ref(entry);
  3117. }
  3118. }
  3119. }
  3120. }
  3121. /* }}} */
  3122. PHPAPI int php_array_merge_recursive(HashTable *dest, HashTable *src) /* {{{ */
  3123. {
  3124. zval *src_entry, *dest_entry;
  3125. zend_string *string_key;
  3126. ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
  3127. if (string_key) {
  3128. if ((dest_entry = zend_hash_find_ex(dest, string_key, 1)) != NULL) {
  3129. zval *src_zval = src_entry;
  3130. zval *dest_zval = dest_entry;
  3131. HashTable *thash;
  3132. zval tmp;
  3133. int ret;
  3134. ZVAL_DEREF(src_zval);
  3135. ZVAL_DEREF(dest_zval);
  3136. thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL;
  3137. if ((thash && GC_IS_RECURSIVE(thash)) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
  3138. zend_throw_error(NULL, "Recursion detected");
  3139. return 0;
  3140. }
  3141. ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
  3142. SEPARATE_ZVAL(dest_entry);
  3143. dest_zval = dest_entry;
  3144. if (Z_TYPE_P(dest_zval) == IS_NULL) {
  3145. convert_to_array_ex(dest_zval);
  3146. add_next_index_null(dest_zval);
  3147. } else {
  3148. convert_to_array_ex(dest_zval);
  3149. }
  3150. ZVAL_UNDEF(&tmp);
  3151. if (Z_TYPE_P(src_zval) == IS_OBJECT) {
  3152. ZVAL_COPY(&tmp, src_zval);
  3153. convert_to_array(&tmp);
  3154. src_zval = &tmp;
  3155. }
  3156. if (Z_TYPE_P(src_zval) == IS_ARRAY) {
  3157. if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
  3158. GC_PROTECT_RECURSION(thash);
  3159. }
  3160. ret = php_array_merge_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
  3161. if (thash && !(GC_FLAGS(thash) & GC_IMMUTABLE)) {
  3162. GC_UNPROTECT_RECURSION(thash);
  3163. }
  3164. if (!ret) {
  3165. return 0;
  3166. }
  3167. } else {
  3168. Z_TRY_ADDREF_P(src_entry);
  3169. zend_hash_next_index_insert(Z_ARRVAL_P(dest_zval), src_zval);
  3170. }
  3171. zval_ptr_dtor(&tmp);
  3172. } else {
  3173. zval *zv = zend_hash_add_new(dest, string_key, src_entry);
  3174. zval_add_ref(zv);
  3175. }
  3176. } else {
  3177. zval *zv = zend_hash_next_index_insert(dest, src_entry);
  3178. zval_add_ref(zv);
  3179. }
  3180. } ZEND_HASH_FOREACH_END();
  3181. return 1;
  3182. }
  3183. /* }}} */
  3184. PHPAPI int php_array_merge(HashTable *dest, HashTable *src) /* {{{ */
  3185. {
  3186. zval *src_entry;
  3187. zend_string *string_key;
  3188. if ((HT_FLAGS(dest) & HASH_FLAG_PACKED) && (HT_FLAGS(src) & HASH_FLAG_PACKED)) {
  3189. zend_hash_extend(dest, zend_hash_num_elements(dest) + zend_hash_num_elements(src), 1);
  3190. ZEND_HASH_FILL_PACKED(dest) {
  3191. ZEND_HASH_FOREACH_VAL(src, src_entry) {
  3192. if (UNEXPECTED(Z_ISREF_P(src_entry)) &&
  3193. UNEXPECTED(Z_REFCOUNT_P(src_entry) == 1)) {
  3194. src_entry = Z_REFVAL_P(src_entry);
  3195. }
  3196. Z_TRY_ADDREF_P(src_entry);
  3197. ZEND_HASH_FILL_ADD(src_entry);
  3198. } ZEND_HASH_FOREACH_END();
  3199. } ZEND_HASH_FILL_END();
  3200. } else {
  3201. ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
  3202. if (UNEXPECTED(Z_ISREF_P(src_entry) &&
  3203. Z_REFCOUNT_P(src_entry) == 1)) {
  3204. src_entry = Z_REFVAL_P(src_entry);
  3205. }
  3206. Z_TRY_ADDREF_P(src_entry);
  3207. if (string_key) {
  3208. zend_hash_update(dest, string_key, src_entry);
  3209. } else {
  3210. zend_hash_next_index_insert_new(dest, src_entry);
  3211. }
  3212. } ZEND_HASH_FOREACH_END();
  3213. }
  3214. return 1;
  3215. }
  3216. /* }}} */
  3217. PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ */
  3218. {
  3219. zval *src_entry, *dest_entry, *src_zval, *dest_zval;
  3220. zend_string *string_key;
  3221. zend_ulong num_key;
  3222. int ret;
  3223. ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
  3224. src_zval = src_entry;
  3225. ZVAL_DEREF(src_zval);
  3226. if (string_key) {
  3227. if (Z_TYPE_P(src_zval) != IS_ARRAY ||
  3228. (dest_entry = zend_hash_find_ex(dest, string_key, 1)) == NULL ||
  3229. (Z_TYPE_P(dest_entry) != IS_ARRAY &&
  3230. (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
  3231. zval *zv = zend_hash_update(dest, string_key, src_entry);
  3232. zval_add_ref(zv);
  3233. continue;
  3234. }
  3235. } else {
  3236. if (Z_TYPE_P(src_zval) != IS_ARRAY ||
  3237. (dest_entry = zend_hash_index_find(dest, num_key)) == NULL ||
  3238. (Z_TYPE_P(dest_entry) != IS_ARRAY &&
  3239. (!Z_ISREF_P(dest_entry) || Z_TYPE_P(Z_REFVAL_P(dest_entry)) != IS_ARRAY))) {
  3240. zval *zv = zend_hash_index_update(dest, num_key, src_entry);
  3241. zval_add_ref(zv);
  3242. continue;
  3243. }
  3244. }
  3245. dest_zval = dest_entry;
  3246. ZVAL_DEREF(dest_zval);
  3247. if (Z_IS_RECURSIVE_P(dest_zval) ||
  3248. Z_IS_RECURSIVE_P(src_zval) ||
  3249. (Z_ISREF_P(src_entry) && Z_ISREF_P(dest_entry) && Z_REF_P(src_entry) == Z_REF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) {
  3250. zend_throw_error(NULL, "Recursion detected");
  3251. return 0;
  3252. }
  3253. ZEND_ASSERT(!Z_ISREF_P(dest_entry) || Z_REFCOUNT_P(dest_entry) > 1);
  3254. SEPARATE_ZVAL(dest_entry);
  3255. dest_zval = dest_entry;
  3256. if (Z_REFCOUNTED_P(dest_zval)) {
  3257. Z_PROTECT_RECURSION_P(dest_zval);
  3258. }
  3259. if (Z_REFCOUNTED_P(src_zval)) {
  3260. Z_PROTECT_RECURSION_P(src_zval);
  3261. }
  3262. ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval));
  3263. if (Z_REFCOUNTED_P(dest_zval)) {
  3264. Z_UNPROTECT_RECURSION_P(dest_zval);
  3265. }
  3266. if (Z_REFCOUNTED_P(src_zval)) {
  3267. Z_UNPROTECT_RECURSION_P(src_zval);
  3268. }
  3269. if (!ret) {
  3270. return 0;
  3271. }
  3272. } ZEND_HASH_FOREACH_END();
  3273. return 1;
  3274. }
  3275. /* }}} */
  3276. static zend_always_inline void php_array_replace_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
  3277. {
  3278. zval *args = NULL;
  3279. zval *arg;
  3280. int argc, i;
  3281. HashTable *dest;
  3282. ZEND_PARSE_PARAMETERS_START(1, -1)
  3283. Z_PARAM_VARIADIC('+', args, argc)
  3284. ZEND_PARSE_PARAMETERS_END();
  3285. for (i = 0; i < argc; i++) {
  3286. zval *arg = args + i;
  3287. if (Z_TYPE_P(arg) != IS_ARRAY) {
  3288. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
  3289. RETURN_THROWS();
  3290. }
  3291. }
  3292. /* copy first array */
  3293. arg = args;
  3294. dest = zend_array_dup(Z_ARRVAL_P(arg));
  3295. ZVAL_ARR(return_value, dest);
  3296. if (recursive) {
  3297. for (i = 1; i < argc; i++) {
  3298. arg = args + i;
  3299. php_array_replace_recursive(dest, Z_ARRVAL_P(arg));
  3300. }
  3301. } else {
  3302. for (i = 1; i < argc; i++) {
  3303. arg = args + i;
  3304. zend_hash_merge(dest, Z_ARRVAL_P(arg), zval_add_ref, 1);
  3305. }
  3306. }
  3307. }
  3308. /* }}} */
  3309. static zend_always_inline void php_array_merge_wrapper(INTERNAL_FUNCTION_PARAMETERS, int recursive) /* {{{ */
  3310. {
  3311. zval *args = NULL;
  3312. zval *arg;
  3313. int argc, i;
  3314. zval *src_entry;
  3315. HashTable *src, *dest;
  3316. uint32_t count = 0;
  3317. ZEND_PARSE_PARAMETERS_START(0, -1)
  3318. Z_PARAM_VARIADIC('+', args, argc)
  3319. ZEND_PARSE_PARAMETERS_END();
  3320. if (argc == 0) {
  3321. RETURN_EMPTY_ARRAY();
  3322. }
  3323. for (i = 0; i < argc; i++) {
  3324. zval *arg = args + i;
  3325. if (Z_TYPE_P(arg) != IS_ARRAY) {
  3326. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(arg));
  3327. RETURN_THROWS();
  3328. }
  3329. count += zend_hash_num_elements(Z_ARRVAL_P(arg));
  3330. }
  3331. if (argc == 2) {
  3332. zval *ret = NULL;
  3333. if (zend_hash_num_elements(Z_ARRVAL(args[0])) == 0) {
  3334. ret = &args[1];
  3335. } else if (zend_hash_num_elements(Z_ARRVAL(args[1])) == 0) {
  3336. ret = &args[0];
  3337. }
  3338. if (ret) {
  3339. if (HT_FLAGS(Z_ARRVAL_P(ret)) & HASH_FLAG_PACKED) {
  3340. if (HT_IS_WITHOUT_HOLES(Z_ARRVAL_P(ret))) {
  3341. ZVAL_COPY(return_value, ret);
  3342. return;
  3343. }
  3344. } else {
  3345. zend_bool copy = 1;
  3346. zend_string *string_key;
  3347. ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(ret), string_key) {
  3348. if (!string_key) {
  3349. copy = 0;
  3350. break;
  3351. }
  3352. } ZEND_HASH_FOREACH_END();
  3353. if (copy) {
  3354. ZVAL_COPY(return_value, ret);
  3355. return;
  3356. }
  3357. }
  3358. }
  3359. }
  3360. arg = args;
  3361. src = Z_ARRVAL_P(arg);
  3362. /* copy first array */
  3363. array_init_size(return_value, count);
  3364. dest = Z_ARRVAL_P(return_value);
  3365. if (HT_FLAGS(src) & HASH_FLAG_PACKED) {
  3366. zend_hash_real_init_packed(dest);
  3367. ZEND_HASH_FILL_PACKED(dest) {
  3368. ZEND_HASH_FOREACH_VAL(src, src_entry) {
  3369. if (UNEXPECTED(Z_ISREF_P(src_entry) &&
  3370. Z_REFCOUNT_P(src_entry) == 1)) {
  3371. src_entry = Z_REFVAL_P(src_entry);
  3372. }
  3373. Z_TRY_ADDREF_P(src_entry);
  3374. ZEND_HASH_FILL_ADD(src_entry);
  3375. } ZEND_HASH_FOREACH_END();
  3376. } ZEND_HASH_FILL_END();
  3377. } else {
  3378. zend_string *string_key;
  3379. zend_hash_real_init_mixed(dest);
  3380. ZEND_HASH_FOREACH_STR_KEY_VAL(src, string_key, src_entry) {
  3381. if (UNEXPECTED(Z_ISREF_P(src_entry) &&
  3382. Z_REFCOUNT_P(src_entry) == 1)) {
  3383. src_entry = Z_REFVAL_P(src_entry);
  3384. }
  3385. Z_TRY_ADDREF_P(src_entry);
  3386. if (EXPECTED(string_key)) {
  3387. _zend_hash_append(dest, string_key, src_entry);
  3388. } else {
  3389. zend_hash_next_index_insert_new(dest, src_entry);
  3390. }
  3391. } ZEND_HASH_FOREACH_END();
  3392. }
  3393. if (recursive) {
  3394. for (i = 1; i < argc; i++) {
  3395. arg = args + i;
  3396. php_array_merge_recursive(dest, Z_ARRVAL_P(arg));
  3397. }
  3398. } else {
  3399. for (i = 1; i < argc; i++) {
  3400. arg = args + i;
  3401. php_array_merge(dest, Z_ARRVAL_P(arg));
  3402. }
  3403. }
  3404. }
  3405. /* }}} */
  3406. /* {{{ proto array array_merge([array ...])
  3407. Merges elements from passed arrays into one array */
  3408. PHP_FUNCTION(array_merge)
  3409. {
  3410. php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  3411. }
  3412. /* }}} */
  3413. /* {{{ proto array array_merge_recursive([array ...])
  3414. Recursively merges elements from passed arrays into one array */
  3415. PHP_FUNCTION(array_merge_recursive)
  3416. {
  3417. php_array_merge_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  3418. }
  3419. /* }}} */
  3420. /* {{{ proto array array_replace(array arr1 [, array ...])
  3421. Replaces elements from passed arrays into one array */
  3422. PHP_FUNCTION(array_replace)
  3423. {
  3424. php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
  3425. }
  3426. /* }}} */
  3427. /* {{{ proto array array_replace_recursive(array arr1 [, array ...])
  3428. Recursively replaces elements from passed arrays into one array */
  3429. PHP_FUNCTION(array_replace_recursive)
  3430. {
  3431. php_array_replace_wrapper(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  3432. }
  3433. /* }}} */
  3434. /* {{{ proto array array_keys(array input [, mixed search_value[, bool strict]])
  3435. Return just the keys from the input array, optionally only for the specified search_value */
  3436. PHP_FUNCTION(array_keys)
  3437. {
  3438. zval *input, /* Input array */
  3439. *search_value = NULL, /* Value to search for */
  3440. *entry, /* An entry in the input array */
  3441. new_val; /* New value */
  3442. zend_bool strict = 0; /* do strict comparison */
  3443. zend_ulong num_idx;
  3444. zend_string *str_idx;
  3445. zend_array *arrval;
  3446. zend_ulong elem_count;
  3447. ZEND_PARSE_PARAMETERS_START(1, 3)
  3448. Z_PARAM_ARRAY(input)
  3449. Z_PARAM_OPTIONAL
  3450. Z_PARAM_ZVAL(search_value)
  3451. Z_PARAM_BOOL(strict)
  3452. ZEND_PARSE_PARAMETERS_END();
  3453. arrval = Z_ARRVAL_P(input);
  3454. elem_count = zend_hash_num_elements(arrval);
  3455. /* Base case: empty input */
  3456. if (!elem_count) {
  3457. RETURN_COPY(input);
  3458. }
  3459. /* Initialize return array */
  3460. if (search_value != NULL) {
  3461. array_init(return_value);
  3462. if (strict) {
  3463. ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
  3464. ZVAL_DEREF(entry);
  3465. if (fast_is_identical_function(search_value, entry)) {
  3466. if (str_idx) {
  3467. ZVAL_STR_COPY(&new_val, str_idx);
  3468. } else {
  3469. ZVAL_LONG(&new_val, num_idx);
  3470. }
  3471. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
  3472. }
  3473. } ZEND_HASH_FOREACH_END();
  3474. } else {
  3475. ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
  3476. if (fast_equal_check_function(search_value, entry)) {
  3477. if (str_idx) {
  3478. ZVAL_STR_COPY(&new_val, str_idx);
  3479. } else {
  3480. ZVAL_LONG(&new_val, num_idx);
  3481. }
  3482. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
  3483. }
  3484. } ZEND_HASH_FOREACH_END();
  3485. }
  3486. } else {
  3487. array_init_size(return_value, elem_count);
  3488. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3489. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3490. if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
  3491. /* Optimistic case: range(0..n-1) for vector-like packed array */
  3492. zend_ulong lval = 0;
  3493. for (; lval < elem_count; ++lval) {
  3494. ZEND_HASH_FILL_SET_LONG(lval);
  3495. ZEND_HASH_FILL_NEXT();
  3496. }
  3497. } else {
  3498. /* Go through input array and add keys to the return array */
  3499. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
  3500. if (str_idx) {
  3501. ZEND_HASH_FILL_SET_STR_COPY(str_idx);
  3502. } else {
  3503. ZEND_HASH_FILL_SET_LONG(num_idx);
  3504. }
  3505. ZEND_HASH_FILL_NEXT();
  3506. } ZEND_HASH_FOREACH_END();
  3507. }
  3508. } ZEND_HASH_FILL_END();
  3509. }
  3510. }
  3511. /* }}} */
  3512. /* {{{ proto int|string|false array_key_first(array stack)
  3513. Get the key of the first element of the array */
  3514. PHP_FUNCTION(array_key_first)
  3515. {
  3516. zval *stack; /* Input stack */
  3517. ZEND_PARSE_PARAMETERS_START(1, 1)
  3518. Z_PARAM_ARRAY(stack)
  3519. ZEND_PARSE_PARAMETERS_END();
  3520. HashTable *target_hash = Z_ARRVAL_P (stack);
  3521. HashPosition pos = 0;
  3522. zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
  3523. }
  3524. /* }}} */
  3525. /* {{{ proto int|string|false array_key_last(array stack)
  3526. Get the key of the last element of the array */
  3527. PHP_FUNCTION(array_key_last)
  3528. {
  3529. zval *stack; /* Input stack */
  3530. HashPosition pos;
  3531. ZEND_PARSE_PARAMETERS_START(1, 1)
  3532. Z_PARAM_ARRAY(stack)
  3533. ZEND_PARSE_PARAMETERS_END();
  3534. HashTable *target_hash = Z_ARRVAL_P (stack);
  3535. zend_hash_internal_pointer_end_ex(target_hash, &pos);
  3536. zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos);
  3537. }
  3538. /* }}} */
  3539. /* {{{ proto array array_values(array input)
  3540. Return just the values from the input array */
  3541. PHP_FUNCTION(array_values)
  3542. {
  3543. zval *input, /* Input array */
  3544. *entry; /* An entry in the input array */
  3545. zend_array *arrval;
  3546. zend_long arrlen;
  3547. ZEND_PARSE_PARAMETERS_START(1, 1)
  3548. Z_PARAM_ARRAY(input)
  3549. ZEND_PARSE_PARAMETERS_END();
  3550. arrval = Z_ARRVAL_P(input);
  3551. /* Return empty input as is */
  3552. arrlen = zend_hash_num_elements(arrval);
  3553. if (!arrlen) {
  3554. RETURN_EMPTY_ARRAY();
  3555. }
  3556. /* Return vector-like packed arrays as-is */
  3557. if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval) &&
  3558. arrval->nNextFreeElement == arrlen) {
  3559. RETURN_COPY(input);
  3560. }
  3561. /* Initialize return array */
  3562. array_init_size(return_value, zend_hash_num_elements(arrval));
  3563. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3564. /* Go through input array and add values to the return array */
  3565. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3566. ZEND_HASH_FOREACH_VAL(arrval, entry) {
  3567. if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
  3568. entry = Z_REFVAL_P(entry);
  3569. }
  3570. Z_TRY_ADDREF_P(entry);
  3571. ZEND_HASH_FILL_ADD(entry);
  3572. } ZEND_HASH_FOREACH_END();
  3573. } ZEND_HASH_FILL_END();
  3574. }
  3575. /* }}} */
  3576. /* {{{ proto array array_count_values(array input)
  3577. Return the value as key and the frequency of that value in input as value */
  3578. PHP_FUNCTION(array_count_values)
  3579. {
  3580. zval *input, /* Input array */
  3581. *entry, /* An entry in the input array */
  3582. *tmp;
  3583. HashTable *myht;
  3584. ZEND_PARSE_PARAMETERS_START(1, 1)
  3585. Z_PARAM_ARRAY(input)
  3586. ZEND_PARSE_PARAMETERS_END();
  3587. /* Initialize return array */
  3588. array_init(return_value);
  3589. /* Go through input array and add values to the return array */
  3590. myht = Z_ARRVAL_P(input);
  3591. ZEND_HASH_FOREACH_VAL(myht, entry) {
  3592. ZVAL_DEREF(entry);
  3593. if (Z_TYPE_P(entry) == IS_LONG) {
  3594. if ((tmp = zend_hash_index_find(Z_ARRVAL_P(return_value), Z_LVAL_P(entry))) == NULL) {
  3595. zval data;
  3596. ZVAL_LONG(&data, 1);
  3597. zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
  3598. } else {
  3599. Z_LVAL_P(tmp)++;
  3600. }
  3601. } else if (Z_TYPE_P(entry) == IS_STRING) {
  3602. if ((tmp = zend_symtable_find(Z_ARRVAL_P(return_value), Z_STR_P(entry))) == NULL) {
  3603. zval data;
  3604. ZVAL_LONG(&data, 1);
  3605. zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
  3606. } else {
  3607. Z_LVAL_P(tmp)++;
  3608. }
  3609. } else {
  3610. php_error_docref(NULL, E_WARNING, "Can only count string and integer values, entry skipped");
  3611. }
  3612. } ZEND_HASH_FOREACH_END();
  3613. }
  3614. /* }}} */
  3615. static inline zval *array_column_fetch_prop(zval *data, zend_string *name_str, zend_long name_long, zval *rv) /* {{{ */
  3616. {
  3617. zval *prop = NULL;
  3618. if (Z_TYPE_P(data) == IS_OBJECT) {
  3619. zend_string *tmp_str;
  3620. /* If name is an integer convert integer to string */
  3621. if (name_str == NULL) {
  3622. tmp_str = zend_long_to_str(name_long);
  3623. } else {
  3624. tmp_str = zend_string_copy(name_str);
  3625. }
  3626. /* The has_property check is first performed in "exists" mode (which returns true for
  3627. * properties that are null but exist) and then in "has" mode to handle objects that
  3628. * implement __isset (which is not called in "exists" mode). */
  3629. if (Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_EXISTS, NULL)
  3630. || Z_OBJ_HANDLER_P(data, has_property)(Z_OBJ_P(data), tmp_str, ZEND_PROPERTY_ISSET, NULL)) {
  3631. prop = Z_OBJ_HANDLER_P(data, read_property)(Z_OBJ_P(data), tmp_str, BP_VAR_R, NULL, rv);
  3632. if (prop) {
  3633. ZVAL_DEREF(prop);
  3634. if (prop != rv) {
  3635. Z_TRY_ADDREF_P(prop);
  3636. }
  3637. }
  3638. }
  3639. zend_string_release(tmp_str);
  3640. } else if (Z_TYPE_P(data) == IS_ARRAY) {
  3641. /* Name is a string */
  3642. if (name_str != NULL) {
  3643. prop = zend_symtable_find(Z_ARRVAL_P(data), name_str);
  3644. } else {
  3645. prop = zend_hash_index_find(Z_ARRVAL_P(data), name_long);
  3646. }
  3647. if (prop) {
  3648. ZVAL_DEREF(prop);
  3649. Z_TRY_ADDREF_P(prop);
  3650. }
  3651. }
  3652. return prop;
  3653. }
  3654. /* }}} */
  3655. /* {{{ proto array array_column(array input, string|int|null column_key[, string|int|null index_key])
  3656. Return the values from a single column in the input array, identified by the
  3657. value_key and optionally indexed by the index_key */
  3658. PHP_FUNCTION(array_column)
  3659. {
  3660. HashTable *input;
  3661. zval *colval, *data, rv;
  3662. zend_string *column_str = NULL;
  3663. zend_long column_long;
  3664. zend_bool column_is_null = 0;
  3665. zend_string *index_str = NULL;
  3666. zend_long index_long;
  3667. zend_bool index_is_null = 1;
  3668. ZEND_PARSE_PARAMETERS_START(2, 3)
  3669. Z_PARAM_ARRAY_HT(input)
  3670. Z_PARAM_STR_OR_LONG_OR_NULL(column_str, column_long, column_is_null)
  3671. Z_PARAM_OPTIONAL
  3672. Z_PARAM_STR_OR_LONG_OR_NULL(index_str, index_long, index_is_null)
  3673. ZEND_PARSE_PARAMETERS_END();
  3674. array_init_size(return_value, zend_hash_num_elements(input));
  3675. /* Index param is not passed */
  3676. if (index_is_null) {
  3677. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3678. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3679. ZEND_HASH_FOREACH_VAL(input, data) {
  3680. ZVAL_DEREF(data);
  3681. if (column_is_null) {
  3682. Z_TRY_ADDREF_P(data);
  3683. colval = data;
  3684. } else if ((colval = array_column_fetch_prop(data, column_str, column_long, &rv)) == NULL) {
  3685. continue;
  3686. }
  3687. ZEND_HASH_FILL_ADD(colval);
  3688. } ZEND_HASH_FOREACH_END();
  3689. } ZEND_HASH_FILL_END();
  3690. } else {
  3691. ZEND_HASH_FOREACH_VAL(input, data) {
  3692. ZVAL_DEREF(data);
  3693. if (column_is_null) {
  3694. Z_TRY_ADDREF_P(data);
  3695. colval = data;
  3696. } else if ((colval = array_column_fetch_prop(data, column_str, column_long, &rv)) == NULL) {
  3697. continue;
  3698. }
  3699. zval rv;
  3700. zval *keyval = array_column_fetch_prop(data, index_str, index_long, &rv);
  3701. if (keyval) {
  3702. array_set_zval_key(Z_ARRVAL_P(return_value), keyval, colval);
  3703. zval_ptr_dtor(colval);
  3704. zval_ptr_dtor(keyval);
  3705. } else {
  3706. zend_hash_next_index_insert(Z_ARRVAL_P(return_value), colval);
  3707. }
  3708. } ZEND_HASH_FOREACH_END();
  3709. }
  3710. }
  3711. /* }}} */
  3712. /* {{{ proto array array_reverse(array input [, bool preserve keys])
  3713. Return input as a new array with the order of the entries reversed */
  3714. PHP_FUNCTION(array_reverse)
  3715. {
  3716. zval *input, /* Input array */
  3717. *entry; /* An entry in the input array */
  3718. zend_string *string_key;
  3719. zend_ulong num_key;
  3720. zend_bool preserve_keys = 0; /* whether to preserve keys */
  3721. ZEND_PARSE_PARAMETERS_START(1, 2)
  3722. Z_PARAM_ARRAY(input)
  3723. Z_PARAM_OPTIONAL
  3724. Z_PARAM_BOOL(preserve_keys)
  3725. ZEND_PARSE_PARAMETERS_END();
  3726. /* Initialize return array */
  3727. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
  3728. if ((HT_FLAGS(Z_ARRVAL_P(input)) & HASH_FLAG_PACKED) && !preserve_keys) {
  3729. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3730. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3731. ZEND_HASH_REVERSE_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
  3732. if (UNEXPECTED(Z_ISREF_P(entry) &&
  3733. Z_REFCOUNT_P(entry) == 1)) {
  3734. entry = Z_REFVAL_P(entry);
  3735. }
  3736. Z_TRY_ADDREF_P(entry);
  3737. ZEND_HASH_FILL_ADD(entry);
  3738. } ZEND_HASH_FOREACH_END();
  3739. } ZEND_HASH_FILL_END();
  3740. } else {
  3741. ZEND_HASH_REVERSE_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, string_key, entry) {
  3742. if (string_key) {
  3743. entry = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, entry);
  3744. } else {
  3745. if (preserve_keys) {
  3746. entry = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, entry);
  3747. } else {
  3748. entry = zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), entry);
  3749. }
  3750. }
  3751. zval_add_ref(entry);
  3752. } ZEND_HASH_FOREACH_END();
  3753. }
  3754. }
  3755. /* }}} */
  3756. /* {{{ proto array array_pad(array input, int pad_size, mixed pad_value)
  3757. Returns a copy of input array padded with pad_value to size pad_size */
  3758. PHP_FUNCTION(array_pad)
  3759. {
  3760. zval *input; /* Input array */
  3761. zval *pad_value; /* Padding value obviously */
  3762. zend_long pad_size; /* Size to pad to */
  3763. zend_long pad_size_abs; /* Absolute value of pad_size */
  3764. zend_long input_size; /* Size of the input array */
  3765. zend_long num_pads; /* How many pads do we need */
  3766. zend_long i;
  3767. zend_string *key;
  3768. zval *value;
  3769. ZEND_PARSE_PARAMETERS_START(3, 3)
  3770. Z_PARAM_ARRAY(input)
  3771. Z_PARAM_LONG(pad_size)
  3772. Z_PARAM_ZVAL(pad_value)
  3773. ZEND_PARSE_PARAMETERS_END();
  3774. /* Do some initial calculations */
  3775. input_size = zend_hash_num_elements(Z_ARRVAL_P(input));
  3776. pad_size_abs = ZEND_ABS(pad_size);
  3777. if (pad_size_abs < 0 || pad_size_abs - input_size > Z_L(1048576)) {
  3778. zend_argument_value_error(2, "must be less than or equal to 1048576");
  3779. RETURN_THROWS();
  3780. }
  3781. if (input_size >= pad_size_abs) {
  3782. /* Copy the original array */
  3783. ZVAL_COPY(return_value, input);
  3784. return;
  3785. }
  3786. num_pads = pad_size_abs - input_size;
  3787. if (Z_REFCOUNTED_P(pad_value)) {
  3788. GC_ADDREF_EX(Z_COUNTED_P(pad_value), num_pads);
  3789. }
  3790. array_init_size(return_value, pad_size_abs);
  3791. if (HT_FLAGS(Z_ARRVAL_P(input)) & HASH_FLAG_PACKED) {
  3792. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  3793. if (pad_size < 0) {
  3794. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3795. for (i = 0; i < num_pads; i++) {
  3796. ZEND_HASH_FILL_ADD(pad_value);
  3797. }
  3798. } ZEND_HASH_FILL_END();
  3799. }
  3800. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3801. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), value) {
  3802. Z_TRY_ADDREF_P(value);
  3803. ZEND_HASH_FILL_ADD(value);
  3804. } ZEND_HASH_FOREACH_END();
  3805. } ZEND_HASH_FILL_END();
  3806. if (pad_size > 0) {
  3807. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  3808. for (i = 0; i < num_pads; i++) {
  3809. ZEND_HASH_FILL_ADD(pad_value);
  3810. }
  3811. } ZEND_HASH_FILL_END();
  3812. }
  3813. } else {
  3814. if (pad_size < 0) {
  3815. for (i = 0; i < num_pads; i++) {
  3816. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
  3817. }
  3818. }
  3819. ZEND_HASH_FOREACH_STR_KEY_VAL_IND(Z_ARRVAL_P(input), key, value) {
  3820. Z_TRY_ADDREF_P(value);
  3821. if (key) {
  3822. zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
  3823. } else {
  3824. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), value);
  3825. }
  3826. } ZEND_HASH_FOREACH_END();
  3827. if (pad_size > 0) {
  3828. for (i = 0; i < num_pads; i++) {
  3829. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), pad_value);
  3830. }
  3831. }
  3832. }
  3833. }
  3834. /* }}} */
  3835. /* {{{ proto array array_flip(array input)
  3836. Return array with key <-> value flipped */
  3837. PHP_FUNCTION(array_flip)
  3838. {
  3839. zval *array, *entry, data;
  3840. zend_ulong num_idx;
  3841. zend_string *str_idx;
  3842. ZEND_PARSE_PARAMETERS_START(1, 1)
  3843. Z_PARAM_ARRAY(array)
  3844. ZEND_PARSE_PARAMETERS_END();
  3845. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
  3846. ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
  3847. ZVAL_DEREF(entry);
  3848. if (Z_TYPE_P(entry) == IS_LONG) {
  3849. if (str_idx) {
  3850. ZVAL_STR_COPY(&data, str_idx);
  3851. } else {
  3852. ZVAL_LONG(&data, num_idx);
  3853. }
  3854. zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(entry), &data);
  3855. } else if (Z_TYPE_P(entry) == IS_STRING) {
  3856. if (str_idx) {
  3857. ZVAL_STR_COPY(&data, str_idx);
  3858. } else {
  3859. ZVAL_LONG(&data, num_idx);
  3860. }
  3861. zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
  3862. } else {
  3863. php_error_docref(NULL, E_WARNING, "Can only flip string and integer values, entry skipped");
  3864. }
  3865. } ZEND_HASH_FOREACH_END();
  3866. }
  3867. /* }}} */
  3868. /* {{{ proto array array_change_key_case(array input [, int case=CASE_LOWER])
  3869. Returns an array with all string keys lowercased [or uppercased] */
  3870. PHP_FUNCTION(array_change_key_case)
  3871. {
  3872. zval *array, *entry;
  3873. zend_string *string_key;
  3874. zend_string *new_key;
  3875. zend_ulong num_key;
  3876. zend_long change_to_upper=0;
  3877. ZEND_PARSE_PARAMETERS_START(1, 2)
  3878. Z_PARAM_ARRAY(array)
  3879. Z_PARAM_OPTIONAL
  3880. Z_PARAM_LONG(change_to_upper)
  3881. ZEND_PARSE_PARAMETERS_END();
  3882. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));
  3883. ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
  3884. if (!string_key) {
  3885. entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
  3886. } else {
  3887. if (change_to_upper) {
  3888. new_key = php_string_toupper(string_key);
  3889. } else {
  3890. new_key = php_string_tolower(string_key);
  3891. }
  3892. entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
  3893. zend_string_release_ex(new_key, 0);
  3894. }
  3895. zval_add_ref(entry);
  3896. } ZEND_HASH_FOREACH_END();
  3897. }
  3898. /* }}} */
  3899. struct bucketindex {
  3900. Bucket b;
  3901. unsigned int i;
  3902. };
  3903. static void array_bucketindex_swap(void *p, void *q) /* {{{ */
  3904. {
  3905. struct bucketindex *f = (struct bucketindex *)p;
  3906. struct bucketindex *g = (struct bucketindex *)q;
  3907. struct bucketindex t;
  3908. t = *f;
  3909. *f = *g;
  3910. *g = t;
  3911. }
  3912. /* }}} */
  3913. /* {{{ proto array array_unique(array input [, int sort_flags])
  3914. Removes duplicate values from array */
  3915. PHP_FUNCTION(array_unique)
  3916. {
  3917. zval *array;
  3918. uint32_t idx;
  3919. Bucket *p;
  3920. struct bucketindex *arTmp, *cmpdata, *lastkept;
  3921. unsigned int i;
  3922. zend_long sort_type = PHP_SORT_STRING;
  3923. bucket_compare_func_t cmp;
  3924. ZEND_PARSE_PARAMETERS_START(1, 2)
  3925. Z_PARAM_ARRAY(array)
  3926. Z_PARAM_OPTIONAL
  3927. Z_PARAM_LONG(sort_type)
  3928. ZEND_PARSE_PARAMETERS_END();
  3929. if (Z_ARRVAL_P(array)->nNumOfElements <= 1) { /* nothing to do */
  3930. ZVAL_COPY(return_value, array);
  3931. return;
  3932. }
  3933. if (sort_type == PHP_SORT_STRING) {
  3934. HashTable seen;
  3935. zend_long num_key;
  3936. zend_string *str_key;
  3937. zval *val;
  3938. zend_hash_init(&seen, zend_hash_num_elements(Z_ARRVAL_P(array)), NULL, NULL, 0);
  3939. array_init(return_value);
  3940. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, str_key, val) {
  3941. zval *retval;
  3942. if (Z_TYPE_P(val) == IS_STRING) {
  3943. retval = zend_hash_add_empty_element(&seen, Z_STR_P(val));
  3944. } else {
  3945. zend_string *tmp_str_val;
  3946. zend_string *str_val = zval_get_tmp_string(val, &tmp_str_val);
  3947. retval = zend_hash_add_empty_element(&seen, str_val);
  3948. zend_tmp_string_release(tmp_str_val);
  3949. }
  3950. if (retval) {
  3951. /* First occurrence of the value */
  3952. if (UNEXPECTED(Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1)) {
  3953. ZVAL_DEREF(val);
  3954. }
  3955. Z_TRY_ADDREF_P(val);
  3956. if (str_key) {
  3957. zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, val);
  3958. } else {
  3959. zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
  3960. }
  3961. }
  3962. } ZEND_HASH_FOREACH_END();
  3963. zend_hash_destroy(&seen);
  3964. return;
  3965. }
  3966. cmp = php_get_data_compare_func(sort_type, 0);
  3967. RETVAL_ARR(zend_array_dup(Z_ARRVAL_P(array)));
  3968. /* create and sort array with pointers to the target_hash buckets */
  3969. arTmp = (struct bucketindex *) pemalloc((Z_ARRVAL_P(array)->nNumOfElements + 1) * sizeof(struct bucketindex), GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
  3970. for (i = 0, idx = 0; idx < Z_ARRVAL_P(array)->nNumUsed; idx++) {
  3971. p = Z_ARRVAL_P(array)->arData + idx;
  3972. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  3973. if (Z_TYPE(p->val) == IS_INDIRECT && Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) continue;
  3974. arTmp[i].b = *p;
  3975. arTmp[i].i = i;
  3976. i++;
  3977. }
  3978. ZVAL_UNDEF(&arTmp[i].b.val);
  3979. zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
  3980. (compare_func_t) cmp, (swap_func_t)array_bucketindex_swap);
  3981. /* go through the sorted array and delete duplicates from the copy */
  3982. lastkept = arTmp;
  3983. for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
  3984. if (cmp(&lastkept->b, &cmpdata->b)) {
  3985. lastkept = cmpdata;
  3986. } else {
  3987. if (lastkept->i > cmpdata->i) {
  3988. p = &lastkept->b;
  3989. lastkept = cmpdata;
  3990. } else {
  3991. p = &cmpdata->b;
  3992. }
  3993. if (p->key == NULL) {
  3994. zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
  3995. } else {
  3996. if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
  3997. zend_delete_global_variable(p->key);
  3998. } else {
  3999. zend_hash_del(Z_ARRVAL_P(return_value), p->key);
  4000. }
  4001. }
  4002. }
  4003. }
  4004. pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);
  4005. }
  4006. /* }}} */
  4007. static int zval_compare(zval *first, zval *second) /* {{{ */
  4008. {
  4009. return string_compare_function(first, second);
  4010. }
  4011. /* }}} */
  4012. static int zval_user_compare(zval *a, zval *b) /* {{{ */
  4013. {
  4014. zval args[2];
  4015. zval retval;
  4016. ZVAL_COPY_VALUE(&args[0], a);
  4017. ZVAL_COPY_VALUE(&args[1], b);
  4018. BG(user_compare_fci).param_count = 2;
  4019. BG(user_compare_fci).params = args;
  4020. BG(user_compare_fci).retval = &retval;
  4021. BG(user_compare_fci).no_separation = 0;
  4022. if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache)) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
  4023. zend_long ret = zval_get_long(&retval);
  4024. zval_ptr_dtor(&retval);
  4025. return ZEND_NORMALIZE_BOOL(ret);
  4026. } else {
  4027. return 0;
  4028. }
  4029. }
  4030. /* }}} */
  4031. static void php_array_intersect_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
  4032. {
  4033. int argc, i;
  4034. zval *args;
  4035. int (*intersect_data_compare_func)(zval *, zval *) = NULL;
  4036. zend_bool ok;
  4037. zval *val, *data;
  4038. int req_args;
  4039. char *param_spec;
  4040. zend_string *key;
  4041. zend_ulong h;
  4042. /* Get the argument count */
  4043. argc = ZEND_NUM_ARGS();
  4044. if (data_compare_type == INTERSECT_COMP_DATA_USER) {
  4045. /* INTERSECT_COMP_DATA_USER - array_uintersect_assoc() */
  4046. req_args = 3;
  4047. param_spec = "+f";
  4048. intersect_data_compare_func = zval_user_compare;
  4049. } else {
  4050. /* INTERSECT_COMP_DATA_NONE - array_intersect_key()
  4051. INTERSECT_COMP_DATA_INTERNAL - array_intersect_assoc() */
  4052. req_args = 2;
  4053. param_spec = "+";
  4054. if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
  4055. intersect_data_compare_func = zval_compare;
  4056. }
  4057. }
  4058. if (argc < req_args) {
  4059. zend_argument_count_error("At least %d parameters are required, %d given", req_args, argc);
  4060. RETURN_THROWS();
  4061. }
  4062. if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
  4063. RETURN_THROWS();
  4064. }
  4065. for (i = 0; i < argc; i++) {
  4066. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4067. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4068. RETURN_THROWS();
  4069. }
  4070. }
  4071. array_init(return_value);
  4072. /* Iterate over keys of the first array (handling possibility of indirects such as in $GLOBALS), to compute keys that are in all of the other arrays. */
  4073. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), h, key, val) {
  4074. if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
  4075. val = Z_REFVAL_P(val);
  4076. }
  4077. if (key == NULL) {
  4078. ok = 1;
  4079. for (i = 1; i < argc; i++) {
  4080. if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) == NULL ||
  4081. (intersect_data_compare_func &&
  4082. intersect_data_compare_func(val, data) != 0)
  4083. ) {
  4084. ok = 0;
  4085. break;
  4086. }
  4087. }
  4088. if (ok) {
  4089. Z_TRY_ADDREF_P(val);
  4090. zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
  4091. }
  4092. } else {
  4093. ok = 1;
  4094. for (i = 1; i < argc; i++) {
  4095. if ((data = zend_hash_find_ex_ind(Z_ARRVAL(args[i]), key, 1)) == NULL ||
  4096. (intersect_data_compare_func &&
  4097. intersect_data_compare_func(val, data) != 0)
  4098. ) {
  4099. ok = 0;
  4100. break;
  4101. }
  4102. }
  4103. if (ok) {
  4104. Z_TRY_ADDREF_P(val);
  4105. zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
  4106. }
  4107. }
  4108. } ZEND_HASH_FOREACH_END();
  4109. }
  4110. /* }}} */
  4111. static void php_array_intersect(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
  4112. {
  4113. zval *args = NULL;
  4114. HashTable *hash;
  4115. int arr_argc, i, c = 0;
  4116. uint32_t idx;
  4117. Bucket **lists, *list, **ptrs, *p;
  4118. uint32_t req_args;
  4119. char *param_spec;
  4120. zend_fcall_info fci1, fci2;
  4121. zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
  4122. zend_fcall_info *fci_key = NULL, *fci_data;
  4123. zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
  4124. PHP_ARRAY_CMP_FUNC_VARS;
  4125. bucket_compare_func_t intersect_key_compare_func;
  4126. bucket_compare_func_t intersect_data_compare_func;
  4127. if (behavior == INTERSECT_NORMAL) {
  4128. intersect_key_compare_func = php_array_key_compare_string;
  4129. if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL) {
  4130. /* array_intersect() */
  4131. req_args = 2;
  4132. param_spec = "+";
  4133. intersect_data_compare_func = php_array_data_compare_string;
  4134. } else if (data_compare_type == INTERSECT_COMP_DATA_USER) {
  4135. /* array_uintersect() */
  4136. req_args = 3;
  4137. param_spec = "+f";
  4138. intersect_data_compare_func = php_array_user_compare;
  4139. } else {
  4140. ZEND_ASSERT(0 && "Invalid data_compare_type");
  4141. return;
  4142. }
  4143. if (ZEND_NUM_ARGS() < req_args) {
  4144. zend_argument_count_error("At least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
  4145. RETURN_THROWS();
  4146. }
  4147. if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
  4148. RETURN_THROWS();
  4149. }
  4150. fci_data = &fci1;
  4151. fci_data_cache = &fci1_cache;
  4152. } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
  4153. /* INTERSECT_KEY is subset of INTERSECT_ASSOC. When having the former
  4154. * no comparison of the data is done (part of INTERSECT_ASSOC) */
  4155. if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
  4156. /* array_intersect_assoc() or array_intersect_key() */
  4157. req_args = 2;
  4158. param_spec = "+";
  4159. intersect_key_compare_func = php_array_key_compare_string;
  4160. intersect_data_compare_func = php_array_data_compare_string;
  4161. } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_INTERNAL) {
  4162. /* array_uintersect_assoc() */
  4163. req_args = 3;
  4164. param_spec = "+f";
  4165. intersect_key_compare_func = php_array_key_compare_string;
  4166. intersect_data_compare_func = php_array_user_compare;
  4167. fci_data = &fci1;
  4168. fci_data_cache = &fci1_cache;
  4169. } else if (data_compare_type == INTERSECT_COMP_DATA_INTERNAL && key_compare_type == INTERSECT_COMP_KEY_USER) {
  4170. /* array_intersect_uassoc() or array_intersect_ukey() */
  4171. req_args = 3;
  4172. param_spec = "+f";
  4173. intersect_key_compare_func = php_array_user_key_compare;
  4174. intersect_data_compare_func = php_array_data_compare_string;
  4175. fci_key = &fci1;
  4176. fci_key_cache = &fci1_cache;
  4177. } else if (data_compare_type == INTERSECT_COMP_DATA_USER && key_compare_type == INTERSECT_COMP_KEY_USER) {
  4178. /* array_uintersect_uassoc() */
  4179. req_args = 4;
  4180. param_spec = "+ff";
  4181. intersect_key_compare_func = php_array_user_key_compare;
  4182. intersect_data_compare_func = php_array_user_compare;
  4183. fci_data = &fci1;
  4184. fci_data_cache = &fci1_cache;
  4185. fci_key = &fci2;
  4186. fci_key_cache = &fci2_cache;
  4187. } else {
  4188. ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
  4189. return;
  4190. }
  4191. if (ZEND_NUM_ARGS() < req_args) {
  4192. zend_argument_count_error("At least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
  4193. RETURN_THROWS();
  4194. }
  4195. if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
  4196. RETURN_THROWS();
  4197. }
  4198. } else {
  4199. ZEND_ASSERT(0 && "Invalid behavior");
  4200. return;
  4201. }
  4202. PHP_ARRAY_CMP_FUNC_BACKUP();
  4203. /* for each argument, create and sort list with pointers to the hash buckets */
  4204. lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
  4205. ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
  4206. if (behavior == INTERSECT_NORMAL && data_compare_type == INTERSECT_COMP_DATA_USER) {
  4207. BG(user_compare_fci) = *fci_data;
  4208. BG(user_compare_fci_cache) = *fci_data_cache;
  4209. } else if (behavior & INTERSECT_ASSOC && key_compare_type == INTERSECT_COMP_KEY_USER) {
  4210. BG(user_compare_fci) = *fci_key;
  4211. BG(user_compare_fci_cache) = *fci_key_cache;
  4212. }
  4213. for (i = 0; i < arr_argc; i++) {
  4214. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4215. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4216. arr_argc = i; /* only free up to i - 1 */
  4217. goto out;
  4218. }
  4219. hash = Z_ARRVAL(args[i]);
  4220. list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
  4221. lists[i] = list;
  4222. ptrs[i] = list;
  4223. for (idx = 0; idx < hash->nNumUsed; idx++) {
  4224. p = hash->arData + idx;
  4225. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  4226. *list++ = *p;
  4227. }
  4228. ZVAL_UNDEF(&list->val);
  4229. if (hash->nNumOfElements > 1) {
  4230. if (behavior == INTERSECT_NORMAL) {
  4231. zend_sort((void *) lists[i], hash->nNumOfElements,
  4232. sizeof(Bucket), (compare_func_t) intersect_data_compare_func,
  4233. (swap_func_t)zend_hash_bucket_swap);
  4234. } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
  4235. zend_sort((void *) lists[i], hash->nNumOfElements,
  4236. sizeof(Bucket), (compare_func_t) intersect_key_compare_func,
  4237. (swap_func_t)zend_hash_bucket_swap);
  4238. }
  4239. }
  4240. }
  4241. /* copy the argument array */
  4242. RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
  4243. /* go through the lists and look for common values */
  4244. while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
  4245. if ((behavior & INTERSECT_ASSOC) /* triggered also when INTERSECT_KEY */
  4246. && key_compare_type == INTERSECT_COMP_KEY_USER) {
  4247. BG(user_compare_fci) = *fci_key;
  4248. BG(user_compare_fci_cache) = *fci_key_cache;
  4249. }
  4250. for (i = 1; i < arr_argc; i++) {
  4251. if (behavior & INTERSECT_NORMAL) {
  4252. while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_data_compare_func(ptrs[0], ptrs[i])))) {
  4253. ptrs[i]++;
  4254. }
  4255. } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
  4256. while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = intersect_key_compare_func(ptrs[0], ptrs[i])))) {
  4257. ptrs[i]++;
  4258. }
  4259. if ((!c && Z_TYPE(ptrs[i]->val) != IS_UNDEF) && (behavior == INTERSECT_ASSOC)) { /* only when INTERSECT_ASSOC */
  4260. /* this means that ptrs[i] is not NULL so we can compare
  4261. * and "c==0" is from last operation
  4262. * in this branch of code we enter only when INTERSECT_ASSOC
  4263. * since when we have INTERSECT_KEY compare of data is not wanted. */
  4264. if (data_compare_type == INTERSECT_COMP_DATA_USER) {
  4265. BG(user_compare_fci) = *fci_data;
  4266. BG(user_compare_fci_cache) = *fci_data_cache;
  4267. }
  4268. if (intersect_data_compare_func(ptrs[0], ptrs[i]) != 0) {
  4269. c = 1;
  4270. if (key_compare_type == INTERSECT_COMP_KEY_USER) {
  4271. BG(user_compare_fci) = *fci_key;
  4272. BG(user_compare_fci_cache) = *fci_key_cache;
  4273. /* When KEY_USER, the last parameter is always the callback */
  4274. }
  4275. /* we are going to the break */
  4276. } else {
  4277. /* continue looping */
  4278. }
  4279. }
  4280. }
  4281. if (Z_TYPE(ptrs[i]->val) == IS_UNDEF) {
  4282. /* delete any values corresponding to remains of ptrs[0] */
  4283. /* and exit because they do not present in at least one of */
  4284. /* the other arguments */
  4285. for (;;) {
  4286. p = ptrs[0]++;
  4287. if (Z_TYPE(p->val) == IS_UNDEF) {
  4288. goto out;
  4289. }
  4290. if (p->key == NULL) {
  4291. zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
  4292. } else {
  4293. zend_hash_del(Z_ARRVAL_P(return_value), p->key);
  4294. }
  4295. }
  4296. }
  4297. if (c) /* here we get if not all are equal */
  4298. break;
  4299. ptrs[i]++;
  4300. }
  4301. if (c) {
  4302. /* Value of ptrs[0] not in all arguments, delete all entries */
  4303. /* with value < value of ptrs[i] */
  4304. for (;;) {
  4305. p = ptrs[0];
  4306. if (p->key == NULL) {
  4307. zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
  4308. } else {
  4309. zend_hash_del(Z_ARRVAL_P(return_value), p->key);
  4310. }
  4311. if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
  4312. goto out;
  4313. }
  4314. if (behavior == INTERSECT_NORMAL) {
  4315. if (0 <= intersect_data_compare_func(ptrs[0], ptrs[i])) {
  4316. break;
  4317. }
  4318. } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
  4319. /* no need of looping because indexes are unique */
  4320. break;
  4321. }
  4322. }
  4323. } else {
  4324. /* ptrs[0] is present in all the arguments */
  4325. /* Skip all entries with same value as ptrs[0] */
  4326. for (;;) {
  4327. if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
  4328. goto out;
  4329. }
  4330. if (behavior == INTERSECT_NORMAL) {
  4331. if (intersect_data_compare_func(ptrs[0] - 1, ptrs[0])) {
  4332. break;
  4333. }
  4334. } else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
  4335. /* no need of looping because indexes are unique */
  4336. break;
  4337. }
  4338. }
  4339. }
  4340. }
  4341. out:
  4342. for (i = 0; i < arr_argc; i++) {
  4343. hash = Z_ARRVAL(args[i]);
  4344. pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
  4345. }
  4346. PHP_ARRAY_CMP_FUNC_RESTORE();
  4347. efree(ptrs);
  4348. efree(lists);
  4349. }
  4350. /* }}} */
  4351. /* {{{ proto array array_intersect_key(array arr1, array arr2 [, array ...])
  4352. Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). Equivalent of array_intersect_assoc() but does not do compare of the data. */
  4353. PHP_FUNCTION(array_intersect_key)
  4354. {
  4355. php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_NONE);
  4356. }
  4357. /* }}} */
  4358. /* {{{ proto array array_intersect_ukey(array arr1, array arr2 [, array ...], callback key_compare_func)
  4359. Returns the entries of arr1 that have keys which are present in all the other arguments. Kind of equivalent to array_diff(array_keys($arr1), array_keys($arr2)[,array_keys(...)]). The comparison of the keys is performed by a user supplied function. Equivalent of array_intersect_uassoc() but does not do compare of the data. */
  4360. PHP_FUNCTION(array_intersect_ukey)
  4361. {
  4362. php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_KEY, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
  4363. }
  4364. /* }}} */
  4365. /* {{{ proto array array_intersect(array arr1, array arr2 [, array ...])
  4366. Returns the entries of arr1 that have values which are present in all the other arguments */
  4367. PHP_FUNCTION(array_intersect)
  4368. {
  4369. php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_INTERNAL);
  4370. }
  4371. /* }}} */
  4372. /* {{{ proto array array_uintersect(array arr1, array arr2 [, array ...], callback data_compare_func)
  4373. Returns the entries of arr1 that have values which are present in all the other arguments. Data is compared by using a user-supplied callback. */
  4374. PHP_FUNCTION(array_uintersect)
  4375. {
  4376. php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_NORMAL, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_INTERNAL);
  4377. }
  4378. /* }}} */
  4379. /* {{{ proto array array_intersect_assoc(array arr1, array arr2 [, array ...])
  4380. Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check */
  4381. PHP_FUNCTION(array_intersect_assoc)
  4382. {
  4383. php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_INTERNAL);
  4384. }
  4385. /* }}} */
  4386. /* {{{ proto array array_intersect_uassoc(array arr1, array arr2 [, array ...], callback key_compare_func) U
  4387. Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check and they are compared by using a user-supplied callback. */
  4388. PHP_FUNCTION(array_intersect_uassoc)
  4389. {
  4390. php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_INTERNAL, INTERSECT_COMP_KEY_USER);
  4391. }
  4392. /* }}} */
  4393. /* {{{ proto array array_uintersect_assoc(array arr1, array arr2 [, array ...], callback data_compare_func) U
  4394. Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Data is compared by using a user-supplied callback. */
  4395. PHP_FUNCTION(array_uintersect_assoc)
  4396. {
  4397. php_array_intersect_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_COMP_DATA_USER);
  4398. }
  4399. /* }}} */
  4400. /* {{{ proto array array_uintersect_uassoc(array arr1, array arr2 [, array ...], callback data_compare_func, callback key_compare_func)
  4401. Returns the entries of arr1 that have values which are present in all the other arguments. Keys are used to do more restrictive check. Both data and keys are compared by using user-supplied callbacks. */
  4402. PHP_FUNCTION(array_uintersect_uassoc)
  4403. {
  4404. php_array_intersect(INTERNAL_FUNCTION_PARAM_PASSTHRU, INTERSECT_ASSOC, INTERSECT_COMP_DATA_USER, INTERSECT_COMP_KEY_USER);
  4405. }
  4406. /* }}} */
  4407. static void php_array_diff_key(INTERNAL_FUNCTION_PARAMETERS, int data_compare_type) /* {{{ */
  4408. {
  4409. int argc, i;
  4410. zval *args;
  4411. int (*diff_data_compare_func)(zval *, zval *) = NULL;
  4412. zend_bool ok;
  4413. zval *val, *data;
  4414. zend_string *key;
  4415. zend_ulong h;
  4416. /* Get the argument count */
  4417. argc = ZEND_NUM_ARGS();
  4418. if (data_compare_type == DIFF_COMP_DATA_USER) {
  4419. if (argc < 3) {
  4420. zend_argument_count_error("At least 3 parameters are required, %d given", ZEND_NUM_ARGS());
  4421. RETURN_THROWS();
  4422. }
  4423. if (zend_parse_parameters(ZEND_NUM_ARGS(), "+f", &args, &argc, &BG(user_compare_fci), &BG(user_compare_fci_cache)) == FAILURE) {
  4424. RETURN_THROWS();
  4425. }
  4426. diff_data_compare_func = zval_user_compare;
  4427. } else {
  4428. if (argc < 2) {
  4429. zend_argument_count_error("At least 2 parameters are required, %d given", ZEND_NUM_ARGS());
  4430. RETURN_THROWS();
  4431. }
  4432. if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
  4433. RETURN_THROWS();
  4434. }
  4435. if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
  4436. diff_data_compare_func = zval_compare;
  4437. }
  4438. }
  4439. for (i = 0; i < argc; i++) {
  4440. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4441. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4442. RETURN_THROWS();
  4443. }
  4444. }
  4445. array_init(return_value);
  4446. /* Iterate over keys of the first array (handling possibility of indirects such as in $GLOBALS), to compute keys that aren't in the other arrays. */
  4447. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), h, key, val) {
  4448. if (Z_ISREF_P(val) && Z_REFCOUNT_P(val) == 1) {
  4449. val = Z_REFVAL_P(val);
  4450. }
  4451. if (key == NULL) {
  4452. ok = 1;
  4453. for (i = 1; i < argc; i++) {
  4454. if ((data = zend_hash_index_find(Z_ARRVAL(args[i]), h)) != NULL &&
  4455. (!diff_data_compare_func ||
  4456. diff_data_compare_func(val, data) == 0)
  4457. ) {
  4458. ok = 0;
  4459. break;
  4460. }
  4461. }
  4462. if (ok) {
  4463. Z_TRY_ADDREF_P(val);
  4464. zend_hash_index_add_new(Z_ARRVAL_P(return_value), h, val);
  4465. }
  4466. } else {
  4467. ok = 1;
  4468. for (i = 1; i < argc; i++) {
  4469. if ((data = zend_hash_find_ex_ind(Z_ARRVAL(args[i]), key, 1)) != NULL &&
  4470. (!diff_data_compare_func ||
  4471. diff_data_compare_func(val, data) == 0)
  4472. ) {
  4473. ok = 0;
  4474. break;
  4475. }
  4476. }
  4477. if (ok) {
  4478. Z_TRY_ADDREF_P(val);
  4479. zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
  4480. }
  4481. }
  4482. } ZEND_HASH_FOREACH_END();
  4483. }
  4484. /* }}} */
  4485. static void php_array_diff(INTERNAL_FUNCTION_PARAMETERS, int behavior, int data_compare_type, int key_compare_type) /* {{{ */
  4486. {
  4487. zval *args = NULL;
  4488. HashTable *hash;
  4489. int arr_argc, i, c;
  4490. uint32_t idx;
  4491. Bucket **lists, *list, **ptrs, *p;
  4492. uint32_t req_args;
  4493. char *param_spec;
  4494. zend_fcall_info fci1, fci2;
  4495. zend_fcall_info_cache fci1_cache = empty_fcall_info_cache, fci2_cache = empty_fcall_info_cache;
  4496. zend_fcall_info *fci_key = NULL, *fci_data;
  4497. zend_fcall_info_cache *fci_key_cache = NULL, *fci_data_cache;
  4498. PHP_ARRAY_CMP_FUNC_VARS;
  4499. bucket_compare_func_t diff_key_compare_func;
  4500. bucket_compare_func_t diff_data_compare_func;
  4501. if (behavior == DIFF_NORMAL) {
  4502. diff_key_compare_func = php_array_key_compare_string;
  4503. if (data_compare_type == DIFF_COMP_DATA_INTERNAL) {
  4504. /* array_diff */
  4505. req_args = 2;
  4506. param_spec = "+";
  4507. diff_data_compare_func = php_array_data_compare_string;
  4508. } else if (data_compare_type == DIFF_COMP_DATA_USER) {
  4509. /* array_udiff */
  4510. req_args = 3;
  4511. param_spec = "+f";
  4512. diff_data_compare_func = php_array_user_compare;
  4513. } else {
  4514. ZEND_ASSERT(0 && "Invalid data_compare_type");
  4515. return;
  4516. }
  4517. if (ZEND_NUM_ARGS() < req_args) {
  4518. zend_argument_count_error("At least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
  4519. RETURN_THROWS();
  4520. }
  4521. if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache) == FAILURE) {
  4522. RETURN_THROWS();
  4523. }
  4524. fci_data = &fci1;
  4525. fci_data_cache = &fci1_cache;
  4526. } else if (behavior & DIFF_ASSOC) { /* triggered also if DIFF_KEY */
  4527. /* DIFF_KEY is subset of DIFF_ASSOC. When having the former
  4528. * no comparison of the data is done (part of DIFF_ASSOC) */
  4529. if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
  4530. /* array_diff_assoc() or array_diff_key() */
  4531. req_args = 2;
  4532. param_spec = "+";
  4533. diff_key_compare_func = php_array_key_compare_string;
  4534. diff_data_compare_func = php_array_data_compare_string;
  4535. } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_INTERNAL) {
  4536. /* array_udiff_assoc() */
  4537. req_args = 3;
  4538. param_spec = "+f";
  4539. diff_key_compare_func = php_array_key_compare_string;
  4540. diff_data_compare_func = php_array_user_compare;
  4541. fci_data = &fci1;
  4542. fci_data_cache = &fci1_cache;
  4543. } else if (data_compare_type == DIFF_COMP_DATA_INTERNAL && key_compare_type == DIFF_COMP_KEY_USER) {
  4544. /* array_diff_uassoc() or array_diff_ukey() */
  4545. req_args = 3;
  4546. param_spec = "+f";
  4547. diff_key_compare_func = php_array_user_key_compare;
  4548. diff_data_compare_func = php_array_data_compare_string;
  4549. fci_key = &fci1;
  4550. fci_key_cache = &fci1_cache;
  4551. } else if (data_compare_type == DIFF_COMP_DATA_USER && key_compare_type == DIFF_COMP_KEY_USER) {
  4552. /* array_udiff_uassoc() */
  4553. req_args = 4;
  4554. param_spec = "+ff";
  4555. diff_key_compare_func = php_array_user_key_compare;
  4556. diff_data_compare_func = php_array_user_compare;
  4557. fci_data = &fci1;
  4558. fci_data_cache = &fci1_cache;
  4559. fci_key = &fci2;
  4560. fci_key_cache = &fci2_cache;
  4561. } else {
  4562. ZEND_ASSERT(0 && "Invalid data_compare_type / key_compare_type");
  4563. return;
  4564. }
  4565. if (ZEND_NUM_ARGS() < req_args) {
  4566. zend_argument_count_error("At least %d parameters are required, %d given", req_args, ZEND_NUM_ARGS());
  4567. RETURN_THROWS();
  4568. }
  4569. if (zend_parse_parameters(ZEND_NUM_ARGS(), param_spec, &args, &arr_argc, &fci1, &fci1_cache, &fci2, &fci2_cache) == FAILURE) {
  4570. RETURN_THROWS();
  4571. }
  4572. } else {
  4573. ZEND_ASSERT(0 && "Invalid behavior");
  4574. return;
  4575. }
  4576. PHP_ARRAY_CMP_FUNC_BACKUP();
  4577. /* for each argument, create and sort list with pointers to the hash buckets */
  4578. lists = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
  4579. ptrs = (Bucket **)safe_emalloc(arr_argc, sizeof(Bucket *), 0);
  4580. if (behavior == DIFF_NORMAL && data_compare_type == DIFF_COMP_DATA_USER) {
  4581. BG(user_compare_fci) = *fci_data;
  4582. BG(user_compare_fci_cache) = *fci_data_cache;
  4583. } else if (behavior & DIFF_ASSOC && key_compare_type == DIFF_COMP_KEY_USER) {
  4584. BG(user_compare_fci) = *fci_key;
  4585. BG(user_compare_fci_cache) = *fci_key_cache;
  4586. }
  4587. for (i = 0; i < arr_argc; i++) {
  4588. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4589. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4590. arr_argc = i; /* only free up to i - 1 */
  4591. goto out;
  4592. }
  4593. hash = Z_ARRVAL(args[i]);
  4594. list = (Bucket *) pemalloc((hash->nNumOfElements + 1) * sizeof(Bucket), GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
  4595. lists[i] = list;
  4596. ptrs[i] = list;
  4597. for (idx = 0; idx < hash->nNumUsed; idx++) {
  4598. p = hash->arData + idx;
  4599. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  4600. *list++ = *p;
  4601. }
  4602. ZVAL_UNDEF(&list->val);
  4603. if (hash->nNumOfElements > 1) {
  4604. if (behavior == DIFF_NORMAL) {
  4605. zend_sort((void *) lists[i], hash->nNumOfElements,
  4606. sizeof(Bucket), (compare_func_t) diff_data_compare_func,
  4607. (swap_func_t)zend_hash_bucket_swap);
  4608. } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
  4609. zend_sort((void *) lists[i], hash->nNumOfElements,
  4610. sizeof(Bucket), (compare_func_t) diff_key_compare_func,
  4611. (swap_func_t)zend_hash_bucket_swap);
  4612. }
  4613. }
  4614. }
  4615. /* copy the argument array */
  4616. RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));
  4617. /* go through the lists and look for values of ptr[0] that are not in the others */
  4618. while (Z_TYPE(ptrs[0]->val) != IS_UNDEF) {
  4619. if ((behavior & DIFF_ASSOC) /* triggered also when DIFF_KEY */
  4620. &&
  4621. key_compare_type == DIFF_COMP_KEY_USER
  4622. ) {
  4623. BG(user_compare_fci) = *fci_key;
  4624. BG(user_compare_fci_cache) = *fci_key_cache;
  4625. }
  4626. c = 1;
  4627. for (i = 1; i < arr_argc; i++) {
  4628. Bucket *ptr = ptrs[i];
  4629. if (behavior == DIFF_NORMAL) {
  4630. while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
  4631. ptrs[i]++;
  4632. }
  4633. } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
  4634. while (Z_TYPE(ptr->val) != IS_UNDEF && (0 != (c = diff_key_compare_func(ptrs[0], ptr)))) {
  4635. ptr++;
  4636. }
  4637. }
  4638. if (!c) {
  4639. if (behavior == DIFF_NORMAL) {
  4640. if (Z_TYPE(ptrs[i]->val) != IS_UNDEF) {
  4641. ptrs[i]++;
  4642. }
  4643. break;
  4644. } else if (behavior == DIFF_ASSOC) { /* only when DIFF_ASSOC */
  4645. /* In this branch is execute only when DIFF_ASSOC. If behavior == DIFF_KEY
  4646. * data comparison is not needed - skipped. */
  4647. if (Z_TYPE(ptr->val) != IS_UNDEF) {
  4648. if (data_compare_type == DIFF_COMP_DATA_USER) {
  4649. BG(user_compare_fci) = *fci_data;
  4650. BG(user_compare_fci_cache) = *fci_data_cache;
  4651. }
  4652. if (diff_data_compare_func(ptrs[0], ptr) != 0) {
  4653. /* the data is not the same */
  4654. c = -1;
  4655. if (key_compare_type == DIFF_COMP_KEY_USER) {
  4656. BG(user_compare_fci) = *fci_key;
  4657. BG(user_compare_fci_cache) = *fci_key_cache;
  4658. }
  4659. } else {
  4660. break;
  4661. /* we have found the element in other arrays thus we don't want it
  4662. * in the return_value -> delete from there */
  4663. }
  4664. }
  4665. } else if (behavior == DIFF_KEY) { /* only when DIFF_KEY */
  4666. /* the behavior here differs from INTERSECT_KEY in php_intersect
  4667. * since in the "diff" case we have to remove the entry from
  4668. * return_value while when doing intersection the entry must not
  4669. * be deleted. */
  4670. break; /* remove the key */
  4671. }
  4672. }
  4673. }
  4674. if (!c) {
  4675. /* ptrs[0] in one of the other arguments */
  4676. /* delete all entries with value as ptrs[0] */
  4677. for (;;) {
  4678. p = ptrs[0];
  4679. if (p->key == NULL) {
  4680. zend_hash_index_del(Z_ARRVAL_P(return_value), p->h);
  4681. } else {
  4682. zend_hash_del(Z_ARRVAL_P(return_value), p->key);
  4683. }
  4684. if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
  4685. goto out;
  4686. }
  4687. if (behavior == DIFF_NORMAL) {
  4688. if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
  4689. break;
  4690. }
  4691. } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
  4692. /* in this case no array_key_compare is needed */
  4693. break;
  4694. }
  4695. }
  4696. } else {
  4697. /* ptrs[0] in none of the other arguments */
  4698. /* skip all entries with value as ptrs[0] */
  4699. for (;;) {
  4700. if (Z_TYPE((++ptrs[0])->val) == IS_UNDEF) {
  4701. goto out;
  4702. }
  4703. if (behavior == DIFF_NORMAL) {
  4704. if (diff_data_compare_func(ptrs[0] - 1, ptrs[0])) {
  4705. break;
  4706. }
  4707. } else if (behavior & DIFF_ASSOC) { /* triggered also when DIFF_KEY */
  4708. /* in this case no array_key_compare is needed */
  4709. break;
  4710. }
  4711. }
  4712. }
  4713. }
  4714. out:
  4715. for (i = 0; i < arr_argc; i++) {
  4716. hash = Z_ARRVAL(args[i]);
  4717. pefree(lists[i], GC_FLAGS(hash) & IS_ARRAY_PERSISTENT);
  4718. }
  4719. PHP_ARRAY_CMP_FUNC_RESTORE();
  4720. efree(ptrs);
  4721. efree(lists);
  4722. }
  4723. /* }}} */
  4724. /* {{{ proto array array_diff_key(array arr1, array arr2 [, array ...])
  4725. Returns the entries of arr1 that have keys which are not present in any of the others arguments. This function is like array_diff() but works on the keys instead of the values. The associativity is preserved. */
  4726. PHP_FUNCTION(array_diff_key)
  4727. {
  4728. php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_NONE);
  4729. }
  4730. /* }}} */
  4731. /* {{{ proto array array_diff_ukey(array arr1, array arr2 [, array ...], callback key_comp_func)
  4732. Returns the entries of arr1 that have keys which are not present in any of the others arguments. User supplied function is used for comparing the keys. This function is like array_udiff() but works on the keys instead of the values. The associativity is preserved. */
  4733. PHP_FUNCTION(array_diff_ukey)
  4734. {
  4735. php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_KEY, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
  4736. }
  4737. /* }}} */
  4738. /* {{{ proto array array_diff(array arr1, array arr2 [, array ...])
  4739. Returns the entries of arr1 that have values which are not present in any of the others arguments. */
  4740. PHP_FUNCTION(array_diff)
  4741. {
  4742. zval *args;
  4743. int argc, i;
  4744. uint32_t num;
  4745. HashTable exclude;
  4746. zval *value;
  4747. zend_string *str, *tmp_str, *key;
  4748. zend_long idx;
  4749. zval dummy;
  4750. if (ZEND_NUM_ARGS() < 2) {
  4751. zend_argument_count_error("At least 2 parameters are required, %d given", ZEND_NUM_ARGS());
  4752. RETURN_THROWS();
  4753. }
  4754. ZEND_PARSE_PARAMETERS_START(1, -1)
  4755. Z_PARAM_VARIADIC('+', args, argc)
  4756. ZEND_PARSE_PARAMETERS_END();
  4757. if (Z_TYPE(args[0]) != IS_ARRAY) {
  4758. zend_argument_type_error(1, "must be of type array, %s given", zend_zval_type_name(&args[0]));
  4759. RETURN_THROWS();
  4760. }
  4761. num = zend_hash_num_elements(Z_ARRVAL(args[0]));
  4762. if (num == 0) {
  4763. for (i = 1; i < argc; i++) {
  4764. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4765. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4766. RETURN_THROWS();
  4767. }
  4768. }
  4769. RETURN_EMPTY_ARRAY();
  4770. } else if (num == 1) {
  4771. int found = 0;
  4772. zend_string *search_str, *tmp_search_str;
  4773. value = NULL;
  4774. ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[0]), value) {
  4775. break;
  4776. } ZEND_HASH_FOREACH_END();
  4777. if (!value) {
  4778. for (i = 1; i < argc; i++) {
  4779. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4780. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4781. RETURN_THROWS();
  4782. }
  4783. }
  4784. RETURN_EMPTY_ARRAY();
  4785. }
  4786. search_str = zval_get_tmp_string(value, &tmp_search_str);
  4787. for (i = 1; i < argc; i++) {
  4788. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4789. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4790. RETURN_THROWS();
  4791. }
  4792. if (!found) {
  4793. ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), value) {
  4794. str = zval_get_tmp_string(value, &tmp_str);
  4795. if (zend_string_equals(search_str, str)) {
  4796. zend_tmp_string_release(tmp_str);
  4797. found = 1;
  4798. break;
  4799. }
  4800. zend_tmp_string_release(tmp_str);
  4801. } ZEND_HASH_FOREACH_END();
  4802. }
  4803. }
  4804. zend_tmp_string_release(tmp_search_str);
  4805. if (found) {
  4806. RETVAL_EMPTY_ARRAY();
  4807. } else {
  4808. ZVAL_COPY(return_value, &args[0]);
  4809. }
  4810. return;
  4811. }
  4812. /* count number of elements */
  4813. num = 0;
  4814. for (i = 1; i < argc; i++) {
  4815. if (Z_TYPE(args[i]) != IS_ARRAY) {
  4816. zend_argument_type_error(i + 1, "must be of type array, %s given", zend_zval_type_name(&args[i]));
  4817. RETURN_THROWS();
  4818. }
  4819. num += zend_hash_num_elements(Z_ARRVAL(args[i]));
  4820. }
  4821. if (num == 0) {
  4822. ZVAL_COPY(return_value, &args[0]);
  4823. return;
  4824. }
  4825. ZVAL_NULL(&dummy);
  4826. /* create exclude map */
  4827. zend_hash_init(&exclude, num, NULL, NULL, 0);
  4828. for (i = 1; i < argc; i++) {
  4829. ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), value) {
  4830. str = zval_get_tmp_string(value, &tmp_str);
  4831. zend_hash_add(&exclude, str, &dummy);
  4832. zend_tmp_string_release(tmp_str);
  4833. } ZEND_HASH_FOREACH_END();
  4834. }
  4835. /* copy all elements of first array that are not in exclude set */
  4836. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL(args[0])));
  4837. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(args[0]), idx, key, value) {
  4838. str = zval_get_tmp_string(value, &tmp_str);
  4839. if (!zend_hash_exists(&exclude, str)) {
  4840. if (key) {
  4841. value = zend_hash_add_new(Z_ARRVAL_P(return_value), key, value);
  4842. } else {
  4843. value = zend_hash_index_add_new(Z_ARRVAL_P(return_value), idx, value);
  4844. }
  4845. zval_add_ref(value);
  4846. }
  4847. zend_tmp_string_release(tmp_str);
  4848. } ZEND_HASH_FOREACH_END();
  4849. zend_hash_destroy(&exclude);
  4850. }
  4851. /* }}} */
  4852. /* {{{ proto array array_udiff(array arr1, array arr2 [, array ...], callback data_comp_func)
  4853. Returns the entries of arr1 that have values which are not present in any of the others arguments. Elements are compared by user supplied function. */
  4854. PHP_FUNCTION(array_udiff)
  4855. {
  4856. php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_NORMAL, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_INTERNAL);
  4857. }
  4858. /* }}} */
  4859. /* {{{ proto array array_diff_assoc(array arr1, array arr2 [, array ...])
  4860. Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal */
  4861. PHP_FUNCTION(array_diff_assoc)
  4862. {
  4863. php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_INTERNAL);
  4864. }
  4865. /* }}} */
  4866. /* {{{ proto array array_diff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func)
  4867. Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Elements are compared by user supplied function. */
  4868. PHP_FUNCTION(array_diff_uassoc)
  4869. {
  4870. php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_INTERNAL, DIFF_COMP_KEY_USER);
  4871. }
  4872. /* }}} */
  4873. /* {{{ proto array array_udiff_assoc(array arr1, array arr2 [, array ...], callback key_comp_func)
  4874. Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys are compared by user supplied function. */
  4875. PHP_FUNCTION(array_udiff_assoc)
  4876. {
  4877. php_array_diff_key(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_COMP_DATA_USER);
  4878. }
  4879. /* }}} */
  4880. /* {{{ proto array array_udiff_uassoc(array arr1, array arr2 [, array ...], callback data_comp_func, callback key_comp_func)
  4881. Returns the entries of arr1 that have values which are not present in any of the others arguments but do additional checks whether the keys are equal. Keys and elements are compared by user supplied functions. */
  4882. PHP_FUNCTION(array_udiff_uassoc)
  4883. {
  4884. php_array_diff(INTERNAL_FUNCTION_PARAM_PASSTHRU, DIFF_ASSOC, DIFF_COMP_DATA_USER, DIFF_COMP_KEY_USER);
  4885. }
  4886. /* }}} */
  4887. #define MULTISORT_ORDER 0
  4888. #define MULTISORT_TYPE 1
  4889. #define MULTISORT_LAST 2
  4890. PHPAPI int php_multisort_compare(const void *a, const void *b) /* {{{ */
  4891. {
  4892. Bucket *ab = *(Bucket **)a;
  4893. Bucket *bb = *(Bucket **)b;
  4894. int r;
  4895. zend_long result;
  4896. r = 0;
  4897. do {
  4898. result = ARRAYG(multisort_func)[r](&ab[r], &bb[r]);
  4899. if (result != 0) {
  4900. return result > 0 ? 1 : -1;
  4901. }
  4902. r++;
  4903. } while (Z_TYPE(ab[r].val) != IS_UNDEF);
  4904. return 0;
  4905. }
  4906. /* }}} */
  4907. #define MULTISORT_ABORT \
  4908. efree(func); \
  4909. efree(arrays); \
  4910. return;
  4911. static void array_bucket_p_sawp(void *p, void *q) /* {{{ */ {
  4912. Bucket *t;
  4913. Bucket **f = (Bucket **)p;
  4914. Bucket **g = (Bucket **)q;
  4915. t = *f;
  4916. *f = *g;
  4917. *g = t;
  4918. }
  4919. /* }}} */
  4920. /* {{{ proto bool array_multisort(array &$array1 [, mixed $array1_sort_order [, mixed $array1_sort_flags [, mixed ... ]]]
  4921. Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
  4922. PHP_FUNCTION(array_multisort)
  4923. {
  4924. zval* args;
  4925. zval** arrays;
  4926. Bucket** indirect;
  4927. uint32_t idx;
  4928. Bucket* p;
  4929. HashTable* hash;
  4930. int argc;
  4931. int array_size;
  4932. int num_arrays = 0;
  4933. int parse_state[MULTISORT_LAST]; /* 0 - flag not allowed 1 - flag allowed */
  4934. int sort_order = PHP_SORT_ASC;
  4935. int sort_type = PHP_SORT_REGULAR;
  4936. int i, k, n;
  4937. bucket_compare_func_t *func;
  4938. ZEND_PARSE_PARAMETERS_START(1, -1)
  4939. Z_PARAM_VARIADIC('+', args, argc)
  4940. ZEND_PARSE_PARAMETERS_END();
  4941. /* Allocate space for storing pointers to input arrays and sort flags. */
  4942. arrays = (zval **)ecalloc(argc, sizeof(zval *));
  4943. for (i = 0; i < MULTISORT_LAST; i++) {
  4944. parse_state[i] = 0;
  4945. }
  4946. func = ARRAYG(multisort_func) = ecalloc(argc, sizeof(bucket_compare_func_t));
  4947. /* Here we go through the input arguments and parse them. Each one can
  4948. * be either an array or a sort flag which follows an array. If not
  4949. * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
  4950. * accordingly. There can't be two sort flags of the same type after an
  4951. * array, and the very first argument has to be an array. */
  4952. for (i = 0; i < argc; i++) {
  4953. zval *arg = &args[i];
  4954. ZVAL_DEREF(arg);
  4955. if (Z_TYPE_P(arg) == IS_ARRAY) {
  4956. SEPARATE_ARRAY(arg);
  4957. /* We see the next array, so we update the sort flags of
  4958. * the previous array and reset the sort flags. */
  4959. if (i > 0) {
  4960. ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
  4961. sort_order = PHP_SORT_ASC;
  4962. sort_type = PHP_SORT_REGULAR;
  4963. }
  4964. arrays[num_arrays++] = arg;
  4965. /* Next one may be an array or a list of sort flags. */
  4966. for (k = 0; k < MULTISORT_LAST; k++) {
  4967. parse_state[k] = 1;
  4968. }
  4969. } else if (Z_TYPE_P(arg) == IS_LONG) {
  4970. switch (Z_LVAL_P(arg) & ~PHP_SORT_FLAG_CASE) {
  4971. case PHP_SORT_ASC:
  4972. case PHP_SORT_DESC:
  4973. /* flag allowed here */
  4974. if (parse_state[MULTISORT_ORDER] == 1) {
  4975. /* Save the flag and make sure then next arg is not the current flag. */
  4976. sort_order = Z_LVAL_P(arg) == PHP_SORT_DESC ? PHP_SORT_DESC : PHP_SORT_ASC;
  4977. parse_state[MULTISORT_ORDER] = 0;
  4978. } else {
  4979. zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
  4980. MULTISORT_ABORT;
  4981. }
  4982. break;
  4983. case PHP_SORT_REGULAR:
  4984. case PHP_SORT_NUMERIC:
  4985. case PHP_SORT_STRING:
  4986. case PHP_SORT_NATURAL:
  4987. case PHP_SORT_LOCALE_STRING:
  4988. /* flag allowed here */
  4989. if (parse_state[MULTISORT_TYPE] == 1) {
  4990. /* Save the flag and make sure then next arg is not the current flag. */
  4991. sort_type = (int)Z_LVAL_P(arg);
  4992. parse_state[MULTISORT_TYPE] = 0;
  4993. } else {
  4994. zend_argument_type_error(i + 1, "must be an array or a sort flag that has not already been specified");
  4995. MULTISORT_ABORT;
  4996. }
  4997. break;
  4998. default:
  4999. zend_argument_value_error(i + 1, "must be a valid sort flag");
  5000. MULTISORT_ABORT;
  5001. break;
  5002. }
  5003. } else {
  5004. zend_argument_type_error(i + 1, "must be an array or a sort flag");
  5005. MULTISORT_ABORT;
  5006. }
  5007. }
  5008. /* Take care of the last array sort flags. */
  5009. ARRAYG(multisort_func)[num_arrays - 1] = php_get_data_compare_func(sort_type, sort_order != PHP_SORT_ASC);
  5010. /* Make sure the arrays are of the same size. */
  5011. array_size = zend_hash_num_elements(Z_ARRVAL_P(arrays[0]));
  5012. for (i = 0; i < num_arrays; i++) {
  5013. if (zend_hash_num_elements(Z_ARRVAL_P(arrays[i])) != (uint32_t)array_size) {
  5014. zend_value_error("Array sizes are inconsistent");
  5015. MULTISORT_ABORT;
  5016. }
  5017. }
  5018. /* If all arrays are empty we don't need to do anything. */
  5019. if (array_size < 1) {
  5020. efree(func);
  5021. efree(arrays);
  5022. RETURN_TRUE;
  5023. }
  5024. /* Create the indirection array. This array is of size MxN, where
  5025. * M is the number of entries in each input array and N is the number
  5026. * of the input arrays + 1. The last column is NULL to indicate the end
  5027. * of the row. */
  5028. indirect = (Bucket **)safe_emalloc(array_size, sizeof(Bucket *), 0);
  5029. for (i = 0; i < array_size; i++) {
  5030. indirect[i] = (Bucket *)safe_emalloc((num_arrays + 1), sizeof(Bucket), 0);
  5031. }
  5032. for (i = 0; i < num_arrays; i++) {
  5033. k = 0;
  5034. for (idx = 0; idx < Z_ARRVAL_P(arrays[i])->nNumUsed; idx++) {
  5035. p = Z_ARRVAL_P(arrays[i])->arData + idx;
  5036. if (Z_TYPE(p->val) == IS_UNDEF) continue;
  5037. indirect[k][i] = *p;
  5038. k++;
  5039. }
  5040. }
  5041. for (k = 0; k < array_size; k++) {
  5042. ZVAL_UNDEF(&indirect[k][num_arrays].val);
  5043. }
  5044. /* Do the actual sort magic - bada-bim, bada-boom. */
  5045. zend_sort(indirect, array_size, sizeof(Bucket *), php_multisort_compare, (swap_func_t)array_bucket_p_sawp);
  5046. /* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
  5047. for (i = 0; i < num_arrays; i++) {
  5048. int repack;
  5049. hash = Z_ARRVAL_P(arrays[i]);
  5050. hash->nNumUsed = array_size;
  5051. hash->nInternalPointer = 0;
  5052. repack = !(HT_FLAGS(hash) & HASH_FLAG_PACKED);
  5053. for (n = 0, k = 0; k < array_size; k++) {
  5054. hash->arData[k] = indirect[k][i];
  5055. if (hash->arData[k].key == NULL) {
  5056. hash->arData[k].h = n++;
  5057. } else {
  5058. repack = 0;
  5059. }
  5060. }
  5061. hash->nNextFreeElement = array_size;
  5062. if (repack) {
  5063. zend_hash_to_packed(hash);
  5064. } else if (!(HT_FLAGS(hash) & HASH_FLAG_PACKED)) {
  5065. zend_hash_rehash(hash);
  5066. }
  5067. }
  5068. /* Clean up. */
  5069. for (i = 0; i < array_size; i++) {
  5070. efree(indirect[i]);
  5071. }
  5072. efree(indirect);
  5073. efree(func);
  5074. efree(arrays);
  5075. RETURN_TRUE;
  5076. }
  5077. /* }}} */
  5078. /* {{{ proto int|string|array array_rand(array input [, int num_req])
  5079. Return key/keys for random entry/entries in the array */
  5080. PHP_FUNCTION(array_rand)
  5081. {
  5082. zval *input;
  5083. zend_long num_req = 1;
  5084. zend_string *string_key;
  5085. zend_ulong num_key;
  5086. int i;
  5087. int num_avail;
  5088. zend_bitset bitset;
  5089. int negative_bitset = 0;
  5090. uint32_t bitset_len;
  5091. ALLOCA_FLAG(use_heap)
  5092. ZEND_PARSE_PARAMETERS_START(1, 2)
  5093. Z_PARAM_ARRAY(input)
  5094. Z_PARAM_OPTIONAL
  5095. Z_PARAM_LONG(num_req)
  5096. ZEND_PARSE_PARAMETERS_END();
  5097. num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
  5098. if (num_avail == 0) {
  5099. zend_argument_value_error(1, "cannot be empty");
  5100. RETURN_THROWS();
  5101. }
  5102. if (num_req == 1) {
  5103. HashTable *ht = Z_ARRVAL_P(input);
  5104. if ((uint32_t)num_avail < ht->nNumUsed - (ht->nNumUsed>>1)) {
  5105. /* If less than 1/2 of elements are used, don't sample. Instead search for a
  5106. * specific offset using linear scan. */
  5107. zend_long i = 0, randval = php_mt_rand_range(0, num_avail - 1);
  5108. ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
  5109. if (i == randval) {
  5110. if (string_key) {
  5111. RETURN_STR_COPY(string_key);
  5112. } else {
  5113. RETURN_LONG(num_key);
  5114. }
  5115. }
  5116. i++;
  5117. } ZEND_HASH_FOREACH_END();
  5118. }
  5119. /* Sample random buckets until we hit one that is not empty.
  5120. * The worst case probability of hitting an empty element is 1-1/2. The worst case
  5121. * probability of hitting N empty elements in a row is (1-1/2)**N.
  5122. * For N=10 this becomes smaller than 0.1%. */
  5123. do {
  5124. zend_long randval = php_mt_rand_range(0, ht->nNumUsed - 1);
  5125. Bucket *bucket = &ht->arData[randval];
  5126. if (!Z_ISUNDEF(bucket->val)) {
  5127. if (bucket->key) {
  5128. RETURN_STR_COPY(bucket->key);
  5129. } else {
  5130. RETURN_LONG(bucket->h);
  5131. }
  5132. }
  5133. } while (1);
  5134. }
  5135. if (num_req <= 0 || num_req > num_avail) {
  5136. zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($arg)");
  5137. RETURN_THROWS();
  5138. }
  5139. /* Make the return value an array only if we need to pass back more than one result. */
  5140. array_init_size(return_value, (uint32_t)num_req);
  5141. if (num_req > (num_avail >> 1)) {
  5142. negative_bitset = 1;
  5143. num_req = num_avail - num_req;
  5144. }
  5145. bitset_len = zend_bitset_len(num_avail);
  5146. bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
  5147. zend_bitset_clear(bitset, bitset_len);
  5148. i = num_req;
  5149. while (i) {
  5150. zend_long randval = php_mt_rand_range(0, num_avail - 1);
  5151. if (!zend_bitset_in(bitset, randval)) {
  5152. zend_bitset_incl(bitset, randval);
  5153. i--;
  5154. }
  5155. }
  5156. /* i = 0; */
  5157. zend_hash_real_init_packed(Z_ARRVAL_P(return_value));
  5158. ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
  5159. /* We can't use zend_hash_index_find()
  5160. * because the array may have string keys or gaps. */
  5161. ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
  5162. if (zend_bitset_in(bitset, i) ^ negative_bitset) {
  5163. if (string_key) {
  5164. ZEND_HASH_FILL_SET_STR_COPY(string_key);
  5165. } else {
  5166. ZEND_HASH_FILL_SET_LONG(num_key);
  5167. }
  5168. ZEND_HASH_FILL_NEXT();
  5169. }
  5170. i++;
  5171. } ZEND_HASH_FOREACH_END();
  5172. } ZEND_HASH_FILL_END();
  5173. free_alloca(bitset, use_heap);
  5174. }
  5175. /* }}} */
  5176. /* {{{ proto int|float array_sum(array input)
  5177. Returns the sum of the array entries */
  5178. PHP_FUNCTION(array_sum)
  5179. {
  5180. zval *input,
  5181. *entry,
  5182. entry_n;
  5183. ZEND_PARSE_PARAMETERS_START(1, 1)
  5184. Z_PARAM_ARRAY(input)
  5185. ZEND_PARSE_PARAMETERS_END();
  5186. ZVAL_LONG(return_value, 0);
  5187. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
  5188. if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
  5189. continue;
  5190. }
  5191. ZVAL_COPY(&entry_n, entry);
  5192. convert_scalar_to_number(&entry_n);
  5193. fast_add_function(return_value, return_value, &entry_n);
  5194. } ZEND_HASH_FOREACH_END();
  5195. }
  5196. /* }}} */
  5197. /* {{{ proto int|float array_product(array input)
  5198. Returns the product of the array entries */
  5199. PHP_FUNCTION(array_product)
  5200. {
  5201. zval *input,
  5202. *entry,
  5203. entry_n;
  5204. double dval;
  5205. ZEND_PARSE_PARAMETERS_START(1, 1)
  5206. Z_PARAM_ARRAY(input)
  5207. ZEND_PARSE_PARAMETERS_END();
  5208. ZVAL_LONG(return_value, 1);
  5209. if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
  5210. return;
  5211. }
  5212. ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
  5213. if (Z_TYPE_P(entry) == IS_ARRAY || Z_TYPE_P(entry) == IS_OBJECT) {
  5214. continue;
  5215. }
  5216. ZVAL_COPY(&entry_n, entry);
  5217. convert_scalar_to_number(&entry_n);
  5218. if (Z_TYPE(entry_n) == IS_LONG && Z_TYPE_P(return_value) == IS_LONG) {
  5219. dval = (double)Z_LVAL_P(return_value) * (double)Z_LVAL(entry_n);
  5220. if ( (double)ZEND_LONG_MIN <= dval && dval <= (double)ZEND_LONG_MAX ) {
  5221. Z_LVAL_P(return_value) *= Z_LVAL(entry_n);
  5222. continue;
  5223. }
  5224. }
  5225. convert_to_double(return_value);
  5226. convert_to_double(&entry_n);
  5227. Z_DVAL_P(return_value) *= Z_DVAL(entry_n);
  5228. } ZEND_HASH_FOREACH_END();
  5229. }
  5230. /* }}} */
  5231. /* {{{ proto mixed array_reduce(array input, mixed callback [, mixed initial])
  5232. Iteratively reduce the array to a single value via the callback. */
  5233. PHP_FUNCTION(array_reduce)
  5234. {
  5235. zval *input;
  5236. zval args[2];
  5237. zval *operand;
  5238. zval retval;
  5239. zend_fcall_info fci;
  5240. zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
  5241. zval *initial = NULL;
  5242. HashTable *htbl;
  5243. ZEND_PARSE_PARAMETERS_START(2, 3)
  5244. Z_PARAM_ARRAY(input)
  5245. Z_PARAM_FUNC(fci, fci_cache)
  5246. Z_PARAM_OPTIONAL
  5247. Z_PARAM_ZVAL(initial)
  5248. ZEND_PARSE_PARAMETERS_END();
  5249. if (ZEND_NUM_ARGS() > 2) {
  5250. ZVAL_COPY(return_value, initial);
  5251. } else {
  5252. ZVAL_NULL(return_value);
  5253. }
  5254. /* (zval **)input points to an element of argument stack
  5255. * the base pointer of which is subject to change.
  5256. * thus we need to keep the pointer to the hashtable for safety */
  5257. htbl = Z_ARRVAL_P(input);
  5258. if (zend_hash_num_elements(htbl) == 0) {
  5259. zend_release_fcall_info_cache(&fci_cache);
  5260. return;
  5261. }
  5262. fci.retval = &retval;
  5263. fci.param_count = 2;
  5264. fci.no_separation = 0;
  5265. ZEND_HASH_FOREACH_VAL(htbl, operand) {
  5266. ZVAL_COPY_VALUE(&args[0], return_value);
  5267. ZVAL_COPY(&args[1], operand);
  5268. fci.params = args;
  5269. if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
  5270. zval_ptr_dtor(&args[1]);
  5271. zval_ptr_dtor(&args[0]);
  5272. ZVAL_COPY_VALUE(return_value, &retval);
  5273. } else {
  5274. zval_ptr_dtor(&args[1]);
  5275. zval_ptr_dtor(&args[0]);
  5276. RETURN_NULL();
  5277. }
  5278. } ZEND_HASH_FOREACH_END();
  5279. zend_release_fcall_info_cache(&fci_cache);
  5280. }
  5281. /* }}} */
  5282. /* {{{ proto array array_filter(array input [, mixed callback [, int use_type]])
  5283. Filters elements from the array via the callback. */
  5284. PHP_FUNCTION(array_filter)
  5285. {
  5286. zval *array;
  5287. zval *operand;
  5288. zval *key;
  5289. zval args[2];
  5290. zval retval;
  5291. zend_bool have_callback = 0;
  5292. zend_long use_type = 0;
  5293. zend_string *string_key;
  5294. zend_fcall_info fci = empty_fcall_info;
  5295. zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
  5296. zend_ulong num_key;
  5297. ZEND_PARSE_PARAMETERS_START(1, 3)
  5298. Z_PARAM_ARRAY(array)
  5299. Z_PARAM_OPTIONAL
  5300. Z_PARAM_FUNC(fci, fci_cache)
  5301. Z_PARAM_LONG(use_type)
  5302. ZEND_PARSE_PARAMETERS_END();
  5303. if (zend_hash_num_elements(Z_ARRVAL_P(array)) == 0) {
  5304. RETVAL_EMPTY_ARRAY();
  5305. zend_release_fcall_info_cache(&fci_cache);
  5306. return;
  5307. }
  5308. array_init(return_value);
  5309. if (ZEND_NUM_ARGS() > 1) {
  5310. have_callback = 1;
  5311. fci.no_separation = 0;
  5312. fci.retval = &retval;
  5313. if (use_type == ARRAY_FILTER_USE_BOTH) {
  5314. fci.param_count = 2;
  5315. key = &args[1];
  5316. } else {
  5317. fci.param_count = 1;
  5318. key = &args[0];
  5319. }
  5320. }
  5321. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(array), num_key, string_key, operand) {
  5322. if (have_callback) {
  5323. if (use_type) {
  5324. /* Set up the key */
  5325. if (!string_key) {
  5326. ZVAL_LONG(key, num_key);
  5327. } else {
  5328. ZVAL_STR_COPY(key, string_key);
  5329. }
  5330. }
  5331. if (use_type != ARRAY_FILTER_USE_KEY) {
  5332. ZVAL_COPY(&args[0], operand);
  5333. }
  5334. fci.params = args;
  5335. if (zend_call_function(&fci, &fci_cache) == SUCCESS) {
  5336. int retval_true;
  5337. zval_ptr_dtor(&args[0]);
  5338. if (use_type == ARRAY_FILTER_USE_BOTH) {
  5339. zval_ptr_dtor(&args[1]);
  5340. }
  5341. retval_true = zend_is_true(&retval);
  5342. zval_ptr_dtor(&retval);
  5343. if (!retval_true) {
  5344. continue;
  5345. }
  5346. } else {
  5347. zval_ptr_dtor(&args[0]);
  5348. if (use_type == ARRAY_FILTER_USE_BOTH) {
  5349. zval_ptr_dtor(&args[1]);
  5350. }
  5351. return;
  5352. }
  5353. } else if (!zend_is_true(operand)) {
  5354. continue;
  5355. }
  5356. if (string_key) {
  5357. operand = zend_hash_add_new(Z_ARRVAL_P(return_value), string_key, operand);
  5358. } else {
  5359. operand = zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, operand);
  5360. }
  5361. zval_add_ref(operand);
  5362. } ZEND_HASH_FOREACH_END();
  5363. zend_release_fcall_info_cache(&fci_cache);
  5364. }
  5365. /* }}} */
  5366. /* {{{ proto array array_map(mixed callback, array input1 [, array input2 ,...])
  5367. Applies the callback to the elements in given arrays. */
  5368. PHP_FUNCTION(array_map)
  5369. {
  5370. zval *arrays = NULL;
  5371. int n_arrays = 0;
  5372. zval result;
  5373. zend_fcall_info fci = empty_fcall_info;
  5374. zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
  5375. int i;
  5376. uint32_t k, maxlen = 0;
  5377. ZEND_PARSE_PARAMETERS_START(2, -1)
  5378. Z_PARAM_FUNC_EX(fci, fci_cache, 1, 0)
  5379. Z_PARAM_VARIADIC('+', arrays, n_arrays)
  5380. ZEND_PARSE_PARAMETERS_END();
  5381. if (n_arrays == 1) {
  5382. zend_ulong num_key;
  5383. zend_string *str_key;
  5384. zval *zv, arg;
  5385. int ret;
  5386. if (Z_TYPE(arrays[0]) != IS_ARRAY) {
  5387. zend_argument_type_error(2, "must be of type array, %s given", zend_zval_type_name(&arrays[0]));
  5388. RETURN_THROWS();
  5389. }
  5390. maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
  5391. /* Short-circuit: if no callback and only one array, just return it. */
  5392. if (!ZEND_FCI_INITIALIZED(fci) || !maxlen) {
  5393. ZVAL_COPY(return_value, &arrays[0]);
  5394. zend_release_fcall_info_cache(&fci_cache);
  5395. return;
  5396. }
  5397. array_init_size(return_value, maxlen);
  5398. zend_hash_real_init(Z_ARRVAL_P(return_value), HT_FLAGS(Z_ARRVAL(arrays[0])) & HASH_FLAG_PACKED);
  5399. ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
  5400. fci.retval = &result;
  5401. fci.param_count = 1;
  5402. fci.params = &arg;
  5403. fci.no_separation = 0;
  5404. ZVAL_COPY(&arg, zv);
  5405. ret = zend_call_function(&fci, &fci_cache);
  5406. i_zval_ptr_dtor(&arg);
  5407. if (ret != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
  5408. zend_array_destroy(Z_ARR_P(return_value));
  5409. RETURN_NULL();
  5410. }
  5411. if (str_key) {
  5412. _zend_hash_append(Z_ARRVAL_P(return_value), str_key, &result);
  5413. } else {
  5414. zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result);
  5415. }
  5416. } ZEND_HASH_FOREACH_END();
  5417. zend_release_fcall_info_cache(&fci_cache);
  5418. } else {
  5419. uint32_t *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition));
  5420. for (i = 0; i < n_arrays; i++) {
  5421. if (Z_TYPE(arrays[i]) != IS_ARRAY) {
  5422. zend_argument_type_error(i + 2, "must be of type array, %s given", zend_zval_type_name(&arrays[i]));
  5423. efree(array_pos);
  5424. RETURN_THROWS();
  5425. }
  5426. if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) {
  5427. maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i]));
  5428. }
  5429. }
  5430. array_init_size(return_value, maxlen);
  5431. if (!ZEND_FCI_INITIALIZED(fci)) {
  5432. zval zv;
  5433. /* We iterate through all the arrays at once. */
  5434. for (k = 0; k < maxlen; k++) {
  5435. /* If no callback, the result will be an array, consisting of current
  5436. * entries from all arrays. */
  5437. array_init_size(&result, n_arrays);
  5438. for (i = 0; i < n_arrays; i++) {
  5439. /* If this array still has elements, add the current one to the
  5440. * parameter list, otherwise use null value. */
  5441. uint32_t pos = array_pos[i];
  5442. while (1) {
  5443. if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
  5444. ZVAL_NULL(&zv);
  5445. break;
  5446. } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
  5447. ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val);
  5448. array_pos[i] = pos + 1;
  5449. break;
  5450. }
  5451. pos++;
  5452. }
  5453. zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv);
  5454. }
  5455. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
  5456. }
  5457. } else {
  5458. zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0);
  5459. /* We iterate through all the arrays at once. */
  5460. for (k = 0; k < maxlen; k++) {
  5461. for (i = 0; i < n_arrays; i++) {
  5462. /* If this array still has elements, add the current one to the
  5463. * parameter list, otherwise use null value. */
  5464. uint32_t pos = array_pos[i];
  5465. while (1) {
  5466. if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) {
  5467. ZVAL_NULL(&params[i]);
  5468. break;
  5469. } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
  5470. ZVAL_COPY(&params[i], &Z_ARRVAL(arrays[i])->arData[pos].val);
  5471. array_pos[i] = pos + 1;
  5472. break;
  5473. }
  5474. pos++;
  5475. }
  5476. }
  5477. fci.retval = &result;
  5478. fci.param_count = n_arrays;
  5479. fci.params = params;
  5480. fci.no_separation = 0;
  5481. if (zend_call_function(&fci, &fci_cache) != SUCCESS || Z_TYPE(result) == IS_UNDEF) {
  5482. efree(array_pos);
  5483. zend_array_destroy(Z_ARR_P(return_value));
  5484. for (i = 0; i < n_arrays; i++) {
  5485. zval_ptr_dtor(&params[i]);
  5486. }
  5487. efree(params);
  5488. RETURN_NULL();
  5489. } else {
  5490. for (i = 0; i < n_arrays; i++) {
  5491. zval_ptr_dtor(&params[i]);
  5492. }
  5493. }
  5494. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result);
  5495. }
  5496. efree(params);
  5497. zend_release_fcall_info_cache(&fci_cache);
  5498. }
  5499. efree(array_pos);
  5500. }
  5501. }
  5502. /* }}} */
  5503. /* {{{ proto bool array_key_exists(mixed key, array search)
  5504. Checks if the given key or index exists in the array */
  5505. PHP_FUNCTION(array_key_exists)
  5506. {
  5507. zval *key;
  5508. HashTable *ht;
  5509. ZEND_PARSE_PARAMETERS_START(2, 2)
  5510. Z_PARAM_ZVAL(key)
  5511. Z_PARAM_ARRAY_HT(ht)
  5512. ZEND_PARSE_PARAMETERS_END();
  5513. switch (Z_TYPE_P(key)) {
  5514. case IS_STRING:
  5515. RETVAL_BOOL(zend_symtable_exists_ind(ht, Z_STR_P(key)));
  5516. break;
  5517. case IS_LONG:
  5518. RETVAL_BOOL(zend_hash_index_exists(ht, Z_LVAL_P(key)));
  5519. break;
  5520. case IS_NULL:
  5521. RETVAL_BOOL(zend_hash_exists_ind(ht, ZSTR_EMPTY_ALLOC()));
  5522. break;
  5523. case IS_DOUBLE:
  5524. RETVAL_BOOL(zend_hash_index_exists(ht, zend_dval_to_lval(Z_DVAL_P(key))));
  5525. break;
  5526. case IS_FALSE:
  5527. RETVAL_BOOL(zend_hash_index_exists(ht, 0));
  5528. break;
  5529. case IS_TRUE:
  5530. RETVAL_BOOL(zend_hash_index_exists(ht, 1));
  5531. break;
  5532. case IS_RESOURCE:
  5533. zend_error(E_WARNING, "Resource ID#%d used as offset, casting to integer (%d)", Z_RES_HANDLE_P(key), Z_RES_HANDLE_P(key));
  5534. RETVAL_BOOL(zend_hash_index_exists(ht, Z_RES_HANDLE_P(key)));
  5535. break;
  5536. default:
  5537. zend_argument_type_error(1, "must be a valid array offset type");
  5538. break;
  5539. }
  5540. }
  5541. /* }}} */
  5542. /* {{{ proto array array_chunk(array input, int size [, bool preserve_keys])
  5543. Split array into chunks */
  5544. PHP_FUNCTION(array_chunk)
  5545. {
  5546. int num_in;
  5547. zend_long size, current = 0;
  5548. zend_string *str_key;
  5549. zend_ulong num_key;
  5550. zend_bool preserve_keys = 0;
  5551. zval *input = NULL;
  5552. zval chunk;
  5553. zval *entry;
  5554. ZEND_PARSE_PARAMETERS_START(2, 3)
  5555. Z_PARAM_ARRAY(input)
  5556. Z_PARAM_LONG(size)
  5557. Z_PARAM_OPTIONAL
  5558. Z_PARAM_BOOL(preserve_keys)
  5559. ZEND_PARSE_PARAMETERS_END();
  5560. /* Do bounds checking for size parameter. */
  5561. if (size < 1) {
  5562. zend_argument_value_error(2, "must be greater than 0");
  5563. RETURN_THROWS();
  5564. }
  5565. num_in = zend_hash_num_elements(Z_ARRVAL_P(input));
  5566. if (size > num_in) {
  5567. if (num_in == 0) {
  5568. RETVAL_EMPTY_ARRAY();
  5569. return;
  5570. }
  5571. size = num_in;
  5572. }
  5573. array_init_size(return_value, (uint32_t)(((num_in - 1) / size) + 1));
  5574. ZVAL_UNDEF(&chunk);
  5575. ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input), num_key, str_key, entry) {
  5576. /* If new chunk, create and initialize it. */
  5577. if (Z_TYPE(chunk) == IS_UNDEF) {
  5578. array_init_size(&chunk, (uint32_t)size);
  5579. }
  5580. /* Add entry to the chunk, preserving keys if necessary. */
  5581. if (preserve_keys) {
  5582. if (str_key) {
  5583. entry = zend_hash_add_new(Z_ARRVAL(chunk), str_key, entry);
  5584. } else {
  5585. entry = zend_hash_index_add_new(Z_ARRVAL(chunk), num_key, entry);
  5586. }
  5587. } else {
  5588. entry = zend_hash_next_index_insert(Z_ARRVAL(chunk), entry);
  5589. }
  5590. zval_add_ref(entry);
  5591. /* If reached the chunk size, add it to the result array, and reset the
  5592. * pointer. */
  5593. if (!(++current % size)) {
  5594. add_next_index_zval(return_value, &chunk);
  5595. ZVAL_UNDEF(&chunk);
  5596. }
  5597. } ZEND_HASH_FOREACH_END();
  5598. /* Add the final chunk if there is one. */
  5599. if (Z_TYPE(chunk) != IS_UNDEF) {
  5600. add_next_index_zval(return_value, &chunk);
  5601. }
  5602. }
  5603. /* }}} */
  5604. /* {{{ proto array array_combine(array keys, array values)
  5605. Creates an array by using the elements of the first parameter as keys and the elements of the second as the corresponding values */
  5606. PHP_FUNCTION(array_combine)
  5607. {
  5608. HashTable *values, *keys;
  5609. uint32_t pos_values = 0;
  5610. zval *entry_keys, *entry_values;
  5611. int num_keys, num_values;
  5612. ZEND_PARSE_PARAMETERS_START(2, 2)
  5613. Z_PARAM_ARRAY_HT(keys)
  5614. Z_PARAM_ARRAY_HT(values)
  5615. ZEND_PARSE_PARAMETERS_END();
  5616. num_keys = zend_hash_num_elements(keys);
  5617. num_values = zend_hash_num_elements(values);
  5618. if (num_keys != num_values) {
  5619. zend_argument_value_error(1, "and argument #2 ($values) must have the same number of elements");
  5620. RETURN_THROWS();
  5621. }
  5622. if (!num_keys) {
  5623. RETURN_EMPTY_ARRAY();
  5624. }
  5625. array_init_size(return_value, num_keys);
  5626. ZEND_HASH_FOREACH_VAL(keys, entry_keys) {
  5627. while (1) {
  5628. if (pos_values >= values->nNumUsed) {
  5629. break;
  5630. } else if (Z_TYPE(values->arData[pos_values].val) != IS_UNDEF) {
  5631. entry_values = &values->arData[pos_values].val;
  5632. if (Z_TYPE_P(entry_keys) == IS_LONG) {
  5633. entry_values = zend_hash_index_update(Z_ARRVAL_P(return_value),
  5634. Z_LVAL_P(entry_keys), entry_values);
  5635. } else {
  5636. zend_string *tmp_key;
  5637. zend_string *key = zval_get_tmp_string(entry_keys, &tmp_key);
  5638. entry_values = zend_symtable_update(Z_ARRVAL_P(return_value),
  5639. key, entry_values);
  5640. zend_tmp_string_release(tmp_key);
  5641. }
  5642. zval_add_ref(entry_values);
  5643. pos_values++;
  5644. break;
  5645. }
  5646. pos_values++;
  5647. }
  5648. } ZEND_HASH_FOREACH_END();
  5649. }
  5650. /* }}} */