static inline void php_output_context_swap(php_output_context *context);
static inline void php_output_context_dtor(php_output_context *context);
-static inline int php_output_stack_pop(int discard, int shutdown TSRMLS_DC);
+static inline int php_output_stack_pop(int flags TSRMLS_DC);
static int php_output_stack_apply_op(void *h, void *c);
static int php_output_stack_apply_clean(void *h, void *c);
Finalizes the most recent output handler at pops it off the stack if the handler is removable */
PHPAPI int php_output_end(TSRMLS_D)
{
- if (php_output_stack_pop(0, 0 TSRMLS_CC)) {
+ if (php_output_stack_pop(PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
return SUCCESS;
}
return FAILURE;
Finalizes all output handlers and ends output buffering without regard whether a handler is removable */
PHPAPI void php_output_end_all(TSRMLS_D)
{
- while (OG(active) && php_output_stack_pop(0, 1 TSRMLS_CC));
+ while (OG(active) && php_output_stack_pop(PHP_OUTPUT_POP_FORCE TSRMLS_CC));
}
/* }}} */
Discards the most recent output handlers buffer and pops it off the stack if the handler is removable */
PHPAPI int php_output_discard(TSRMLS_D)
{
- if (php_output_stack_pop(1, 0 TSRMLS_CC)) {
+ if (php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_TRY TSRMLS_CC)) {
return SUCCESS;
}
return FAILURE;
PHPAPI void php_output_discard_all(TSRMLS_D)
{
while (OG(active)) {
- php_output_stack_pop(1, 1 TSRMLS_CC);
+ php_output_stack_pop(PHP_OUTPUT_POP_DISCARD|PHP_OUTPUT_POP_FORCE TSRMLS_CC);
}
}
/* }}} */
{
zval *handler_name = NULL;
php_output_handler *handler = NULL;
- php_output_handler_context_func_t *internal = NULL;
+ php_output_handler_alias_ctor_t *alias = NULL;
switch (Z_TYPE_P(output_handler)) {
case IS_NULL:
+ handler = php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
break;
case IS_STRING:
case IS_UNICODE:
- if (Z_UNILEN_P(output_handler) && (internal = php_output_handler_alias(output_handler TSRMLS_CC))) {
- return php_output_handler_create_internal(output_handler, *internal, chunk_size, flags TSRMLS_CC);
+ if (Z_UNILEN_P(output_handler) && (alias = php_output_handler_alias(output_handler TSRMLS_CC))) {
+ handler = (*alias)(output_handler TSRMLS_CC);
+ break;
}
default:
MAKE_STD_ZVAL(handler_name);
handler->func.user = output_handler;
}
zval_ptr_dtor(&handler_name);
- return handler;
}
- return php_output_handler_create_internal(OG(default_output_handler_name), php_output_handler_default_func, chunk_size, flags TSRMLS_CC);
+ return handler;
}
/* }}} */
PHPAPI int php_output_handler_conflict(zval *handler_new, zval *handler_set TSRMLS_DC)
{
if (php_output_handler_started(handler_set TSRMLS_CC)) {
- php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%v' conflicts with '%v'", Z_UNIVAL_P(handler_new), Z_UNIVAL_P(handler_set));
+ if (zend_binary_zval_strcmp(handler_new, handler_set)) {
+ php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%v' conflicts with '%v'", Z_UNIVAL_P(handler_new), Z_UNIVAL_P(handler_set));
+ } else {
+ php_error_docref("ref.outcontrol" TSRMLS_CC, E_WARNING, "output handler '%v' cannot be used twice", Z_UNIVAL_P(handler_new));
+ }
return 1;
}
return 0;
/* {{{ php_output_handler_context_func_t php_output_handler_alias(zval *name TSRMLS_DC)
Get an internal output handler for a user handler if it exists */
-PHPAPI php_output_handler_context_func_t *php_output_handler_alias(zval *name TSRMLS_DC)
+PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(zval *name TSRMLS_DC)
{
- php_output_handler_context_func_t *func = NULL;
+ php_output_handler_alias_ctor_t *func = NULL;
zend_u_hash_find(&php_output_handler_aliases, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), (void *) &func);
return func;
}
/* }}} */
-/* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_context_func_t func TSRMLS_DC)
+/* {{{ SUCCESS|FAILURE php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC)
Registers an internal output handler as alias for a user handler */
-PHPAPI int php_output_handler_alias_register_ex(zval *name, php_output_handler_context_func_t func TSRMLS_DC)
+PHPAPI int php_output_handler_alias_register(zval *name, php_output_handler_alias_ctor_t func TSRMLS_DC)
{
if (!EG(current_module)) {
zend_error(E_ERROR, "Cannot register an output handler alias outside of MINIT");
return FAILURE;
}
- return zend_u_hash_update(&php_output_handler_aliases, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), &func, sizeof(php_output_handler_context_func_t *), NULL);
+ return zend_u_hash_update(&php_output_handler_aliases, Z_TYPE_P(name), Z_UNIVAL_P(name), Z_UNILEN_P(name), &func, sizeof(php_output_handler_alias_ctor_t *), NULL);
}
/* }}} */
case PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL:
*(int *) arg = OG(running)->level;
case PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE:
- OG(running)->flags &= ~PHP_OUTPUT_HANDLER_STDFLAGS;
+ OG(running)->flags &= ~(PHP_OUTPUT_HANDLER_REMOVABLE|PHP_OUTPUT_HANDLER_CLEANABLE);
return SUCCESS;
case PHP_OUTPUT_HANDLER_HOOK_DISABLE:
OG(running)->flags |= PHP_OUTPUT_HANDLER_DISABLED;
} else {
/* need to start? */
if (!(handler->flags & PHP_OUTPUT_HANDLER_STARTED)) {
- handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
op |= PHP_OUTPUT_HANDLER_START;
}
status = PHP_OUTPUT_HANDLER_FAILURE;
}
}
+ handler->flags |= PHP_OUTPUT_HANDLER_STARTED;
OG(running) = NULL;
}
}
/* }}} */
-/* {{{ static int php_output_stack_pop(int discard, int shutdown TSRMLS_DC)
- Pops an output handler off the stack, ignores whether the handler is removable if shutdown==1, discards the handlers output if discard==1 */
-static inline int php_output_stack_pop(int discard, int shutdown TSRMLS_DC)
+/* {{{ static int php_output_stack_pop(int flags TSRMLS_DC)
+ Pops an output handler off the stack */
+static inline int php_output_stack_pop(int flags TSRMLS_DC)
{
php_output_context context;
php_output_handler **current, *orphan = OG(active);
if (!orphan) {
- php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer. No buffer to %s", discard?"discard":"send", discard?"discard":"send");
+ if (!(flags & PHP_OUTPUT_POP_SILENT)) {
+ 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");
+ }
return 0;
- } else if (!shutdown && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
- php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %v (%d)", discard?"discard":"send", Z_UNIVAL_P(orphan->name), orphan->level);
+ } else if (!(flags & PHP_OUTPUT_POP_FORCE) && !(orphan->flags & PHP_OUTPUT_HANDLER_REMOVABLE)) {
+ if (!(flags & PHP_OUTPUT_POP_SILENT)) {
+ php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to %s buffer of %v (%d)", (flags&PHP_OUTPUT_POP_DISCARD)?"discard":"send", Z_UNIVAL_P(orphan->name), orphan->level);
+ }
return 0;
} else {
php_output_context_init(&context, PHP_OUTPUT_HANDLER_FINAL TSRMLS_CC);
context.op |= PHP_OUTPUT_HANDLER_START;
}
/* signal that we're cleaning up */
- if (discard) {
+ if (flags & PHP_OUTPUT_POP_DISCARD) {
context.op |= PHP_OUTPUT_HANDLER_CLEAN;
orphan->buffer.used = 0;
}
}
/* pass output along */
- if (context.out.data && context.out.used && !discard) {
+ if (context.out.data && context.out.used && !(flags & PHP_OUTPUT_POP_DISCARD)) {
php_output_write(context.out.data, context.out.used TSRMLS_CC);
}
#define PHP_OUTPUT_HANDLER_SUCCESS 1
#define PHP_OUTPUT_HANDLER_NO_DATA 2
+/* php_output_stack_pop() flags */
+#define PHP_OUTPUT_POP_TRY 0x000
+#define PHP_OUTPUT_POP_FORCE 0x001
+#define PHP_OUTPUT_POP_DISCARD 0x010
+#define PHP_OUTPUT_POP_SILENT 0x100
+
/* real global flags */
#define PHP_OUTPUT_IMPLICITFLUSH 0x01
#define PHP_OUTPUT_DISABLED 0x02
typedef void (*php_output_handler_func_t)(char *output, uint output_len, char **handled_output, uint *handled_output_len, int mode TSRMLS_DC);
/* new-style, opaque context callback */
typedef int (*php_output_handler_context_func_t)(void **handler_context, php_output_context *output_context);
+/* output handler context dtor */
+typedef void (*php_output_handler_context_dtor_t)(void *opaq TSRMLS_DC);
/* conflict check callback */
typedef int (*php_output_handler_conflict_check_t)(zval *handler_name TSRMLS_DC);
+/* ctor for aliases */
+typedef struct _php_output_handler *(*php_output_handler_alias_ctor_t)(zval *handler_name TSRMLS_DC);
typedef struct _php_output_handler {
zval *name;
PHPAPI int php_output_handler_conflict_register(zval *handler_name, php_output_handler_conflict_check_t check_func TSRMLS_DC);
PHPAPI int php_output_handler_reverse_conflict_register(zval *handler_name, php_output_handler_conflict_check_t check_func TSRMLS_DC);
-PHPAPI php_output_handler_context_func_t *php_output_handler_alias(zval *handler_name TSRMLS_DC);
-PHPAPI int php_output_handler_alias_register(zval *handler_name, php_output_handler_context_func_t func TSRMLS_DC);
+#define PHP_OUTPUT_CONFLICT_REGISTER(name, func) \
+{ \
+ zval tmp_z; \
+ char *tmp_s = (name); \
+ INIT_PZVAL(&tmp_z); \
+ ZVAL_ASCII_STRING(&tmp_z, tmp_s, ZSTR_DUPLICATE); \
+ php_output_handler_conflict_register(&tmp_z, func TSRMLS_CC); \
+ zval_dtor(&tmp_z); \
+}
+
+#define PHP_OUTPUT_CONFLICT(check_name, action) \
+{ \
+ char *tmp_s = (check_name); \
+ zval tmp_z; \
+ INIT_PZVAL(&tmp_z); \
+ ZVAL_ASCII_STRING(&tmp_z, tmp_s, ZSTR_DUPLICATE); \
+ if (php_output_handler_conflict(handler_name, &tmp_z TSRMLS_CC)) { \
+ zval_dtor(&tmp_z); \
+ action; \
+ } \
+ zval_dtor(&tmp_z); \
+}
+
+PHPAPI php_output_handler_alias_ctor_t *php_output_handler_alias(zval *handler_name TSRMLS_DC);
+PHPAPI int php_output_handler_alias_register(zval *handler_name, php_output_handler_alias_ctor_t func TSRMLS_DC);
+
+#define PHP_OUTPUT_ALIAS_REGISTER(name, func) \
+{ \
+ zval tmp_z; \
+ char *tmp_s = (name); \
+ INIT_PZVAL(&tmp_z); \
+ ZVAL_ASCII_STRING(&tmp_z, tmp_s, ZSTR_DUPLICATE); \
+ php_output_handler_alias_register(&tmp_z, func TSRMLS_CC); \
+ zval_dtor(&tmp_z); \
+}
END_EXTERN_C()