Development

/branches/1.0/lib/response/sfWebResponse.class.php

You must first sign up to be able to contribute.

root/branches/1.0/lib/response/sfWebResponse.class.php

Revision 24619, 15.2 kB (checked in by FabianLange, 5 years ago)

[1.0, 1.2, 1.3, 1.4] fixed incorrect array access of lastModified header which only was an array pre 1.0. This was effectively preventing 304 Not Modified response from working correctly. Fixed phpdoc referring to array as return type of getHttpHeader() (fixes #6633, #7539)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * sfWebResponse class.
13  *
14  * This class manages web reponses. It supports cookies and headers management.
15  *
16  * @package    symfony
17  * @subpackage response
18  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
19  * @version    SVN: $Id$
20  */
21 class sfWebResponse extends sfResponse
22 {
23   protected
24     $cookies     = array(),
25     $statusCode  = 200,
26     $statusText  = 'OK',
27     $statusTexts = array(),
28     $headerOnly  = false;
29
30   /**
31    * Initializes this sfWebResponse.
32    *
33    * @param sfContext A sfContext instance
34    *
35    * @return boolean true, if initialization completes successfully, otherwise false
36    *
37    * @throws <b>sfInitializationException</b> If an error occurs while initializing this Response
38    */
39   public function initialize($context, $parameters = array())
40   {
41     parent::initialize($context, $parameters);
42
43     if ('HEAD' == $context->getRequest()->getMethodName())
44     {
45       $this->setHeaderOnly(true);
46     }
47
48     $this->statusTexts = array(
49       '100' => 'Continue',
50       '101' => 'Switching Protocols',
51       '200' => 'OK',
52       '201' => 'Created',
53       '202' => 'Accepted',
54       '203' => 'Non-Authoritative Information',
55       '204' => 'No Content',
56       '205' => 'Reset Content',
57       '206' => 'Partial Content',
58       '300' => 'Multiple Choices',
59       '301' => 'Moved Permanently',
60       '302' => 'Found',
61       '303' => 'See Other',
62       '304' => 'Not Modified',
63       '305' => 'Use Proxy',
64       '306' => '(Unused)',
65       '307' => 'Temporary Redirect',
66       '400' => 'Bad Request',
67       '401' => 'Unauthorized',
68       '402' => 'Payment Required',
69       '403' => 'Forbidden',
70       '404' => 'Not Found',
71       '405' => 'Method Not Allowed',
72       '406' => 'Not Acceptable',
73       '407' => 'Proxy Authentication Required',
74       '408' => 'Request Timeout',
75       '409' => 'Conflict',
76       '410' => 'Gone',
77       '411' => 'Length Required',
78       '412' => 'Precondition Failed',
79       '413' => 'Request Entity Too Large',
80       '414' => 'Request-URI Too Long',
81       '415' => 'Unsupported Media Type',
82       '416' => 'Requested Range Not Satisfiable',
83       '417' => 'Expectation Failed',
84       '500' => 'Internal Server Error',
85       '501' => 'Not Implemented',
86       '502' => 'Bad Gateway',
87       '503' => 'Service Unavailable',
88       '504' => 'Gateway Timeout',
89       '505' => 'HTTP Version Not Supported',
90     );
91   }
92
93   /**
94    * Sets if the response consist of just HTTP headers.
95    *
96    * @param boolean
97    */
98   public function setHeaderOnly($value = true)
99   {
100     $this->headerOnly = (boolean) $value;
101   }
102
103   /**
104    * Returns if the response must only consist of HTTP headers.
105    *
106    * @return boolean returns true if, false otherwise
107    */
108   public function isHeaderOnly()
109   {
110     return $this->headerOnly;
111   }
112
113   /**
114    * Sets a cookie.
115    *
116    * @param string HTTP header name
117    * @param string Value for the cookie
118    * @param string Cookie expiration period
119    * @param string Path
120    * @param string Domain name
121    * @param boolean If secure
122    * @param boolean If uses only HTTP
123    *
124    * @throws <b>sfException</b> If fails to set the cookie
125    */
126   public function setCookie($name, $value, $expire = null, $path = '/', $domain = '', $secure = false, $httpOnly = false)
127   {
128     if ($expire !== null)
129     {
130       if (is_numeric($expire))
131       {
132         $expire = (int) $expire;
133       }
134       else
135       {
136         $expire = strtotime($expire);
137         if ($expire === false || $expire == -1)
138         {
139           throw new sfException('Your expire parameter is not valid.');
140         }
141       }
142     }
143
144     $this->cookies[] = array(
145       'name'     => $name,
146       'value'    => $value,
147       'expire'   => $expire,
148       'path'     => $path,
149       'domain'   => $domain,
150       'secure'   => $secure ? true : false,
151       'httpOnly' => $httpOnly,
152     );
153   }
154
155   /**
156    * Sets response status code.
157    *
158    * @param string HTTP status code
159    * @param string HTTP status text
160    *
161    */
162   public function setStatusCode($code, $name = null)
163   {
164     $this->statusCode = $code;
165     $this->statusText = null !== $name ? $name : $this->statusTexts[$code];
166   }
167
168   /**
169    * Retrieves status code for the current web response.
170    *
171    * @return string Status code
172    */
173   public function getStatusCode()
174   {
175     return $this->statusCode;
176   }
177
178   /**
179    * Sets a HTTP header.
180    *
181    * @param string HTTP header name
182    * @param string Value
183    * @param boolean Replace for the value
184    *
185    */
186   public function setHttpHeader($name, $value, $replace = true)
187   {
188     $name = $this->normalizeHeaderName($name);
189
190     if ('Content-Type' == $name)
191     {
192       if ($replace || !$this->getHttpHeader('Content-Type', null))
193       {
194         $this->setContentType($value);
195       }
196
197       return;
198     }
199
200     if (!$replace)
201     {
202       $current = $this->getParameter($name, '', 'symfony/response/http/headers');
203       $value = ($current ? $current.', ' : '').$value;
204     }
205
206     $this->setParameter($name, $value, 'symfony/response/http/headers');
207   }
208
209   /**
210    * Gets HTTP header current value.
211    *
212    * @return string
213    */
214   public function getHttpHeader($name, $default = null)
215   {
216     return $this->getParameter($this->normalizeHeaderName($name), $default, 'symfony/response/http/headers');
217   }
218
219   /**
220    * Has a HTTP header.
221    *
222    * @return boolean
223    */
224   public function hasHttpHeader($name)
225   {
226     return $this->hasParameter($this->normalizeHeaderName($name), 'symfony/response/http/headers');
227   }
228
229   /**
230    * Sets response content type.
231    *
232    * @param string Content type
233    *
234    */
235   public function setContentType($value)
236   {
237     // add charset if needed (only on text content)
238     if (false === stripos($value, 'charset') && (0 === stripos($value, 'text/') || strlen($value) - 3 === strripos($value, 'xml')))
239     {
240       $value .= '; charset='.sfConfig::get('sf_charset');
241     }
242
243     $this->setParameter('Content-Type', $value, 'symfony/response/http/headers');
244   }
245
246   /**
247    * Gets response content type.
248    *
249    * @return array
250    */
251   public function getContentType()
252   {
253     return $this->getHttpHeader('Content-Type', 'text/html; charset='.sfConfig::get('sf_charset'));
254   }
255
256   /**
257    * Send HTTP headers and cookies.
258    *
259    */
260   public function sendHttpHeaders()
261   {
262     $headers = $this->getParameterHolder()->getAll('symfony/response/http/headers');
263
264     // status
265     $status = 'HTTP/1.0 '.$this->statusCode.' '.$this->statusText;
266     header($status);
267
268     if (substr(php_sapi_name(), 0, 3) == 'cgi')
269     {
270       // fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line
271       // so we can safely unset them. see ticket #3191
272       unset($headers['Status']);
273     }
274
275     if (sfConfig::get('sf_logging_enabled'))
276     {
277       $this->getContext()->getLogger()->info('{sfResponse} send status "'.$status.'"');
278     }
279
280     // headers
281     foreach ($headers as $name => $value)
282     {
283       header($name.': '.$value);
284
285       if (sfConfig::get('sf_logging_enabled') && $value != '')
286       {
287         $this->getContext()->getLogger()->info('{sfResponse} send header "'.$name.'": "'.$value.'"');
288       }
289     }
290
291     // cookies
292     foreach ($this->cookies as $cookie)
293     {
294       if (version_compare(phpversion(), '5.2', '>='))
295       {
296         setrawcookie($cookie['name'], $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httpOnly']);
297       }
298       else
299       {
300         setrawcookie($cookie['name'], $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure']);
301       }
302
303       if (sfConfig::get('sf_logging_enabled'))
304       {
305         $this->getContext()->getLogger()->info('{sfResponse} send cookie "'.$cookie['name'].'": "'.$cookie['value'].'"');
306       }
307     }
308   }
309
310   /**
311    * Send content for the current web response.
312    *
313    */
314   public function sendContent()
315   {
316     if (!$this->headerOnly)
317     {
318       parent::sendContent();
319     }
320   }
321
322   /**
323    * Retrieves a normalized Header.
324    *
325    * @param string Header name
326    *
327    * @return string Normalized header
328    */
329   protected function normalizeHeaderName($name)
330   {
331     return preg_replace('/\-(.)/e', "'-'.strtoupper('\\1')", strtr(ucfirst(strtolower($name)), '_', '-'));
332   }
333
334   /**
335    * Retrieves a formated date.
336    *
337    * @param string Timestamp
338    * @param string Format type
339    *
340    * @return string Formated date
341    */
342   public function getDate($timestamp, $type = 'rfc1123')
343   {
344     $type = strtolower($type);
345
346     if ($type == 'rfc1123')
347     {
348       return substr(gmdate('r', $timestamp), 0, -5).'GMT';
349     }
350     else if ($type == 'rfc1036')
351     {
352       return gmdate('l, d-M-y H:i:s ', $timestamp).'GMT';
353     }
354     else if ($type == 'asctime')
355     {
356       return gmdate('D M j H:i:s', $timestamp);
357     }
358     else
359     {
360       $error = 'The second getDate() method parameter must be one of: rfc1123, rfc1036 or asctime';
361
362       throw new sfParameterException($error);
363     }
364   }
365
366   /**
367    * Adds vary to a http header.
368    *
369    * @param string HTTP header
370    */
371   public function addVaryHttpHeader($header)
372   {
373     $vary = $this->getHttpHeader('Vary');
374     $currentHeaders = array();
375     if ($vary)
376     {
377       $currentHeaders = preg_split('/\s*,\s*/', $vary);
378     }
379     $header = $this->normalizeHeaderName($header);
380
381     if (!in_array($header, $currentHeaders))
382     {
383       $currentHeaders[] = $header;
384       $this->setHttpHeader('Vary', implode(', ', $currentHeaders));
385     }
386   }
387
388   /**
389    * Adds an control cache http header.
390    *
391    * @param string HTTP header
392    * @param string Value for the http header
393    */
394   public function addCacheControlHttpHeader($name, $value = null)
395   {
396     $cacheControl = $this->getHttpHeader('Cache-Control');
397     $currentHeaders = array();
398     if ($cacheControl)
399     {
400       foreach (preg_split('/\s*,\s*/', $cacheControl) as $tmp)
401       {
402         $tmp = explode('=', $tmp);
403         $currentHeaders[$tmp[0]] = isset($tmp[1]) ? $tmp[1] : null;
404       }
405     }
406     $currentHeaders[strtr(strtolower($name), '_', '-')] = $value;
407
408     $headers = array();
409     foreach ($currentHeaders as $key => $value)
410     {
411       $headers[] = $key.(null !== $value ? '='.$value : '');
412     }
413
414     $this->setHttpHeader('Cache-Control', implode(', ', $headers));
415   }
416
417   /**
418    * Retrieves meta headers for the current web response.
419    *
420    * @return string Meta headers
421    */
422   public function getHttpMetas()
423   {
424     return $this->getParameterHolder()->getAll('helper/asset/auto/httpmeta');
425   }
426
427   /**
428    * Adds meta headers to the current web response.
429    *
430    * @param string Key to replace
431    * @param string Value for the replacement
432    * @param boolean Replace or not
433    */
434   public function addHttpMeta($key, $value, $replace = true)
435   {
436     $key = $this->normalizeHeaderName($key);
437
438     // set HTTP header
439     $this->setHttpHeader($key, $value, $replace);
440
441     if ('Content-Type' == $key)
442     {
443       $value = $this->getContentType();
444     }
445     else if (!$replace)
446     {
447       $current = $this->getParameter($key, '', 'helper/asset/auto/httpmeta');
448       $value = ($current ? $current.', ' : '').$value;
449     }
450
451     $this->setParameter($key, $value, 'helper/asset/auto/httpmeta');
452   }
453
454   /**
455    * Retrieves all meta headers for the current web response.
456    *
457    * @return array List of meta headers
458    */
459   public function getMetas()
460   {
461     return $this->getParameterHolder()->getAll('helper/asset/auto/meta');
462   }
463
464   /**
465    * Adds a meta header to the current web response.
466    *
467    * @param string Name of the header
468    * @param string Meta header to be set
469    * @param boolean true if it's replaceable
470    * @param boolean true for escaping the header
471    */
472   public function addMeta($key, $value, $replace = true, $escape = true)
473   {
474     $key = strtolower($key);
475
476     if (sfConfig::get('sf_i18n'))
477     {
478       $value = $this->getContext()->getI18N()->__($value);
479     }
480
481     if ($escape)
482     {
483       $value = htmlspecialchars($value, ENT_QUOTES, sfConfig::get('sf_charset'));
484     }
485
486     if ($replace || !$this->getParameter($key, null, 'helper/asset/auto/meta'))
487     {
488       $this->setParameter($key, $value, 'helper/asset/auto/meta');
489     }
490   }
491
492   /**
493    * Retrieves title for the current web response.
494    *
495    * @return string Title
496    */
497   public function getTitle()
498   {
499     return $this->getParameter('title', '', 'helper/asset/auto/meta');
500   }
501
502   /**
503    * Sets title for the current web response.
504    *
505    * @param string Title name
506    * @param boolean true, for escaping the title
507    */
508   public function setTitle($title, $escape = true)
509   {
510     $this->addMeta('title', $title, true, $escape);
511   }
512
513   /**
514    * Retrieves stylesheets for the current web response.
515    *
516    * @param string Position
517    *
518    * @return string Stylesheets
519    */
520   public function getStylesheets($position = '')
521   {
522     return $this->getParameterHolder()->getAll('helper/asset/auto/stylesheet'.($position ? '/'.$position : ''));
523   }
524
525   /**
526    * Adds an stylesheet to the current web response.
527    *
528    * @param string Stylesheet
529    * @param string Position
530    * @param string Stylesheet options
531    */
532   public function addStylesheet($css, $position = '', $options = array())
533   {
534     $this->setParameter($css, $options, 'helper/asset/auto/stylesheet'.($position ? '/'.$position : ''));
535   }
536
537   /**
538    * Retrieves javascript code from the current web response.
539    *
540    * @param string Directory delimiter
541    *
542    * @return string Javascript code
543    */
544   public function getJavascripts($position = '')
545   {
546     return $this->getParameterHolder()->getAll('helper/asset/auto/javascript'.($position ? '/'.$position : ''));
547   }
548
549   /**
550    * Adds javascript code to the current web response.
551    *
552    * @param string Javascript code
553    * @param string Directory delimiter
554    */
555   public function addJavascript($js, $position = '')
556   {
557     $this->setParameter($js, $js, 'helper/asset/auto/javascript'.($position ? '/'.$position : ''));
558   }
559
560   /**
561    * Retrieves cookies from the current web response.
562    *
563    * @return array Cookies
564    */
565   public function getCookies()
566   {
567     $cookies = array();
568     foreach ($this->cookies as $cookie)
569     {
570       $cookies[$cookie['name']] = $cookie;
571     }
572
573     return $cookies;
574   }
575
576   /**
577    * Retrieves HTTP headers from the current web response.
578    *
579    * @return string HTTP headers
580    */
581   public function getHttpHeaders()
582   {
583     return $this->getParameterHolder()->getAll('symfony/response/http/headers');
584   }
585
586   /**
587    * Cleans HTTP headers from the current web response.
588    */
589   public function clearHttpHeaders()
590   {
591     $this->getParameterHolder()->removeNamespace('symfony/response/http/headers');
592   }
593
594   /**
595    * Copies a propertie to a new one.
596    *
597    * @param sfResponse Response instance
598    */
599   public function mergeProperties($response)
600   {
601     $this->parameterHolder = clone $response->getParameterHolder();
602   }
603
604   /**
605    * Retrieves all objects handlers for the current web response.
606    *
607    * @return array Objects instance
608    */
609   public function __sleep()
610   {
611     return array('content', 'statusCode', 'statusText', 'parameterHolder');
612   }
613
614   /**
615    * Reconstructs any result that web response instance needs.
616    */
617   public function __wakeup()
618   {
619   }
620
621   /**
622    * Executes the shutdown procedure.
623    */
624   public function shutdown()
625   {
626   }
627 }
628
Note: See TracBrowser for help on using the browser.