output.c 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2016 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Zeev Suraski <zeev@zend.com> |
  16. | Thies C. Arntzen <thies@thieso.net> |
  17. | Marcus Boerger <helly@php.net> |
  18. | New API: Michael Wallner <mike@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. /* $Id$ */
  22. #ifndef PHP_OUTPUT_DEBUG
  23. # define PHP_OUTPUT_DEBUG 0
  24. #endif
  25. #ifndef PHP_OUTPUT_NOINLINE
  26. # define PHP_OUTPUT_NOINLINE 0
  27. #endif
  28. #include "php.h"
  29. #include "ext/standard/head.h"
  30. #include "ext/standard/url_scanner_ex.h"
  31. #include "SAPI.h"
  32. #include "zend_stack.h"
  33. #include "php_output.h"
  34. PHPAPI ZEND_DECLARE_MODULE_GLOBALS(output);
  35. const char php_output_default_handler_name[sizeof("default output handler")] = "default output handler";
  36. const char php_output_devnull_handler_name[sizeof("null output handler")] = "null output handler";
  37. #if PHP_OUTPUT_NOINLINE || PHP_OUTPUT_DEBUG
  38. # undef inline
  39. # define inline
  40. #endif
  41. /* {{{ aliases, conflict and reverse conflict hash tables */
  42. static HashTable php_output_handler_aliases;
  43. static HashTable php_output_handler_conflicts;
  44. static HashTable php_output_handler_reverse_conflicts;
  45. /* }}} */
  46. /* {{{ forward declarations */
  47. static inline int php_output_lock_error(int op TSRMLS_DC);
  48. static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC);
  49. static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC);
  50. static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context);
  51. static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC);
  52. static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry);
  53. static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC);
  54. static inline void php_output_context_reset(php_output_context *context);
  55. static inline void php_output_context_swap(php_output_context *context);
  56. static inline void php_output_context_dtor(php_output_context *context);
  57. static inline int php_output_stack_pop(int flags TSRMLS_DC);
  58. static int php_output_stack_apply_op(void *h, void *c);
  59. static int php_output_stack_apply_clean(void *h, void *c);
  60. static int php_output_stack_apply_list(void *h, void *z);
  61. static int php_output_stack_apply_status(void *h, void *z);
  62. static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context);
  63. static int php_output_handler_default_func(void **handler_context, php_output_context *output_context);
  64. static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context);
  65. /* }}} */
  66. /* {{{ static void php_output_init_globals(zend_output_globals *G)
  67. * Initialize the module globals on MINIT */
  68. static inline void php_output_init_globals(zend_output_globals *G)
  69. {
  70. memset(G, 0, sizeof(*G));
  71. }
  72. /* }}} */
  73. /* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */
  74. static int php_output_stdout(const char *str, size_t str_len)
  75. {
  76. fwrite(str, 1, str_len, stdout);
  77. return str_len;
  78. }
  79. static int php_output_stderr(const char *str, size_t str_len)
  80. {
  81. fwrite(str, 1, str_len, stderr);
  82. /* See http://support.microsoft.com/kb/190351 */
  83. #ifdef PHP_WIN32
  84. fflush(stderr);
  85. #endif
  86. return str_len;
  87. }
  88. static int (*php_output_direct)(const char *str, size_t str_len) = php_output_stderr;
  89. /* }}} */
  90. /* {{{ void php_output_header(TSRMLS_D) */
  91. static void php_output_header(TSRMLS_D)
  92. {
  93. if (!SG(headers_sent)) {
  94. if (!OG(output_start_filename)) {
  95. if (zend_is_compiling(TSRMLS_C)) {
  96. OG(output_start_filename) = zend_get_compiled_filename(TSRMLS_C);
  97. OG(output_start_lineno) = zend_get_compiled_lineno(TSRMLS_C);
  98. } else if (zend_is_executing(TSRMLS_C)) {
  99. OG(output_start_filename) = zend_get_executed_filename(TSRMLS_C);
  100. OG(output_start_lineno) = zend_get_executed_lineno(TSRMLS_C);
  101. }
  102. #if PHP_OUTPUT_DEBUG
  103. fprintf(stderr, "!!! output started at: %s (%d)\n", OG(output_start_filename), OG(output_start_lineno));
  104. #endif
  105. }
  106. if (!php_header(TSRMLS_C)) {
  107. OG(flags) |= PHP_OUTPUT_DISABLED;
  108. }
  109. }
  110. }
  111. /* }}} */
  112. /* {{{ void php_output_startup(void)
  113. * Set up module globals and initalize the conflict and reverse conflict hash tables */
  114. PHPAPI void php_output_startup(void)
  115. {
  116. ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL);
  117. zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1);
  118. zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1);
  119. zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1);
  120. php_output_direct = php_output_stdout;
  121. }
  122. /* }}} */
  123. /* {{{ void php_output_shutdown(void)
  124. * Destroy module globals and the conflict and reverse conflict hash tables */
  125. PHPAPI void php_output_shutdown(void)
  126. {
  127. php_output_direct = php_output_stderr;
  128. zend_hash_destroy(&php_output_handler_aliases);
  129. zend_hash_destroy(&php_output_handler_conflicts);
  130. zend_hash_destroy(&php_output_handler_reverse_conflicts);
  131. }
  132. /* }}} */
  133. /* {{{ SUCCESS|FAILURE php_output_activate(TSRMLS_D)
  134. * Reset output globals and setup the output handler stack */
  135. PHPAPI int php_output_activate(TSRMLS_D)
  136. {
  137. #ifdef ZTS
  138. memset((*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)], 0, sizeof(zend_output_globals));
  139. #else
  140. memset(&output_globals, 0, sizeof(zend_output_globals));
  141. #endif
  142. zend_stack_init(&OG(handlers));
  143. OG(flags) |= PHP_OUTPUT_ACTIVATED;
  144. return SUCCESS;
  145. }
  146. /* }}} */
  147. /* {{{ void php_output_deactivate(TSRMLS_D)
  148. * Destroy the output handler stack */
  149. PHPAPI void php_output_deactivate(TSRMLS_D)
  150. {
  151. php_output_handler **handler = NULL;
  152. if ((OG(flags) & PHP_OUTPUT_ACTIVATED)) {
  153. php_output_header(TSRMLS_C);
  154. OG(flags) ^= PHP_OUTPUT_ACTIVATED;
  155. OG(active) = NULL;
  156. OG(running) = NULL;
  157. /* release all output handlers */
  158. if (OG(handlers).elements) {
  159. while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) {
  160. php_output_handler_free(handler TSRMLS_CC);
  161. zend_stack_del_top(&OG(handlers));
  162. }
  163. zend_stack_destroy(&OG(handlers));
  164. }
  165. }
  166. }
  167. /* }}} */
  168. /* {{{ void php_output_register_constants() */
  169. PHPAPI void php_output_register_constants(TSRMLS_D)
  170. {
  171. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START", PHP_OUTPUT_HANDLER_START, CONST_CS | CONST_PERSISTENT);
  172. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
  173. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH", PHP_OUTPUT_HANDLER_FLUSH, CONST_CS | CONST_PERSISTENT);
  174. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN", PHP_OUTPUT_HANDLER_CLEAN, CONST_CS | CONST_PERSISTENT);
  175. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
  176. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT", PHP_OUTPUT_HANDLER_WRITE, CONST_CS | CONST_PERSISTENT);
  177. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END", PHP_OUTPUT_HANDLER_FINAL, CONST_CS | CONST_PERSISTENT);
  178. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE", PHP_OUTPUT_HANDLER_CLEANABLE, CONST_CS | CONST_PERSISTENT);
  179. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE", PHP_OUTPUT_HANDLER_FLUSHABLE, CONST_CS | CONST_PERSISTENT);
  180. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE", PHP_OUTPUT_HANDLER_REMOVABLE, CONST_CS | CONST_PERSISTENT);
  181. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS", PHP_OUTPUT_HANDLER_STDFLAGS, CONST_CS | CONST_PERSISTENT);
  182. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED", PHP_OUTPUT_HANDLER_STARTED, CONST_CS | CONST_PERSISTENT);
  183. REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED", PHP_OUTPUT_HANDLER_DISABLED, CONST_CS | CONST_PERSISTENT);
  184. }
  185. /* }}} */
  186. /* {{{ void php_output_set_status(int status TSRMLS_DC)
  187. * Used by SAPIs to disable output */
  188. PHPAPI void php_output_set_status(int status TSRMLS_DC)
  189. {
  190. OG(flags) = (OG(flags) & ~0xf) | (status & 0xf);
  191. }
  192. /* }}} */
  193. /* {{{ int php_output_get_status(TSRMLS_C)
  194. * Get output control status */
  195. PHPAPI int php_output_get_status(TSRMLS_D)
  196. {
  197. return (
  198. OG(flags)
  199. | (OG(active) ? PHP_OUTPUT_ACTIVE : 0)
  200. | (OG(running)? PHP_OUTPUT_LOCKED : 0)
  201. ) & 0xff;
  202. }
  203. /* }}} */
  204. /* {{{ int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
  205. * Unbuffered write */
  206. PHPAPI int php_output_write_unbuffered(const char *str, size_t len TSRMLS_DC)
  207. {
  208. #if PHP_DEBUG
  209. if (len > UINT_MAX) {
  210. php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
  211. "output will be truncated %lu => %lu",
  212. (unsigned long) len, (unsigned long) (len % UINT_MAX));
  213. }
  214. #endif
  215. if (OG(flags) & PHP_OUTPUT_DISABLED) {
  216. return 0;
  217. }
  218. if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
  219. return sapi_module.ub_write(str, len TSRMLS_CC);
  220. }
  221. return php_output_direct(str, len);
  222. }
  223. /* }}} */
  224. /* {{{ int php_output_write(const char *str, size_t len TSRMLS_DC)
  225. * Buffered write */
  226. PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC)
  227. {
  228. #if PHP_DEBUG
  229. if (len > UINT_MAX) {
  230. php_error(E_WARNING, "Attempt to output more than UINT_MAX bytes at once; "
  231. "output will be truncated %lu => %lu",
  232. (unsigned long) len, (unsigned long) (len % UINT_MAX));
  233. }
  234. #endif
  235. if (OG(flags) & PHP_OUTPUT_DISABLED) {
  236. return 0;
  237. }
  238. if (OG(flags) & PHP_OUTPUT_ACTIVATED) {
  239. php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC);
  240. return (int) len;
  241. }
  242. return php_output_direct(str, len);
  243. }
  244. /* }}} */
  245. /* {{{ SUCCESS|FAILURE php_output_flush(TSRMLS_D)
  246. * Flush the most recent output handlers buffer */
  247. PHPAPI int php_output_flush(TSRMLS_D)
  248. {
  249. php_output_context context;
  250. if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_FLUSHABLE)) {
  251. php_output_context_init(&context, PHP_OUTPUT_HANDLER_FLUSH TSRMLS_CC);
  252. php_output_handler_op(OG(active), &context);
  253. if (context.out.data && context.out.used) {
  254. zend_stack_del_top(&OG(handlers));
  255. php_output_write(context.out.data, context.out.used TSRMLS_CC);
  256. zend_stack_push(&OG(handlers), &OG(active), sizeof(php_output_handler *));
  257. }
  258. php_output_context_dtor(&context);
  259. return SUCCESS;
  260. }
  261. return FAILURE;
  262. }
  263. /* }}} */
  264. /* {{{ void php_output_flush_all(TSRMLS_C)
  265. * Flush all output buffers subsequently */
  266. PHPAPI void php_output_flush_all(TSRMLS_D)
  267. {
  268. if (OG(active)) {
  269. php_output_op(PHP_OUTPUT_HANDLER_FLUSH, NULL, 0 TSRMLS_CC);
  270. }
  271. }
  272. /* }}} */
  273. /* {{{ SUCCESS|FAILURE php_output_clean(TSRMLS_D)
  274. * Cleans the most recent output handlers buffer if the handler is cleanable */
  275. PHPAPI int php_output_clean(TSRMLS_D)
  276. {
  277. php_output_context context;
  278. if (OG(active) && (OG(active)->flags & PHP_OUTPUT_HANDLER_CLEANABLE)) {
  279. php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
  280. php_output_handler_op(OG(active), &context);
  281. php_output_context_dtor(&context);
  282. return SUCCESS;
  283. }
  284. return FAILURE;
  285. }
  286. /* }}} */
  287. /* {{{ void php_output_clean_all(TSRMLS_D)
  288. * Cleans all output handler buffers, without regard whether the handler is cleanable */
  289. PHPAPI void php_output_clean_all(TSRMLS_D)
  290. {
  291. php_output_context context;
  292. if (OG(active)) {
  293. php_output_context_init(&context, PHP_OUTPUT_HANDLER_CLEAN TSRMLS_CC);
  294. zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_clean, &context);
  295. }
  296. }
  297. /* {{{ SUCCESS|FAILURE php_output_end(TSRMLS_D)
  298. * Finalizes the most recent output handler at pops it off the stack if the handler is removable */
  299. PHPAPI int php_output_end(TSRMLS_D)
  300. {
  301. if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
  302. return SUCCESS;
  303. }
  304. return FAILURE;
  305. }
  306. /* }}} */
  307. /* {{{ void php_output_end_all(TSRMLS_D)
  308. * Finalizes all output handlers and ends output buffering without regard whether a handler is removable */
  309. PHPAPI void php_output_end_all(TSRMLS_D)
  310. {
  311. while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC));
  312. }
  313. /* }}} */
  314. /* {{{ SUCCESS|FAILURE php_output_discard(TSRMLS_D)
  315. * Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */
  316. PHPAPI int php_output_discard(TSRMLS_D)
  317. {
  318. if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
  319. return SUCCESS;
  320. }
  321. return FAILURE;
  322. }
  323. /* }}} */
  324. /* {{{ void php_output_discard_all(TSRMLS_D)
  325. * Discard all output handlers and buffers without regard whether a handler is removable */
  326. PHPAPI void php_output_discard_all(TSRMLS_D)
  327. {
  328. while (OG(active)) {
  329. php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC);
  330. }
  331. }
  332. /* }}} */
  333. /* {{{ int php_output_get_level(TSRMLS_D)
  334. * Get output buffering level, ie. how many output handlers the stack contains */
  335. PHPAPI int php_output_get_level(TSRMLS_D)
  336. {
  337. return OG(active) ? zend_stack_count(&OG(handlers)) : 0;
  338. }
  339. /* }}} */
  340. /* {{{ SUCCESS|FAILURE php_output_get_contents(zval *z TSRMLS_DC)
  341. * Get the contents of the active output handlers buffer */
  342. PHPAPI int php_output_get_contents(zval *p TSRMLS_DC)
  343. {
  344. if (OG(active)) {
  345. ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used, 1);
  346. return SUCCESS;
  347. } else {
  348. ZVAL_NULL(p);
  349. return FAILURE;
  350. }
  351. }
  352. /* {{{ SUCCESS|FAILURE php_output_get_length(zval *z TSRMLS_DC)
  353. * Get the length of the active output handlers buffer */
  354. PHPAPI int php_output_get_length(zval *p TSRMLS_DC)
  355. {
  356. if (OG(active)) {
  357. ZVAL_LONG(p, OG(active)->buffer.used);
  358. return SUCCESS;
  359. } else {
  360. ZVAL_NULL(p);
  361. return FAILURE;
  362. }
  363. }
  364. /* }}} */
  365. /* {{{ php_output_handler* php_output_get_active_handler(TSRMLS_D)
  366. * Get active output handler */
  367. PHPAPI php_output_handler* php_output_get_active_handler(TSRMLS_D)
  368. {
  369. return OG(active);
  370. }
  371. /* }}} */
  372. /* {{{ SUCCESS|FAILURE php_output_handler_start_default(TSRMLS_D)
  373. * Start a "default output handler" */
  374. PHPAPI int php_output_start_default(TSRMLS_D)
  375. {
  376. php_output_handler *handler;
  377. handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, 0, PHP_OUTPUT_HANDLER_STDFLAGS TSRMLS_CC);
  378. if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
  379. return SUCCESS;
  380. }
  381. php_output_handler_free(&handler TSRMLS_CC);
  382. return FAILURE;
  383. }
  384. /* }}} */
  385. /* {{{ SUCCESS|FAILURE php_output_handler_start_devnull(TSRMLS_D)
  386. * Start a "null output handler" */
  387. PHPAPI int php_output_start_devnull(TSRMLS_D)
  388. {
  389. php_output_handler *handler;
  390. handler = php_output_handler_create_internal(ZEND_STRL(php_output_devnull_handler_name), php_output_handler_devnull_func, PHP_OUTPUT_HANDLER_DEFAULT_SIZE, 0 TSRMLS_CC);
  391. if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
  392. return SUCCESS;
  393. }
  394. php_output_handler_free(&handler TSRMLS_CC);
  395. return FAILURE;
  396. }
  397. /* }}} */
  398. /* {{{ SUCCESS|FAILURE php_output_start_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
  399. * Start a user level output handler */
  400. PHPAPI int php_output_start_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
  401. {
  402. php_output_handler *handler;
  403. if (output_handler) {
  404. handler = php_output_handler_create_user(output_handler, chunk_size, flags TSRMLS_CC);
  405. } else {
  406. handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
  407. }
  408. if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
  409. return SUCCESS;
  410. }
  411. php_output_handler_free(&handler TSRMLS_CC);
  412. return FAILURE;
  413. }
  414. /* }}} */
  415. /* {{{ SUCCESS|FAILURE php_output_start_internal(zval *name, php_output_handler_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
  416. * Start an internal output handler that does not have to maintain a non-global state */
  417. PHPAPI int php_output_start_internal(const char *name, size_t name_len, php_output_handler_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
  418. {
  419. php_output_handler *handler;
  420. handler = php_output_handler_create_internal(name, name_len, php_output_handler_compat_func, chunk_size, flags TSRMLS_CC);
  421. php_output_handler_set_context(handler, output_handler, NULL TSRMLS_CC);
  422. if (SUCCESS == php_output_handler_start(handler TSRMLS_CC)) {
  423. return SUCCESS;
  424. }
  425. php_output_handler_free(&handler TSRMLS_CC);
  426. return FAILURE;
  427. }
  428. /* }}} */
  429. /* {{{ php_output_handler *php_output_handler_create_user(zval *handler, size_t chunk_size, int flags TSRMLS_DC)
  430. * Create a user level output handler */
  431. PHPAPI php_output_handler *php_output_handler_create_user(zval *output_handler, size_t chunk_size, int flags TSRMLS_DC)
  432. {
  433. char *handler_name = NULL, *error = NULL;
  434. php_output_handler *handler = NULL;
  435. php_output_handler_alias_ctor_t *alias = NULL;
  436. php_output_handler_user_func_t *user = NULL;
  437. switch (Z_TYPE_P(output_handler)) {
  438. case IS_NULL:
  439. handler = php_output_handler_create_internal(ZEND_STRL(php_output_default_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
  440. break;
  441. case IS_STRING:
  442. if (Z_STRLEN_P(output_handler) && (alias = php_output_handler_alias(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler) TSRMLS_CC))) {
  443. handler = (*alias)(Z_STRVAL_P(output_handler), Z_STRLEN_P(output_handler), chunk_size, flags TSRMLS_CC);
  444. break;
  445. }
  446. default:
  447. user = ecalloc(1, sizeof(php_output_handler_user_func_t));
  448. if (SUCCESS == zend_fcall_info_init(output_handler, 0, &user->fci, &user->fcc, &handler_name, &error TSRMLS_CC)) {
  449. handler = php_output_handler_init(handler_name, strlen(handler_name), chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_USER TSRMLS_CC);
  450. Z_ADDREF_P(output_handler);
  451. user->zoh = output_handler;
  452. handler->func.user = user;
  453. } else {
  454. efree(user);
  455. }
  456. if (error) {
  457. php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "%s", error);
  458. efree(error);
  459. }
  460. if (handler_name) {
  461. efree(handler_name);
  462. }
  463. }
  464. return handler;
  465. }
  466. /* }}} */
  467. /* {{{ php_output_handler *php_output_handler_create_internal(zval *name, php_output_handler_context_func_t handler, size_t chunk_size, int flags TSRMLS_DC)
  468. * Create an internal output handler that can maintain a non-global state */
  469. PHPAPI php_output_handler *php_output_handler_create_internal(const char *name, size_t name_len, php_output_handler_context_func_t output_handler, size_t chunk_size, int flags TSRMLS_DC)
  470. {
  471. php_output_handler *handler;
  472. handler = php_output_handler_init(name, name_len, chunk_size, (flags & ~0xf) | PHP_OUTPUT_HANDLER_INTERNAL TSRMLS_CC);
  473. handler->func.internal = output_handler;
  474. return handler;
  475. }
  476. /* }}} */
  477. /* {{{ void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
  478. * Set the context/state of an output handler. Calls the dtor of the previous context if there is one */
  479. PHPAPI void php_output_handler_set_context(php_output_handler *handler, void *opaq, void (*dtor)(void* TSRMLS_DC) TSRMLS_DC)
  480. {
  481. if (handler->dtor && handler->opaq) {
  482. handler->dtor(handler->opaq TSRMLS_CC);
  483. }
  484. handler->dtor = dtor;
  485. handler->opaq = opaq;
  486. }
  487. /* }}} */
  488. /* {{{ SUCCESS|FAILURE php_output_handler_start(php_output_handler *handler TSRMLS_DC)
  489. * Starts the set up output handler and pushes it on top of the stack. Checks for any conflicts regarding the output handler to start */
  490. PHPAPI int php_output_handler_start(php_output_handler *handler TSRMLS_DC)
  491. {
  492. HashPosition pos;
  493. HashTable *rconflicts;
  494. php_output_handler_conflict_check_t *conflict;
  495. if (php_output_lock_error(PHP_OUTPUT_HANDLER_START TSRMLS_CC) || !handler) {
  496. return FAILURE;
  497. }
  498. if (SUCCESS == zend_hash_find(&php_output_handler_conflicts, handler->name, handler->name_len+1, (void *) &conflict)) {
  499. if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
  500. return FAILURE;
  501. }
  502. }
  503. if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, handler->name, handler->name_len+1, (void *) &rconflicts)) {
  504. for (zend_hash_internal_pointer_reset_ex(rconflicts, &pos);
  505. zend_hash_get_current_data_ex(rconflicts, (void *) &conflict, &pos) == SUCCESS;
  506. zend_hash_move_forward_ex(rconflicts, &pos)
  507. ) {
  508. if (SUCCESS != (*conflict)(handler->name, handler->name_len TSRMLS_CC)) {
  509. return FAILURE;
  510. }
  511. }
  512. }
  513. /* zend_stack_push never returns SUCCESS but FAILURE or stack level */
  514. if (FAILURE == (handler->level = zend_stack_push(&OG(handlers), &handler, sizeof(php_output_handler *)))) {
  515. return FAILURE;
  516. }
  517. OG(active) = handler;
  518. return SUCCESS;
  519. }
  520. /* }}} */
  521. /* {{{ int php_output_handler_started(zval *name TSRMLS_DC)
  522. * Check whether a certain output handler is in use */
  523. PHPAPI int php_output_handler_started(const char *name, size_t name_len TSRMLS_DC)
  524. {
  525. php_output_handler ***handlers;
  526. int i, count = php_output_get_level(TSRMLS_C);
  527. if (count) {
  528. handlers = (php_output_handler ***) zend_stack_base(&OG(handlers));
  529. for (i = 0; i < count; ++i) {
  530. if (name_len == (*(handlers[i]))->name_len && !memcmp((*(handlers[i]))->name, name, name_len)) {
  531. return 1;
  532. }
  533. }
  534. }
  535. return 0;
  536. }
  537. /* }}} */
  538. /* {{{ int php_output_handler_conflict(zval *handler_new, zval *handler_old TSRMLS_DC)
  539. * Check whether a certain handler is in use and issue a warning that the new handler would conflict with the already used one */
  540. PHPAPI int php_output_handler_conflict(const char *handler_new, size_t handler_new_len, const char *handler_set, size_t handler_set_len TSRMLS_DC)
  541. {
  542. if (php_output_handler_started(handler_set, handler_set_len TSRMLS_CC)) {
  543. if (handler_new_len != handler_set_len || memcmp(handler_new, handler_set, handler_set_len)) {
  544. php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' conflicts with '%s'", handler_new, handler_set);
  545. } else {
  546. php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%s' cannot be used twice", handler_new);
  547. }
  548. return 1;
  549. }
  550. return 0;
  551. }
  552. /* }}} */
  553. /* {{{ SUCCESS|FAILURE php_output_handler_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
  554. * Register a conflict checking function on MINIT */
  555. PHPAPI int php_output_handler_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
  556. {
  557. if (!EG(current_module)) {
  558. zend_error(E_ERROR, "Cannot register an output handler conflict outside of MINIT");
  559. return FAILURE;
  560. }
  561. return zend_hash_update(&php_output_handler_conflicts, name, name_len+1, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
  562. }
  563. /* }}} */
  564. /* {{{ SUCCESS|FAILURE php_output_handler_reverse_conflict_register(zval *name, php_output_handler_conflict_check_t check_func TSRMLS_DC)
  565. * Register a reverse conflict checking function on MINIT */
  566. PHPAPI int php_output_handler_reverse_conflict_register(const char *name, size_t name_len, php_output_handler_conflict_check_t check_func TSRMLS_DC)
  567. {
  568. HashTable rev, *rev_ptr = NULL;
  569. if (!EG(current_module)) {
  570. zend_error(E_ERROR, "Cannot register a reverse output handler conflict outside of MINIT");
  571. return FAILURE;
  572. }
  573. if (SUCCESS == zend_hash_find(&php_output_handler_reverse_conflicts, name, name_len+1, (void *) &rev_ptr)) {
  574. return zend_hash_next_index_insert(rev_ptr, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL);
  575. } else {
  576. zend_hash_init(&rev, 1, NULL, NULL, 1);
  577. if (SUCCESS != zend_hash_next_index_insert(&rev, &check_func, sizeof(php_output_handler_conflict_check_t *), NULL)) {
  578. zend_hash_destroy(&rev);
  579. return FAILURE;
  580. }
  581. if (SUCCESS != zend_hash_update(&php_output_handler_reverse_conflicts, name, name_len+1, &rev, sizeof(HashTable), NULL)) {
  582. zend_hash_destroy(&rev);
  583. return FAILURE;
  584. }
  585. return SUCCESS;
  586. }
  587. }
  588. /* }}} */
  589. /* {{{ php_output_handler_alias_ctor_t php_output_handler_alias(zval *name TSRMLS_DC)
  590. * Get an internal output handler for a user handler if it exists */
  591. PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(const char *name, size_t name_len TSRMLS_DC)
  592. {
  593. php_output_handler_alias_ctor_t *func = NULL;
  594. zend_hash_find(&php_output_handler_aliases, name, name_len+1, (void *) &func);
  595. return func;
  596. }
  597. /* }}} */
  598. /* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC)
  599. * Registers an internal output handler as alias for a user handler */
  600. PHPAPI int php_output_handler_alias_register(const char *name, size_t name_len, php_output_handler_alias_ctor_t func TSRMLS_DC)
  601. {
  602. if (!EG(current_module)) {
  603. zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT");
  604. return FAILURE;
  605. }
  606. return zend_hash_update(&php_output_handler_aliases, name, name_len+1, &func, sizeof(php_output_handler_alias_ctor_t *), NULL);
  607. }
  608. /* }}} */
  609. /* {{{ SUCCESS|FAILURE php_output_handler_hook(php_output_handler_hook_t type, void *arg TSMRLS_DC)
  610. * Output handler hook for output handler functions to check/modify the current handlers abilities */
  611. PHPAPI int php_output_handler_hook(php_output_handler_hook_t type, void *arg TSRMLS_DC)
  612. {
  613. if (OG(running)) {
  614. switch (type) {
  615. case PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ:
  616. *(void ***) arg = &OG(running)->opaq;
  617. return SUCCESS;
  618. case PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS:
  619. *(int *) arg = OG(running)->flags;
  620. return SUCCESS;
  621. case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL:
  622. *(int *) arg = OG(running)->level;
  623. return SUCCESS;
  624. case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE:
  625. OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE);
  626. return SUCCESS;
  627. case PHP_OUTPUT_HANDLER_HOOK_DISABLE:
  628. OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED;
  629. return SUCCESS;
  630. default:
  631. break;
  632. }
  633. }
  634. return FAILURE;
  635. }
  636. /* }}} */
  637. /* {{{ void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
  638. * Destroy an output handler */
  639. PHPAPI void php_output_handler_dtor(php_output_handler *handler TSRMLS_DC)
  640. {
  641. STR_FREE(handler->name);
  642. STR_FREE(handler->buffer.data);
  643. if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
  644. zval_ptr_dtor(&handler->func.user->zoh);
  645. efree(handler->func.user);
  646. }
  647. if (handler->dtor && handler->opaq) {
  648. handler->dtor(handler->opaq TSRMLS_CC);
  649. }
  650. memset(handler, 0, sizeof(*handler));
  651. }
  652. /* }}} */
  653. /* {{{ void php_output_handler_free(php_output_handler **handler TSMRLS_DC)
  654. * Destroy and free an output handler */
  655. PHPAPI void php_output_handler_free(php_output_handler **h TSRMLS_DC)
  656. {
  657. if (*h) {
  658. php_output_handler_dtor(*h TSRMLS_CC);
  659. efree(*h);
  660. *h = NULL;
  661. }
  662. }
  663. /* }}} */
  664. /* void php_output_set_implicit_flush(int enabled TSRMLS_DC)
  665. * Enable or disable implicit flush */
  666. PHPAPI void php_output_set_implicit_flush(int flush TSRMLS_DC)
  667. {
  668. if (flush) {
  669. OG(flags) |= PHP_OUTPUT_IMPLICITFLUSH;
  670. } else {
  671. OG(flags) &= ~PHP_OUTPUT_IMPLICITFLUSH;
  672. }
  673. }
  674. /* }}} */
  675. /* {{{ char *php_output_get_start_filename(TSRMLS_D)
  676. * Get the file name where output has started */
  677. PHPAPI const char *php_output_get_start_filename(TSRMLS_D)
  678. {
  679. return OG(output_start_filename);
  680. }
  681. /* }}} */
  682. /* {{{ int php_output_get_start_lineno(TSRMLS_D)
  683. * Get the line number where output has started */
  684. PHPAPI int php_output_get_start_lineno(TSRMLS_D)
  685. {
  686. return OG(output_start_lineno);
  687. }
  688. /* }}} */
  689. /* {{{ static int php_output_lock_error(int op TSRMLS_DC)
  690. * Checks whether an unallowed operation is attempted from within the output handler and issues a fatal error */
  691. static inline int php_output_lock_error(int op TSRMLS_DC)
  692. {
  693. /* if there's no ob active, ob has been stopped */
  694. if (op && OG(active) && OG(running)) {
  695. /* fatal error */
  696. php_output_deactivate(TSRMLS_C);
  697. php_error_docref("ref.outcontrol" TSRMLS_CC, E_ERROR, "Cannot use output buffering in output buffering display handlers");
  698. return 1;
  699. }
  700. return 0;
  701. }
  702. /* }}} */
  703. /* {{{ static php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
  704. * Initialize a new output context */
  705. static inline php_output_context *php_output_context_init(php_output_context *context, int op TSRMLS_DC)
  706. {
  707. if (!context) {
  708. context = emalloc(sizeof(php_output_context));
  709. }
  710. memset(context, 0, sizeof(php_output_context));
  711. TSRMLS_SET_CTX(context->tsrm_ls);
  712. context->op = op;
  713. return context;
  714. }
  715. /* }}} */
  716. /* {{{ static void php_output_context_reset(php_output_context *context)
  717. * Reset an output context */
  718. static inline void php_output_context_reset(php_output_context *context)
  719. {
  720. int op = context->op;
  721. php_output_context_dtor(context);
  722. memset(context, 0, sizeof(php_output_context));
  723. context->op = op;
  724. }
  725. /* }}} */
  726. /* {{{ static void php_output_context_feed(php_output_context *context, char *, size_t, size_t)
  727. * Feed output contexts input buffer */
  728. static inline void php_output_context_feed(php_output_context *context, char *data, size_t size, size_t used, zend_bool free)
  729. {
  730. if (context->in.free && context->in.data) {
  731. efree(context->in.data);
  732. }
  733. context->in.data = data;
  734. context->in.used = used;
  735. context->in.free = free;
  736. context->in.size = size;
  737. }
  738. /* }}} */
  739. /* {{{ static void php_output_context_swap(php_output_context *context)
  740. * Swap output contexts buffers */
  741. static inline void php_output_context_swap(php_output_context *context)
  742. {
  743. if (context->in.free && context->in.data) {
  744. efree(context->in.data);
  745. }
  746. context->in.data = context->out.data;
  747. context->in.used = context->out.used;
  748. context->in.free = context->out.free;
  749. context->in.size = context->out.size;
  750. context->out.data = NULL;
  751. context->out.used = 0;
  752. context->out.free = 0;
  753. context->out.size = 0;
  754. }
  755. /* }}} */
  756. /* {{{ static void php_output_context_pass(php_output_context *context)
  757. * Pass input to output buffer */
  758. static inline void php_output_context_pass(php_output_context *context)
  759. {
  760. context->out.data = context->in.data;
  761. context->out.used = context->in.used;
  762. context->out.size = context->in.size;
  763. context->out.free = context->in.free;
  764. context->in.data = NULL;
  765. context->in.used = 0;
  766. context->in.free = 0;
  767. context->in.size = 0;
  768. }
  769. /* }}} */
  770. /* {{{ static void php_output_context_dtor(php_output_context *context)
  771. * Destroy the contents of an output context */
  772. static inline void php_output_context_dtor(php_output_context *context)
  773. {
  774. if (context->in.free && context->in.data) {
  775. efree(context->in.data);
  776. context->in.data = NULL;
  777. }
  778. if (context->out.free && context->out.data) {
  779. efree(context->out.data);
  780. context->out.data = NULL;
  781. }
  782. }
  783. /* }}} */
  784. /* {{{ static php_output_handler *php_output_handler_init(zval *name, size_t chunk_size, int flags TSRMLS_DC)
  785. * Allocates and initializes a php_output_handler structure */
  786. static inline php_output_handler *php_output_handler_init(const char *name, size_t name_len, size_t chunk_size, int flags TSRMLS_DC)
  787. {
  788. php_output_handler *handler;
  789. handler = ecalloc(1, sizeof(php_output_handler));
  790. handler->name = estrndup(name, name_len);
  791. handler->name_len = name_len;
  792. handler->size = chunk_size;
  793. handler->flags = flags;
  794. handler->buffer.size = PHP_OUTPUT_HANDLER_INITBUF_SIZE(chunk_size);
  795. handler->buffer.data = emalloc(handler->buffer.size);
  796. return handler;
  797. }
  798. /* }}} */
  799. /* {{{ static int php_output_handler_appen(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
  800. * Appends input to the output handlers buffer and indicates whether the buffer does not have to be processed by the output handler */
  801. static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf TSRMLS_DC)
  802. {
  803. if (buf->used) {
  804. OG(flags) |= PHP_OUTPUT_WRITTEN;
  805. /* store it away */
  806. if ((handler->buffer.size - handler->buffer.used) <= buf->used) {
  807. size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size);
  808. size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used));
  809. size_t grow_max = MAX(grow_int, grow_buf);
  810. handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);
  811. handler->buffer.size += grow_max;
  812. }
  813. memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
  814. handler->buffer.used += buf->used;
  815. /* chunked buffering */
  816. if (handler->size && (handler->buffer.used >= handler->size)) {
  817. /* store away errors and/or any intermediate output */
  818. return OG(running) ? 1 : 0;
  819. }
  820. }
  821. return 1;
  822. }
  823. /* }}} */
  824. /* {{{ static php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
  825. * Output handler operation dispatcher, applying context op to the php_output_handler handler */
  826. static inline php_output_handler_status_t php_output_handler_op(php_output_handler *handler, php_output_context *context)
  827. {
  828. php_output_handler_status_t status;
  829. int original_op = context->op;
  830. PHP_OUTPUT_TSRMLS(context);
  831. #if PHP_OUTPUT_DEBUG
  832. fprintf(stderr, ">>> op(%d, "
  833. "handler=%p, "
  834. "name=%s, "
  835. "flags=%d, "
  836. "buffer.data=%s, "
  837. "buffer.used=%zu, "
  838. "buffer.size=%zu, "
  839. "in.data=%s, "
  840. "in.used=%zu)\n",
  841. context->op,
  842. handler,
  843. handler->name,
  844. handler->flags,
  845. handler->buffer.used?handler->buffer.data:"",
  846. handler->buffer.used,
  847. handler->buffer.size,
  848. context->in.used?context->in.data:"",
  849. context->in.used
  850. );
  851. #endif
  852. if (php_output_lock_error(context->op TSRMLS_CC)) {
  853. /* fatal error */
  854. return PHP_OUTPUT_HANDLER_FAILURE;
  855. }
  856. /* storable? */
  857. if (php_output_handler_append(handler, &context->in TSRMLS_CC) && !context->op) {
  858. context->op = original_op;
  859. return PHP_OUTPUT_HANDLER_NO_DATA;
  860. } else {
  861. /* need to start? */
  862. if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) {
  863. context->op |= PHP_OUTPUT_HANDLER_START;
  864. }
  865. OG(running) = handler;
  866. if (handler->flags & PHP_OUTPUT_HANDLER_USER) {
  867. zval *retval = NULL, *ob_data, *ob_mode;
  868. MAKE_STD_ZVAL(ob_data);
  869. ZVAL_STRINGL(ob_data, handler->buffer.data, handler->buffer.used, 1);
  870. MAKE_STD_ZVAL(ob_mode);
  871. ZVAL_LONG(ob_mode, (long) context->op);
  872. zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 2, &ob_data, &ob_mode);
  873. #define PHP_OUTPUT_USER_SUCCESS(retval) (retval && !(Z_TYPE_P(retval) == IS_BOOL && Z_BVAL_P(retval)==0))
  874. if (SUCCESS == zend_fcall_info_call(&handler->func.user->fci, &handler->func.user->fcc, &retval, NULL TSRMLS_CC) && PHP_OUTPUT_USER_SUCCESS(retval)) {
  875. /* user handler may have returned TRUE */
  876. status = PHP_OUTPUT_HANDLER_NO_DATA;
  877. if (Z_TYPE_P(retval) != IS_BOOL) {
  878. convert_to_string_ex(&retval);
  879. if (Z_STRLEN_P(retval)) {
  880. context->out.data = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval));
  881. context->out.used = Z_STRLEN_P(retval);
  882. context->out.free = 1;
  883. status = PHP_OUTPUT_HANDLER_SUCCESS;
  884. }
  885. }
  886. } else {
  887. /* call failed, pass internal buffer along */
  888. status = PHP_OUTPUT_HANDLER_FAILURE;
  889. }
  890. zend_fcall_info_argn(&handler->func.user->fci TSRMLS_CC, 0);
  891. zval_ptr_dtor(&ob_data);
  892. zval_ptr_dtor(&ob_mode);
  893. if (retval) {
  894. zval_ptr_dtor(&retval);
  895. }
  896. } else {
  897. php_output_context_feed(context, handler->buffer.data, handler->buffer.size, handler->buffer.used, 0);
  898. if (SUCCESS == handler->func.internal(&handler->opaq, context)) {
  899. if (context->out.used) {
  900. status = PHP_OUTPUT_HANDLER_SUCCESS;
  901. } else {
  902. status = PHP_OUTPUT_HANDLER_NO_DATA;
  903. }
  904. } else {
  905. status = PHP_OUTPUT_HANDLER_FAILURE;
  906. }
  907. }
  908. handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
  909. OG(running) = NULL;
  910. }
  911. switch (status) {
  912. case PHP_OUTPUT_HANDLER_FAILURE:
  913. /* disable this handler */
  914. handler->flags |= PHP_OUTPUT_HANDLER_DISABLED;
  915. /* discard any output */
  916. if (context->out.data && context->out.free) {
  917. efree(context->out.data);
  918. }
  919. /* returns handlers buffer */
  920. context->out.data = handler->buffer.data;
  921. context->out.used = handler->buffer.used;
  922. context->out.free = 1;
  923. handler->buffer.data = NULL;
  924. handler->buffer.used = 0;
  925. handler->buffer.size = 0;
  926. break;
  927. case PHP_OUTPUT_HANDLER_NO_DATA:
  928. /* handler ate all */
  929. php_output_context_reset(context);
  930. /* no break */
  931. case PHP_OUTPUT_HANDLER_SUCCESS:
  932. /* no more buffered data */
  933. handler->buffer.used = 0;
  934. handler->flags |= PHP_OUTPUT_HANDLER_PROCESSED;
  935. break;
  936. }
  937. context->op = original_op;
  938. return status;
  939. }
  940. /* }}} */
  941. /* {{{ static void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
  942. * Output op dispatcher, passes input and output handlers output through the output handler stack until it gets written to the SAPI */
  943. static inline void php_output_op(int op, const char *str, size_t len TSRMLS_DC)
  944. {
  945. php_output_context context;
  946. php_output_handler **active;
  947. int obh_cnt;
  948. if (php_output_lock_error(op TSRMLS_CC)) {
  949. return;
  950. }
  951. php_output_context_init(&context, op TSRMLS_CC);
  952. /*
  953. * broken up for better performance:
  954. * - apply op to the one active handler; note that OG(active) might be popped off the stack on a flush
  955. * - or apply op to the handler stack
  956. */
  957. if (OG(active) && (obh_cnt = zend_stack_count(&OG(handlers)))) {
  958. context.in.data = (char *) str;
  959. context.in.used = len;
  960. if (obh_cnt > 1) {
  961. zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_TOPDOWN, php_output_stack_apply_op, &context);
  962. } else if ((SUCCESS == zend_stack_top(&OG(handlers), (void *) &active)) && (!((*active)->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
  963. php_output_handler_op(*active, &context);
  964. } else {
  965. php_output_context_pass(&context);
  966. }
  967. } else {
  968. context.out.data = (char *) str;
  969. context.out.used = len;
  970. }
  971. if (context.out.data && context.out.used) {
  972. php_output_header(TSRMLS_C);
  973. if (!(OG(flags) & PHP_OUTPUT_DISABLED)) {
  974. #if PHP_OUTPUT_DEBUG
  975. fprintf(stderr, "::: sapi_write('%s', %zu)\n", context.out.data, context.out.used);
  976. #endif
  977. sapi_module.ub_write(context.out.data, context.out.used TSRMLS_CC);
  978. if (OG(flags) & PHP_OUTPUT_IMPLICITFLUSH) {
  979. sapi_flush(TSRMLS_C);
  980. }
  981. OG(flags) |= PHP_OUTPUT_SENT;
  982. }
  983. }
  984. php_output_context_dtor(&context);
  985. }
  986. /* }}} */
  987. /* {{{ static int php_output_stack_apply_op(void *h, void *c)
  988. * Operation callback for the stack apply function */
  989. static int php_output_stack_apply_op(void *h, void *c)
  990. {
  991. int was_disabled;
  992. php_output_handler_status_t status;
  993. php_output_handler *handler = *(php_output_handler **) h;
  994. php_output_context *context = (php_output_context *) c;
  995. if ((was_disabled = (handler->flags & PHP_OUTPUT_HANDLER_DISABLED))) {
  996. status = PHP_OUTPUT_HANDLER_FAILURE;
  997. } else {
  998. status = php_output_handler_op(handler, context);
  999. }
  1000. /*
  1001. * handler ate all => break
  1002. * handler returned data or failed resp. is disabled => continue
  1003. */
  1004. switch (status) {
  1005. case PHP_OUTPUT_HANDLER_NO_DATA:
  1006. return 1;
  1007. case PHP_OUTPUT_HANDLER_SUCCESS:
  1008. /* swap contexts buffers, unless this is the last handler in the stack */
  1009. if (handler->level) {
  1010. php_output_context_swap(context);
  1011. }
  1012. return 0;
  1013. case PHP_OUTPUT_HANDLER_FAILURE:
  1014. default:
  1015. if (was_disabled) {
  1016. /* pass input along, if it's the last handler in the stack */
  1017. if (!handler->level) {
  1018. php_output_context_pass(context);
  1019. }
  1020. } else {
  1021. /* swap buffers, unless this is the last handler */
  1022. if (handler->level) {
  1023. php_output_context_swap(context);
  1024. }
  1025. }
  1026. return 0;
  1027. }
  1028. }
  1029. /* }}} */
  1030. /* {{{ static int php_output_stack_apply_clean(void *h, void *c)
  1031. * Clean callback for the stack apply function */
  1032. static int php_output_stack_apply_clean(void *h, void *c)
  1033. {
  1034. php_output_handler *handler = *(php_output_handler **) h;
  1035. php_output_context *context = (php_output_context *) c;
  1036. handler->buffer.used = 0;
  1037. php_output_handler_op(handler, context);
  1038. php_output_context_reset(context);
  1039. return 0;
  1040. }
  1041. /* }}} */
  1042. /* {{{ static int php_output_stack_apply_list(void *h, void *z)
  1043. * List callback for the stack apply function */
  1044. static int php_output_stack_apply_list(void *h, void *z)
  1045. {
  1046. php_output_handler *handler = *(php_output_handler **) h;
  1047. zval *array = (zval *) z;
  1048. add_next_index_stringl(array, handler->name, handler->name_len, 1);
  1049. return 0;
  1050. }
  1051. /* }}} */
  1052. /* {{{ static int php_output_stack_apply_status(void *h, void *z)
  1053. * Status callback for the stack apply function */
  1054. static int php_output_stack_apply_status(void *h, void *z)
  1055. {
  1056. php_output_handler *handler = *(php_output_handler **) h;
  1057. zval *array = (zval *) z;
  1058. add_next_index_zval(array, php_output_handler_status(handler, NULL));
  1059. return 0;
  1060. }
  1061. /* {{{ static zval *php_output_handler_status(php_output_handler *handler, zval *entry)
  1062. * Returns an array with the status of the output handler */
  1063. static inline zval *php_output_handler_status(php_output_handler *handler, zval *entry)
  1064. {
  1065. if (!entry) {
  1066. MAKE_STD_ZVAL(entry);
  1067. array_init(entry);
  1068. }
  1069. add_assoc_stringl(entry, "name", handler->name, handler->name_len, 1);
  1070. add_assoc_long(entry, "type", (long) (handler->flags & 0xf));
  1071. add_assoc_long(entry, "flags", (long) handler->flags);
  1072. add_assoc_long(entry, "level", (long) handler->level);
  1073. add_assoc_long(entry, "chunk_size", (long) handler->size);
  1074. add_assoc_long(entry, "buffer_size", (long) handler->buffer.size);
  1075. add_assoc_long(entry, "buffer_used", (long) handler->buffer.used);
  1076. return entry;
  1077. }
  1078. /* }}} */
  1079. /* {{{ static int php_output_stack_pop(int flags TSRMLS_DC)
  1080. * Pops an output handler off the stack */
  1081. static inline int php_output_stack_pop(int flags TSRMLS_DC)
  1082. {
  1083. php_output_context context;
  1084. php_output_handler **current, *orphan = OG(active);
  1085. if (!orphan) {
  1086. if (!(flags & PHP_OUTPUT_POP_SILENT)) {
  1087. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer. No buffer to %s", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send");
  1088. }
  1089. return 0;
  1090. } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
  1091. if (!(flags & PHP_OUTPUT_POP_SILENT)) {
  1092. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %s (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", orphan->name, orphan->level);
  1093. }
  1094. return 0;
  1095. } else {
  1096. php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC);
  1097. /* don't run the output handler if it's disabled */
  1098. if (!(orphan->flags & PHP_OUTPUT_HANDLER_DISABLED)) {
  1099. /* didn't it start yet? */
  1100. if (!(orphan->flags & PHP_OUTPUT_HANDLER_STARTED)) {
  1101. context.op |= PHP_OUTPUT_HANDLER_START;
  1102. }
  1103. /* signal that we're cleaning up */
  1104. if (flags & PHP_OUTPUT_POP_DISCARD) {
  1105. context.op |= PHP_OUTPUT_HANDLER_CLEAN;
  1106. }
  1107. php_output_handler_op(orphan, &context);
  1108. }
  1109. /* pop it off the stack */
  1110. zend_stack_del_top(&OG(handlers));
  1111. if (SUCCESS == zend_stack_top(&OG(handlers), (void *) &current)) {
  1112. OG(active) = *current;
  1113. } else {
  1114. OG(active) = NULL;
  1115. }
  1116. /* pass output along */
  1117. if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) {
  1118. php_output_write(context.out.data, context.out.used TSRMLS_CC);
  1119. }
  1120. /* destroy the handler (after write!) */
  1121. php_output_handler_free(&orphan TSRMLS_CC);
  1122. php_output_context_dtor(&context);
  1123. return 1;
  1124. }
  1125. }
  1126. /* }}} */
  1127. /* {{{ static SUCCESS|FAILURE php_output_handler_compat_func(void *ctx, php_output_context *)
  1128. * php_output_handler_context_func_t for php_output_handler_func_t output handlers */
  1129. static int php_output_handler_compat_func(void **handler_context, php_output_context *output_context)
  1130. {
  1131. php_output_handler_func_t func = *(php_output_handler_func_t *) handler_context;
  1132. PHP_OUTPUT_TSRMLS(output_context);
  1133. if (func) {
  1134. char *out_str = NULL;
  1135. uint out_len = 0;
  1136. func(output_context->in.data, output_context->in.used, &out_str, &out_len, output_context->op TSRMLS_CC);
  1137. if (out_str) {
  1138. output_context->out.data = out_str;
  1139. output_context->out.used = out_len;
  1140. output_context->out.free = 1;
  1141. } else {
  1142. php_output_context_pass(output_context);
  1143. }
  1144. return SUCCESS;
  1145. }
  1146. return FAILURE;
  1147. }
  1148. /* }}} */
  1149. /* {{{ static SUCCESS|FAILURE php_output_handler_default_func(void *ctx, php_output_context *)
  1150. * Default output handler */
  1151. static int php_output_handler_default_func(void **handler_context, php_output_context *output_context)
  1152. {
  1153. php_output_context_pass(output_context);
  1154. return SUCCESS;
  1155. }
  1156. /* }}} */
  1157. /* {{{ static SUCCESS|FAILURE php_output_handler_devnull_func(void *ctx, php_output_context *)
  1158. * Null output handler */
  1159. static int php_output_handler_devnull_func(void **handler_context, php_output_context *output_context)
  1160. {
  1161. return SUCCESS;
  1162. }
  1163. /* }}} */
  1164. /*
  1165. * USERLAND (nearly 1:1 of old output.c)
  1166. */
  1167. /* {{{ proto bool ob_start([string|array user_function [, int chunk_size [, int flags]]])
  1168. Turn on Output Buffering (specifying an optional output handler). */
  1169. PHP_FUNCTION(ob_start)
  1170. {
  1171. zval *output_handler = NULL;
  1172. long chunk_size = 0;
  1173. long flags = PHP_OUTPUT_HANDLER_STDFLAGS;
  1174. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z/ll", &output_handler, &chunk_size, &flags) == FAILURE) {
  1175. return;
  1176. }
  1177. if (chunk_size < 0) {
  1178. chunk_size = 0;
  1179. }
  1180. if (php_output_start_user(output_handler, chunk_size, flags TSRMLS_CC) == FAILURE) {
  1181. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to create buffer");
  1182. RETURN_FALSE;
  1183. }
  1184. RETURN_TRUE;
  1185. }
  1186. /* }}} */
  1187. /* {{{ proto bool ob_flush(void)
  1188. Flush (send) contents of the output buffer. The last buffer content is sent to next buffer */
  1189. PHP_FUNCTION(ob_flush)
  1190. {
  1191. if (zend_parse_parameters_none() == FAILURE) {
  1192. return;
  1193. }
  1194. if (!OG(active)) {
  1195. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer. No buffer to flush");
  1196. RETURN_FALSE;
  1197. }
  1198. if (SUCCESS != php_output_flush(TSRMLS_C)) {
  1199. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to flush buffer of %s (%d)", OG(active)->name, OG(active)->level);
  1200. RETURN_FALSE;
  1201. }
  1202. RETURN_TRUE;
  1203. }
  1204. /* }}} */
  1205. /* {{{ proto bool ob_clean(void)
  1206. Clean (delete) the current output buffer */
  1207. PHP_FUNCTION(ob_clean)
  1208. {
  1209. if (zend_parse_parameters_none() == FAILURE) {
  1210. return;
  1211. }
  1212. if (!OG(active)) {
  1213. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
  1214. RETURN_FALSE;
  1215. }
  1216. if (SUCCESS != php_output_clean(TSRMLS_C)) {
  1217. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
  1218. RETURN_FALSE;
  1219. }
  1220. RETURN_TRUE;
  1221. }
  1222. /* }}} */
  1223. /* {{{ proto bool ob_end_flush(void)
  1224. Flush (send) the output buffer, and delete current output buffer */
  1225. PHP_FUNCTION(ob_end_flush)
  1226. {
  1227. if (zend_parse_parameters_none() == FAILURE) {
  1228. return;
  1229. }
  1230. if (!OG(active)) {
  1231. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
  1232. RETURN_FALSE;
  1233. }
  1234. RETURN_BOOL(SUCCESS == php_output_end(TSRMLS_C));
  1235. }
  1236. /* }}} */
  1237. /* {{{ proto bool ob_end_clean(void)
  1238. Clean the output buffer, and delete current output buffer */
  1239. PHP_FUNCTION(ob_end_clean)
  1240. {
  1241. if (zend_parse_parameters_none() == FAILURE) {
  1242. return;
  1243. }
  1244. if (!OG(active)) {
  1245. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
  1246. RETURN_FALSE;
  1247. }
  1248. RETURN_BOOL(SUCCESS == php_output_discard(TSRMLS_C));
  1249. }
  1250. /* }}} */
  1251. /* {{{ proto bool ob_get_flush(void)
  1252. Get current buffer contents, flush (send) the output buffer, and delete current output buffer */
  1253. PHP_FUNCTION(ob_get_flush)
  1254. {
  1255. if (zend_parse_parameters_none() == FAILURE) {
  1256. return;
  1257. }
  1258. if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
  1259. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete and flush buffer. No buffer to delete or flush");
  1260. RETURN_FALSE;
  1261. }
  1262. if (SUCCESS != php_output_end(TSRMLS_C)) {
  1263. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
  1264. }
  1265. }
  1266. /* }}} */
  1267. /* {{{ proto bool ob_get_clean(void)
  1268. Get current buffer contents and delete current output buffer */
  1269. PHP_FUNCTION(ob_get_clean)
  1270. {
  1271. if (zend_parse_parameters_none() == FAILURE) {
  1272. return;
  1273. }
  1274. if(!OG(active)) {
  1275. RETURN_FALSE;
  1276. }
  1277. if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
  1278. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
  1279. RETURN_FALSE;
  1280. }
  1281. if (SUCCESS != php_output_discard(TSRMLS_C)) {
  1282. php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer of %s (%d)", OG(active)->name, OG(active)->level);
  1283. }
  1284. }
  1285. /* }}} */
  1286. /* {{{ proto string ob_get_contents(void)
  1287. Return the contents of the output buffer */
  1288. PHP_FUNCTION(ob_get_contents)
  1289. {
  1290. if (zend_parse_parameters_none() == FAILURE) {
  1291. return;
  1292. }
  1293. if (php_output_get_contents(return_value TSRMLS_CC) == FAILURE) {
  1294. RETURN_FALSE;
  1295. }
  1296. }
  1297. /* }}} */
  1298. /* {{{ proto int ob_get_level(void)
  1299. Return the nesting level of the output buffer */
  1300. PHP_FUNCTION(ob_get_level)
  1301. {
  1302. if (zend_parse_parameters_none() == FAILURE) {
  1303. return;
  1304. }
  1305. RETURN_LONG(php_output_get_level(TSRMLS_C));
  1306. }
  1307. /* }}} */
  1308. /* {{{ proto int ob_get_length(void)
  1309. Return the length of the output buffer */
  1310. PHP_FUNCTION(ob_get_length)
  1311. {
  1312. if (zend_parse_parameters_none() == FAILURE) {
  1313. return;
  1314. }
  1315. if (php_output_get_length(return_value TSRMLS_CC) == FAILURE) {
  1316. RETURN_FALSE;
  1317. }
  1318. }
  1319. /* }}} */
  1320. /* {{{ proto false|array ob_list_handlers()
  1321. List all output_buffers in an array */
  1322. PHP_FUNCTION(ob_list_handlers)
  1323. {
  1324. if (zend_parse_parameters_none() == FAILURE) {
  1325. return;
  1326. }
  1327. array_init(return_value);
  1328. if (!OG(active)) {
  1329. return;
  1330. }
  1331. zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_list, return_value);
  1332. }
  1333. /* }}} */
  1334. /* {{{ proto false|array ob_get_status([bool full_status])
  1335. Return the status of the active or all output buffers */
  1336. PHP_FUNCTION(ob_get_status)
  1337. {
  1338. zend_bool full_status = 0;
  1339. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &full_status) == FAILURE) {
  1340. return;
  1341. }
  1342. array_init(return_value);
  1343. if (!OG(active)) {
  1344. return;
  1345. }
  1346. if (full_status) {
  1347. zend_stack_apply_with_argument(&OG(handlers), ZEND_STACK_APPLY_BOTTOMUP, php_output_stack_apply_status, return_value);
  1348. } else {
  1349. php_output_handler_status(OG(active), return_value);
  1350. }
  1351. }
  1352. /* }}} */
  1353. /* {{{ proto void ob_implicit_flush([int flag])
  1354. Turn implicit flush on/off and is equivalent to calling flush() after every output call */
  1355. PHP_FUNCTION(ob_implicit_flush)
  1356. {
  1357. long flag = 1;
  1358. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flag) == FAILURE) {
  1359. return;
  1360. }
  1361. php_output_set_implicit_flush(flag TSRMLS_CC);
  1362. }
  1363. /* }}} */
  1364. /* {{{ proto bool output_reset_rewrite_vars(void)
  1365. Reset(clear) URL rewriter values */
  1366. PHP_FUNCTION(output_reset_rewrite_vars)
  1367. {
  1368. if (php_url_scanner_reset_vars(TSRMLS_C) == SUCCESS) {
  1369. RETURN_TRUE;
  1370. } else {
  1371. RETURN_FALSE;
  1372. }
  1373. }
  1374. /* }}} */
  1375. /* {{{ proto bool output_add_rewrite_var(string name, string value)
  1376. Add URL rewriter values */
  1377. PHP_FUNCTION(output_add_rewrite_var)
  1378. {
  1379. char *name, *value;
  1380. int name_len, value_len;
  1381. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
  1382. return;
  1383. }
  1384. if (php_url_scanner_add_var(name, name_len, value, value_len, 1 TSRMLS_CC) == SUCCESS) {
  1385. RETURN_TRUE;
  1386. } else {
  1387. RETURN_FALSE;
  1388. }
  1389. }
  1390. /* }}} */
  1391. /*
  1392. * Local variables:
  1393. * tab-width: 4
  1394. * c-basic-offset: 4
  1395. * End:
  1396. * vim600: sw=4 ts=4 fdm=marker
  1397. * vim<600: sw=4 ts=4
  1398. */