2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
30 #include "php_http_api.h"
31 #include "php_http_std_defs.h"
32 #include "php_http_response_object.h"
33 #include "php_http_exception_object.h"
34 #include "php_http_send_api.h"
35 #include "php_http_cache_api.h"
39 ZEND_EXTERN_MODULE_GLOBALS(http
);
41 #define GET_STATIC_PROP(n) *GET_STATIC_PROP_EX(http_response_object_ce, n)
43 #define HTTP_BEGIN_ARGS(method, req_args) HTTP_BEGIN_ARGS_EX(HttpResponse, method, 0, req_args)
44 #define HTTP_EMPTY_ARGS(method, ret_ref) HTTP_EMPTY_ARGS_EX(HttpResponse, method, ret_ref)
45 #define HTTP_RESPONSE_ME(method, visibility) PHP_ME(HttpResponse, method, HTTP_ARGS(HttpResponse, method), visibility|ZEND_ACC_STATIC)
47 HTTP_EMPTY_ARGS(getETag
, 0);
48 HTTP_BEGIN_ARGS(setETag
, 1)
52 HTTP_EMPTY_ARGS(getCache
, 0);
53 HTTP_BEGIN_ARGS(setCache
, 1)
54 HTTP_ARG_VAL(cache
, 0)
57 HTTP_EMPTY_ARGS(getGzip
, 0);
58 HTTP_BEGIN_ARGS(setGzip
, 1)
62 HTTP_EMPTY_ARGS(getCacheControl
, 0);
63 HTTP_BEGIN_ARGS(setCacheControl
, 1)
64 HTTP_ARG_VAL(cache_control
, 0)
65 HTTP_ARG_VAL(max_age
, 0)
68 HTTP_EMPTY_ARGS(getContentType
, 0);
69 HTTP_BEGIN_ARGS(setContentType
, 1)
70 HTTP_ARG_VAL(content_type
, 0)
73 HTTP_EMPTY_ARGS(getContentDisposition
, 0);
74 HTTP_BEGIN_ARGS(setContentDisposition
, 1)
75 HTTP_ARG_VAL(filename
, 0)
76 HTTP_ARG_VAL(send_inline
, 0)
79 HTTP_EMPTY_ARGS(getThrottleDelay
, 0);
80 HTTP_BEGIN_ARGS(setThrottleDelay
, 1)
81 HTTP_ARG_VAL(seconds
, 0)
84 HTTP_EMPTY_ARGS(getBufferSize
, 0);
85 HTTP_BEGIN_ARGS(setBufferSize
, 1)
86 HTTP_ARG_VAL(bytes
, 0)
89 HTTP_EMPTY_ARGS(getData
, 0);
90 HTTP_BEGIN_ARGS(setData
, 1)
94 HTTP_EMPTY_ARGS(getStream
, 0);
95 HTTP_BEGIN_ARGS(setStream
, 1)
96 HTTP_ARG_VAL(stream
, 0)
99 HTTP_EMPTY_ARGS(getFile
, 0);
100 HTTP_BEGIN_ARGS(setFile
, 1)
101 HTTP_ARG_VAL(filepath
, 0)
104 HTTP_BEGIN_ARGS(send
, 0)
105 HTTP_ARG_VAL(clean_ob
, 0)
108 #define http_response_object_declare_default_properties() _http_response_object_declare_default_properties(TSRMLS_C)
109 static inline void _http_response_object_declare_default_properties(TSRMLS_D
);
111 zend_class_entry
*http_response_object_ce
;
112 zend_function_entry http_response_object_fe
[] = {
114 HTTP_RESPONSE_ME(setETag
, ZEND_ACC_PUBLIC
)
115 HTTP_RESPONSE_ME(getETag
, ZEND_ACC_PUBLIC
)
117 HTTP_RESPONSE_ME(setContentDisposition
, ZEND_ACC_PUBLIC
)
118 HTTP_RESPONSE_ME(getContentDisposition
, ZEND_ACC_PUBLIC
)
120 HTTP_RESPONSE_ME(setContentType
, ZEND_ACC_PUBLIC
)
121 HTTP_RESPONSE_ME(getContentType
, ZEND_ACC_PUBLIC
)
123 HTTP_RESPONSE_ME(setCache
, ZEND_ACC_PUBLIC
)
124 HTTP_RESPONSE_ME(getCache
, ZEND_ACC_PUBLIC
)
126 HTTP_RESPONSE_ME(setCacheControl
, ZEND_ACC_PUBLIC
)
127 HTTP_RESPONSE_ME(getCacheControl
, ZEND_ACC_PUBLIC
)
129 HTTP_RESPONSE_ME(setGzip
, ZEND_ACC_PUBLIC
)
130 HTTP_RESPONSE_ME(getGzip
, ZEND_ACC_PUBLIC
)
132 HTTP_RESPONSE_ME(setThrottleDelay
, ZEND_ACC_PUBLIC
)
133 HTTP_RESPONSE_ME(getThrottleDelay
, ZEND_ACC_PUBLIC
)
135 HTTP_RESPONSE_ME(setBufferSize
, ZEND_ACC_PUBLIC
)
136 HTTP_RESPONSE_ME(getBufferSize
, ZEND_ACC_PUBLIC
)
138 HTTP_RESPONSE_ME(setData
, ZEND_ACC_PUBLIC
)
139 HTTP_RESPONSE_ME(getData
, ZEND_ACC_PUBLIC
)
141 HTTP_RESPONSE_ME(setFile
, ZEND_ACC_PUBLIC
)
142 HTTP_RESPONSE_ME(getFile
, ZEND_ACC_PUBLIC
)
144 HTTP_RESPONSE_ME(setStream
, ZEND_ACC_PUBLIC
)
145 HTTP_RESPONSE_ME(getStream
, ZEND_ACC_PUBLIC
)
147 HTTP_RESPONSE_ME(send
, ZEND_ACC_PUBLIC
)
151 static zend_object_handlers http_response_object_handlers
;
153 void _http_response_object_init(INIT_FUNC_ARGS
)
155 HTTP_REGISTER_CLASS(HttpResponse
, http_response_object
, NULL
, 0);
156 http_response_object_declare_default_properties();
159 static inline void _http_response_object_declare_default_properties(TSRMLS_D
)
161 zend_class_entry
*ce
= http_response_object_ce
;
163 DCL_STATIC_PROP(PRIVATE
, bool, sent
, 0);
164 DCL_STATIC_PROP(PRIVATE
, bool, catch, 0);
165 DCL_STATIC_PROP(PRIVATE
, long, mode
, -1);
166 DCL_STATIC_PROP(PROTECTED
, bool, cache
, 0);
167 DCL_STATIC_PROP(PROTECTED
, bool, gzip
, 0);
168 DCL_STATIC_PROP(PROTECTED
, long, stream
, 0);
169 DCL_STATIC_PROP(PROTECTED
, string
, file
, "");
170 DCL_STATIC_PROP(PROTECTED
, string
, data
, "");
171 DCL_STATIC_PROP(PROTECTED
, string
, eTag
, "");
172 DCL_STATIC_PROP(PROTECTED
, long, lastModified
, 0);
173 DCL_STATIC_PROP(PROTECTED
, string
, cacheControl
, HTTP_DEFAULT_CACHECONTROL
);
174 DCL_STATIC_PROP(PROTECTED
, string
, contentType
, INI_STR("default_content_type"));
175 DCL_STATIC_PROP(PROTECTED
, string
, contentDisposition
, "");
176 DCL_STATIC_PROP(PROTECTED
, long, bufferSize
, HTTP_SENDBUF_SIZE
);
177 DCL_STATIC_PROP(PROTECTED
, double, throttleDelay
, 0.0);
180 /* ### USERLAND ### */
182 /* {{{ proto bool HttpResponse::setCache(bool cache)
184 * Whether it sould be attempted to cache the entitity.
185 * This will result in necessary caching headers and checks of clients
186 * "If-Modified-Since" and "If-None-Match" headers. If one of those headers
187 * matches a "304 Not Modified" status code will be issued.
189 * NOTE: If you're using sessions, be shure that you set session.cache_limiter
190 * to something more appropriate than "no-cache"!
192 PHP_METHOD(HttpResponse
, setCache
)
194 zend_bool do_cache
= 0;
196 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "b", &do_cache
)) {
200 ZVAL_LONG(GET_STATIC_PROP(cache
), do_cache
);
205 /* {{{ proto bool HttpResponse::getCache()
207 * Get current caching setting.
209 PHP_METHOD(HttpResponse
, getCache
)
214 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(cache
)));
219 /* {{{ proto bool HttpResponse::setGzip(bool gzip)
221 * Enable on-thy-fly gzipping of the sent entity. NOT IMPLEMENTED YET.
223 PHP_METHOD(HttpResponse
, setGzip
)
225 zend_bool do_gzip
= 0;
227 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "b", &do_gzip
)) {
231 ZVAL_LONG(GET_STATIC_PROP(gzip
), do_gzip
);
236 /* {{{ proto bool HttpResponse::getGzip()
238 * Get current gzipping setting.
240 PHP_METHOD(HttpResponse
, getGzip
)
245 RETURN_BOOL(Z_LVAL_P(GET_STATIC_PROP(gzip
)));
250 /* {{{ proto bool HttpResponse::setCacheControl(string control[, long max_age = 0])
252 * Set a custom cache-control header, usually being "private" or "public";
253 * The max_age parameter controls how long the cache entry is valid on the client side.
255 PHP_METHOD(HttpResponse
, setCacheControl
)
257 char *ccontrol
, *cctl
;
261 #define HTTP_CACHECONTROL_TEMPLATE "%s, must-revalidate, max_age=%ld"
263 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|b", &ccontrol
, &cc_len
, &max_age
)) {
267 if (strcmp(ccontrol
, "public") && strcmp(ccontrol
, "private") && strcmp(ccontrol
, "no-cache")) {
268 http_error_ex(E_WARNING
, HTTP_E_PARAM
, "Cache-Control '%s' doesn't match public, private or no-cache", ccontrol
);
271 spprintf(&cctl
, 0, HTTP_CACHECONTROL_TEMPLATE
, ccontrol
, max_age
);
272 ZVAL_STRING_FREE(GET_STATIC_PROP(cacheControl
), cctl
, 0);
278 /* {{{ proto string HttpResponse::getCacheControl()
280 * Get current Cache-Control header setting.
282 PHP_METHOD(HttpResponse
, getCacheControl
)
287 zval
*ccontrol
= GET_STATIC_PROP(cacheControl
);
288 RETURN_STRINGL(Z_STRVAL_P(ccontrol
), Z_STRLEN_P(ccontrol
), 1);
293 /* {{{ proto bool HttpResponse::setContentType(string content_type)
295 * Set the content-type of the sent entity.
297 PHP_METHOD(HttpResponse
, setContentType
)
302 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &ctype
, &ctype_len
)) {
306 if (!strchr(ctype
, '/')) {
307 http_error_ex(E_WARNING
, HTTP_E_PARAM
, "Content type '%s' doesn't seem to contain a primary and a secondary part", ctype
);
311 ZVAL_STRINGL_FREE(GET_STATIC_PROP(contentType
), ctype
, ctype_len
, 1);
316 /* {{{ proto string HttpResponse::getContentType()
318 * Get current Content-Type header setting.
320 PHP_METHOD(HttpResponse
, getContentType
)
325 zval
*ctype
= GET_STATIC_PROP(contentType
);
326 RETURN_STRINGL(Z_STRVAL_P(ctype
), Z_STRLEN_P(ctype
), 1);
331 /* {{{ proto bool HttpResponse::setContentDisposition(string filename[, bool inline = false])
333 * Set the Content-Disposition of the sent entity. This setting aims to suggest
334 * the receiveing user agent how to handle the sent entity; usually the client
335 * will show the user a "Save As..." popup.
337 PHP_METHOD(HttpResponse
, setContentDisposition
)
341 zend_bool send_inline
= 0;
343 #define HTTP_CONTENTDISPOSITION_TEMPLATE "%s; filename=\"%s\""
345 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s|b", &file
, &file_len
, &send_inline
)) {
349 spprintf(&cd
, 0, HTTP_CONTENTDISPOSITION_TEMPLATE
, send_inline
? "inline" : "attachment", file
);
350 ZVAL_STRING_FREE(GET_STATIC_PROP(contentDisposition
), cd
, 0);
355 /* {{{ proto string HttpResponse::getContentDisposition()
357 * Get current Content-Disposition setting.
359 PHP_METHOD(HttpResponse
, getContentDisposition
)
364 zval
*cd
= GET_STATIC_PROP(contentDisposition
);
365 RETURN_STRINGL(Z_STRVAL_P(cd
), Z_STRLEN_P(cd
), 1);
370 /* {{{ proto bool HttpResponse::setETag(string etag)
372 * Set a custom ETag. Use this only if you know what you're doing.
374 PHP_METHOD(HttpResponse
, setETag
)
379 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "s", &etag
, &etag_len
)) {
383 ZVAL_STRINGL_FREE(GET_STATIC_PROP(eTag
), etag
, etag_len
, 1);
388 /* {{{ proto string HttpResponse::getETag()
390 * Get the previously set custom ETag.
392 PHP_METHOD(HttpResponse
, getETag
)
397 zval
*etag
= GET_STATIC_PROP(eTag
);
398 RETURN_STRINGL(Z_STRVAL_P(etag
), Z_STRLEN_P(etag
), 1);
403 /* {{{ proto void HttpResponse::setThrottleDelay(double seconds)
406 PHP_METHOD(HttpResponse
, setThrottleDelay
)
410 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "d", &seconds
)) {
411 ZVAL_DOUBLE(GET_STATIC_PROP(throttleDelay
), seconds
);
416 /* {{{ proto double HttpResponse::getThrottleDelay()
419 PHP_METHOD(HttpResponse
, getThrottleDelay
)
424 RETURN_DOUBLE(Z_DVAL_P(GET_STATIC_PROP(throttleDelay
)));
429 /* {{{ proto void HttpResponse::setBufferSize(long bytes)
432 PHP_METHOD(HttpResponse
, setBufferSize
)
436 if (SUCCESS
== zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "l", &bytes
)) {
437 ZVAL_LONG(GET_STATIC_PROP(bufferSize
), bytes
);
442 /* {{{ proto long HttpResponse::getBufferSize()
445 PHP_METHOD(HttpResponse
, getBufferSize
)
450 RETURN_LONG(Z_LVAL_P(GET_STATIC_PROP(bufferSize
)));
455 /* {{{ proto bool HttpResponse::setData(string data)
457 * Set the data to be sent.
459 PHP_METHOD(HttpResponse
, setData
)
461 zval
*the_data
, *data
;
463 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z/", &the_data
)) {
467 convert_to_string_ex(&the_data
);
468 data
= GET_STATIC_PROP(data
);
469 ZVAL_STRINGL_FREE(data
, Z_STRVAL_P(the_data
), Z_STRLEN_P(the_data
), 1);
470 ZVAL_LONG(GET_STATIC_PROP(lastModified
), http_last_modified(the_data
, SEND_DATA
));
471 ZVAL_LONG(GET_STATIC_PROP(mode
), SEND_DATA
);
476 /* {{{ proto string HttpResponse::getData()
478 * Get the previously set data to be sent.
480 PHP_METHOD(HttpResponse
, getData
)
485 zval
*the_data
= GET_STATIC_PROP(data
);
486 RETURN_STRINGL(Z_STRVAL_P(the_data
), Z_STRLEN_P(the_data
), 1);
491 /* {{{ proto bool HttpResponse::setStream(resource stream)
493 * Set the resource to be sent.
495 PHP_METHOD(HttpResponse
, setStream
)
498 php_stream
*the_real_stream
;
500 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "r", &the_stream
)) {
504 php_stream_from_zval(the_real_stream
, &the_stream
);
505 ZVAL_LONG(GET_STATIC_PROP(stream
), Z_LVAL_P(the_stream
));
506 ZVAL_LONG(GET_STATIC_PROP(lastModified
), http_last_modified(the_real_stream
, SEND_RSRC
));
507 ZVAL_LONG(GET_STATIC_PROP(mode
), SEND_RSRC
);
512 /* {{{ proto resource HttpResponse::getStream()
514 * Get the previously set resource to be sent.
516 PHP_METHOD(HttpResponse
, getStream
)
521 RETURN_RESOURCE(Z_LVAL_P(GET_STATIC_PROP(stream
)));
526 /* {{{ proto bool HttpResponse::setFile(string file)
528 * Set the file to be sent.
530 PHP_METHOD(HttpResponse
, setFile
)
534 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "z/", &the_file
)) {
538 convert_to_string_ex(&the_file
);
539 ZVAL_STRINGL_FREE(GET_STATIC_PROP(file
), Z_STRVAL_P(the_file
), Z_STRLEN_P(the_file
), 1);
540 ZVAL_LONG(GET_STATIC_PROP(lastModified
), http_last_modified(the_file
, -1));
541 ZVAL_LONG(GET_STATIC_PROP(mode
), -1);
547 /* {{{ proto string HttpResponse::getFile()
549 * Get the previously set file to be sent.
551 PHP_METHOD(HttpResponse
, getFile
)
556 zval
*the_file
= GET_STATIC_PROP(file
);
557 RETURN_STRINGL(Z_STRVAL_P(the_file
), Z_STRLEN_P(the_file
), 1);
562 /* {{{ proto bool HttpResponse::send([bool clean_ob = true])
564 * Finally send the entity.
566 PHP_METHOD(HttpResponse
, send
)
568 zval
*do_cache
, *do_gzip
;
569 zend_bool clean_ob
= 1;
571 if (SUCCESS
!= zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "|b", &clean_ob
)) {
576 /* interrupt on-the-fly etag generation */
577 HTTP_G(etag
).started
= 0;
578 /* discard previous output buffers */
579 //php_end_ob_buffers(0 TSRMLS_CC);
583 if (Z_LVAL_P(GET_STATIC_PROP(gzip
))) {
584 php_start_ob_buffer_named("ob_gzhandler", 0, 1 TSRMLS_CC
);
588 if (Z_LVAL_P(GET_STATIC_PROP(cache
))) {
591 zval
*cctl
, *etag
, *lmod
;
593 etag
= GET_STATIC_PROP(eTag
);
594 lmod
= GET_STATIC_PROP(lastModified
);
595 cctl
= GET_STATIC_PROP(cacheControl
);
597 http_cache_etag(Z_STRVAL_P(etag
), Z_STRLEN_P(etag
),Z_STRVAL_P(cctl
), Z_STRLEN_P(cctl
));
598 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
));
603 zval
*ctype
= GET_STATIC_PROP(contentType
);
604 if (Z_STRLEN_P(ctype
)) {
605 http_send_content_type(Z_STRVAL_P(ctype
), Z_STRLEN_P(ctype
));
607 http_send_content_type("application/x-octetstream", lenof("application/x-octetstream"));
611 /* content disposition */
613 zval
*cd
= GET_STATIC_PROP(contentDisposition
);
614 if (Z_STRLEN_P(cd
)) {
617 spprintf(&cds
, 0, "Content-Disposition: %s", Z_STRVAL_P(cd
));
618 http_send_header(cds
);
625 HTTP_G(send
).buffer_size
= Z_LVAL_P(GET_STATIC_PROP(bufferSize
));
626 HTTP_G(send
).throttle_delay
= Z_DVAL_P(GET_STATIC_PROP(throttleDelay
));
631 switch (Z_LVAL_P(GET_STATIC_PROP(mode
)))
635 zval
*zdata
= GET_STATIC_PROP(data
);
636 RETURN_SUCCESS(http_send_data(Z_STRVAL_P(zdata
), Z_STRLEN_P(zdata
)));
641 php_stream
*the_real_stream
;
642 zval
*the_stream
= GET_STATIC_PROP(stream
);
643 php_stream_from_zval(the_real_stream
, &the_stream
);
644 RETURN_SUCCESS(http_send_stream(the_real_stream
));
649 RETURN_SUCCESS(http_send_file(Z_STRVAL_P(GET_STATIC_PROP(file
))));
656 #endif /* ZEND_ENGINE_2 */
663 * vim600: noet sw=4 ts=4 fdm=marker
664 * vim<600: noet sw=4 ts=4