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