PageRenderTime 57ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/standard/string.c

http://github.com/php/php-src
C | 6164 lines | 4888 code | 692 blank | 584 comment | 1224 complexity | 55ac1c61da69a0feb623e86f03d5457b MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  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: Rasmus Lerdorf <rasmus@php.net> |
  14. | Stig Sæther Bakken <ssb@php.net> |
  15. | Zeev Suraski <zeev@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include <stdio.h>
  19. #include "php.h"
  20. #include "php_rand.h"
  21. #include "php_string.h"
  22. #include "php_variables.h"
  23. #include <locale.h>
  24. #ifdef HAVE_LANGINFO_H
  25. # include <langinfo.h>
  26. #endif
  27. #ifdef HAVE_MONETARY_H
  28. # include <monetary.h>
  29. #endif
  30. /*
  31. * This define is here because some versions of libintl redefine setlocale
  32. * to point to libintl_setlocale. That's a ridiculous thing to do as far
  33. * as I am concerned, but with this define and the subsequent undef we
  34. * limit the damage to just the actual setlocale() call in this file
  35. * without turning zif_setlocale into zif_libintl_setlocale. -Rasmus
  36. */
  37. #define php_my_setlocale setlocale
  38. #ifdef HAVE_LIBINTL
  39. # include <libintl.h> /* For LC_MESSAGES */
  40. #ifdef setlocale
  41. # undef setlocale
  42. #endif
  43. #endif
  44. #include "scanf.h"
  45. #include "zend_API.h"
  46. #include "zend_execute.h"
  47. #include "php_globals.h"
  48. #include "basic_functions.h"
  49. #include "zend_smart_str.h"
  50. #include <Zend/zend_exceptions.h>
  51. #ifdef ZTS
  52. #include "TSRM.h"
  53. #endif
  54. /* For str_getcsv() support */
  55. #include "ext/standard/file.h"
  56. /* For php_next_utf8_char() */
  57. #include "ext/standard/html.h"
  58. #define STR_PAD_LEFT 0
  59. #define STR_PAD_RIGHT 1
  60. #define STR_PAD_BOTH 2
  61. #define PHP_PATHINFO_DIRNAME 1
  62. #define PHP_PATHINFO_BASENAME 2
  63. #define PHP_PATHINFO_EXTENSION 4
  64. #define PHP_PATHINFO_FILENAME 8
  65. #define PHP_PATHINFO_ALL (PHP_PATHINFO_DIRNAME | PHP_PATHINFO_BASENAME | PHP_PATHINFO_EXTENSION | PHP_PATHINFO_FILENAME)
  66. #define STR_STRSPN 0
  67. #define STR_STRCSPN 1
  68. /* {{{ register_string_constants
  69. */
  70. void register_string_constants(INIT_FUNC_ARGS)
  71. {
  72. REGISTER_LONG_CONSTANT("STR_PAD_LEFT", STR_PAD_LEFT, CONST_CS | CONST_PERSISTENT);
  73. REGISTER_LONG_CONSTANT("STR_PAD_RIGHT", STR_PAD_RIGHT, CONST_CS | CONST_PERSISTENT);
  74. REGISTER_LONG_CONSTANT("STR_PAD_BOTH", STR_PAD_BOTH, CONST_CS | CONST_PERSISTENT);
  75. REGISTER_LONG_CONSTANT("PATHINFO_DIRNAME", PHP_PATHINFO_DIRNAME, CONST_CS | CONST_PERSISTENT);
  76. REGISTER_LONG_CONSTANT("PATHINFO_BASENAME", PHP_PATHINFO_BASENAME, CONST_CS | CONST_PERSISTENT);
  77. REGISTER_LONG_CONSTANT("PATHINFO_EXTENSION", PHP_PATHINFO_EXTENSION, CONST_CS | CONST_PERSISTENT);
  78. REGISTER_LONG_CONSTANT("PATHINFO_FILENAME", PHP_PATHINFO_FILENAME, CONST_CS | CONST_PERSISTENT);
  79. /* If last members of struct lconv equal CHAR_MAX, no grouping is done */
  80. REGISTER_LONG_CONSTANT("CHAR_MAX", CHAR_MAX, CONST_CS | CONST_PERSISTENT);
  81. REGISTER_LONG_CONSTANT("LC_CTYPE", LC_CTYPE, CONST_CS | CONST_PERSISTENT);
  82. REGISTER_LONG_CONSTANT("LC_NUMERIC", LC_NUMERIC, CONST_CS | CONST_PERSISTENT);
  83. REGISTER_LONG_CONSTANT("LC_TIME", LC_TIME, CONST_CS | CONST_PERSISTENT);
  84. REGISTER_LONG_CONSTANT("LC_COLLATE", LC_COLLATE, CONST_CS | CONST_PERSISTENT);
  85. REGISTER_LONG_CONSTANT("LC_MONETARY", LC_MONETARY, CONST_CS | CONST_PERSISTENT);
  86. REGISTER_LONG_CONSTANT("LC_ALL", LC_ALL, CONST_CS | CONST_PERSISTENT);
  87. # ifdef LC_MESSAGES
  88. REGISTER_LONG_CONSTANT("LC_MESSAGES", LC_MESSAGES, CONST_CS | CONST_PERSISTENT);
  89. # endif
  90. }
  91. /* }}} */
  92. int php_tag_find(char *tag, size_t len, const char *set);
  93. /* this is read-only, so it's ok */
  94. ZEND_SET_ALIGNED(16, static char hexconvtab[]) = "0123456789abcdef";
  95. /* localeconv mutex */
  96. #ifdef ZTS
  97. static MUTEX_T locale_mutex = NULL;
  98. #endif
  99. /* {{{ php_bin2hex
  100. */
  101. static zend_string *php_bin2hex(const unsigned char *old, const size_t oldlen)
  102. {
  103. zend_string *result;
  104. size_t i, j;
  105. result = zend_string_safe_alloc(oldlen, 2 * sizeof(char), 0, 0);
  106. for (i = j = 0; i < oldlen; i++) {
  107. ZSTR_VAL(result)[j++] = hexconvtab[old[i] >> 4];
  108. ZSTR_VAL(result)[j++] = hexconvtab[old[i] & 15];
  109. }
  110. ZSTR_VAL(result)[j] = '\0';
  111. return result;
  112. }
  113. /* }}} */
  114. /* {{{ php_hex2bin
  115. */
  116. static zend_string *php_hex2bin(const unsigned char *old, const size_t oldlen)
  117. {
  118. size_t target_length = oldlen >> 1;
  119. zend_string *str = zend_string_alloc(target_length, 0);
  120. unsigned char *ret = (unsigned char *)ZSTR_VAL(str);
  121. size_t i, j;
  122. for (i = j = 0; i < target_length; i++) {
  123. unsigned char c = old[j++];
  124. unsigned char l = c & ~0x20;
  125. int is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
  126. unsigned char d;
  127. /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
  128. if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
  129. d = (l - 0x10 - 0x27 * is_letter) << 4;
  130. } else {
  131. zend_string_efree(str);
  132. return NULL;
  133. }
  134. c = old[j++];
  135. l = c & ~0x20;
  136. is_letter = ((unsigned int) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(unsigned int) - 1);
  137. if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(unsigned int) - 1)) | is_letter)) {
  138. d |= l - 0x10 - 0x27 * is_letter;
  139. } else {
  140. zend_string_efree(str);
  141. return NULL;
  142. }
  143. ret[i] = d;
  144. }
  145. ret[i] = '\0';
  146. return str;
  147. }
  148. /* }}} */
  149. /* {{{ localeconv_r
  150. * glibc's localeconv is not reentrant, so lets make it so ... sorta */
  151. PHPAPI struct lconv *localeconv_r(struct lconv *out)
  152. {
  153. #ifdef ZTS
  154. tsrm_mutex_lock( locale_mutex );
  155. #endif
  156. /* cur->locinfo is struct __crt_locale_info which implementation is
  157. hidden in vc14. TODO revisit this and check if a workaround available
  158. and needed. */
  159. #if defined(PHP_WIN32) && _MSC_VER < 1900 && defined(ZTS)
  160. {
  161. /* Even with the enabled per thread locale, localeconv
  162. won't check any locale change in the master thread. */
  163. _locale_t cur = _get_current_locale();
  164. *out = *cur->locinfo->lconv;
  165. _free_locale(cur);
  166. }
  167. #else
  168. /* localeconv doesn't return an error condition */
  169. *out = *localeconv();
  170. #endif
  171. #ifdef ZTS
  172. tsrm_mutex_unlock( locale_mutex );
  173. #endif
  174. return out;
  175. }
  176. /* }}} */
  177. #ifdef ZTS
  178. /* {{{ PHP_MINIT_FUNCTION
  179. */
  180. PHP_MINIT_FUNCTION(localeconv)
  181. {
  182. locale_mutex = tsrm_mutex_alloc();
  183. return SUCCESS;
  184. }
  185. /* }}} */
  186. /* {{{ PHP_MSHUTDOWN_FUNCTION
  187. */
  188. PHP_MSHUTDOWN_FUNCTION(localeconv)
  189. {
  190. tsrm_mutex_free( locale_mutex );
  191. locale_mutex = NULL;
  192. return SUCCESS;
  193. }
  194. /* }}} */
  195. #endif
  196. /* {{{ proto string bin2hex(string data)
  197. Converts the binary representation of data to hex */
  198. PHP_FUNCTION(bin2hex)
  199. {
  200. zend_string *result;
  201. zend_string *data;
  202. ZEND_PARSE_PARAMETERS_START(1, 1)
  203. Z_PARAM_STR(data)
  204. ZEND_PARSE_PARAMETERS_END();
  205. result = php_bin2hex((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
  206. RETURN_STR(result);
  207. }
  208. /* }}} */
  209. /* {{{ proto string|false hex2bin(string data)
  210. Converts the hex representation of data to binary */
  211. PHP_FUNCTION(hex2bin)
  212. {
  213. zend_string *result, *data;
  214. ZEND_PARSE_PARAMETERS_START(1, 1)
  215. Z_PARAM_STR(data)
  216. ZEND_PARSE_PARAMETERS_END();
  217. if (ZSTR_LEN(data) % 2 != 0) {
  218. php_error_docref(NULL, E_WARNING, "Hexadecimal input string must have an even length");
  219. RETURN_FALSE;
  220. }
  221. result = php_hex2bin((unsigned char *)ZSTR_VAL(data), ZSTR_LEN(data));
  222. if (!result) {
  223. php_error_docref(NULL, E_WARNING, "Input string must be hexadecimal string");
  224. RETURN_FALSE;
  225. }
  226. RETVAL_STR(result);
  227. }
  228. /* }}} */
  229. static void php_spn_common_handler(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
  230. {
  231. zend_string *s11, *s22;
  232. zend_long start = 0, len = 0;
  233. ZEND_PARSE_PARAMETERS_START(2, 4)
  234. Z_PARAM_STR(s11)
  235. Z_PARAM_STR(s22)
  236. Z_PARAM_OPTIONAL
  237. Z_PARAM_LONG(start)
  238. Z_PARAM_LONG(len)
  239. ZEND_PARSE_PARAMETERS_END();
  240. if (ZEND_NUM_ARGS() < 4) {
  241. len = ZSTR_LEN(s11);
  242. }
  243. /* look at substr() function for more information */
  244. if (start < 0) {
  245. start += (zend_long)ZSTR_LEN(s11);
  246. if (start < 0) {
  247. start = 0;
  248. }
  249. } else if ((size_t)start > ZSTR_LEN(s11)) {
  250. RETURN_FALSE;
  251. }
  252. if (len < 0) {
  253. len += (ZSTR_LEN(s11) - start);
  254. if (len < 0) {
  255. len = 0;
  256. }
  257. }
  258. if (len > (zend_long)ZSTR_LEN(s11) - start) {
  259. len = ZSTR_LEN(s11) - start;
  260. }
  261. if(len == 0) {
  262. RETURN_LONG(0);
  263. }
  264. if (behavior == STR_STRSPN) {
  265. RETURN_LONG(php_strspn(ZSTR_VAL(s11) + start /*str1_start*/,
  266. ZSTR_VAL(s22) /*str2_start*/,
  267. ZSTR_VAL(s11) + start + len /*str1_end*/,
  268. ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
  269. } else if (behavior == STR_STRCSPN) {
  270. RETURN_LONG(php_strcspn(ZSTR_VAL(s11) + start /*str1_start*/,
  271. ZSTR_VAL(s22) /*str2_start*/,
  272. ZSTR_VAL(s11) + start + len /*str1_end*/,
  273. ZSTR_VAL(s22) + ZSTR_LEN(s22) /*str2_end*/));
  274. }
  275. }
  276. /* }}} */
  277. /* {{{ proto int|false strspn(string str, string mask [, int start [, int len]])
  278. Finds length of initial segment consisting entirely of characters found in mask. If start or/and length is provided works like strspn(substr($s,$start,$len),$good_chars) */
  279. PHP_FUNCTION(strspn)
  280. {
  281. php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRSPN);
  282. }
  283. /* }}} */
  284. /* {{{ proto int|false strcspn(string str, string mask [, int start [, int len]])
  285. Finds length of initial segment consisting entirely of characters not found in mask. If start or/and length is provide works like strcspn(substr($s,$start,$len),$bad_chars) */
  286. PHP_FUNCTION(strcspn)
  287. {
  288. php_spn_common_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, STR_STRCSPN);
  289. }
  290. /* }}} */
  291. /* {{{ PHP_MINIT_FUNCTION(nl_langinfo) */
  292. #if HAVE_NL_LANGINFO
  293. PHP_MINIT_FUNCTION(nl_langinfo)
  294. {
  295. #define REGISTER_NL_LANGINFO_CONSTANT(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS | CONST_PERSISTENT)
  296. #ifdef ABDAY_1
  297. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_1);
  298. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_2);
  299. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_3);
  300. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_4);
  301. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_5);
  302. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_6);
  303. REGISTER_NL_LANGINFO_CONSTANT(ABDAY_7);
  304. #endif
  305. #ifdef DAY_1
  306. REGISTER_NL_LANGINFO_CONSTANT(DAY_1);
  307. REGISTER_NL_LANGINFO_CONSTANT(DAY_2);
  308. REGISTER_NL_LANGINFO_CONSTANT(DAY_3);
  309. REGISTER_NL_LANGINFO_CONSTANT(DAY_4);
  310. REGISTER_NL_LANGINFO_CONSTANT(DAY_5);
  311. REGISTER_NL_LANGINFO_CONSTANT(DAY_6);
  312. REGISTER_NL_LANGINFO_CONSTANT(DAY_7);
  313. #endif
  314. #ifdef ABMON_1
  315. REGISTER_NL_LANGINFO_CONSTANT(ABMON_1);
  316. REGISTER_NL_LANGINFO_CONSTANT(ABMON_2);
  317. REGISTER_NL_LANGINFO_CONSTANT(ABMON_3);
  318. REGISTER_NL_LANGINFO_CONSTANT(ABMON_4);
  319. REGISTER_NL_LANGINFO_CONSTANT(ABMON_5);
  320. REGISTER_NL_LANGINFO_CONSTANT(ABMON_6);
  321. REGISTER_NL_LANGINFO_CONSTANT(ABMON_7);
  322. REGISTER_NL_LANGINFO_CONSTANT(ABMON_8);
  323. REGISTER_NL_LANGINFO_CONSTANT(ABMON_9);
  324. REGISTER_NL_LANGINFO_CONSTANT(ABMON_10);
  325. REGISTER_NL_LANGINFO_CONSTANT(ABMON_11);
  326. REGISTER_NL_LANGINFO_CONSTANT(ABMON_12);
  327. #endif
  328. #ifdef MON_1
  329. REGISTER_NL_LANGINFO_CONSTANT(MON_1);
  330. REGISTER_NL_LANGINFO_CONSTANT(MON_2);
  331. REGISTER_NL_LANGINFO_CONSTANT(MON_3);
  332. REGISTER_NL_LANGINFO_CONSTANT(MON_4);
  333. REGISTER_NL_LANGINFO_CONSTANT(MON_5);
  334. REGISTER_NL_LANGINFO_CONSTANT(MON_6);
  335. REGISTER_NL_LANGINFO_CONSTANT(MON_7);
  336. REGISTER_NL_LANGINFO_CONSTANT(MON_8);
  337. REGISTER_NL_LANGINFO_CONSTANT(MON_9);
  338. REGISTER_NL_LANGINFO_CONSTANT(MON_10);
  339. REGISTER_NL_LANGINFO_CONSTANT(MON_11);
  340. REGISTER_NL_LANGINFO_CONSTANT(MON_12);
  341. #endif
  342. #ifdef AM_STR
  343. REGISTER_NL_LANGINFO_CONSTANT(AM_STR);
  344. #endif
  345. #ifdef PM_STR
  346. REGISTER_NL_LANGINFO_CONSTANT(PM_STR);
  347. #endif
  348. #ifdef D_T_FMT
  349. REGISTER_NL_LANGINFO_CONSTANT(D_T_FMT);
  350. #endif
  351. #ifdef D_FMT
  352. REGISTER_NL_LANGINFO_CONSTANT(D_FMT);
  353. #endif
  354. #ifdef T_FMT
  355. REGISTER_NL_LANGINFO_CONSTANT(T_FMT);
  356. #endif
  357. #ifdef T_FMT_AMPM
  358. REGISTER_NL_LANGINFO_CONSTANT(T_FMT_AMPM);
  359. #endif
  360. #ifdef ERA
  361. REGISTER_NL_LANGINFO_CONSTANT(ERA);
  362. #endif
  363. #ifdef ERA_YEAR
  364. REGISTER_NL_LANGINFO_CONSTANT(ERA_YEAR);
  365. #endif
  366. #ifdef ERA_D_T_FMT
  367. REGISTER_NL_LANGINFO_CONSTANT(ERA_D_T_FMT);
  368. #endif
  369. #ifdef ERA_D_FMT
  370. REGISTER_NL_LANGINFO_CONSTANT(ERA_D_FMT);
  371. #endif
  372. #ifdef ERA_T_FMT
  373. REGISTER_NL_LANGINFO_CONSTANT(ERA_T_FMT);
  374. #endif
  375. #ifdef ALT_DIGITS
  376. REGISTER_NL_LANGINFO_CONSTANT(ALT_DIGITS);
  377. #endif
  378. #ifdef INT_CURR_SYMBOL
  379. REGISTER_NL_LANGINFO_CONSTANT(INT_CURR_SYMBOL);
  380. #endif
  381. #ifdef CURRENCY_SYMBOL
  382. REGISTER_NL_LANGINFO_CONSTANT(CURRENCY_SYMBOL);
  383. #endif
  384. #ifdef CRNCYSTR
  385. REGISTER_NL_LANGINFO_CONSTANT(CRNCYSTR);
  386. #endif
  387. #ifdef MON_DECIMAL_POINT
  388. REGISTER_NL_LANGINFO_CONSTANT(MON_DECIMAL_POINT);
  389. #endif
  390. #ifdef MON_THOUSANDS_SEP
  391. REGISTER_NL_LANGINFO_CONSTANT(MON_THOUSANDS_SEP);
  392. #endif
  393. #ifdef MON_GROUPING
  394. REGISTER_NL_LANGINFO_CONSTANT(MON_GROUPING);
  395. #endif
  396. #ifdef POSITIVE_SIGN
  397. REGISTER_NL_LANGINFO_CONSTANT(POSITIVE_SIGN);
  398. #endif
  399. #ifdef NEGATIVE_SIGN
  400. REGISTER_NL_LANGINFO_CONSTANT(NEGATIVE_SIGN);
  401. #endif
  402. #ifdef INT_FRAC_DIGITS
  403. REGISTER_NL_LANGINFO_CONSTANT(INT_FRAC_DIGITS);
  404. #endif
  405. #ifdef FRAC_DIGITS
  406. REGISTER_NL_LANGINFO_CONSTANT(FRAC_DIGITS);
  407. #endif
  408. #ifdef P_CS_PRECEDES
  409. REGISTER_NL_LANGINFO_CONSTANT(P_CS_PRECEDES);
  410. #endif
  411. #ifdef P_SEP_BY_SPACE
  412. REGISTER_NL_LANGINFO_CONSTANT(P_SEP_BY_SPACE);
  413. #endif
  414. #ifdef N_CS_PRECEDES
  415. REGISTER_NL_LANGINFO_CONSTANT(N_CS_PRECEDES);
  416. #endif
  417. #ifdef N_SEP_BY_SPACE
  418. REGISTER_NL_LANGINFO_CONSTANT(N_SEP_BY_SPACE);
  419. #endif
  420. #ifdef P_SIGN_POSN
  421. REGISTER_NL_LANGINFO_CONSTANT(P_SIGN_POSN);
  422. #endif
  423. #ifdef N_SIGN_POSN
  424. REGISTER_NL_LANGINFO_CONSTANT(N_SIGN_POSN);
  425. #endif
  426. #ifdef DECIMAL_POINT
  427. REGISTER_NL_LANGINFO_CONSTANT(DECIMAL_POINT);
  428. #endif
  429. #ifdef RADIXCHAR
  430. REGISTER_NL_LANGINFO_CONSTANT(RADIXCHAR);
  431. #endif
  432. #ifdef THOUSANDS_SEP
  433. REGISTER_NL_LANGINFO_CONSTANT(THOUSANDS_SEP);
  434. #endif
  435. #ifdef THOUSEP
  436. REGISTER_NL_LANGINFO_CONSTANT(THOUSEP);
  437. #endif
  438. #ifdef GROUPING
  439. REGISTER_NL_LANGINFO_CONSTANT(GROUPING);
  440. #endif
  441. #ifdef YESEXPR
  442. REGISTER_NL_LANGINFO_CONSTANT(YESEXPR);
  443. #endif
  444. #ifdef NOEXPR
  445. REGISTER_NL_LANGINFO_CONSTANT(NOEXPR);
  446. #endif
  447. #ifdef YESSTR
  448. REGISTER_NL_LANGINFO_CONSTANT(YESSTR);
  449. #endif
  450. #ifdef NOSTR
  451. REGISTER_NL_LANGINFO_CONSTANT(NOSTR);
  452. #endif
  453. #ifdef CODESET
  454. REGISTER_NL_LANGINFO_CONSTANT(CODESET);
  455. #endif
  456. #undef REGISTER_NL_LANGINFO_CONSTANT
  457. return SUCCESS;
  458. }
  459. /* }}} */
  460. /* {{{ proto string|false nl_langinfo(int item)
  461. Query language and locale information */
  462. PHP_FUNCTION(nl_langinfo)
  463. {
  464. zend_long item;
  465. char *value;
  466. ZEND_PARSE_PARAMETERS_START(1, 1)
  467. Z_PARAM_LONG(item)
  468. ZEND_PARSE_PARAMETERS_END();
  469. switch(item) { /* {{{ */
  470. #ifdef ABDAY_1
  471. case ABDAY_1:
  472. case ABDAY_2:
  473. case ABDAY_3:
  474. case ABDAY_4:
  475. case ABDAY_5:
  476. case ABDAY_6:
  477. case ABDAY_7:
  478. #endif
  479. #ifdef DAY_1
  480. case DAY_1:
  481. case DAY_2:
  482. case DAY_3:
  483. case DAY_4:
  484. case DAY_5:
  485. case DAY_6:
  486. case DAY_7:
  487. #endif
  488. #ifdef ABMON_1
  489. case ABMON_1:
  490. case ABMON_2:
  491. case ABMON_3:
  492. case ABMON_4:
  493. case ABMON_5:
  494. case ABMON_6:
  495. case ABMON_7:
  496. case ABMON_8:
  497. case ABMON_9:
  498. case ABMON_10:
  499. case ABMON_11:
  500. case ABMON_12:
  501. #endif
  502. #ifdef MON_1
  503. case MON_1:
  504. case MON_2:
  505. case MON_3:
  506. case MON_4:
  507. case MON_5:
  508. case MON_6:
  509. case MON_7:
  510. case MON_8:
  511. case MON_9:
  512. case MON_10:
  513. case MON_11:
  514. case MON_12:
  515. #endif
  516. #ifdef AM_STR
  517. case AM_STR:
  518. #endif
  519. #ifdef PM_STR
  520. case PM_STR:
  521. #endif
  522. #ifdef D_T_FMT
  523. case D_T_FMT:
  524. #endif
  525. #ifdef D_FMT
  526. case D_FMT:
  527. #endif
  528. #ifdef T_FMT
  529. case T_FMT:
  530. #endif
  531. #ifdef T_FMT_AMPM
  532. case T_FMT_AMPM:
  533. #endif
  534. #ifdef ERA
  535. case ERA:
  536. #endif
  537. #ifdef ERA_YEAR
  538. case ERA_YEAR:
  539. #endif
  540. #ifdef ERA_D_T_FMT
  541. case ERA_D_T_FMT:
  542. #endif
  543. #ifdef ERA_D_FMT
  544. case ERA_D_FMT:
  545. #endif
  546. #ifdef ERA_T_FMT
  547. case ERA_T_FMT:
  548. #endif
  549. #ifdef ALT_DIGITS
  550. case ALT_DIGITS:
  551. #endif
  552. #ifdef INT_CURR_SYMBOL
  553. case INT_CURR_SYMBOL:
  554. #endif
  555. #ifdef CURRENCY_SYMBOL
  556. case CURRENCY_SYMBOL:
  557. #endif
  558. #ifdef CRNCYSTR
  559. case CRNCYSTR:
  560. #endif
  561. #ifdef MON_DECIMAL_POINT
  562. case MON_DECIMAL_POINT:
  563. #endif
  564. #ifdef MON_THOUSANDS_SEP
  565. case MON_THOUSANDS_SEP:
  566. #endif
  567. #ifdef MON_GROUPING
  568. case MON_GROUPING:
  569. #endif
  570. #ifdef POSITIVE_SIGN
  571. case POSITIVE_SIGN:
  572. #endif
  573. #ifdef NEGATIVE_SIGN
  574. case NEGATIVE_SIGN:
  575. #endif
  576. #ifdef INT_FRAC_DIGITS
  577. case INT_FRAC_DIGITS:
  578. #endif
  579. #ifdef FRAC_DIGITS
  580. case FRAC_DIGITS:
  581. #endif
  582. #ifdef P_CS_PRECEDES
  583. case P_CS_PRECEDES:
  584. #endif
  585. #ifdef P_SEP_BY_SPACE
  586. case P_SEP_BY_SPACE:
  587. #endif
  588. #ifdef N_CS_PRECEDES
  589. case N_CS_PRECEDES:
  590. #endif
  591. #ifdef N_SEP_BY_SPACE
  592. case N_SEP_BY_SPACE:
  593. #endif
  594. #ifdef P_SIGN_POSN
  595. case P_SIGN_POSN:
  596. #endif
  597. #ifdef N_SIGN_POSN
  598. case N_SIGN_POSN:
  599. #endif
  600. #ifdef DECIMAL_POINT
  601. case DECIMAL_POINT:
  602. #elif defined(RADIXCHAR)
  603. case RADIXCHAR:
  604. #endif
  605. #ifdef THOUSANDS_SEP
  606. case THOUSANDS_SEP:
  607. #elif defined(THOUSEP)
  608. case THOUSEP:
  609. #endif
  610. #ifdef GROUPING
  611. case GROUPING:
  612. #endif
  613. #ifdef YESEXPR
  614. case YESEXPR:
  615. #endif
  616. #ifdef NOEXPR
  617. case NOEXPR:
  618. #endif
  619. #ifdef YESSTR
  620. case YESSTR:
  621. #endif
  622. #ifdef NOSTR
  623. case NOSTR:
  624. #endif
  625. #ifdef CODESET
  626. case CODESET:
  627. #endif
  628. break;
  629. default:
  630. php_error_docref(NULL, E_WARNING, "Item '" ZEND_LONG_FMT "' is not valid", item);
  631. RETURN_FALSE;
  632. }
  633. /* }}} */
  634. value = nl_langinfo(item);
  635. if (value == NULL) {
  636. RETURN_FALSE;
  637. } else {
  638. RETURN_STRING(value);
  639. }
  640. }
  641. #endif
  642. /* }}} */
  643. /* {{{ proto int strcoll(string str1, string str2)
  644. Compares two strings using the current locale */
  645. PHP_FUNCTION(strcoll)
  646. {
  647. zend_string *s1, *s2;
  648. ZEND_PARSE_PARAMETERS_START(2, 2)
  649. Z_PARAM_STR(s1)
  650. Z_PARAM_STR(s2)
  651. ZEND_PARSE_PARAMETERS_END();
  652. RETURN_LONG(strcoll((const char *) ZSTR_VAL(s1),
  653. (const char *) ZSTR_VAL(s2)));
  654. }
  655. /* }}} */
  656. /* {{{ php_charmask
  657. * Fills a 256-byte bytemask with input. You can specify a range like 'a..z',
  658. * it needs to be incrementing.
  659. * Returns: FAILURE/SUCCESS whether the input was correct (i.e. no range errors)
  660. */
  661. static inline int php_charmask(const unsigned char *input, size_t len, char *mask)
  662. {
  663. const unsigned char *end;
  664. unsigned char c;
  665. int result = SUCCESS;
  666. memset(mask, 0, 256);
  667. for (end = input+len; input < end; input++) {
  668. c=*input;
  669. if ((input+3 < end) && input[1] == '.' && input[2] == '.'
  670. && input[3] >= c) {
  671. memset(mask+c, 1, input[3] - c + 1);
  672. input+=3;
  673. } else if ((input+1 < end) && input[0] == '.' && input[1] == '.') {
  674. /* Error, try to be as helpful as possible:
  675. (a range ending/starting with '.' won't be captured here) */
  676. if (end-len >= input) { /* there was no 'left' char */
  677. php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the left of '..'");
  678. result = FAILURE;
  679. continue;
  680. }
  681. if (input+2 >= end) { /* there is no 'right' char */
  682. php_error_docref(NULL, E_WARNING, "Invalid '..'-range, no character to the right of '..'");
  683. result = FAILURE;
  684. continue;
  685. }
  686. if (input[-1] > input[2]) { /* wrong order */
  687. php_error_docref(NULL, E_WARNING, "Invalid '..'-range, '..'-range needs to be incrementing");
  688. result = FAILURE;
  689. continue;
  690. }
  691. /* FIXME: better error (a..b..c is the only left possibility?) */
  692. php_error_docref(NULL, E_WARNING, "Invalid '..'-range");
  693. result = FAILURE;
  694. continue;
  695. } else {
  696. mask[c]=1;
  697. }
  698. }
  699. return result;
  700. }
  701. /* }}} */
  702. /* {{{ php_trim_int()
  703. * mode 1 : trim left
  704. * mode 2 : trim right
  705. * mode 3 : trim left and right
  706. * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
  707. */
  708. static zend_always_inline zend_string *php_trim_int(zend_string *str, char *what, size_t what_len, int mode)
  709. {
  710. const char *start = ZSTR_VAL(str);
  711. const char *end = start + ZSTR_LEN(str);
  712. char mask[256];
  713. if (what) {
  714. if (what_len == 1) {
  715. char p = *what;
  716. if (mode & 1) {
  717. while (start != end) {
  718. if (*start == p) {
  719. start++;
  720. } else {
  721. break;
  722. }
  723. }
  724. }
  725. if (mode & 2) {
  726. while (start != end) {
  727. if (*(end-1) == p) {
  728. end--;
  729. } else {
  730. break;
  731. }
  732. }
  733. }
  734. } else {
  735. php_charmask((unsigned char*)what, what_len, mask);
  736. if (mode & 1) {
  737. while (start != end) {
  738. if (mask[(unsigned char)*start]) {
  739. start++;
  740. } else {
  741. break;
  742. }
  743. }
  744. }
  745. if (mode & 2) {
  746. while (start != end) {
  747. if (mask[(unsigned char)*(end-1)]) {
  748. end--;
  749. } else {
  750. break;
  751. }
  752. }
  753. }
  754. }
  755. } else {
  756. if (mode & 1) {
  757. while (start != end) {
  758. unsigned char c = (unsigned char)*start;
  759. if (c <= ' ' &&
  760. (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
  761. start++;
  762. } else {
  763. break;
  764. }
  765. }
  766. }
  767. if (mode & 2) {
  768. while (start != end) {
  769. unsigned char c = (unsigned char)*(end-1);
  770. if (c <= ' ' &&
  771. (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')) {
  772. end--;
  773. } else {
  774. break;
  775. }
  776. }
  777. }
  778. }
  779. if (ZSTR_LEN(str) == end - start) {
  780. return zend_string_copy(str);
  781. } else if (end - start == 0) {
  782. return ZSTR_EMPTY_ALLOC();
  783. } else {
  784. return zend_string_init(start, end - start, 0);
  785. }
  786. }
  787. /* }}} */
  788. /* {{{ php_trim_int()
  789. * mode 1 : trim left
  790. * mode 2 : trim right
  791. * mode 3 : trim left and right
  792. * what indicates which chars are to be trimmed. NULL->default (' \t\n\r\v\0')
  793. */
  794. PHPAPI zend_string *php_trim(zend_string *str, char *what, size_t what_len, int mode)
  795. {
  796. return php_trim_int(str, what, what_len, mode);
  797. }
  798. /* }}} */
  799. /* {{{ php_do_trim
  800. * Base for trim(), rtrim() and ltrim() functions.
  801. */
  802. static zend_always_inline void php_do_trim(INTERNAL_FUNCTION_PARAMETERS, int mode)
  803. {
  804. zend_string *str;
  805. zend_string *what = NULL;
  806. ZEND_PARSE_PARAMETERS_START(1, 2)
  807. Z_PARAM_STR(str)
  808. Z_PARAM_OPTIONAL
  809. Z_PARAM_STR(what)
  810. ZEND_PARSE_PARAMETERS_END();
  811. ZVAL_STR(return_value, php_trim_int(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));
  812. }
  813. /* }}} */
  814. /* {{{ proto string trim(string str [, string character_mask])
  815. Strips whitespace from the beginning and end of a string */
  816. PHP_FUNCTION(trim)
  817. {
  818. php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 3);
  819. }
  820. /* }}} */
  821. /* {{{ proto string rtrim(string str [, string character_mask])
  822. Removes trailing whitespace */
  823. PHP_FUNCTION(rtrim)
  824. {
  825. php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 2);
  826. }
  827. /* }}} */
  828. /* {{{ proto string ltrim(string str [, string character_mask])
  829. Strips whitespace from the beginning of a string */
  830. PHP_FUNCTION(ltrim)
  831. {
  832. php_do_trim(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
  833. }
  834. /* }}} */
  835. /* {{{ proto string wordwrap(string str [, int width [, string break [, bool cut]]])
  836. Wraps buffer to selected number of characters using string break char */
  837. PHP_FUNCTION(wordwrap)
  838. {
  839. zend_string *text;
  840. char *breakchar = "\n";
  841. size_t newtextlen, chk, breakchar_len = 1;
  842. size_t alloced;
  843. zend_long current = 0, laststart = 0, lastspace = 0;
  844. zend_long linelength = 75;
  845. zend_bool docut = 0;
  846. zend_string *newtext;
  847. ZEND_PARSE_PARAMETERS_START(1, 4)
  848. Z_PARAM_STR(text)
  849. Z_PARAM_OPTIONAL
  850. Z_PARAM_LONG(linelength)
  851. Z_PARAM_STRING(breakchar, breakchar_len)
  852. Z_PARAM_BOOL(docut)
  853. ZEND_PARSE_PARAMETERS_END();
  854. if (ZSTR_LEN(text) == 0) {
  855. RETURN_EMPTY_STRING();
  856. }
  857. if (breakchar_len == 0) {
  858. zend_argument_value_error(3, "cannot be empty");
  859. RETURN_THROWS();
  860. }
  861. if (linelength == 0 && docut) {
  862. zend_argument_value_error(4, "cannot be true when argument #2 ($width) is 0");
  863. RETURN_THROWS();
  864. }
  865. /* Special case for a single-character break as it needs no
  866. additional storage space */
  867. if (breakchar_len == 1 && !docut) {
  868. newtext = zend_string_init(ZSTR_VAL(text), ZSTR_LEN(text), 0);
  869. laststart = lastspace = 0;
  870. for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
  871. if (ZSTR_VAL(text)[current] == breakchar[0]) {
  872. laststart = lastspace = current + 1;
  873. } else if (ZSTR_VAL(text)[current] == ' ') {
  874. if (current - laststart >= linelength) {
  875. ZSTR_VAL(newtext)[current] = breakchar[0];
  876. laststart = current + 1;
  877. }
  878. lastspace = current;
  879. } else if (current - laststart >= linelength && laststart != lastspace) {
  880. ZSTR_VAL(newtext)[lastspace] = breakchar[0];
  881. laststart = lastspace + 1;
  882. }
  883. }
  884. RETURN_NEW_STR(newtext);
  885. } else {
  886. /* Multiple character line break or forced cut */
  887. if (linelength > 0) {
  888. chk = (size_t)(ZSTR_LEN(text)/linelength + 1);
  889. newtext = zend_string_safe_alloc(chk, breakchar_len, ZSTR_LEN(text), 0);
  890. alloced = ZSTR_LEN(text) + chk * breakchar_len + 1;
  891. } else {
  892. chk = ZSTR_LEN(text);
  893. alloced = ZSTR_LEN(text) * (breakchar_len + 1) + 1;
  894. newtext = zend_string_safe_alloc(ZSTR_LEN(text), breakchar_len + 1, 0, 0);
  895. }
  896. /* now keep track of the actual new text length */
  897. newtextlen = 0;
  898. laststart = lastspace = 0;
  899. for (current = 0; current < (zend_long)ZSTR_LEN(text); current++) {
  900. if (chk == 0) {
  901. alloced += (size_t) (((ZSTR_LEN(text) - current + 1)/linelength + 1) * breakchar_len) + 1;
  902. newtext = zend_string_extend(newtext, alloced, 0);
  903. chk = (size_t) ((ZSTR_LEN(text) - current)/linelength) + 1;
  904. }
  905. /* when we hit an existing break, copy to new buffer, and
  906. * fix up laststart and lastspace */
  907. if (ZSTR_VAL(text)[current] == breakchar[0]
  908. && current + breakchar_len < ZSTR_LEN(text)
  909. && !strncmp(ZSTR_VAL(text) + current, breakchar, breakchar_len)) {
  910. memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart + breakchar_len);
  911. newtextlen += current - laststart + breakchar_len;
  912. current += breakchar_len - 1;
  913. laststart = lastspace = current + 1;
  914. chk--;
  915. }
  916. /* if it is a space, check if it is at the line boundary,
  917. * copy and insert a break, or just keep track of it */
  918. else if (ZSTR_VAL(text)[current] == ' ') {
  919. if (current - laststart >= linelength) {
  920. memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
  921. newtextlen += current - laststart;
  922. memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
  923. newtextlen += breakchar_len;
  924. laststart = current + 1;
  925. chk--;
  926. }
  927. lastspace = current;
  928. }
  929. /* if we are cutting, and we've accumulated enough
  930. * characters, and we haven't see a space for this line,
  931. * copy and insert a break. */
  932. else if (current - laststart >= linelength
  933. && docut && laststart >= lastspace) {
  934. memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
  935. newtextlen += current - laststart;
  936. memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
  937. newtextlen += breakchar_len;
  938. laststart = lastspace = current;
  939. chk--;
  940. }
  941. /* if the current word puts us over the linelength, copy
  942. * back up until the last space, insert a break, and move
  943. * up the laststart */
  944. else if (current - laststart >= linelength
  945. && laststart < lastspace) {
  946. memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, lastspace - laststart);
  947. newtextlen += lastspace - laststart;
  948. memcpy(ZSTR_VAL(newtext) + newtextlen, breakchar, breakchar_len);
  949. newtextlen += breakchar_len;
  950. laststart = lastspace = lastspace + 1;
  951. chk--;
  952. }
  953. }
  954. /* copy over any stragglers */
  955. if (laststart != current) {
  956. memcpy(ZSTR_VAL(newtext) + newtextlen, ZSTR_VAL(text) + laststart, current - laststart);
  957. newtextlen += current - laststart;
  958. }
  959. ZSTR_VAL(newtext)[newtextlen] = '\0';
  960. /* free unused memory */
  961. newtext = zend_string_truncate(newtext, newtextlen, 0);
  962. RETURN_NEW_STR(newtext);
  963. }
  964. }
  965. /* }}} */
  966. /* {{{ php_explode
  967. */
  968. PHPAPI void php_explode(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
  969. {
  970. const char *p1 = ZSTR_VAL(str);
  971. const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
  972. const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
  973. zval tmp;
  974. if (p2 == NULL) {
  975. ZVAL_STR_COPY(&tmp, str);
  976. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  977. } else {
  978. do {
  979. size_t l = p2 - p1;
  980. if (l == 0) {
  981. ZVAL_EMPTY_STRING(&tmp);
  982. } else if (l == 1) {
  983. ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR((zend_uchar)(*p1)));
  984. } else {
  985. ZVAL_STRINGL(&tmp, p1, p2 - p1);
  986. }
  987. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  988. p1 = p2 + ZSTR_LEN(delim);
  989. p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
  990. } while (p2 != NULL && --limit > 1);
  991. if (p1 <= endp) {
  992. ZVAL_STRINGL(&tmp, p1, endp - p1);
  993. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  994. }
  995. }
  996. }
  997. /* }}} */
  998. /* {{{ php_explode_negative_limit
  999. */
  1000. PHPAPI void php_explode_negative_limit(const zend_string *delim, zend_string *str, zval *return_value, zend_long limit)
  1001. {
  1002. #define EXPLODE_ALLOC_STEP 64
  1003. const char *p1 = ZSTR_VAL(str);
  1004. const char *endp = ZSTR_VAL(str) + ZSTR_LEN(str);
  1005. const char *p2 = php_memnstr(ZSTR_VAL(str), ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
  1006. zval tmp;
  1007. if (p2 == NULL) {
  1008. /*
  1009. do nothing since limit <= -1, thus if only one chunk - 1 + (limit) <= 0
  1010. by doing nothing we return empty array
  1011. */
  1012. } else {
  1013. size_t allocated = EXPLODE_ALLOC_STEP, found = 0;
  1014. zend_long i, to_return;
  1015. const char **positions = emalloc(allocated * sizeof(char *));
  1016. positions[found++] = p1;
  1017. do {
  1018. if (found >= allocated) {
  1019. allocated = found + EXPLODE_ALLOC_STEP;/* make sure we have enough memory */
  1020. positions = erealloc(positions, allocated*sizeof(char *));
  1021. }
  1022. positions[found++] = p1 = p2 + ZSTR_LEN(delim);
  1023. p2 = php_memnstr(p1, ZSTR_VAL(delim), ZSTR_LEN(delim), endp);
  1024. } while (p2 != NULL);
  1025. to_return = limit + found;
  1026. /* limit is at least -1 therefore no need of bounds checking : i will be always less than found */
  1027. for (i = 0; i < to_return; i++) { /* this checks also for to_return > 0 */
  1028. ZVAL_STRINGL(&tmp, positions[i], (positions[i+1] - ZSTR_LEN(delim)) - positions[i]);
  1029. zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
  1030. }
  1031. efree((void *)positions);
  1032. }
  1033. #undef EXPLODE_ALLOC_STEP
  1034. }
  1035. /* }}} */
  1036. /* {{{ proto array explode(string separator, string str [, int limit])
  1037. Splits a string on string separator and return array of components. If limit is positive only limit number of components is returned. If limit is negative all components except the last abs(limit) are returned. */
  1038. PHP_FUNCTION(explode)
  1039. {
  1040. zend_string *str, *delim;
  1041. zend_long limit = ZEND_LONG_MAX; /* No limit */
  1042. zval tmp;
  1043. ZEND_PARSE_PARAMETERS_START(2, 3)
  1044. Z_PARAM_STR(delim)
  1045. Z_PARAM_STR(str)
  1046. Z_PARAM_OPTIONAL
  1047. Z_PARAM_LONG(limit)
  1048. ZEND_PARSE_PARAMETERS_END();
  1049. if (ZSTR_LEN(delim) == 0) {
  1050. zend_argument_value_error(1, "cannot be empty");
  1051. RETURN_THROWS();
  1052. }
  1053. array_init(return_value);
  1054. if (ZSTR_LEN(str) == 0) {
  1055. if (limit >= 0) {
  1056. ZVAL_EMPTY_STRING(&tmp);
  1057. zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
  1058. }
  1059. return;
  1060. }
  1061. if (limit > 1) {
  1062. php_explode(delim, str, return_value, limit);
  1063. } else if (limit < 0) {
  1064. php_explode_negative_limit(delim, str, return_value, limit);
  1065. } else {
  1066. ZVAL_STR_COPY(&tmp, str);
  1067. zend_hash_index_add_new(Z_ARRVAL_P(return_value), 0, &tmp);
  1068. }
  1069. }
  1070. /* }}} */
  1071. /* {{{ proto string join([string glue,] array pieces)
  1072. An alias for implode */
  1073. /* }}} */
  1074. /* {{{ php_implode
  1075. */
  1076. PHPAPI void php_implode(const zend_string *glue, HashTable *pieces, zval *return_value)
  1077. {
  1078. zval *tmp;
  1079. int numelems;
  1080. zend_string *str;
  1081. char *cptr;
  1082. size_t len = 0;
  1083. struct {
  1084. zend_string *str;
  1085. zend_long lval;
  1086. } *strings, *ptr;
  1087. ALLOCA_FLAG(use_heap)
  1088. numelems = zend_hash_num_elements(pieces);
  1089. if (numelems == 0) {
  1090. RETURN_EMPTY_STRING();
  1091. } else if (numelems == 1) {
  1092. /* loop to search the first not undefined element... */
  1093. ZEND_HASH_FOREACH_VAL_IND(pieces, tmp) {
  1094. RETURN_STR(zval_get_string(tmp));
  1095. } ZEND_HASH_FOREACH_END();
  1096. }
  1097. ptr = strings = do_alloca((sizeof(*strings)) * numelems, use_heap);
  1098. ZEND_HASH_FOREACH_VAL_IND(pieces, tmp) {
  1099. if (EXPECTED(Z_TYPE_P(tmp) == IS_STRING)) {
  1100. ptr->str = Z_STR_P(tmp);
  1101. len += ZSTR_LEN(ptr->str);
  1102. ptr->lval = 0;
  1103. ptr++;
  1104. } else if (UNEXPECTED(Z_TYPE_P(tmp) == IS_LONG)) {
  1105. zend_long val = Z_LVAL_P(tmp);
  1106. ptr->str = NULL;
  1107. ptr->lval = val;
  1108. ptr++;
  1109. if (val <= 0) {
  1110. len++;
  1111. }
  1112. while (val) {
  1113. val /= 10;
  1114. len++;
  1115. }
  1116. } else {
  1117. ptr->str = zval_get_string_func(tmp);
  1118. len += ZSTR_LEN(ptr->str);
  1119. ptr->lval = 1;
  1120. ptr++;
  1121. }
  1122. } ZEND_HASH_FOREACH_END();
  1123. /* numelems can not be 0, we checked above */
  1124. str = zend_string_safe_alloc(numelems - 1, ZSTR_LEN(glue), len, 0);
  1125. cptr = ZSTR_VAL(str) + ZSTR_LEN(str);
  1126. *cptr = 0;
  1127. while (1) {
  1128. ptr--;
  1129. if (EXPECTED(ptr->str)) {
  1130. cptr -= ZSTR_LEN(ptr->str);
  1131. memcpy(cptr, ZSTR_VAL(ptr->str), ZSTR_LEN(ptr->str));
  1132. if (ptr->lval) {
  1133. zend_string_release_ex(ptr->str, 0);
  1134. }
  1135. } else {
  1136. char *oldPtr = cptr;
  1137. char oldVal = *cptr;
  1138. cptr = zend_print_long_to_buf(cptr, ptr->lval);
  1139. *oldPtr = oldVal;
  1140. }
  1141. if (ptr == strings) {
  1142. break;
  1143. }
  1144. cptr -= ZSTR_LEN(glue);
  1145. memcpy(cptr, ZSTR_VAL(glue), ZSTR_LEN(glue));
  1146. }
  1147. free_alloca(strings, use_heap);
  1148. RETURN_NEW_STR(str);
  1149. }
  1150. /* }}} */
  1151. /* {{{ proto string implode([string glue,] array pieces)
  1152. Joins array elements placing glue string between items and return one string */
  1153. PHP_FUNCTION(implode)
  1154. {
  1155. zend_string *arg1_str = NULL;
  1156. HashTable *arg1_array = NULL;
  1157. zend_array *pieces = NULL;
  1158. ZEND_PARSE_PARAMETERS_START(1, 2)
  1159. Z_PARAM_STR_OR_ARRAY_HT(arg1_str, arg1_array)
  1160. Z_PARAM_OPTIONAL
  1161. Z_PARAM_ARRAY_HT(pieces)
  1162. ZEND_PARSE_PARAMETERS_END();
  1163. if (pieces == NULL) {
  1164. if (arg1_array == NULL) {
  1165. zend_type_error("%s(): Argument #1 ($pieces) must be of type array, string given", get_active_function_name());
  1166. RETURN_THROWS();
  1167. }
  1168. arg1_str = ZSTR_EMPTY_ALLOC();
  1169. pieces = arg1_array;
  1170. } else {
  1171. if (arg1_str == NULL) {
  1172. zend_argument_type_error(1, "must be of type string, array given");
  1173. RETURN_THROWS();
  1174. }
  1175. }
  1176. php_implode(arg1_str, pieces, return_value);
  1177. }
  1178. /* }}} */
  1179. #define STRTOK_TABLE(p) BG(strtok_table)[(unsigned char) *p]
  1180. /* {{{ proto string|false strtok([string str,] string token)
  1181. Tokenize a string */
  1182. PHP_FUNCTION(strtok)
  1183. {
  1184. zend_string *str, *tok = NULL;
  1185. char *token;
  1186. char *token_end;
  1187. char *p;
  1188. char *pe;
  1189. size_t skipped = 0;
  1190. ZEND_PARSE_PARAMETERS_START(1, 2)
  1191. Z_PARAM_STR(str)
  1192. Z_PARAM_OPTIONAL
  1193. Z_PARAM_STR(tok)
  1194. ZEND_PARSE_PARAMETERS_END();
  1195. if (ZEND_NUM_ARGS() == 1) {
  1196. tok = str;
  1197. } else {
  1198. zval_ptr_dtor(&BG(strtok_zval));
  1199. ZVAL_STRINGL(&BG(strtok_zval), ZSTR_VAL(str), ZSTR_LEN(str));
  1200. BG(strtok_last) = BG(strtok_string) = Z_STRVAL(BG(strtok_zval));
  1201. BG(strtok_len) = ZSTR_LEN(str);
  1202. }
  1203. p = BG(strtok_last); /* Where we start to search */
  1204. pe = BG(strtok_string) + BG(strtok_len);
  1205. if (!p || p >= pe) {
  1206. RETURN_FALSE;
  1207. }
  1208. token = ZSTR_VAL(tok);
  1209. token_end = token + ZSTR_LEN(tok);
  1210. while (token < token_end) {
  1211. STRTOK_TABLE(token++) = 1;
  1212. }
  1213. /* Skip leading delimiters */
  1214. while (STRTOK_TABLE(p)) {
  1215. if (++p >= pe) {
  1216. /* no other chars left */
  1217. BG(strtok_last) = NULL;
  1218. RETVAL_FALSE;
  1219. goto restore;
  1220. }
  1221. skipped++;
  1222. }
  1223. /* We know at this place that *p is no delimiter, so skip it */
  1224. while (++p < pe) {
  1225. if (STRTOK_TABLE(p)) {
  1226. goto return_token;
  1227. }
  1228. }
  1229. if (p - BG(strtok_last)) {
  1230. return_token:
  1231. RETVAL_STRINGL(BG(strtok_last) + skipped, (p - BG(strtok_last)) - skipped);
  1232. BG(strtok_last) = p + 1;
  1233. } else {
  1234. RETVAL_FALSE;
  1235. BG(strtok_last) = NULL;
  1236. }
  1237. /* Restore table -- usually faster then memset'ing the table on every invocation */
  1238. restore:
  1239. token = ZSTR_VAL(tok);
  1240. while (token < token_end) {
  1241. STRTOK_TABLE(token++) = 0;
  1242. }
  1243. }
  1244. /* }}} */
  1245. /* {{{ php_strtoupper
  1246. */
  1247. PHPAPI char *php_strtoupper(char *s, size_t len)
  1248. {
  1249. unsigned char *c;
  1250. const unsigned char *e;
  1251. c = (unsigned char *)s;
  1252. e = (unsigned char *)c+len;
  1253. while (c < e) {
  1254. *c = toupper(*c);
  1255. c++;
  1256. }
  1257. return s;
  1258. }
  1259. /* }}} */
  1260. /* {{{ php_string_toupper
  1261. */
  1262. PHPAPI zend_string *php_string_toupper(zend_string *s)
  1263. {
  1264. unsigned char *c;
  1265. const unsigned char *e;
  1266. c = (unsigned char *)ZSTR_VAL(s);
  1267. e = c + ZSTR_LEN(s);
  1268. while (c < e) {
  1269. if (islower(*c)) {
  1270. register unsigned char *r;
  1271. zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
  1272. if (c != (unsigned char*)ZSTR_VAL(s)) {
  1273. memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
  1274. }
  1275. r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
  1276. while (c < e) {
  1277. *r = toupper(*c);
  1278. r++;
  1279. c++;
  1280. }
  1281. *r = '\0';
  1282. return res;
  1283. }
  1284. c++;
  1285. }
  1286. return zend_string_copy(s);
  1287. }
  1288. /* }}} */
  1289. /* {{{ proto string strtoupper(string str)
  1290. Makes a string uppercase */
  1291. PHP_FUNCTION(strtoupper)
  1292. {
  1293. zend_string *arg;
  1294. ZEND_PARSE_PARAMETERS_START(1, 1)
  1295. Z_PARAM_STR(arg)
  1296. ZEND_PARSE_PARAMETERS_END();
  1297. RETURN_STR(php_string_toupper(arg));
  1298. }
  1299. /* }}} */
  1300. /* {{{ php_strtolower
  1301. */
  1302. PHPAPI char *php_strtolower(char *s, size_t len)
  1303. {
  1304. unsigned char *c;
  1305. const unsigned char *e;
  1306. c = (unsigned char *)s;
  1307. e = c+len;
  1308. while (c < e) {
  1309. *c = tolower(*c);
  1310. c++;
  1311. }
  1312. return s;
  1313. }
  1314. /* }}} */
  1315. /* {{{ php_string_tolower
  1316. */
  1317. PHPAPI zend_string *php_string_tolower(zend_string *s)
  1318. {
  1319. unsigned char *c;
  1320. const unsigned char *e;
  1321. if (EXPECTED(!BG(ctype_string))) {
  1322. return zend_string_tolower(s);
  1323. } else {
  1324. c = (unsigned char *)ZSTR_VAL(s);
  1325. e = c + ZSTR_LEN(s);
  1326. while (c < e) {
  1327. if (isupper(*c)) {
  1328. register unsigned char *r;
  1329. zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);
  1330. if (c != (unsigned char*)ZSTR_VAL(s)) {
  1331. memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
  1332. }
  1333. r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
  1334. while (c < e) {
  1335. *r = tolower(*c);
  1336. r++;
  1337. c++;
  1338. }
  1339. *r = '\0';
  1340. return res;
  1341. }
  1342. c++;
  1343. }
  1344. return zend_string_copy(s);
  1345. }
  1346. }
  1347. /* }}} */
  1348. /* {{{ proto string strtolower(string str)
  1349. Makes a string lowercase */
  1350. PHP_FUNCTION(strtolower)
  1351. {
  1352. zend_string *str;
  1353. ZEND_PARSE_PARAMETERS_START(1, 1)
  1354. Z_PARAM_STR(str)
  1355. ZEND_PARSE_PARAMETERS_END();
  1356. RETURN_STR(php_string_tolower(str));
  1357. }
  1358. /* }}} */
  1359. /* {{{ php_basename
  1360. */
  1361. PHPAPI zend_string *php_basename(const char *s, size_t len, char *suffix, size_t suffix_len)
  1362. {
  1363. /* State 0 is directly after a directory separator (or at the start of the string).
  1364. * State 1 is everything else. */
  1365. int state = 0;
  1366. const char *basename_start = s;
  1367. const char *basename_end = s;
  1368. while (len > 0) {
  1369. int inc_len = (*s == '\0' ? 1 : php_mblen(s, len));
  1370. switch (inc_len) {
  1371. case 0:
  1372. goto quit_loop;
  1373. case 1:
  1374. #if defined(PHP_WIN32)
  1375. if (*s == '/' || *s == '\\') {
  1376. #else
  1377. if (*s == '/') {
  1378. #endif
  1379. if (state == 1) {
  1380. state = 0;
  1381. basename_end = s;
  1382. }
  1383. #if defined(PHP_WIN32)
  1384. /* Catch relative paths in c:file.txt style. They're not to confuse
  1385. with the NTFS streams. This part ensures also, that no drive
  1386. letter traversing happens. */
  1387. } else if ((*s == ':' && (s - basename_start == 1))) {
  1388. if (state == 0) {
  1389. basename_start = s;
  1390. state = 1;
  1391. } else {
  1392. basename_end = s;
  1393. state = 0;
  1394. }
  1395. #endif
  1396. } else {
  1397. if (state == 0) {
  1398. basename_start = s;
  1399. state = 1;
  1400. }
  1401. }
  1402. break;
  1403. default:
  1404. if (inc_len < 0) {
  1405. /* If character is invalid, treat it like other non-significant characters. */
  1406. inc_len = 1;
  1407. php_mb_reset();
  1408. }
  1409. if (state == 0) {
  1410. basename_start = s;
  1411. state = 1;
  1412. }
  1413. break;
  1414. }
  1415. s += inc_len;
  1416. len -= inc_len;
  1417. }
  1418. quit_loop:
  1419. if (state == 1) {
  1420. basename_end = s;
  1421. }
  1422. if (suffix != NULL && suffix_len < (size_t)(basename_end - basename_start) &&
  1423. memcmp(basename_end - suffix_len, suffix, suffix_len) == 0) {
  1424. basename_end -= suffix_len;
  1425. }
  1426. return zend_string_init(basename_start, basename_end - basename_start, 0);
  1427. }
  1428. /* }}} */
  1429. /* {{{ proto string basename(string path [, string suffix])
  1430. Returns the filename component of the path */
  1431. PHP_FUNCTION(basename)
  1432. {
  1433. char *string, *suffix = NULL;
  1434. size_t string_len, suffix_len = 0;
  1435. ZEND_PARSE_PARAMETERS_START(1, 2)
  1436. Z_PARAM_STRING(string, string_len)
  1437. Z_PARAM_OPTIONAL
  1438. Z_PARAM_STRING(suffix, suffix_len)
  1439. ZEND_PARSE_PARAMETERS_END();
  1440. RETURN_STR(php_basename(string, string_len, suffix, suffix_len));
  1441. }
  1442. /* }}} */
  1443. /* {{{ php_dirname
  1444. Returns directory name component of path */
  1445. PHPAPI size_t php_dirname(char *path, size_t len)
  1446. {
  1447. return zend_dirname(path, len);
  1448. }
  1449. /* }}} */
  1450. /* {{{ proto string dirname(string path[, int levels])
  1451. Returns the directory name component of the path */
  1452. PHP_FUNCTION(dirname)
  1453. {
  1454. char *str;
  1455. size_t str_len;
  1456. zend_string *ret;
  1457. zend_long levels = 1;
  1458. ZEND_PARSE_PARAMETERS_START(1, 2)
  1459. Z_PARAM_STRING(str, str_len)
  1460. Z_PARAM_OPTIONAL
  1461. Z_PARAM_LONG(levels)
  1462. ZEND_PARSE_PARAMETERS_END();
  1463. ret = zend_string_init(str, str_len, 0);
  1464. if (levels == 1) {
  1465. /* Default case */
  1466. #ifdef PHP_WIN32
  1467. ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len);
  1468. #else
  1469. ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len);
  1470. #endif
  1471. } else if (levels < 1) {
  1472. zend_argument_value_error(2, "must be greater than or equal to 1");
  1473. zend_string_efree(ret);
  1474. RETURN_THROWS();
  1475. } else {
  1476. /* Some levels up */
  1477. do {
  1478. #ifdef PHP_WIN32
  1479. ZSTR_LEN(ret) = php_win32_ioutil_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
  1480. #else
  1481. ZSTR_LEN(ret) = zend_dirname(ZSTR_VAL(ret), str_len = ZSTR_LEN(ret));
  1482. #endif
  1483. } while (ZSTR_LEN(ret) < str_len && --levels);
  1484. }
  1485. RETURN_NEW_STR(ret);
  1486. }
  1487. /* }}} */
  1488. /* {{{ proto array|string pathinfo(string path[, int options])
  1489. Returns information about a certain string */
  1490. PHP_FUNCTION(pathinfo)
  1491. {
  1492. zval tmp;
  1493. char *path, *dirname;
  1494. size_t path_len;
  1495. int have_basename;
  1496. zend_long opt = PHP_PATHINFO_ALL;
  1497. zend_string *ret = NULL;
  1498. ZEND_PARSE_PARAMETERS_START(1, 2)
  1499. Z_PARAM_STRING(path, path_len)
  1500. Z_PARAM_OPTIONAL
  1501. Z_PARAM_LONG(opt)
  1502. ZEND_PARSE_PARAMETERS_END();
  1503. have_basename = ((opt & PHP_PATHINFO_BASENAME) == PHP_PATHINFO_BASENAME);
  1504. array_init(&tmp);
  1505. if ((opt & PHP_PATHINFO_DIRNAME) == PHP_PATHINFO_DIRNAME) {
  1506. dirname = estrndup(path, path_len);
  1507. php_dirname(dirname, path_len);
  1508. if (*dirname) {
  1509. add_assoc_string(&tmp, "dirname", dirname);
  1510. }
  1511. efree(dirname);
  1512. }
  1513. if (have_basename) {
  1514. ret = php_basename(path, path_len, NULL, 0);
  1515. add_assoc_str(&tmp, "basename", zend_string_copy(ret));
  1516. }
  1517. if ((opt & PHP_PATHINFO_EXTENSION) == PHP_PATHINFO_EXTENSION) {
  1518. const char *p;
  1519. ptrdiff_t idx;
  1520. if (!have_basename) {
  1521. ret = php_basename(path, path_len, NULL, 0);
  1522. }
  1523. p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
  1524. if (p) {
  1525. idx = p - ZSTR_VAL(ret);
  1526. add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
  1527. }
  1528. }
  1529. if ((opt & PHP_PATHINFO_FILENAME) == PHP_PATHINFO_FILENAME) {
  1530. const char *p;
  1531. ptrdiff_t idx;
  1532. /* Have we already looked up the basename? */
  1533. if (!have_basename && !ret) {
  1534. ret = php_basename(path, path_len, NULL, 0);
  1535. }
  1536. p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
  1537. idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
  1538. add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
  1539. }
  1540. if (ret) {
  1541. zend_string_release_ex(ret, 0);
  1542. }
  1543. if (opt == PHP_PATHINFO_ALL) {
  1544. ZVAL_COPY_VALUE(return_value, &tmp);
  1545. } else {
  1546. zval *element;
  1547. if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
  1548. ZVAL_COPY_DEREF(return_value, element);
  1549. } else {
  1550. ZVAL_EMPTY_STRING(return_value);
  1551. }
  1552. zval_ptr_dtor(&tmp);
  1553. }
  1554. }
  1555. /* }}} */
  1556. /* {{{ php_stristr
  1557. case insensitive strstr */
  1558. PHPAPI char *php_stristr(char *s, char *t, size_t s_len, size_t t_len)
  1559. {
  1560. php_strtolower(s, s_len);
  1561. php_strtolower(t, t_len);
  1562. return (char*)php_memnstr(s, t, t_len, s + s_len);
  1563. }
  1564. /* }}} */
  1565. /* {{{ php_strspn
  1566. */
  1567. PHPAPI size_t php_strspn(char *s1, char *s2, char *s1_end, char *s2_end)
  1568. {
  1569. register const char *p = s1, *spanp;
  1570. register char c = *p;
  1571. cont:
  1572. for (spanp = s2; p != s1_end && spanp != s2_end;) {
  1573. if (*spanp++ == c) {
  1574. c = *(++p);
  1575. goto cont;
  1576. }
  1577. }
  1578. return (p - s1);
  1579. }
  1580. /* }}} */
  1581. /* {{{ php_strcspn
  1582. */
  1583. PHPAPI size_t php_strcspn(char *s1, char *s2, char *s1_end, char *s2_end)
  1584. {
  1585. register const char *p, *spanp;
  1586. register char c = *s1;
  1587. for (p = s1;;) {
  1588. spanp = s2;
  1589. do {
  1590. if (*spanp == c || p == s1_end) {
  1591. return p - s1;
  1592. }
  1593. } while (spanp++ < (s2_end - 1));
  1594. c = *++p;
  1595. }
  1596. /* NOTREACHED */
  1597. }
  1598. /* }}} */
  1599. /* {{{ proto string|false stristr(string haystack, string needle[, bool part])
  1600. Finds first occurrence of a string within another, case insensitive */
  1601. PHP_FUNCTION(stristr)
  1602. {
  1603. zend_string *haystack, *needle;
  1604. const char *found = NULL;
  1605. size_t found_offset;
  1606. char *haystack_dup;
  1607. char *orig_needle;
  1608. zend_bool part = 0;
  1609. ZEND_PARSE_PARAMETERS_START(2, 3)
  1610. Z_PARAM_STR(haystack)
  1611. Z_PARAM_STR(needle)
  1612. Z_PARAM_OPTIONAL
  1613. Z_PARAM_BOOL(part)
  1614. ZEND_PARSE_PARAMETERS_END();
  1615. haystack_dup = estrndup(ZSTR_VAL(haystack), ZSTR_LEN(haystack));
  1616. orig_needle = estrndup(ZSTR_VAL(needle), ZSTR_LEN(needle));
  1617. found = php_stristr(haystack_dup, orig_needle, ZSTR_LEN(haystack), ZSTR_LEN(needle));
  1618. efree(orig_needle);
  1619. if (found) {
  1620. found_offset = found - haystack_dup;
  1621. if (part) {
  1622. RETVAL_STRINGL(ZSTR_VAL(haystack), found_offset);
  1623. } else {
  1624. RETVAL_STRINGL(ZSTR_VAL(haystack) + found_offset, ZSTR_LEN(haystack) - found_offset);
  1625. }
  1626. } else {
  1627. RETVAL_FALSE;
  1628. }
  1629. efree(haystack_dup);
  1630. }
  1631. /* }}} */
  1632. /* {{{ proto string|false strstr(string haystack, string needle[, bool part])
  1633. Finds first occurrence of a string within another */
  1634. PHP_FUNCTION(strstr)
  1635. {
  1636. zend_string *haystack, *needle;
  1637. const char *found = NULL;
  1638. zend_long found_offset;
  1639. zend_bool part = 0;
  1640. ZEND_PARSE_PARAMETERS_START(2, 3)
  1641. Z_PARAM_STR(haystack)
  1642. Z_PARAM_STR(needle)
  1643. Z_PARAM_OPTIONAL
  1644. Z_PARAM_BOOL(part)
  1645. ZEND_PARSE_PARAMETERS_END();
  1646. found = php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
  1647. if (found) {
  1648. found_offset = found - ZSTR_VAL(haystack);
  1649. if (part) {
  1650. RETURN_STRINGL(ZSTR_VAL(haystack), found_offset);
  1651. } else {
  1652. RETURN_STRINGL(found, ZSTR_LEN(haystack) - found_offset);
  1653. }
  1654. }
  1655. RETURN_FALSE;
  1656. }
  1657. /* }}} */
  1658. /* {{{ proto bool str_contains(string haystack, string needle)
  1659. Checks if a string contains another */
  1660. PHP_FUNCTION(str_contains)
  1661. {
  1662. zend_string *haystack, *needle;
  1663. ZEND_PARSE_PARAMETERS_START(2, 2)
  1664. Z_PARAM_STR(haystack)
  1665. Z_PARAM_STR(needle)
  1666. ZEND_PARSE_PARAMETERS_END();
  1667. RETURN_BOOL(php_memnstr(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle), ZSTR_VAL(haystack) + ZSTR_LEN(haystack)));
  1668. }
  1669. /* }}} */
  1670. /* {{{ proto bool str_starts_with(string haystack, string needle)
  1671. Checks if haystack starts with needle */
  1672. PHP_FUNCTION(str_starts_with)
  1673. {
  1674. zend_string *haystack, *needle;
  1675. ZEND_PARSE_PARAMETERS_START(2, 2)
  1676. Z_PARAM_STR(haystack)
  1677. Z_PARAM_STR(needle)
  1678. ZEND_PARSE_PARAMETERS_END();
  1679. if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
  1680. RETURN_FALSE;
  1681. }
  1682. RETURN_BOOL(memcmp(ZSTR_VAL(haystack), ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
  1683. }
  1684. /* }}} */
  1685. /* {{{ proto bool str_ends_with(string haystack, string needle)
  1686. Checks if haystack ends with needle */
  1687. PHP_FUNCTION(str_ends_with)
  1688. {
  1689. zend_string *haystack, *needle;
  1690. ZEND_PARSE_PARAMETERS_START(2, 2)
  1691. Z_PARAM_STR(haystack)
  1692. Z_PARAM_STR(needle)
  1693. ZEND_PARSE_PARAMETERS_END();
  1694. if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
  1695. RETURN_FALSE;
  1696. }
  1697. RETURN_BOOL(memcmp(
  1698. ZSTR_VAL(haystack) + ZSTR_LEN(haystack) - ZSTR_LEN(needle),
  1699. ZSTR_VAL(needle), ZSTR_LEN(needle)) == 0);
  1700. }
  1701. /* }}} */
  1702. /* {{{ proto string strchr(string haystack, string needle)
  1703. An alias for strstr */
  1704. /* }}} */
  1705. /* {{{ proto int|false strpos(string haystack, string needle [, int offset])
  1706. Finds position of first occurrence of a string within another */
  1707. PHP_FUNCTION(strpos)
  1708. {
  1709. zend_string *haystack, *needle;
  1710. const char *found = NULL;
  1711. zend_long offset = 0;
  1712. ZEND_PARSE_PARAMETERS_START(2, 3)
  1713. Z_PARAM_STR(haystack)
  1714. Z_PARAM_STR(needle)
  1715. Z_PARAM_OPTIONAL
  1716. Z_PARAM_LONG(offset)
  1717. ZEND_PARSE_PARAMETERS_END();
  1718. if (offset < 0) {
  1719. offset += (zend_long)ZSTR_LEN(haystack);
  1720. }
  1721. if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
  1722. zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
  1723. RETURN_THROWS();
  1724. }
  1725. found = (char*)php_memnstr(ZSTR_VAL(haystack) + offset,
  1726. ZSTR_VAL(needle), ZSTR_LEN(needle),
  1727. ZSTR_VAL(haystack) + ZSTR_LEN(haystack));
  1728. if (found) {
  1729. RETURN_LONG(found - ZSTR_VAL(haystack));
  1730. } else {
  1731. RETURN_FALSE;
  1732. }
  1733. }
  1734. /* }}} */
  1735. /* {{{ proto int|false stripos(string haystack, string needle [, int offset])
  1736. Finds position of first occurrence of a string within another, case insensitive */
  1737. PHP_FUNCTION(stripos)
  1738. {
  1739. const char *found = NULL;
  1740. zend_string *haystack, *needle;
  1741. zend_long offset = 0;
  1742. zend_string *needle_dup = NULL, *haystack_dup;
  1743. ZEND_PARSE_PARAMETERS_START(2, 3)
  1744. Z_PARAM_STR(haystack)
  1745. Z_PARAM_STR(needle)
  1746. Z_PARAM_OPTIONAL
  1747. Z_PARAM_LONG(offset)
  1748. ZEND_PARSE_PARAMETERS_END();
  1749. if (offset < 0) {
  1750. offset += (zend_long)ZSTR_LEN(haystack);
  1751. }
  1752. if (offset < 0 || (size_t)offset > ZSTR_LEN(haystack)) {
  1753. zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
  1754. RETURN_THROWS();
  1755. }
  1756. if (ZSTR_LEN(needle) > ZSTR_LEN(haystack)) {
  1757. RETURN_FALSE;
  1758. }
  1759. haystack_dup = php_string_tolower(haystack);
  1760. needle_dup = php_string_tolower(needle);
  1761. found = (char*)php_memnstr(ZSTR_VAL(haystack_dup) + offset,
  1762. ZSTR_VAL(needle_dup), ZSTR_LEN(needle_dup), ZSTR_VAL(haystack_dup) + ZSTR_LEN(haystack));
  1763. if (found) {
  1764. RETVAL_LONG(found - ZSTR_VAL(haystack_dup));
  1765. } else {
  1766. RETVAL_FALSE;
  1767. }
  1768. zend_string_release_ex(haystack_dup, 0);
  1769. zend_string_release_ex(needle_dup, 0);
  1770. }
  1771. /* }}} */
  1772. /* {{{ proto int|false strrpos(string haystack, string needle [, int offset])
  1773. Finds position of last occurrence of a string within another string */
  1774. PHP_FUNCTION(strrpos)
  1775. {
  1776. zend_string *needle;
  1777. zend_string *haystack;
  1778. zend_long offset = 0;
  1779. const char *p, *e, *found;
  1780. ZEND_PARSE_PARAMETERS_START(2, 3)
  1781. Z_PARAM_STR(haystack)
  1782. Z_PARAM_STR(needle)
  1783. Z_PARAM_OPTIONAL
  1784. Z_PARAM_LONG(offset)
  1785. ZEND_PARSE_PARAMETERS_END();
  1786. if (offset >= 0) {
  1787. if ((size_t)offset > ZSTR_LEN(haystack)) {
  1788. zend_argument_value_error(3, "must be contained in argument #1 ($haystack)");
  1789. RETURN_THROWS();
  1790. }
  1791. p = ZSTR_VAL(ha

Large files files are truncated, but you can click here to view the full file