Coverage Report

Created: 2025-06-13 06:43

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