- gcc didn't like those changes
[m6w6/ext-http] / http_response_object.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #define HTTP_WANT_MAGIC
21 #include "php_http.h"
22
23 /* broken static properties in PHP 5.0 */
24 #if defined(ZEND_ENGINE_2) && !defined(WONKY)
25
26 #include "SAPI.h"
27 #include "php_ini.h"
28
29 #include "php_http_api.h"
30 #include "php_http_cache_api.h"
31 #include "php_http_exception_object.h"
32 #include "php_http_headers_api.h"
33 #include "php_http_response_object.h"
34 #include "php_http_send_api.h"
35
36
37 ZEND_EXTERN_MODULE_GLOBALS(http);
38
39 #define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n)
40 #define UPD_STATIC_PROP(t, n, v) UPD_STATIC_PROP_EX(http_response_object_ce, t, n, v)
41 #define SET_STATIC_PROP(n, v) SET_STATIC_PROP_EX(http_response_object_ce, n, v)
42 #define UPD_STATIC_STRL(n, v, l) UPD_STATIC_STRL_EX(http_response_object_ce, n, v, l)
43
44 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args)
45 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref)
46 #define HTTP_RESPONSE_ME(method, visibility) PHP_ME(HttpResponse, method, HTTP_ARGS(HttpResponse, method), visibility|ZEND_ACC_STATIC)
47 #define HTTP_RESPONSE_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpResponse, method))
48
49 HTTP_BEGIN_ARGS(setHeader, 2)
50 HTTP_ARG_VAL(name, 0)
51 HTTP_ARG_VAL(value, 0)
52 HTTP_ARG_VAL(replace, 0)
53 HTTP_END_ARGS;
54
55 HTTP_BEGIN_ARGS(getHeader, 0)
56 HTTP_ARG_VAL(name, 0)
57 HTTP_END_ARGS;
58
59 HTTP_EMPTY_ARGS(getETag, 0);
60 HTTP_BEGIN_ARGS(setETag, 1)
61 HTTP_ARG_VAL(etag, 0)
62 HTTP_END_ARGS;
63
64 HTTP_EMPTY_ARGS(getLastModified, 0);
65 HTTP_BEGIN_ARGS(setLastModified, 1)
66 HTTP_ARG_VAL(timestamp, 0)
67 HTTP_END_ARGS;
68
69 HTTP_EMPTY_ARGS(getCache, 0);
70 HTTP_BEGIN_ARGS(setCache, 1)
71 HTTP_ARG_VAL(cache, 0)
72 HTTP_END_ARGS;
73
74 HTTP_EMPTY_ARGS(getGzip, 0);
75 HTTP_BEGIN_ARGS(setGzip, 1)
76 HTTP_ARG_VAL(gzip, 0)
77 HTTP_END_ARGS;
78
79 HTTP_EMPTY_ARGS(getCacheControl, 0);
80 HTTP_BEGIN_ARGS(setCacheControl, 1)
81 HTTP_ARG_VAL(cache_control, 0)
82 HTTP_ARG_VAL(max_age, 0)
83 HTTP_END_ARGS;
84
85 HTTP_EMPTY_ARGS(getContentType, 0);
86 HTTP_BEGIN_ARGS(setContentType, 1)
87 HTTP_ARG_VAL(content_type, 0)
88 HTTP_END_ARGS;
89
90 HTTP_BEGIN_ARGS(guessContentType, 1)
91 HTTP_ARG_VAL(magic_file, 0)
92 HTTP_ARG_VAL(magic_mode, 0)
93 HTTP_END_ARGS;
94
95 HTTP_EMPTY_ARGS(getContentDisposition, 0);
96 HTTP_BEGIN_ARGS(setContentDisposition, 1)
97 HTTP_ARG_VAL(filename, 0)
98 HTTP_ARG_VAL(send_inline, 0)
99 HTTP_END_ARGS;
100
101 HTTP_EMPTY_ARGS(getThrottleDelay, 0);
102 HTTP_BEGIN_ARGS(setThrottleDelay, 1)
103 HTTP_ARG_VAL(seconds, 0)
104 HTTP_END_ARGS;
105
106 HTTP_EMPTY_ARGS(getBufferSize, 0);
107 HTTP_BEGIN_ARGS(setBufferSize, 1)
108 HTTP_ARG_VAL(bytes, 0)
109 HTTP_END_ARGS;
110
111 HTTP_EMPTY_ARGS(getData, 0);
112 HTTP_BEGIN_ARGS(setData, 1)
113 HTTP_ARG_VAL(data, 0)
114 HTTP_END_ARGS;
115
116 HTTP_EMPTY_ARGS(getStream, 0);
117 HTTP_BEGIN_ARGS(setStream, 1)
118 HTTP_ARG_VAL(stream, 0)
119 HTTP_END_ARGS;
120
121 HTTP_EMPTY_ARGS(getFile, 0);
122 HTTP_BEGIN_ARGS(setFile, 1)
123 HTTP_ARG_VAL(filepath, 0)
124 HTTP_END_ARGS;
125
126 HTTP_BEGIN_ARGS(send, 0)
127 HTTP_ARG_VAL(clean_ob, 0)
128 HTTP_END_ARGS;
129
130 HTTP_EMPTY_ARGS(capture, 0);
131
132 HTTP_BEGIN_ARGS(redirect, 0)
133 HTTP_ARG_VAL(url, 0)
134 HTTP_ARG_VAL(params, 0)
135 HTTP_ARG_VAL(session, 0)
136 HTTP_ARG_VAL(permanent, 0)
137 HTTP_END_ARGS;
138
139 HTTP_BEGIN_ARGS(status, 1)
140 HTTP_ARG_VAL(code, 0)
141 HTTP_END_ARGS;
142
143 HTTP_EMPTY_ARGS(getRequestHeaders, 0);
144 HTTP_EMPTY_ARGS(getRequestBody, 0);
145
146 #define http_response_object_declare_default_properties() _http_response_object_declare_default_properties(TSRMLS_C)
147 static inline void _http_response_object_declare_default_properties(TSRMLS_D);
148 #define http_grab_response_headers _http_grab_response_headers
149 static void _http_grab_response_headers(void *data, void *arg TSRMLS_DC);
150
151 zend_class_entry *http_response_object_ce;
152 zend_function_entry http_response_object_fe[] = {
153
154 HTTP_RESPONSE_ME(setHeader, ZEND_ACC_PUBLIC)
155 HTTP_RESPONSE_ME(getHeader, ZEND_ACC_PUBLIC)
156
157 HTTP_RESPONSE_ME(setETag, ZEND_ACC_PUBLIC)
158 HTTP_RESPONSE_ME(getETag, ZEND_ACC_PUBLIC)
159
160 HTTP_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC)
161 HTTP_RESPONSE_ME(getLastModified, ZEND_ACC_PUBLIC)
162
163 HTTP_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
164 HTTP_RESPONSE_ME(getContentDisposition, ZEND_ACC_PUBLIC)
165
166 HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
167 HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC)
168
169 HTTP_RESPONSE_ME(guessContentType, ZEND_ACC_PUBLIC)
170
171 HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC)
172 HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC)
173
174 HTTP_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC)
175 HTTP_RESPONSE_ME(getCacheControl, ZEND_ACC_PUBLIC)
176
177 HTTP_RESPONSE_ME(setGzip, ZEND_ACC_PUBLIC)
178 HTTP_RESPONSE_ME(getGzip, ZEND_ACC_PUBLIC)
179
180 HTTP_RESPONSE_ME(setThrottleDelay, ZEND_ACC_PUBLIC)
181 HTTP_RESPONSE_ME(getThrottleDelay, ZEND_ACC_PUBLIC)
182
183 HTTP_RESPONSE_ME(setBufferSize, ZEND_ACC_PUBLIC)
184 HTTP_RESPONSE_ME(getBufferSize, ZEND_ACC_PUBLIC)
185
186 HTTP_RESPONSE_ME(setData, ZEND_ACC_PUBLIC)
187 HTTP_RESPONSE_ME(getData, ZEND_ACC_PUBLIC)
188
189 HTTP_RESPONSE_ME(setFile, ZEND_ACC_PUBLIC)
190 HTTP_RESPONSE_ME(getFile, ZEND_ACC_PUBLIC)
191
192 HTTP_RESPONSE_ME(setStream, ZEND_ACC_PUBLIC)
193 HTTP_RESPONSE_ME(getStream, ZEND_ACC_PUBLIC)
194
195 HTTP_RESPONSE_ME(send, ZEND_ACC_PUBLIC)
196 HTTP_RESPONSE_ME(capture, ZEND_ACC_PUBLIC)
197
198 HTTP_RESPONSE_ALIAS(redirect, http_redirect)
199 HTTP_RESPONSE_ALIAS(status, http_send_status)
200 HTTP_RESPONSE_ALIAS(getRequestHeaders, http_get_request_headers)
201 HTTP_RESPONSE_ALIAS(getRequestBody, http_get_request_body)
202
203 EMPTY_FUNCTION_ENTRY
204 };
205
206 PHP_MINIT_FUNCTION(http_response_object)
207 {
208 HTTP_REGISTER_CLASS(HttpResponse, http_response_object, NULL, 0);
209 http_response_object_declare_default_properties();
210 return SUCCESS;
211 }
212
213 static inline void _http_response_object_declare_default_properties(TSRMLS_D)
214 {
215 zend_class_entry *ce = http_response_object_ce;
216
217 DCL_STATIC_PROP(PRIVATE, bool, sent, 0);
218 DCL_STATIC_PROP(PRIVATE, bool, catch, 0);
219 DCL_STATIC_PROP(PRIVATE, long, mode, -1);
220 DCL_STATIC_PROP(PRIVATE, long, stream, 0);
221 DCL_STATIC_PROP_N(PRIVATE, file);
222 DCL_STATIC_PROP_N(PRIVATE, data);
223 DCL_STATIC_PROP(PROTECTED, bool, cache, 0);
224 DCL_STATIC_PROP(PROTECTED, bool, gzip, 0);
225 DCL_STATIC_PROP_N(PROTECTED, eTag);
226 DCL_STATIC_PROP(PROTECTED, long, lastModified, 0);
227 DCL_STATIC_PROP_N(PROTECTED, cacheControl);
228 DCL_STATIC_PROP_N(PROTECTED, contentType);
229 DCL_STATIC_PROP_N(PROTECTED, contentDisposition);
230 DCL_STATIC_PROP(PROTECTED, long, bufferSize, HTTP_SENDBUF_SIZE);
231 DCL_STATIC_PROP(PROTECTED, double, throttleDelay, 0.0);
232
233 #ifndef WONKY
234 DCL_CONST(long, "REDIRECT", HTTP_REDIRECT);
235 DCL_CONST(long, "REDIRECT_PERM", HTTP_REDIRECT_PERM);
236 DCL_CONST(long, "REDIRECT_POST", HTTP_REDIRECT_POST);
237 DCL_CONST(long, "REDIRECT_TEMP", HTTP_REDIRECT_TEMP);
238 #endif /* WONKY */
239 }
240
241 static void _http_grab_response_headers(void *data, void *arg TSRMLS_DC)
242 {
243 phpstr_appendl(PHPSTR(arg), ((sapi_header_struct *)data)->header);
244 phpstr_appends(PHPSTR(arg), HTTP_CRLF);
245 }
246
247 /* ### USERLAND ### */
248
249 /* {{{ proto static bool HttpResponse::setHeader(string name, mixed value[, bool replace = true])
250 *
251 * Send an HTTP header.
252 *
253 * Expects a string parameter containing the name of the header and a mixed
254 * parameter containing the value of the header, which will be converted to
255 * a string. Additionally accepts an optional boolean parameter, which
256 * specifies whether an existing header should be replaced. If the second
257 * parameter is unset no header with this name will be sent.
258 *
259 * Returns TRUE on success, or FALSE on failure.
260 *
261 * Throws HttpHeaderException if http.only_exceptions is TRUE.
262 */
263 PHP_METHOD(HttpResponse, setHeader)
264 {
265 zend_bool replace = 1;
266 char *name;
267 int name_len = 0;
268 zval *value = NULL, *orig = NULL;
269
270 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/!|b", &name, &name_len, &value, &replace)) {
271 RETURN_FALSE;
272 }
273 if (SG(headers_sent)) {
274 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot add another header when headers have already been sent");
275 RETURN_FALSE;
276 }
277 if (!name_len) {
278 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot send anonymous headers");
279 RETURN_FALSE;
280 }
281
282 /* delete header if value == null */
283 if (!value || Z_TYPE_P(value) == IS_NULL) {
284 RETURN_SUCCESS(http_send_header_ex(name, name_len, "", 0, replace, NULL));
285 }
286 /* send multiple header if replace is false and value is an array */
287 if (!replace && Z_TYPE_P(value) == IS_ARRAY) {
288 zval **data;
289 HashPosition pos;
290
291 FOREACH_VAL(pos, value, data) {
292 zval *orig = *data;
293
294 convert_to_string_ex(data);
295 if (SUCCESS != http_send_header_ex(name, name_len, Z_STRVAL_PP(data), Z_STRLEN_PP(data), 0, NULL)) {
296 if (orig != *data) {
297 zval_ptr_dtor(data);
298 }
299 RETURN_FALSE;
300 }
301 if (orig != *data) {
302 zval_ptr_dtor(data);
303 }
304 }
305 RETURN_TRUE;
306 }
307 /* send standard header */
308 orig = value;
309 convert_to_string_ex(&value);
310 RETVAL_SUCCESS(http_send_header_ex(name, name_len, Z_STRVAL_P(value), Z_STRLEN_P(value), replace, NULL));
311 if (orig != value) {
312 zval_ptr_dtor(&value);
313 }
314 }
315 /* }}} */
316
317 /* {{{ proto static mixed HttpResponse::getHeader([string name])
318 *
319 * Get header(s) about to be sent.
320 *
321 * Accepts a string as optional parameter which specifies the name of the
322 * header to read. If the parameter is empty or omitted, an associative array
323 * with all headers will be returned.
324 *
325 * Returns either a string containing the value of the header matching name,
326 * FALSE on failure, or an associative array with all headers.
327 */
328 PHP_METHOD(HttpResponse, getHeader)
329 {
330 char *name = NULL;
331 int name_len = 0;
332 phpstr headers;
333
334 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) {
335 RETURN_FALSE;
336 }
337
338 phpstr_init(&headers);
339 zend_llist_apply_with_argument(&SG(sapi_headers).headers, http_grab_response_headers, &headers TSRMLS_CC);
340 phpstr_fix(&headers);
341
342 if (name && name_len) {
343 zval **header;
344 HashTable headers_ht;
345
346 zend_hash_init(&headers_ht, sizeof(zval *), NULL, ZVAL_PTR_DTOR, 0);
347 if ( (SUCCESS == http_parse_headers_ex(PHPSTR_VAL(&headers), &headers_ht, 1)) &&
348 (SUCCESS == zend_hash_find(&headers_ht, name, name_len + 1, (void **) &header))) {
349 RETVAL_ZVAL(*header, 1, 0);
350 } else {
351 RETVAL_NULL();
352 }
353 zend_hash_destroy(&headers_ht);
354 } else {
355 array_init(return_value);
356 http_parse_headers_ex(PHPSTR_VAL(&headers), Z_ARRVAL_P(return_value), 1);
357 }
358
359 phpstr_dtor(&headers);
360 }
361 /* }}} */
362
363 /* {{{ proto static bool HttpResponse::setCache(bool cache)
364 *
365 * Whether it sould be attempted to cache the entitity.
366 * This will result in necessary caching headers and checks of clients
367 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
368 * matches a "304 Not Modified" status code will be issued.
369 *
370 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
371 * to something more appropriate than "no-cache"!
372 *
373 * Expects a boolean as parameter specifying whether caching should be attempted.
374 *
375 * Returns TRUE ons success, or FALSE on failure.
376 */
377 PHP_METHOD(HttpResponse, setCache)
378 {
379 zend_bool do_cache = 0;
380
381 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
382 RETURN_FALSE;
383 }
384
385 RETURN_SUCCESS(UPD_STATIC_PROP(bool, cache, do_cache));
386 }
387 /* }}} */
388
389 /* {{{ proto static bool HttpResponse::getCache()
390 *
391 * Get current caching setting.
392 *
393 * Returns TRUE if caching should be attempted, else FALSE.
394 */
395 PHP_METHOD(HttpResponse, getCache)
396 {
397 NO_ARGS;
398
399 IF_RETVAL_USED {
400 zval *cache_p, *cache = convert_to_type_ex(IS_BOOL, GET_STATIC_PROP(cache), &cache_p);
401
402 RETVAL_ZVAL(cache, 1, 0);
403
404 if (cache_p) {
405 zval_ptr_dtor(&cache_p);
406 }
407 }
408 }
409 /* }}}*/
410
411 /* {{{ proto static bool HttpResponse::setGzip(bool gzip)
412 *
413 * Enable on-thy-fly gzipping of the sent entity.
414 *
415 * Expects a boolean as parameter indicating if GZip compression should be enabled.
416 *
417 * Returns TRUE on success, or FALSE on failure.
418 */
419 PHP_METHOD(HttpResponse, setGzip)
420 {
421 zend_bool do_gzip = 0;
422
423 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
424 RETURN_FALSE;
425 }
426
427 RETURN_SUCCESS(UPD_STATIC_PROP(bool, gzip, do_gzip));
428 }
429 /* }}} */
430
431 /* {{{ proto static bool HttpResponse::getGzip()
432 *
433 * Get current gzipping setting.
434 *
435 * Returns TRUE if GZip compression is enabled, else FALSE.
436 */
437 PHP_METHOD(HttpResponse, getGzip)
438 {
439 NO_ARGS;
440
441 IF_RETVAL_USED {
442 zval *gzip_p, *gzip = convert_to_type_ex(IS_BOOL, GET_STATIC_PROP(gzip), &gzip_p);
443
444 RETVAL_ZVAL(gzip, 1, 0);
445
446 if (gzip_p) {
447 zval_ptr_dtor(&gzip_p);
448 }
449 }
450 }
451 /* }}} */
452
453 /* {{{ proto static bool HttpResponse::setCacheControl(string control[, int max_age = 0])
454 *
455 * Set a custom cache-control header, usually being "private" or "public";
456 * The max_age parameter controls how long the cache entry is valid on the client side.
457 *
458 * Expects a string parameter containing the primary cache control setting.
459 * Addtitionally accepts an int parameter specifying the max-age setting.
460 *
461 * Returns TRUE on success, or FALSE if control does not match one of
462 * "public" , "private" or "no-cache".
463 *
464 * Throws HttpInvalidParamException if http.only_exceptions is TRUE.
465 */
466 PHP_METHOD(HttpResponse, setCacheControl)
467 {
468 char *ccontrol, *cctl;
469 int cc_len;
470 long max_age = 0;
471
472 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ccontrol, &cc_len, &max_age)) {
473 RETURN_FALSE;
474 }
475
476 if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) {
477 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
478 RETURN_FALSE;
479 } else {
480 size_t cctl_len = spprintf(&cctl, 0, "%s, must-revalidate, max-age=%ld", ccontrol, max_age);
481 RETVAL_SUCCESS(UPD_STATIC_STRL(cacheControl, cctl, cctl_len));
482 efree(cctl);
483 }
484 }
485 /* }}} */
486
487 /* {{{ proto static string HttpResponse::getCacheControl()
488 *
489 * Get current Cache-Control header setting.
490 *
491 * Returns the current cache control setting as a string like sent in a header.
492 */
493 PHP_METHOD(HttpResponse, getCacheControl)
494 {
495 NO_ARGS;
496
497 IF_RETVAL_USED {
498 zval *ccontrol_p, *ccontrol = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(cacheControl), &ccontrol_p);
499
500 RETVAL_ZVAL(ccontrol, 1, 0);
501
502 if (ccontrol_p) {
503 zval_ptr_dtor(&ccontrol_p);
504 }
505 }
506 }
507 /* }}} */
508
509 /* {{{ proto static bool HttpResponse::setContentType(string content_type)
510 *
511 * Set the content-type of the sent entity.
512 *
513 * Expects a string as parameter specifying the content type of the sent entity.
514 *
515 * Returns TRUE on success, or FALSE if the content type does not seem to
516 * contain a primary and secondary content type part.
517 *
518 * Throws HttpInvalidParamException if http.only_exceptions is TRUE.
519 */
520 PHP_METHOD(HttpResponse, setContentType)
521 {
522 char *ctype;
523 int ctype_len;
524
525 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
526 RETURN_FALSE;
527 }
528
529 HTTP_CHECK_CONTENT_TYPE(ctype, RETURN_FALSE);
530 RETURN_SUCCESS(UPD_STATIC_STRL(contentType, ctype, ctype_len));
531 }
532 /* }}} */
533
534 /* {{{ proto static string HttpResponse::getContentType()
535 *
536 * Get current Content-Type header setting.
537 *
538 * Returns the currently set content type as string.
539 */
540 PHP_METHOD(HttpResponse, getContentType)
541 {
542 NO_ARGS;
543
544 IF_RETVAL_USED {
545 zval *ctype_p, *ctype = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(contentType), &ctype_p);
546
547 RETVAL_ZVAL(ctype, 1, 0);
548
549 if (ctype_p) {
550 zval_ptr_dtor(&ctype_p);
551 }
552 }
553 }
554 /* }}} */
555
556 /* {{{ proto static string HttpResponse::guessContentType(string magic_file[, int magic_mode = MAGIC_MIME])
557 *
558 * Attempts to guess the content type of supplied payload through libmagic.
559 * If the attempt is successful, the guessed content type will automatically
560 * be set as response content type.
561 *
562 * Expects a string parameter specifying the magic.mime database to use.
563 * Additionally accepts an optional int parameter, being flags for libmagic.
564 *
565 * Returns the guessed content type on success, or FALSE on failure.
566 *
567 * Throws HttpRuntimeException, HttpInvalidParamException
568 * if http.only_exceptions is TRUE.
569 */
570 PHP_METHOD(HttpResponse, guessContentType)
571 {
572 char *magic_file, *ct = NULL;
573 int magic_file_len;
574 long magic_mode = 0;
575
576 RETVAL_FALSE;
577
578 #ifdef HTTP_HAVE_MAGIC
579 magic_mode = MAGIC_MIME;
580
581 SET_EH_THROW_HTTP();
582 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &magic_file, &magic_file_len, &magic_mode)) {
583 switch (Z_LVAL_P(GET_STATIC_PROP(mode))) {
584 case SEND_DATA:
585 {
586 zval *data = GET_STATIC_PROP(data);
587 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(data), Z_STRLEN_P(data), SEND_DATA);
588 }
589 break;
590
591 case SEND_RSRC:
592 {
593 php_stream *s;
594 zval *z = GET_STATIC_PROP(stream);
595 z->type = IS_RESOURCE;
596 php_stream_from_zval(s, &z);
597 ct = http_guess_content_type(magic_file, magic_mode, s, 0, SEND_RSRC);
598 }
599 break;
600
601 default:
602 ct = http_guess_content_type(magic_file, magic_mode, Z_STRVAL_P(GET_STATIC_PROP(file)), 0, -1);
603 break;
604 }
605 if (ct) {
606 UPD_STATIC_PROP(string, contentType, ct);
607 RETVAL_STRING(ct, 0);
608 }
609 }
610 SET_EH_NORMAL();
611 #else
612 http_error(HE_THROW, HTTP_E_RUNTIME, "Cannot guess Content-Type; libmagic not available");
613 #endif
614 }
615 /* }}} */
616
617 /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
618 *
619 * Set the Content-Disposition. The Content-Disposition header is very useful
620 * if the data actually sent came from a file or something similar, that should
621 * be "saved" by the client/user (i.e. by browsers "Save as..." popup window).
622 *
623 * Expects a string parameter specifying the file name the "Save as..." dialogue
624 * should display. Optionally accepts a bool parameter, which, if set to true
625 * and the user agent knows how to handle the content type, will probably not
626 * cause the popup window to be shown.
627 *
628 * Returns TRUE on success or FALSE on failure.
629 */
630 PHP_METHOD(HttpResponse, setContentDisposition)
631 {
632 char *file, *cd;
633 int file_len;
634 size_t cd_len;
635 zend_bool send_inline = 0;
636
637 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
638 RETURN_FALSE;
639 }
640
641 cd_len = spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
642 RETVAL_SUCCESS(UPD_STATIC_STRL(contentDisposition, cd, cd_len));
643 efree(cd);
644 }
645 /* }}} */
646
647 /* {{{ proto static string HttpResponse::getContentDisposition()
648 *
649 * Get current Content-Disposition setting.
650 *
651 * Returns the current content disposition as string like sent in a header.
652 */
653 PHP_METHOD(HttpResponse, getContentDisposition)
654 {
655 NO_ARGS;
656
657 IF_RETVAL_USED {
658 zval *cd_p, *cd = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(contentDisposition), &cd_p);
659
660 RETVAL_ZVAL(cd, 1, 0);
661
662 if (cd_p) {
663 zval_ptr_dtor(&cd_p);
664 }
665 }
666 }
667 /* }}} */
668
669 /* {{{ proto static bool HttpResponse::setETag(string etag)
670 *
671 * Set a custom ETag. Use this only if you know what you're doing.
672 *
673 * Expects an unquoted string as parameter containing the ETag.
674 *
675 * Returns TRUE on success, or FALSE on failure.
676 */
677 PHP_METHOD(HttpResponse, setETag)
678 {
679 char *etag;
680 int etag_len;
681
682 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
683 RETURN_FALSE;
684 }
685
686 RETURN_SUCCESS(UPD_STATIC_STRL(eTag, etag, etag_len));
687 }
688 /* }}} */
689
690 /* {{{ proto static string HttpResponse::getETag()
691 *
692 * Get calculated or previously set custom ETag.
693 *
694 * Returns the calculated or previously set ETag as unquoted string.
695 */
696 PHP_METHOD(HttpResponse, getETag)
697 {
698 NO_ARGS;
699
700 IF_RETVAL_USED {
701 zval *etag_p, *etag = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(eTag), &etag_p);
702
703 RETVAL_ZVAL(etag, 1, 0);
704
705 if (etag_p) {
706 zval_ptr_dtor(&etag_p);
707 }
708 }
709 }
710 /* }}} */
711
712 /* {{{ proto static bool HttpResponse::setLastModified(int timestamp)
713 *
714 * Set a custom Last-Modified date.
715 *
716 * Expects an unix timestamp as parameter representing the last modification
717 * time of the sent entity.
718 *
719 * Returns TRUE on success, or FALSE on failure.
720 */
721 PHP_METHOD(HttpResponse, setLastModified)
722 {
723 long lm;
724
725 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) {
726 RETURN_FALSE;
727 }
728
729 RETURN_SUCCESS(UPD_STATIC_PROP(long, lastModified, lm));
730 }
731 /* }}} */
732
733 /* {{{ proto static int HttpResponse::getLastModified()
734 *
735 * Get calculated or previously set custom Last-Modified date.
736 *
737 * Returns the calculated or previously set unix timestamp.
738 */
739 PHP_METHOD(HttpResponse, getLastModified)
740 {
741 NO_ARGS;
742
743 IF_RETVAL_USED {
744 zval *lm_p, *lm = convert_to_type_ex(IS_LONG, GET_STATIC_PROP(lastModified), &lm_p);
745
746 RETVAL_ZVAL(lm, 1, 0);
747
748 if (lm_p) {
749 zval_ptr_dtor(&lm_p);
750 }
751 }
752 }
753 /* }}} */
754
755 /* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds)
756 *
757 * Sets the throttle delay for use with HttpResponse::setBufferSize().
758 *
759 * Provides a basic throttling mechanism, which will yield the current process
760 * resp. thread until the entity has been completely sent, though.
761 *
762 * Note: This doesn't really work with the FastCGI SAPI.
763 *
764 * Expects a double parameter specifying the seconds too sleep() after
765 * each chunk sent.
766 *
767 * Returns TRUE on success, or FALSE on failure.
768 */
769 PHP_METHOD(HttpResponse, setThrottleDelay)
770 {
771 double seconds;
772
773 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) {
774 RETURN_FALSE;
775 }
776 RETURN_SUCCESS(UPD_STATIC_PROP(double, throttleDelay, seconds));
777 }
778 /* }}} */
779
780 /* {{{ proto static double HttpResponse::getThrottleDelay()
781 *
782 * Get the current throttle delay.
783 *
784 * Returns a double representing the throttle delay in seconds.
785 */
786 PHP_METHOD(HttpResponse, getThrottleDelay)
787 {
788 NO_ARGS;
789
790 IF_RETVAL_USED {
791 zval *delay_p, *delay = convert_to_type_ex(IS_DOUBLE, GET_STATIC_PROP(throttleDelay), &delay_p);
792
793 RETVAL_ZVAL(delay, 1, 0);
794
795 if (delay_p) {
796 zval_ptr_dtor(&delay_p);
797 }
798 }
799 }
800 /* }}} */
801
802 /* {{{ proto static bool HttpResponse::setBufferSize(int bytes)
803 *
804 * Sets the send buffer size for use with HttpResponse::setThrottleDelay().
805 *
806 * Provides a basic throttling mechanism, which will yield the current process
807 * resp. thread until the entity has been completely sent, though.
808 *
809 * Note: This doesn't really work with the FastCGI SAPI.
810 *
811 * Expects an int parameter representing the chunk size in bytes.
812 *
813 * Returns TRUE on success, or FALSE on failure.
814 */
815 PHP_METHOD(HttpResponse, setBufferSize)
816 {
817 long bytes;
818
819 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) {
820 RETURN_FALSE;
821 }
822 RETURN_SUCCESS(UPD_STATIC_PROP(long, bufferSize, bytes));
823 }
824 /* }}} */
825
826 /* {{{ proto static int HttpResponse::getBufferSize()
827 *
828 * Get current buffer size.
829 *
830 * Returns an int representing the current buffer size in bytes.
831 */
832 PHP_METHOD(HttpResponse, getBufferSize)
833 {
834 NO_ARGS;
835
836 IF_RETVAL_USED {
837 zval *size_p, *size = convert_to_type_ex(IS_LONG, GET_STATIC_PROP(bufferSize), &size_p);
838
839 RETVAL_ZVAL(size, 1, 0);
840
841 if (size_p) {
842 zval_ptr_dtor(&size_p);
843 }
844 }
845 }
846 /* }}} */
847
848 /* {{{ proto static bool HttpResponse::setData(mixed data)
849 *
850 * Set the data to be sent.
851 *
852 * Expects one parameter, which will be converted to a string and contains
853 * the data to send.
854 *
855 * Returns TRUE on success, or FALSE on failure.
856 */
857 PHP_METHOD(HttpResponse, setData)
858 {
859 char *etag;
860 zval *the_data;
861
862 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
863 RETURN_FALSE;
864 }
865 if (Z_TYPE_P(the_data) != IS_STRING) {
866 convert_to_string_ex(&the_data);
867 }
868
869 if ( (SUCCESS != SET_STATIC_PROP(data, the_data)) ||
870 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_DATA))) {
871 RETURN_FALSE;
872 }
873
874 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_data, SEND_DATA));
875 if ((etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA))) {
876 UPD_STATIC_PROP(string, eTag, etag);
877 efree(etag);
878 }
879
880 RETURN_TRUE;
881 }
882 /* }}} */
883
884 /* {{{ proto static string HttpResponse::getData()
885 *
886 * Get the previously set data to be sent.
887 *
888 * Returns a string containing the previously set data to send.
889 */
890 PHP_METHOD(HttpResponse, getData)
891 {
892 NO_ARGS;
893
894 IF_RETVAL_USED {
895 zval *the_data = GET_STATIC_PROP(data);
896
897 RETURN_ZVAL(the_data, 1, 0);
898 }
899 }
900 /* }}} */
901
902 /* {{{ proto static bool HttpResponse::setStream(resource stream)
903 *
904 * Set the resource to be sent.
905 *
906 * Expects a resource parameter referencing an already opened stream from
907 * which the data to send will be read.
908 *
909 * Returns TRUE on success, or FALSE on failure.
910 */
911 PHP_METHOD(HttpResponse, setStream)
912 {
913 char *etag;
914 zval *the_stream;
915 php_stream *the_real_stream;
916 php_stream_statbuf ssb;
917
918 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
919 RETURN_FALSE;
920 }
921
922 php_stream_from_zval(the_real_stream, &the_stream);
923 if (php_stream_stat(the_real_stream, &ssb)) {
924 RETURN_FALSE;
925 }
926
927 if ( (SUCCESS != UPD_STATIC_PROP(long, stream, Z_LVAL_P(the_stream))) ||
928 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_RSRC))) {
929 RETURN_FALSE;
930 }
931 zend_list_addref(Z_LVAL_P(the_stream));
932
933 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_real_stream, SEND_RSRC));
934 if ((etag = http_etag(the_real_stream, 0, SEND_RSRC))) {
935 UPD_STATIC_PROP(string, eTag, etag);
936 efree(etag);
937 }
938
939 RETURN_TRUE;
940 }
941 /* }}} */
942
943 /* {{{ proto static resource HttpResponse::getStream()
944 *
945 * Get the previously set resource to be sent.
946 *
947 * Returns the previously set resource.
948 */
949 PHP_METHOD(HttpResponse, getStream)
950 {
951 NO_ARGS;
952
953 IF_RETVAL_USED {
954 zval *stream_p;
955
956 RETVAL_RESOURCE(Z_LVAL_P(convert_to_type_ex(IS_LONG, GET_STATIC_PROP(stream), &stream_p)));
957
958 if (stream_p) {
959 zval_ptr_dtor(&stream_p);
960 }
961 }
962 }
963 /* }}} */
964
965 /* {{{ proto static bool HttpResponse::setFile(string file)
966 *
967 * Set the file to be sent.
968 *
969 * Expects a string as parameter, specifying the path to the file to send.
970 *
971 * Returns TRUE on success, or FALSE on failure.
972 */
973 PHP_METHOD(HttpResponse, setFile)
974 {
975 char *the_file, *etag;
976 int file_len;
977 php_stream_statbuf ssb;
978
979 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) {
980 RETURN_FALSE;
981 }
982
983 if (php_stream_stat_path(the_file, &ssb)) {
984 RETURN_FALSE;
985 }
986
987 if ( (SUCCESS != UPD_STATIC_STRL(file, the_file, file_len)) ||
988 (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) {
989 RETURN_FALSE;
990 }
991
992 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_file, -1));
993 if ((etag = http_etag(the_file, 0, -1))) {
994 UPD_STATIC_PROP(string, eTag, etag);
995 efree(etag);
996 }
997
998 RETURN_TRUE;
999 }
1000 /* }}} */
1001
1002 /* {{{ proto static string HttpResponse::getFile()
1003 *
1004 * Get the previously set file to be sent.
1005 *
1006 * Returns the previously set path to the file to send as string.
1007 */
1008 PHP_METHOD(HttpResponse, getFile)
1009 {
1010 NO_ARGS;
1011
1012 IF_RETVAL_USED {
1013 zval *the_file_p, *the_file = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(file), &the_file_p);
1014
1015 RETVAL_ZVAL(the_file, 1, 0);
1016
1017 if (the_file_p) {
1018 zval_ptr_dtor(&the_file_p);
1019 }
1020 }
1021 }
1022 /* }}} */
1023
1024 /* {{{ proto static bool HttpResponse::send([bool clean_ob = true])
1025 *
1026 * Finally send the entity.
1027 *
1028 * Accepts an optional boolean parameter, specifying wheter the ouput
1029 * buffers should be discarded prior sending. A successful caching attempt
1030 * will cause a script termination, and write a log entry if the INI setting
1031 * http.cache_log is set.
1032 *
1033 * Returns TRUE on success, or FALSE on failure.
1034 *
1035 * Throws HttpHeaderException, HttpResponseException if http.onyl_excpetions is TRUE.
1036 *
1037 * Example:
1038 * <pre>
1039 * <?php
1040 * HttpResponse::setCache(true);
1041 * HttpResponse::setContentType('application/pdf');
1042 * HttpResponse::setContentDisposition("$user.pdf", false);
1043 * HttpResponse::setFile('sheet.pdf');
1044 * HttpResponse::send();
1045 * ?>
1046 * </pre>
1047 */
1048 PHP_METHOD(HttpResponse, send)
1049 {
1050 zval *sent;
1051 zend_bool clean_ob = 1;
1052
1053 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) {
1054 RETURN_FALSE;
1055 }
1056
1057 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
1058
1059 sent = GET_STATIC_PROP(sent);
1060 if (Z_LVAL_P(sent)) {
1061 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent");
1062 RETURN_FALSE;
1063 } else {
1064 Z_LVAL_P(sent) = 1;
1065 }
1066
1067 /* capture mode */
1068 if (zval_is_true(GET_STATIC_PROP(catch))) {
1069 zval *etag_p, *the_data;
1070
1071 MAKE_STD_ZVAL(the_data);
1072 php_ob_get_buffer(the_data TSRMLS_CC);
1073 SET_STATIC_PROP(data, the_data);
1074 ZVAL_LONG(GET_STATIC_PROP(mode), SEND_DATA);
1075
1076 if (!Z_STRLEN_P(convert_to_type_ex(IS_STRING, GET_STATIC_PROP(eTag), &etag_p))) {
1077 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
1078 if (etag) {
1079 UPD_STATIC_PROP(string, eTag, etag);
1080 efree(etag);
1081 }
1082 }
1083 zval_ptr_dtor(&the_data);
1084
1085 if (etag_p) {
1086 zval_ptr_dtor(&etag_p);
1087 }
1088
1089 clean_ob = 1;
1090 }
1091
1092 if (clean_ob) {
1093 /* interrupt on-the-fly etag generation */
1094 HTTP_G(etag).started = 0;
1095 /* discard previous output buffers */
1096 php_end_ob_buffers(0 TSRMLS_CC);
1097 }
1098
1099 /* caching */
1100 if (zval_is_true(GET_STATIC_PROP(cache))) {
1101 zval *cctl, *cctl_p, *etag, *etag_p, *lmod, *lmod_p;
1102
1103 etag = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(eTag), &etag_p);
1104 lmod = convert_to_type_ex(IS_LONG, GET_STATIC_PROP(lastModified), &lmod_p);
1105 cctl = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(cacheControl), &cctl_p);
1106
1107 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
1108 http_cache_last_modified(Z_LVAL_P(lmod), Z_LVAL_P(lmod) ? Z_LVAL_P(lmod) : time(NULL), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
1109
1110 if (etag_p) zval_ptr_dtor(&etag_p);
1111 if (lmod_p) zval_ptr_dtor(&lmod_p);
1112 if (cctl_p) zval_ptr_dtor(&cctl_p);
1113
1114 if (php_ob_handler_used("blackhole" TSRMLS_CC)) {
1115 RETURN_TRUE;
1116 }
1117 }
1118
1119 /* content type */
1120 {
1121 zval *ctype_p, *ctype = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(contentType), &ctype_p);
1122 if (Z_STRLEN_P(ctype)) {
1123 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
1124 } else {
1125 char *ctypes = INI_STR("default_mimetype");
1126 size_t ctlen = ctypes ? strlen(ctypes) : 0;
1127
1128 if (ctlen) {
1129 http_send_content_type(ctypes, ctlen);
1130 } else {
1131 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
1132 }
1133 }
1134 if (ctype_p) {
1135 zval_ptr_dtor(&ctype_p);
1136 }
1137 }
1138
1139 /* content disposition */
1140 {
1141 zval *cd_p, *cd = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(contentDisposition), &cd_p);
1142 if (Z_STRLEN_P(cd)) {
1143 http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL);
1144 }
1145 if (cd_p) {
1146 zval_ptr_dtor(&cd_p);
1147 }
1148 }
1149
1150 /* throttling */
1151 {
1152 zval *bsize_p, *bsize = convert_to_type_ex(IS_LONG, GET_STATIC_PROP(bufferSize), &bsize_p);
1153 zval *delay_p, *delay = convert_to_type_ex(IS_DOUBLE, GET_STATIC_PROP(throttleDelay), &delay_p);
1154 HTTP_G(send).buffer_size = Z_LVAL_P(bsize);
1155 HTTP_G(send).throttle_delay = Z_DVAL_P(delay);
1156 if (bsize_p) zval_ptr_dtor(&bsize_p);
1157 if (delay_p) zval_ptr_dtor(&delay_p);
1158 }
1159
1160 /* gzip */
1161 HTTP_G(send).gzip_encoding = zval_is_true(GET_STATIC_PROP(gzip));
1162
1163 /* start ob */
1164 php_start_ob_buffer(NULL, HTTP_G(send).buffer_size, 0 TSRMLS_CC);
1165
1166 /* send */
1167 switch (Z_LVAL_P(GET_STATIC_PROP(mode)))
1168 {
1169 case SEND_DATA:
1170 {
1171 zval *zdata_p, *zdata = convert_to_type_ex(IS_STRING, GET_STATIC_PROP(data), &zdata_p);
1172 RETVAL_SUCCESS(http_send_data_ex(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata), 1));
1173 if (zdata_p) zval_ptr_dtor(&zdata_p);
1174 return;
1175 }
1176
1177 case SEND_RSRC:
1178 {
1179 php_stream *the_real_stream;
1180 zval *the_stream_p, *the_stream = convert_to_type_ex(IS_LONG, GET_STATIC_PROP(stream), &the_stream_p);
1181 the_stream->type = IS_RESOURCE;
1182 php_stream_from_zval(the_real_stream, &the_stream);
1183 RETVAL_SUCCESS(http_send_stream_ex(the_real_stream, 0, 1));
1184 if (the_stream_p) zval_ptr_dtor(&the_stream_p);
1185 return;
1186 }
1187
1188 default:
1189 {
1190 zval *file_p;
1191 RETVAL_SUCCESS(http_send_file_ex(Z_STRVAL_P(convert_to_type_ex(IS_STRING, GET_STATIC_PROP(file), &file_p)), 1));
1192 if (file_p) zval_ptr_dtor(&file_p);
1193 return;
1194 }
1195 }
1196 }
1197 /* }}} */
1198
1199 /* {{{ proto static void HttpResponse::capture()
1200 *
1201 * Capture script output.
1202 *
1203 * Example:
1204 * <pre>
1205 * <?php
1206 * HttpResponse::setCache(true);
1207 * HttpResponse::capture();
1208 * // script follows
1209 * ?>
1210 * </pre>
1211 */
1212 PHP_METHOD(HttpResponse, capture)
1213 {
1214 NO_ARGS;
1215
1216 HTTP_CHECK_HEADERS_SENT(RETURN_FALSE);
1217
1218 UPD_STATIC_PROP(long, catch, 1);
1219
1220 php_end_ob_buffers(0 TSRMLS_CC);
1221 php_start_ob_buffer(NULL, 40960, 0 TSRMLS_CC);
1222
1223 /* register shutdown function */
1224 {
1225 zval func, retval, arg, *argp[1];
1226
1227 INIT_PZVAL(&arg);
1228 INIT_PZVAL(&func);
1229 INIT_PZVAL(&retval);
1230 ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0);
1231
1232 array_init(&arg);
1233 add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1);
1234 add_next_index_stringl(&arg, "send", lenof("send"), 1);
1235 argp[0] = &arg;
1236 call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC);
1237 zval_dtor(&arg);
1238 }
1239 }
1240 /* }}} */
1241
1242 #endif /* ZEND_ENGINE_2 && !WONKY */
1243
1244 /*
1245 * Local variables:
1246 * tab-width: 4
1247 * c-basic-offset: 4
1248 * End:
1249 * vim600: noet sw=4 ts=4 fdm=marker
1250 * vim<600: noet sw=4 ts=4
1251 */
1252