- introduce a force flag for no_cache
[m6w6/ext-http] / http_response_object.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22 #include "php.h"
23
24 #include "missing.h"
25
26 /* broken static properties in PHP 5.0 */
27 #if defined(ZEND_ENGINE_2) && !defined(WONKY)
28
29 #include "SAPI.h"
30 #include "php_ini.h"
31
32 #include "php_http.h"
33 #include "php_http_api.h"
34 #include "php_http_std_defs.h"
35 #include "php_http_response_object.h"
36 #include "php_http_exception_object.h"
37 #include "php_http_send_api.h"
38 #include "php_http_cache_api.h"
39
40 ZEND_EXTERN_MODULE_GLOBALS(http);
41
42 #define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n)
43 #define UPD_STATIC_PROP(t, n, v) UPD_STATIC_PROP_EX(http_response_object_ce, t, n, v)
44 #define SET_STATIC_PROP(n, v) SET_STATIC_PROP_EX(http_response_object_ce, n, v)
45
46 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args)
47 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref)
48 #define HTTP_RESPONSE_ME(method, visibility) PHP_ME(HttpResponse, method, HTTP_ARGS(HttpResponse, method), visibility|ZEND_ACC_STATIC)
49 #define HTTP_RESPONSE_ALIAS(method, func) HTTP_STATIC_ME_ALIAS(method, func, HTTP_ARGS(HttpResponse, method))
50
51 HTTP_BEGIN_ARGS(setHeader, 2)
52 HTTP_ARG_VAL(name, 0)
53 HTTP_ARG_VAL(value, 0)
54 HTTP_ARG_VAL(replace, 0)
55 HTTP_END_ARGS;
56
57 HTTP_BEGIN_ARGS(getHeader, 0)
58 HTTP_ARG_VAL(name, 0)
59 HTTP_END_ARGS;
60
61 HTTP_EMPTY_ARGS(getETag, 0);
62 HTTP_BEGIN_ARGS(setETag, 1)
63 HTTP_ARG_VAL(etag, 0)
64 HTTP_END_ARGS;
65
66 HTTP_EMPTY_ARGS(getLastModified, 0);
67 HTTP_BEGIN_ARGS(setLastModified, 1)
68 HTTP_ARG_VAL(timestamp, 0)
69 HTTP_END_ARGS;
70
71 HTTP_EMPTY_ARGS(getCache, 0);
72 HTTP_BEGIN_ARGS(setCache, 1)
73 HTTP_ARG_VAL(cache, 0)
74 HTTP_END_ARGS;
75
76 HTTP_EMPTY_ARGS(getGzip, 0);
77 HTTP_BEGIN_ARGS(setGzip, 1)
78 HTTP_ARG_VAL(gzip, 0)
79 HTTP_END_ARGS;
80
81 HTTP_EMPTY_ARGS(getCacheControl, 0);
82 HTTP_BEGIN_ARGS(setCacheControl, 1)
83 HTTP_ARG_VAL(cache_control, 0)
84 HTTP_ARG_VAL(max_age, 0)
85 HTTP_END_ARGS;
86
87 HTTP_EMPTY_ARGS(getContentType, 0);
88 HTTP_BEGIN_ARGS(setContentType, 1)
89 HTTP_ARG_VAL(content_type, 0)
90 HTTP_END_ARGS;
91
92 HTTP_EMPTY_ARGS(getContentDisposition, 0);
93 HTTP_BEGIN_ARGS(setContentDisposition, 1)
94 HTTP_ARG_VAL(filename, 0)
95 HTTP_ARG_VAL(send_inline, 0)
96 HTTP_END_ARGS;
97
98 HTTP_EMPTY_ARGS(getThrottleDelay, 0);
99 HTTP_BEGIN_ARGS(setThrottleDelay, 1)
100 HTTP_ARG_VAL(seconds, 0)
101 HTTP_END_ARGS;
102
103 HTTP_EMPTY_ARGS(getBufferSize, 0);
104 HTTP_BEGIN_ARGS(setBufferSize, 1)
105 HTTP_ARG_VAL(bytes, 0)
106 HTTP_END_ARGS;
107
108 HTTP_EMPTY_ARGS(getData, 0);
109 HTTP_BEGIN_ARGS(setData, 1)
110 HTTP_ARG_VAL(data, 0)
111 HTTP_END_ARGS;
112
113 HTTP_EMPTY_ARGS(getStream, 0);
114 HTTP_BEGIN_ARGS(setStream, 1)
115 HTTP_ARG_VAL(stream, 0)
116 HTTP_END_ARGS;
117
118 HTTP_EMPTY_ARGS(getFile, 0);
119 HTTP_BEGIN_ARGS(setFile, 1)
120 HTTP_ARG_VAL(filepath, 0)
121 HTTP_END_ARGS;
122
123 HTTP_BEGIN_ARGS(send, 0)
124 HTTP_ARG_VAL(clean_ob, 0)
125 HTTP_END_ARGS;
126
127 HTTP_EMPTY_ARGS(capture, 0);
128
129 HTTP_BEGIN_ARGS(redirect, 0)
130 HTTP_ARG_VAL(url, 0)
131 HTTP_ARG_VAL(params, 0)
132 HTTP_ARG_VAL(session, 0)
133 HTTP_ARG_VAL(permanent, 0)
134 HTTP_END_ARGS;
135
136 HTTP_BEGIN_ARGS(status, 1)
137 HTTP_ARG_VAL(code, 0)
138 HTTP_END_ARGS;
139
140 HTTP_EMPTY_ARGS(getRequestHeaders, 0);
141 HTTP_EMPTY_ARGS(getRequestBody, 0);
142
143 #define http_response_object_declare_default_properties() _http_response_object_declare_default_properties(TSRMLS_C)
144 static inline void _http_response_object_declare_default_properties(TSRMLS_D);
145
146 #if BROKEN_STATICS
147 HashTable http_response_statics;
148 #endif
149
150 zend_class_entry *http_response_object_ce;
151 zend_function_entry http_response_object_fe[] = {
152
153 HTTP_RESPONSE_ME(setHeader, ZEND_ACC_PUBLIC)
154 HTTP_RESPONSE_ME(getHeader, ZEND_ACC_PUBLIC)
155
156 HTTP_RESPONSE_ME(setETag, ZEND_ACC_PUBLIC)
157 HTTP_RESPONSE_ME(getETag, ZEND_ACC_PUBLIC)
158
159 HTTP_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC)
160 HTTP_RESPONSE_ME(getLastModified, ZEND_ACC_PUBLIC)
161
162 HTTP_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
163 HTTP_RESPONSE_ME(getContentDisposition, ZEND_ACC_PUBLIC)
164
165 HTTP_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
166 HTTP_RESPONSE_ME(getContentType, ZEND_ACC_PUBLIC)
167
168 HTTP_RESPONSE_ME(setCache, ZEND_ACC_PUBLIC)
169 HTTP_RESPONSE_ME(getCache, ZEND_ACC_PUBLIC)
170
171 HTTP_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC)
172 HTTP_RESPONSE_ME(getCacheControl, ZEND_ACC_PUBLIC)
173
174 HTTP_RESPONSE_ME(setGzip, ZEND_ACC_PUBLIC)
175 HTTP_RESPONSE_ME(getGzip, ZEND_ACC_PUBLIC)
176
177 HTTP_RESPONSE_ME(setThrottleDelay, ZEND_ACC_PUBLIC)
178 HTTP_RESPONSE_ME(getThrottleDelay, ZEND_ACC_PUBLIC)
179
180 HTTP_RESPONSE_ME(setBufferSize, ZEND_ACC_PUBLIC)
181 HTTP_RESPONSE_ME(getBufferSize, ZEND_ACC_PUBLIC)
182
183 HTTP_RESPONSE_ME(setData, ZEND_ACC_PUBLIC)
184 HTTP_RESPONSE_ME(getData, ZEND_ACC_PUBLIC)
185
186 HTTP_RESPONSE_ME(setFile, ZEND_ACC_PUBLIC)
187 HTTP_RESPONSE_ME(getFile, ZEND_ACC_PUBLIC)
188
189 HTTP_RESPONSE_ME(setStream, ZEND_ACC_PUBLIC)
190 HTTP_RESPONSE_ME(getStream, ZEND_ACC_PUBLIC)
191
192 HTTP_RESPONSE_ME(send, ZEND_ACC_PUBLIC)
193 HTTP_RESPONSE_ME(capture, ZEND_ACC_PUBLIC)
194
195 HTTP_RESPONSE_ALIAS(redirect, http_redirect)
196 HTTP_RESPONSE_ALIAS(status, http_send_status)
197 HTTP_RESPONSE_ALIAS(getRequestHeaders, http_get_request_headers)
198 HTTP_RESPONSE_ALIAS(getRequestBody, http_get_request_body)
199
200 {NULL, NULL, NULL}
201 };
202
203 void _http_response_object_init(INIT_FUNC_ARGS)
204 {
205 HTTP_REGISTER_CLASS(HttpResponse, http_response_object, NULL, 0);
206 http_response_object_declare_default_properties();
207 }
208
209 static inline void _http_response_object_declare_default_properties(TSRMLS_D)
210 {
211 zend_class_entry *ce = http_response_object_ce;
212
213 DCL_STATIC_PROP(PRIVATE, bool, sent, 0);
214 DCL_STATIC_PROP(PRIVATE, bool, catch, 0);
215 DCL_STATIC_PROP(PRIVATE, long, mode, -1);
216 DCL_STATIC_PROP(PROTECTED, bool, cache, 0);
217 DCL_STATIC_PROP(PROTECTED, bool, gzip, 0);
218 DCL_STATIC_PROP(PROTECTED, long, stream, 0);
219 DCL_STATIC_PROP_N(PROTECTED, file);
220 DCL_STATIC_PROP_N(PROTECTED, data);
221 DCL_STATIC_PROP_N(PROTECTED, eTag);
222 DCL_STATIC_PROP(PROTECTED, long, lastModified, 0);
223 DCL_STATIC_PROP_N(PROTECTED, cacheControl);
224 DCL_STATIC_PROP_N(PROTECTED, contentType);
225 DCL_STATIC_PROP_N(PROTECTED, contentDisposition);
226 DCL_STATIC_PROP(PROTECTED, long, bufferSize, HTTP_SENDBUF_SIZE);
227 DCL_STATIC_PROP(PROTECTED, double, throttleDelay, 0.0);
228 DCL_STATIC_PROP_N(PROTECTED, headers);
229 }
230
231 /* ### USERLAND ### */
232
233 /* {{{ proto static bool HttpResponse::setHeader(string name, mixed value[, bool replace = true)
234 */
235 PHP_METHOD(HttpResponse, setHeader)
236 {
237 zend_bool replace = 1;
238 char *name;
239 int name_len = 0;
240 zval *value = NULL, *headers, **header;
241
242 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz/!|b", &name, &name_len, &value, &replace)) {
243 RETURN_FALSE;
244 }
245 if (!name_len) {
246 http_error(HE_WARNING, HTTP_E_HEADER, "Cannot send anonymous headers");
247 RETURN_FALSE;
248 }
249
250 headers = GET_STATIC_PROP(headers);
251
252 if (Z_TYPE_P(headers) != IS_ARRAY) {
253 convert_to_array(headers);
254 }
255
256 /* delete header if value == null */
257 if (!value || Z_TYPE_P(value) == IS_NULL) {
258 RETURN_SUCCESS(zend_hash_del(Z_ARRVAL_P(headers), name, name_len + 1));
259 }
260
261 if (Z_TYPE_P(value) != IS_STRING) {
262 convert_to_string_ex(&value);
263 }
264
265 /* convert old header to an array and add new one if header exists and replace == false */
266 if (replace || (SUCCESS != zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header))) {
267 RETURN_SUCCESS(add_assoc_stringl_ex(headers, name, name_len + 1, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
268 } else {
269 convert_to_array(*header);
270 RETURN_SUCCESS(add_next_index_stringl(*header, Z_STRVAL_P(value), Z_STRLEN_P(value), 1));
271 }
272 }
273 /* }}} */
274
275 /* {{{ proto static mixed HttpResponse::getHeader([string name])
276 */
277 PHP_METHOD(HttpResponse, getHeader)
278 {
279 char *name = NULL;
280 int name_len = 0;
281 zval *headers, **header;
282
283 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &name_len)) {
284 RETURN_FALSE;
285 }
286
287 headers = GET_STATIC_PROP(headers);
288 if (Z_TYPE_P(headers) != IS_ARRAY) {
289 convert_to_array(headers);
290 }
291
292 if (!name || !name_len) {
293 array_init(return_value);
294 array_copy(headers, return_value);
295 } else if (SUCCESS == zend_hash_find(Z_ARRVAL_P(headers), name, name_len + 1, (void **) &header)) {
296 RETURN_ZVAL(*header, ZVAL_PTR_DTOR, 1);
297 } else {
298 RETURN_NULL();
299 }
300 }
301 /* }}} */
302
303 /* {{{ proto static bool HttpResponse::setCache(bool cache)
304 *
305 * Whether it sould be attempted to cache the entitity.
306 * This will result in necessary caching headers and checks of clients
307 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
308 * matches a "304 Not Modified" status code will be issued.
309 *
310 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
311 * to something more appropriate than "no-cache"!
312 */
313 PHP_METHOD(HttpResponse, setCache)
314 {
315 zend_bool do_cache = 0;
316
317 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_cache)) {
318 RETURN_FALSE;
319 }
320
321 RETURN_SUCCESS(UPD_STATIC_PROP(bool, cache, do_cache));
322 }
323 /* }}} */
324
325 /* {{{ proto static bool HttpResponse::getCache()
326 *
327 * Get current caching setting.
328 */
329 PHP_METHOD(HttpResponse, getCache)
330 {
331 NO_ARGS;
332
333 IF_RETVAL_USED {
334 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(cache)));
335 }
336 }
337 /* }}}*/
338
339 /* {{{ proto static bool HttpResponse::setGzip(bool gzip)
340 *
341 * Enable on-thy-fly gzipping of the sent entity.
342 */
343 PHP_METHOD(HttpResponse, setGzip)
344 {
345 zend_bool do_gzip = 0;
346
347 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &do_gzip)) {
348 RETURN_FALSE;
349 }
350
351 RETURN_SUCCESS(UPD_STATIC_PROP(bool, gzip, do_gzip));
352 }
353 /* }}} */
354
355 /* {{{ proto static bool HttpResponse::getGzip()
356 *
357 * Get current gzipping setting.
358 */
359 PHP_METHOD(HttpResponse, getGzip)
360 {
361 NO_ARGS;
362
363 IF_RETVAL_USED {
364 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(gzip)));
365 }
366 }
367 /* }}} */
368
369 /* {{{ proto static bool HttpResponse::setCacheControl(string control[, long max_age = 0])
370 *
371 * Set a custom cache-control header, usually being "private" or "public";
372 * The max_age parameter controls how long the cache entry is valid on the client side.
373 */
374 PHP_METHOD(HttpResponse, setCacheControl)
375 {
376 char *ccontrol, *cctl;
377 int cc_len;
378 long max_age = 0;
379
380 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ccontrol, &cc_len, &max_age)) {
381 RETURN_FALSE;
382 }
383
384 if (strcmp(ccontrol, "public") && strcmp(ccontrol, "private") && strcmp(ccontrol, "no-cache")) {
385 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol);
386 RETURN_FALSE;
387 } else {
388 spprintf(&cctl, 0, "%s, must-revalidate, max_age=%ld", ccontrol, max_age);
389 RETVAL_SUCCESS(UPD_STATIC_PROP(string, cacheControl, cctl));
390 efree(cctl);
391 }
392 }
393 /* }}} */
394
395 /* {{{ proto static string HttpResponse::getCacheControl()
396 *
397 * Get current Cache-Control header setting.
398 */
399 PHP_METHOD(HttpResponse, getCacheControl)
400 {
401 NO_ARGS;
402
403 IF_RETVAL_USED {
404 zval *ccontrol = GET_STATIC_PROP(cacheControl);
405 RETURN_STRINGL(Z_STRVAL_P(ccontrol), Z_STRLEN_P(ccontrol), 1);
406 }
407 }
408 /* }}} */
409
410 /* {{{ proto static bool HttpResponse::setContentType(string content_type)
411 *
412 * Set the content-type of the sent entity.
413 */
414 PHP_METHOD(HttpResponse, setContentType)
415 {
416 char *ctype;
417 int ctype_len;
418
419 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &ctype, &ctype_len)) {
420 RETURN_FALSE;
421 }
422
423 if (!strchr(ctype, '/')) {
424 http_error_ex(HE_WARNING, HTTP_E_INVALID_PARAM, "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype);
425 RETURN_FALSE;
426 }
427
428 RETURN_SUCCESS(UPD_STATIC_PROP(string, contentType, ctype));
429 }
430 /* }}} */
431
432 /* {{{ proto static string HttpResponse::getContentType()
433 *
434 * Get current Content-Type header setting.
435 */
436 PHP_METHOD(HttpResponse, getContentType)
437 {
438 NO_ARGS;
439
440 IF_RETVAL_USED {
441 zval *ctype = GET_STATIC_PROP(contentType);
442 RETURN_STRINGL(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype), 1);
443 }
444 }
445 /* }}} */
446
447 /* {{{ proto static bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
448 *
449 * Set the Content-Disposition of the sent entity. This setting aims to suggest
450 * the receiveing user agent how to handle the sent entity; usually the client
451 * will show the user a "Save As..." popup.
452 */
453 PHP_METHOD(HttpResponse, setContentDisposition)
454 {
455 char *file, *cd;
456 int file_len;
457 zend_bool send_inline = 0;
458
459 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &file, &file_len, &send_inline)) {
460 RETURN_FALSE;
461 }
462
463 spprintf(&cd, 0, "%s; filename=\"%s\"", send_inline ? "inline" : "attachment", file);
464 RETVAL_SUCCESS(UPD_STATIC_PROP(string, contentDisposition, cd));
465 efree(cd);
466 }
467 /* }}} */
468
469 /* {{{ proto static string HttpResponse::getContentDisposition()
470 *
471 * Get current Content-Disposition setting.
472 */
473 PHP_METHOD(HttpResponse, getContentDisposition)
474 {
475 NO_ARGS;
476
477 IF_RETVAL_USED {
478 zval *cd = GET_STATIC_PROP(contentDisposition);
479 RETURN_STRINGL(Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1);
480 }
481 }
482 /* }}} */
483
484 /* {{{ proto static bool HttpResponse::setETag(string etag)
485 *
486 * Set a custom ETag. Use this only if you know what you're doing.
487 */
488 PHP_METHOD(HttpResponse, setETag)
489 {
490 char *etag;
491 int etag_len;
492
493 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &etag, &etag_len)) {
494 RETURN_FALSE;
495 }
496
497 RETURN_SUCCESS(UPD_STATIC_PROP(string, eTag, etag));
498 }
499 /* }}} */
500
501 /* {{{ proto static string HttpResponse::getETag()
502 *
503 * Get calculated or previously set custom ETag.
504 */
505 PHP_METHOD(HttpResponse, getETag)
506 {
507 NO_ARGS;
508
509 IF_RETVAL_USED {
510 zval *etag = GET_STATIC_PROP(eTag);
511 RETURN_STRINGL(Z_STRVAL_P(etag), Z_STRLEN_P(etag), 1);
512 }
513 }
514 /* }}} */
515
516 /* {{{ proto static bool HttpResponse::setLastModified(long timestamp)
517 *
518 * Set a custom Last-Modified date.
519 */
520 PHP_METHOD(HttpResponse, setLastModified)
521 {
522 long lm;
523
524 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &lm)) {
525 RETURN_FALSE;
526 }
527
528 RETURN_SUCCESS(UPD_STATIC_PROP(long, lastModified, lm));
529 }
530 /* }}} */
531
532 /* {{{ proto static HttpResponse::getLastModified()
533 *
534 * Get calculated or previously set custom Last-Modified date.
535 */
536 PHP_METHOD(HttpResponse, getLastModified)
537 {
538 NO_ARGS;
539
540 IF_RETVAL_USED {
541 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(lastModified)));
542 }
543 }
544 /* }}} */
545
546 /* {{{ proto static bool HttpResponse::setThrottleDelay(double seconds)
547 *
548 */
549 PHP_METHOD(HttpResponse, setThrottleDelay)
550 {
551 double seconds;
552
553 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &seconds)) {
554 RETURN_FALSE;
555 }
556 RETURN_SUCCESS(UPD_STATIC_PROP(double, throttleDelay, seconds));
557 }
558 /* }}} */
559
560 /* {{{ proto static double HttpResponse::getThrottleDelay()
561 *
562 */
563 PHP_METHOD(HttpResponse, getThrottleDelay)
564 {
565 NO_ARGS;
566
567 IF_RETVAL_USED {
568 RETURN_DOUBLE(Z_DVAL_P(GET_STATIC_PROP(throttleDelay)));
569 }
570 }
571 /* }}} */
572
573 /* {{{ proto static bool HttpResponse::setBufferSize(long bytes)
574 *
575 */
576 PHP_METHOD(HttpResponse, setBufferSize)
577 {
578 long bytes;
579
580 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &bytes)) {
581 RETURN_FALSE;
582 }
583 RETURN_SUCCESS(UPD_STATIC_PROP(long, bufferSize, bytes));
584 }
585 /* }}} */
586
587 /* {{{ proto static long HttpResponse::getBufferSize()
588 *
589 */
590 PHP_METHOD(HttpResponse, getBufferSize)
591 {
592 NO_ARGS;
593
594 IF_RETVAL_USED {
595 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(bufferSize)));
596 }
597 }
598 /* }}} */
599
600 /* {{{ proto static bool HttpResponse::setData(string data)
601 *
602 * Set the data to be sent.
603 */
604 PHP_METHOD(HttpResponse, setData)
605 {
606 zval *the_data;
607
608 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &the_data)) {
609 RETURN_FALSE;
610 }
611 if (Z_TYPE_P(the_data) != IS_STRING) {
612 convert_to_string_ex(&the_data);
613 }
614
615 if ( (SUCCESS != SET_STATIC_PROP(data, the_data)) ||
616 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_DATA))) {
617 RETURN_FALSE;
618 }
619
620 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
621 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_data, SEND_DATA));
622 }
623 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
624 char *etag = http_etag(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), SEND_DATA);
625 UPD_STATIC_PROP(string, eTag, etag);
626 efree(etag);
627 }
628
629 RETURN_TRUE;
630 }
631 /* }}} */
632
633 /* {{{ proto static string HttpResponse::getData()
634 *
635 * Get the previously set data to be sent.
636 */
637 PHP_METHOD(HttpResponse, getData)
638 {
639 NO_ARGS;
640
641 IF_RETVAL_USED {
642 zval *the_data = GET_STATIC_PROP(data);
643 RETURN_STRINGL(Z_STRVAL_P(the_data), Z_STRLEN_P(the_data), 1);
644 }
645 }
646 /* }}} */
647
648 /* {{{ proto static bool HttpResponse::setStream(resource stream)
649 *
650 * Set the resource to be sent.
651 */
652 PHP_METHOD(HttpResponse, setStream)
653 {
654 zval *the_stream;
655 php_stream *the_real_stream;
656
657 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &the_stream)) {
658 RETURN_FALSE;
659 }
660 zend_list_addref(Z_LVAL_P(the_stream));
661 php_stream_from_zval(the_real_stream, &the_stream);
662
663 if ( (SUCCESS != UPD_STATIC_PROP(long, stream, Z_LVAL_P(the_stream))) ||
664 (SUCCESS != UPD_STATIC_PROP(long, mode, SEND_RSRC))) {
665 RETURN_FALSE;
666 }
667
668 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)) > 0)) {
669 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_real_stream, SEND_RSRC));
670 }
671 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
672 char *etag = http_etag(the_real_stream, 0, SEND_RSRC);
673 UPD_STATIC_PROP(string, eTag, etag);
674 efree(etag);
675 }
676
677 RETURN_TRUE;
678 }
679 /* }}} */
680
681 /* {{{ proto static resource HttpResponse::getStream()
682 *
683 * Get the previously set resource to be sent.
684 */
685 PHP_METHOD(HttpResponse, getStream)
686 {
687 NO_ARGS;
688
689 IF_RETVAL_USED {
690 RETURN_RESOURCE(Z_LVAL_P(GET_STATIC_PROP(stream)));
691 }
692 }
693 /* }}} */
694
695 /* {{{ proto static bool HttpResponse::setFile(string file)
696 *
697 * Set the file to be sent.
698 */
699 PHP_METHOD(HttpResponse, setFile)
700 {
701 char *the_file;
702 int file_len;
703
704 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &the_file, &file_len)) {
705 RETURN_FALSE;
706 }
707
708 if ( (SUCCESS != UPD_STATIC_PROP(string, file, the_file)) ||
709 (SUCCESS != UPD_STATIC_PROP(long, mode, -1))) {
710 RETURN_FALSE;
711 }
712
713 if (!(Z_LVAL_P(GET_STATIC_PROP(lastModified)))) {
714 UPD_STATIC_PROP(long, lastModified, http_last_modified(the_file, -1));
715 }
716 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
717 char *etag = http_etag(the_file, 0, -1);
718 UPD_STATIC_PROP(string, eTag, etag);
719 efree(etag);
720 }
721
722 RETURN_TRUE;
723 }
724 /* }}} */
725
726 /* {{{ proto static string HttpResponse::getFile()
727 *
728 * Get the previously set file to be sent.
729 */
730 PHP_METHOD(HttpResponse, getFile)
731 {
732 NO_ARGS;
733
734 IF_RETVAL_USED {
735 zval *the_file = GET_STATIC_PROP(file);
736 RETURN_STRINGL(Z_STRVAL_P(the_file), Z_STRLEN_P(the_file), 1);
737 }
738 }
739 /* }}} */
740
741 /* {{{ proto static bool HttpResponse::send([bool clean_ob = true])
742 *
743 * Finally send the entity.
744 *
745 * Example:
746 * <pre>
747 * <?php
748 * HttpResponse::setCache(true);
749 * HttpResponse::setContentType('application/pdf');
750 * HttpResponse::setContentDisposition("$user.pdf", false);
751 * HttpResponse::setFile('sheet.pdf');
752 * HttpResponse::send();
753 * ?>
754 * </pre>
755 */
756 PHP_METHOD(HttpResponse, send)
757 {
758 zval *sent, *headers;
759 zend_bool clean_ob = 1;
760
761 if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean_ob)) {
762 RETURN_FALSE;
763 }
764 if (SG(headers_sent)) {
765 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, headers have already been sent");
766 RETURN_FALSE;
767 }
768
769 sent = GET_STATIC_PROP(sent);
770 if (Z_LVAL_P(sent)) {
771 http_error(HE_WARNING, HTTP_E_RESPONSE, "Cannot send HttpResponse, response has already been sent");
772 RETURN_FALSE;
773 } else {
774 Z_LVAL_P(sent) = 1;
775 }
776
777 /* capture mode */
778 if (Z_BVAL_P(GET_STATIC_PROP(catch))) {
779 zval the_data;
780
781 INIT_PZVAL(&the_data);
782 php_ob_get_buffer(&the_data TSRMLS_CC);
783 SET_STATIC_PROP(data, &the_data);
784 ZVAL_LONG(GET_STATIC_PROP(mode), SEND_DATA);
785
786 if (!Z_STRLEN_P(GET_STATIC_PROP(eTag))) {
787 char *etag = http_etag(Z_STRVAL(the_data), Z_STRLEN(the_data), SEND_DATA);
788 UPD_STATIC_PROP(string, eTag, etag);
789 efree(etag);
790 }
791 zval_dtor(&the_data);
792
793 clean_ob = 1;
794 }
795
796 if (clean_ob) {
797 /* interrupt on-the-fly etag generation */
798 HTTP_G(etag).started = 0;
799 /* discard previous output buffers */
800 php_end_ob_buffers(0 TSRMLS_CC);
801 }
802
803 /* custom headers */
804 headers = GET_STATIC_PROP(headers);
805 if (Z_TYPE_P(headers) == IS_ARRAY) {
806 char *name = NULL;
807 ulong idx = 0;
808 zval **value;
809
810 FOREACH_KEYVAL(headers, name, idx, value) {
811 if (name) {
812 if (Z_TYPE_PP(value) == IS_ARRAY) {
813 zend_bool first = 1;
814 zval **data;
815
816 FOREACH_VAL(*value, data) {
817 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL);
818 first = 0;
819 }
820 } else {
821 http_send_header_ex(name, strlen(name), Z_STRVAL_PP(value), Z_STRLEN_PP(value), 1, NULL);
822 }
823 name = NULL;
824 }
825 }
826 }
827
828 /* gzip */
829 if (Z_LVAL_P(GET_STATIC_PROP(gzip))) {
830 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC);
831 } else {
832 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
833 }
834
835 /* caching */
836 if (Z_LVAL_P(GET_STATIC_PROP(cache))) {
837 zval *cctl, *etag, *lmod;
838
839 etag = GET_STATIC_PROP(eTag);
840 lmod = GET_STATIC_PROP(lastModified);
841 cctl = GET_STATIC_PROP(cacheControl);
842
843 http_cache_etag(Z_STRVAL_P(etag), Z_STRLEN_P(etag), Z_STRVAL_P(cctl), Z_STRLEN_P(cctl));
844 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));
845 }
846
847 /* content type */
848 {
849 zval *ctype = GET_STATIC_PROP(contentType);
850 if (Z_STRLEN_P(ctype)) {
851 http_send_content_type(Z_STRVAL_P(ctype), Z_STRLEN_P(ctype));
852 } else {
853 char *ctypes = INI_STR("default_mimetype");
854 size_t ctlen = ctypes ? strlen(ctypes) : 0;
855
856 if (ctlen) {
857 http_send_content_type(ctypes, ctlen);
858 } else {
859 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
860 }
861 }
862 }
863
864 /* content disposition */
865 {
866 zval *cd = GET_STATIC_PROP(contentDisposition);
867 if (Z_STRLEN_P(cd)) {
868 http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL);
869 }
870 }
871
872 /* throttling */
873 {
874 HTTP_G(send).buffer_size = Z_LVAL_P(GET_STATIC_PROP(bufferSize));
875 HTTP_G(send).throttle_delay = Z_DVAL_P(GET_STATIC_PROP(throttleDelay));
876 }
877
878 /* send */
879 {
880 switch (Z_LVAL_P(GET_STATIC_PROP(mode)))
881 {
882 case SEND_DATA:
883 {
884 zval *zdata = GET_STATIC_PROP(data);
885 RETURN_SUCCESS(http_send_data_ex(Z_STRVAL_P(zdata), Z_STRLEN_P(zdata), 1));
886 }
887
888 case SEND_RSRC:
889 {
890 php_stream *the_real_stream;
891 zval *the_stream = GET_STATIC_PROP(stream);
892 the_stream->type = IS_RESOURCE;
893 php_stream_from_zval(the_real_stream, &the_stream);
894 RETURN_SUCCESS(http_send_stream_ex(the_real_stream, 0, 1));
895 }
896
897 default:
898 {
899 RETURN_SUCCESS(http_send_file_ex(Z_STRVAL_P(GET_STATIC_PROP(file)), 1));
900 }
901 }
902 }
903 }
904 /* }}} */
905
906 /* {{{ proto static void HttpResponse::capture()
907 *
908 * Capture script output.
909 *
910 * Example:
911 * <pre>
912 * <?php
913 * HttpResponse::setCache(true);
914 * HttpResponse::capture();
915 * // script follows
916 * // note that you need to call
917 * HttpResponse::send();
918 * // at the end of the script unless
919 * // you use PHP-5.1 or greater
920 * ?>
921 * </pre>
922 */
923 PHP_METHOD(HttpResponse, capture)
924 {
925 zval do_catch;
926
927 NO_ARGS;
928
929 INIT_PZVAL(&do_catch);
930 ZVAL_LONG(&do_catch, 1);
931
932 SET_STATIC_PROP(catch, &do_catch);
933
934 php_end_ob_buffers(0 TSRMLS_CC);
935 php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC);
936
937 #if (PHP_MAJOR_VERSION > 5) || (PHP_MINOR_VERSION > 0)
938 /* register shutdown function --
939 messing around with ob and headers only works in PHP-5.1 or greater */
940 {
941 zval func, retval, arg, *argp[1];
942
943 INIT_PZVAL(&arg);
944 INIT_PZVAL(&func);
945 INIT_PZVAL(&retval);
946 ZVAL_STRINGL(&func, "register_shutdown_function", lenof("register_shutdown_function"), 0);
947
948 array_init(&arg);
949 add_next_index_stringl(&arg, "HttpResponse", lenof("HttpResponse"), 1);
950 add_next_index_stringl(&arg, "send", lenof("send"), 1);
951 argp[0] = &arg;
952 call_user_function(EG(function_table), NULL, &func, &retval, 1, argp TSRMLS_CC);
953 zval_dtor(&arg);
954 }
955 #endif
956 }
957 /* }}} */
958
959 #endif /* ZEND_ENGINE_2 */
960
961 /*
962 * Local variables:
963 * tab-width: 4
964 * c-basic-offset: 4
965 * End:
966 * vim600: noet sw=4 ts=4 fdm=marker
967 * vim<600: noet sw=4 ts=4
968 */
969