Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: sfWebResponse.class.php 31399 2010-11-15 16:48:22Z fabien $
20
 */
21
class sfWebResponse extends sfResponse
22
{
23
  const
24
    FIRST  = 'first',
25
    MIDDLE = '',
26
    LAST   = 'last',
27
    ALL    = 'ALL',
28
    RAW    = 'RAW';
29
 
30
  protected
31
    $cookies     = array(),
32
    $statusCode  = 200,
33
    $statusText  = 'OK',
34
    $headerOnly  = false,
35
    $headers     = array(),
36
    $metas       = array(),
37
    $httpMetas   = array(),
38
    $positions   = array('first', '', 'last'),
39
    $stylesheets = array(),
40
    $javascripts = array(),
41
    $slots       = array();
42
 
43
  static protected $statusTexts = array(
44
    '100' => 'Continue',
45
    '101' => 'Switching Protocols',
46
    '200' => 'OK',
47
    '201' => 'Created',
48
    '202' => 'Accepted',
49
    '203' => 'Non-Authoritative Information',
50
    '204' => 'No Content',
51
    '205' => 'Reset Content',
52
    '206' => 'Partial Content',
53
    '300' => 'Multiple Choices',
54
    '301' => 'Moved Permanently',
55
    '302' => 'Found',
56
    '303' => 'See Other',
57
    '304' => 'Not Modified',
58
    '305' => 'Use Proxy',
59
    '306' => '(Unused)',
60
    '307' => 'Temporary Redirect',
61
    '400' => 'Bad Request',
62
    '401' => 'Unauthorized',
63
    '402' => 'Payment Required',
64
    '403' => 'Forbidden',
65
    '404' => 'Not Found',
66
    '405' => 'Method Not Allowed',
67
    '406' => 'Not Acceptable',
68
    '407' => 'Proxy Authentication Required',
69
    '408' => 'Request Timeout',
70
    '409' => 'Conflict',
71
    '410' => 'Gone',
72
    '411' => 'Length Required',
73
    '412' => 'Precondition Failed',
74
    '413' => 'Request Entity Too Large',
75
    '414' => 'Request-URI Too Long',
76
    '415' => 'Unsupported Media Type',
77
    '416' => 'Requested Range Not Satisfiable',
78
    '417' => 'Expectation Failed',
79
    '500' => 'Internal Server Error',
80
    '501' => 'Not Implemented',
81
    '502' => 'Bad Gateway',
82
    '503' => 'Service Unavailable',
83
    '504' => 'Gateway Timeout',
84
    '505' => 'HTTP Version Not Supported',
85
  );
86
 
87
  /**
88
   * Initializes this sfWebResponse.
89
   *
90
   * Available options:
91
   *
92
   *  * charset:           The charset to use (utf-8 by default)
93
   *  * content_type:      The content type (text/html by default)
94
   *  * send_http_headers: Whether to send HTTP headers or not (true by default)
95
   *  * http_protocol:     The HTTP protocol to use for the response (HTTP/1.0 by default)
96
   *
97
   * @param  sfEventDispatcher $dispatcher  An sfEventDispatcher instance
98
   * @param  array             $options     An array of options
99
   *
100
   * @return bool true, if initialization completes successfully, otherwise false
101
   *
102
   * @throws <b>sfInitializationException</b> If an error occurs while initializing this sfResponse
103
   *
104
   * @see sfResponse
105
   */
106
  public function initialize(sfEventDispatcher $dispatcher, $options = array())
107
  {
108
    parent::initialize($dispatcher, $options);
109
 
110
    $this->javascripts = array_combine($this->positions, array_fill(0, count($this->positions), array()));
111
    $this->stylesheets = array_combine($this->positions, array_fill(0, count($this->positions), array()));
112
 
113
    if (!isset($this->options['charset']))
114
    {
115
      $this->options['charset'] = 'utf-8';
116
    }
117
 
118
    if (!isset($this->options['send_http_headers']))
119
    {
120
      $this->options['send_http_headers'] = true;
121
    }
122
 
123
    if (!isset($this->options['http_protocol']))
124
    {
125
      $this->options['http_protocol'] = 'HTTP/1.0';
126
    }
127
 
128
    $this->options['content_type'] = $this->fixContentType(isset($this->options['content_type']) ? $this->options['content_type'] : 'text/html');
129
  }
130
 
131
  /**
132
   * Sets if the response consist of just HTTP headers.
133
   *
134
   * @param bool $value
135
   */
136
  public function setHeaderOnly($value = true)
137
  {
138
    $this->headerOnly = (boolean) $value;
139
  }
140
 
141
  /**
142
   * Returns if the response must only consist of HTTP headers.
143
   *
144
   * @return bool returns true if, false otherwise
145
   */
146
  public function isHeaderOnly()
147
  {
148
    return $this->headerOnly;
149
  }
150
 
151
  /**
152
   * Sets a cookie.
153
   *
154
   * @param  string  $name      HTTP header name
155
   * @param  string  $value     Value for the cookie
156
   * @param  string  $expire    Cookie expiration period
157
   * @param  string  $path      Path
158
   * @param  string  $domain    Domain name
159
   * @param  bool    $secure    If secure
160
   * @param  bool    $httpOnly  If uses only HTTP
161
   *
162
   * @throws <b>sfException</b> If fails to set the cookie
163
   */
164
  public function setCookie($name, $value, $expire = null, $path = '/', $domain = '', $secure = false, $httpOnly = false)
165
  {
166
    if ($expire !== null)
167
    {
168
      if (is_numeric($expire))
169
      {
170
        $expire = (int) $expire;
171
      }
172
      else
173
      {
174
        $expire = strtotime($expire);
175
        if ($expire === false || $expire == -1)
176
        {
177
          throw new sfException('Your expire parameter is not valid.');
178
        }
179
      }
180
    }
181
 
182
    $this->cookies[$name] = array(
183
      'name'     => $name,
184
      'value'    => $value,
185
      'expire'   => $expire,
186
      'path'     => $path,
187
      'domain'   => $domain,
188
      'secure'   => $secure ? true : false,
189
      'httpOnly' => $httpOnly,
190
    );
191
  }
192
 
193
  /**
194
   * Sets response status code.
195
   *
196
   * @param string $code  HTTP status code
197
   * @param string $name  HTTP status text
198
   *
199
   */
200
  public function setStatusCode($code, $name = null)
201
  {
202
    $this->statusCode = $code;
203
    $this->statusText = null !== $name ? $name : self::$statusTexts[$code];
204
  }
205
 
206
  /**
207
   * Retrieves status text for the current web response.
208
   *
209
   * @return string Status text
210
   */
211
  public function getStatusText()
212
  {
213
    return $this->statusText;
214
  }
215
 
216
  /**
217
   * Retrieves status code for the current web response.
218
   *
219
   * @return integer Status code
220
   */
221
  public function getStatusCode()
222
  {
223
    return $this->statusCode;
224
  }
225
 
226
  /**
227
   * Sets a HTTP header.
228
   *
229
   * @param string  $name     HTTP header name
230
   * @param string  $value    Value (if null, remove the HTTP header)
231
   * @param bool    $replace  Replace for the value
232
   *
233
   */
234
  public function setHttpHeader($name, $value, $replace = true)
235
  {
236
    $name = $this->normalizeHeaderName($name);
237
 
238
    if (null === $value)
239
    {
240
      unset($this->headers[$name]);
241
 
242
      return;
243
    }
244
 
245
    if ('Content-Type' == $name)
246
    {
247
      if ($replace || !$this->getHttpHeader('Content-Type', null))
248
      {
249
        $this->setContentType($value);
250
      }
251
 
252
      return;
253
    }
254
 
255
    if (!$replace)
256
    {
257
      $current = isset($this->headers[$name]) ? $this->headers[$name] : '';
258
      $value = ($current ? $current.', ' : '').$value;
259
    }
260
 
261
    $this->headers[$name] = $value;
262
  }
263
 
264
  /**
265
   * Gets HTTP header current value.
266
   *
267
   * @param  string $name     HTTP header name
268
   * @param  string $default  Default value returned if named HTTP header is not found
269
   *
270
   * @return string
271
   */
272
  public function getHttpHeader($name, $default = null)
273
  {
274
    $name = $this->normalizeHeaderName($name);
275
 
276
    return isset($this->headers[$name]) ? $this->headers[$name] : $default;
277
  }
278
 
279
  /**
280
   * Checks if response has given HTTP header.
281
   *
282
   * @param  string $name  HTTP header name
283
   *
284
   * @return bool
285
   */
286
  public function hasHttpHeader($name)
287
  {
288
    return array_key_exists($this->normalizeHeaderName($name), $this->headers);
289
  }
290
 
291
  /**
292
   * Sets response content type.
293
   *
294
   * @param string $value  Content type
295
   *
296
   */
297
  public function setContentType($value)
298
  {
299
    $this->headers['Content-Type'] = $this->fixContentType($value);
300
  }
301
 
302
  /**
303
   * Gets the current charset as defined by the content type.
304
   *
305
   * @return string The current charset
306
   */
307
  public function getCharset()
308
  {
309
    return $this->options['charset'];
310
  }
311
 
312
  /**
313
   * Gets response content type.
314
   *
315
   * @return array
316
   */
317
  public function getContentType()
318
  {
319
    return $this->getHttpHeader('Content-Type', $this->options['content_type']);
320
  }
321
 
322
  /**
323
   * Sends HTTP headers and cookies. Only the first invocation of this method will send the headers.
324
   * Subsequent invocations will silently do nothing. This allows certain actions to send headers early,
325
   * while still using the standard controller.
326
   */
327
  public function sendHttpHeaders()
328
  {
329
    if (!$this->options['send_http_headers'])
330
    {
331
      return;
332
    }
333
 
334
    // status
335
    $status = $this->options['http_protocol'].' '.$this->statusCode.' '.$this->statusText;
336
    header($status);
337
 
338
    if (substr(php_sapi_name(), 0, 3) == 'cgi')
339
    {
340
      // fastcgi servers cannot send this status information because it was sent by them already due to the HTT/1.0 line
341
      // so we can safely unset them. see ticket #3191
342
      unset($this->headers['Status']);
343
    }
344
 
345
    if ($this->options['logging'])
346
    {
347
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Send status "%s"', $status))));
348
    }
349
 
350
    // headers
351
    if (!$this->getHttpHeader('Content-Type'))
352
    {
353
      $this->setContentType($this->options['content_type']);
354
    }
355
    foreach ($this->headers as $name => $value)
356
    {
357
      header($name.': '.$value);
358
 
359
      if ($value != '' && $this->options['logging'])
360
      {
361
        $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Send header "%s: %s"', $name, $value))));
362
      }
363
    }
364
 
365
    // cookies
366
    foreach ($this->cookies as $cookie)
367
    {
368
      setrawcookie($cookie['name'], $cookie['value'], $cookie['expire'], $cookie['path'], $cookie['domain'], $cookie['secure'], $cookie['httpOnly']);
369
 
370
      if ($this->options['logging'])
371
      {
372
        $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Send cookie "%s": "%s"', $cookie['name'], $cookie['value']))));
373
      }
374
    }
375
    // prevent resending the headers
376
    $this->options['send_http_headers'] = false;
377
  }
378
 
379
  /**
380
   * Send content for the current web response.
381
   *
382
   */
383
  public function sendContent()
384
  {
385
    if (!$this->headerOnly)
386
    {
387
      parent::sendContent();
388
    }
389
  }
390
 
391
  /**
392
   * Sends the HTTP headers and the content.
393
   */
394
  public function send()
395
  {
396
    $this->sendHttpHeaders();
397
    $this->sendContent();
398
  }
399
 
400
  /**
401
   * Retrieves a normalized Header.
402
   *
403
   * @param  string $name  Header name
404
   *
405
   * @return string Normalized header
406
   */
407
  protected function normalizeHeaderName($name)
408
  {
409
    return preg_replace('/\-(.)/e', "'-'.strtoupper('\\1')", strtr(ucfirst(strtolower($name)), '_', '-'));
410
  }
411
 
412
  /**
413
   * Retrieves a formated date.
414
   *
415
   * @param  string $timestamp  Timestamp
416
   * @param  string $type       Format type
417
   *
418
   * @return string Formatted date
419
   */
420
  static public function getDate($timestamp, $type = 'rfc1123')
421
  {
422
    $type = strtolower($type);
423
 
424
    if ($type == 'rfc1123')
425
    {
426
      return substr(gmdate('r', $timestamp), 0, -5).'GMT';
427
    }
428
    else if ($type == 'rfc1036')
429
    {
430
      return gmdate('l, d-M-y H:i:s ', $timestamp).'GMT';
431
    }
432
    else if ($type == 'asctime')
433
    {
434
      return gmdate('D M j H:i:s', $timestamp);
435
    }
436
    else
437
    {
438
      throw new InvalidArgumentException('The second getDate() method parameter must be one of: rfc1123, rfc1036 or asctime.');
439
    }
440
  }
441
 
442
  /**
443
   * Adds vary to a http header.
444
   *
445
   * @param string $header  HTTP header
446
   */
447
  public function addVaryHttpHeader($header)
448
  {
449
    $vary = $this->getHttpHeader('Vary');
450
    $currentHeaders = array();
451
    if ($vary)
452
    {
453
      $currentHeaders = preg_split('/\s*,\s*/', $vary);
454
    }
455
    $header = $this->normalizeHeaderName($header);
456
 
457
    if (!in_array($header, $currentHeaders))
458
    {
459
      $currentHeaders[] = $header;
460
      $this->setHttpHeader('Vary', implode(', ', $currentHeaders));
461
    }
462
  }
463
 
464
  /**
465
   * Adds an control cache http header.
466
   *
467
   * @param string $name   HTTP header
468
   * @param string $value  Value for the http header
469
   */
470
  public function addCacheControlHttpHeader($name, $value = null)
471
  {
472
    $cacheControl = $this->getHttpHeader('Cache-Control');
473
    $currentHeaders = array();
474
    if ($cacheControl)
475
    {
476
      foreach (preg_split('/\s*,\s*/', $cacheControl) as $tmp)
477
      {
478
        $tmp = explode('=', $tmp);
479
        $currentHeaders[$tmp[0]] = isset($tmp[1]) ? $tmp[1] : null;
480
      }
481
    }
482
    $currentHeaders[strtr(strtolower($name), '_', '-')] = $value;
483
 
484
    $headers = array();
485
    foreach ($currentHeaders as $key => $value)
486
    {
487
      $headers[] = $key.(null !== $value ? '='.$value : '');
488
    }
489
 
490
    $this->setHttpHeader('Cache-Control', implode(', ', $headers));
491
  }
492
 
493
  /**
494
   * Retrieves meta headers for the current web response.
495
   *
496
   * @return string Meta headers
497
   */
498
  public function getHttpMetas()
499
  {
500
    return $this->httpMetas;
501
  }
502
 
503
  /**
504
   * Adds a HTTP meta header.
505
   *
506
   * @param string  $key      Key to replace
507
   * @param string  $value    HTTP meta header value (if null, remove the HTTP meta)
508
   * @param bool    $replace  Replace or not
509
   */
510
  public function addHttpMeta($key, $value, $replace = true)
511
  {
512
    $key = $this->normalizeHeaderName($key);
513
 
514
    // set HTTP header
515
    $this->setHttpHeader($key, $value, $replace);
516
 
517
    if (null === $value)
518
    {
519
      unset($this->httpMetas[$key]);
520
 
521
      return;
522
    }
523
 
524
    if ('Content-Type' == $key)
525
    {
526
      $value = $this->getContentType();
527
    }
528
    elseif (!$replace)
529
    {
530
      $current = isset($this->httpMetas[$key]) ? $this->httpMetas[$key] : '';
531
      $value = ($current ? $current.', ' : '').$value;
532
    }
533
 
534
    $this->httpMetas[$key] = $value;
535
  }
536
 
537
  /**
538
   * Retrieves all meta headers.
539
   *
540
   * @return array List of meta headers
541
   */
542
  public function getMetas()
543
  {
544
    return $this->metas;
545
  }
546
 
547
  /**
548
   * Adds a meta header.
549
   *
550
   * @param string  $key      Name of the header
551
   * @param string  $value    Meta header value (if null, remove the meta)
552
   * @param bool    $replace  true if it's replaceable
553
   * @param bool    $escape   true for escaping the header
554
   */
555
  public function addMeta($key, $value, $replace = true, $escape = true)
556
  {
557
    $key = strtolower($key);
558
 
559
    if (null === $value)
560
    {
561
      unset($this->metas[$key]);
562
 
563
      return;
564
    }
565
 
566
    // FIXME: If you use the i18n layer and escape the data here, it won't work
567
    // see include_metas() in AssetHelper
568
    if ($escape)
569
    {
570
      $value = htmlspecialchars($value, ENT_QUOTES, $this->options['charset']);
571
    }
572
 
573
    $current = isset($this->metas[$key]) ? $this->metas[$key] : null;
574
    if ($replace || !$current)
575
    {
576
      $this->metas[$key] = $value;
577
    }
578
  }
579
 
580
  /**
581
   * Retrieves title for the current web response.
582
   *
583
   * @return string Title
584
   */
585
  public function getTitle()
586
  {
587
    return isset($this->metas['title']) ? $this->metas['title'] : '';
588
  }
589
 
590
  /**
591
   * Sets title for the current web response.
592
   *
593
   * @param string  $title   Title name
594
   * @param bool    $escape  true, for escaping the title
595
   */
596
  public function setTitle($title, $escape = true)
597
  {
598
    $this->addMeta('title', $title, true, $escape);
599
  }
600
 
601
  /**
602
   * Returns the available position names for stylesheets and javascripts in order.
603
   *
604
   * @return array An array of position names
605
   */
606
  public function getPositions()
607
  {
608
    return $this->positions;
609
  }
610
 
611
  /**
612
   * Retrieves stylesheets for the current web response.
613
   *
614
   * By default, the position is sfWebResponse::ALL,
615
   * and the method returns all stylesheets ordered by position.
616
   *
617
   * @param  string  $position The position
618
   *
619
   * @return array   An associative array of stylesheet files as keys and options as values
620
   */
621
  public function getStylesheets($position = self::ALL)
622
  {
623
    if (self::ALL === $position)
624
    {
625
      $stylesheets = array();
626
      foreach ($this->getPositions() as $position)
627
      {
628
        foreach ($this->stylesheets[$position] as $file => $options)
629
        {
630
          $stylesheets[$file] = $options;
631
        }
632
      }
633
 
634
      return $stylesheets;
635
    }
636
    else if (self::RAW === $position)
637
    {
638
      return $this->stylesheets;
639
    }
640
 
641
    $this->validatePosition($position);
642
 
643
    return $this->stylesheets[$position];
644
  }
645
 
646
  /**
647
   * Adds a stylesheet to the current web response.
648
   *
649
   * @param string $file      The stylesheet file
650
   * @param string $position  Position
651
   * @param string $options   Stylesheet options
652
   */
653
  public function addStylesheet($file, $position = '', $options = array())
654
  {
655
    $this->validatePosition($position);
656
 
657
    $this->stylesheets[$position][$file] = $options;
658
  }
659
 
660
  /**
661
   * Removes a stylesheet from the current web response.
662
   *
663
   * @param string $file The stylesheet file to remove
664
   */
665
  public function removeStylesheet($file)
666
  {
667
    foreach ($this->getPositions() as $position)
668
    {
669
      unset($this->stylesheets[$position][$file]);
670
    }
671
  }
672
 
673
  /**
674
   * Retrieves javascript files from the current web response.
675
   *
676
   * By default, the position is sfWebResponse::ALL,
677
   * and the method returns all javascripts ordered by position.
678
   *
679
   * @param  string $position  The position
680
   *
681
   * @return array An associative array of javascript files as keys and options as values
682
   */
683
  public function getJavascripts($position = self::ALL)
684
  {
685
    if (self::ALL === $position)
686
    {
687
      $javascripts = array();
688
      foreach ($this->getPositions() as $position)
689
      {
690
        foreach ($this->javascripts[$position] as $file => $options)
691
        {
692
          $javascripts[$file] = $options;
693
        }
694
      }
695
 
696
      return $javascripts;
697
    }
698
    else if (self::RAW === $position)
699
    {
700
      return $this->javascripts;
701
    }
702
 
703
    $this->validatePosition($position);
704
 
705
    return $this->javascripts[$position];
706
  }
707
 
708
  /**
709
   * Adds javascript code to the current web response.
710
   *
711
   * @param string $file      The JavaScript file
712
   * @param string $position  Position
713
   * @param string $options   Javascript options
714
   */
715
  public function addJavascript($file, $position = '', $options = array())
716
  {
717
    $this->validatePosition($position);
718
 
719
    $this->javascripts[$position][$file] = $options;
720
  }
721
 
722
  /**
723
   * Removes a JavaScript file from the current web response.
724
   *
725
   * @param string $file The Javascript file to remove
726
   */
727
  public function removeJavascript($file)
728
  {
729
    foreach ($this->getPositions() as $position)
730
    {
731
      unset($this->javascripts[$position][$file]);
732
    }
733
  }
734
 
735
  /**
736
   * Retrieves slots from the current web response.
737
   *
738
   * @return string Javascript code
739
   */
740
  public function getSlots()
741
  {
742
    return $this->slots;
743
  }
744
 
745
  /**
746
   * Sets a slot content.
747
   *
748
   * @param string $name     Slot name
749
   * @param string $content  Content
750
   */
751
  public function setSlot($name, $content)
752
  {
753
    $this->slots[$name] = $content;
754
  }
755
 
756
  /**
757
   * Retrieves cookies from the current web response.
758
   *
759
   * @return array Cookies
760
   */
761
  public function getCookies()
762
  {
763
    return $this->cookies;
764
  }
765
 
766
  /**
767
   * Retrieves HTTP headers from the current web response.
768
   *
769
   * @return string HTTP headers
770
   */
771
  public function getHttpHeaders()
772
  {
773
    return $this->headers;
774
  }
775
 
776
  /**
777
   * Cleans HTTP headers from the current web response.
778
   */
779
  public function clearHttpHeaders()
780
  {
781
    $this->headers = array();
782
  }
783
 
784
  /**
785
   * Copies all properties from a given sfWebResponse object to the current one.
786
   *
787
   * @param sfWebResponse $response  An sfWebResponse instance
788
   */
789
  public function copyProperties(sfWebResponse $response)
790
  {
791
    $this->options     = $response->getOptions();
792
    $this->headers     = $response->getHttpHeaders();
793
    $this->metas       = $response->getMetas();
794
    $this->httpMetas   = $response->getHttpMetas();
795
    $this->stylesheets = $response->getStylesheets(self::RAW);
796
    $this->javascripts = $response->getJavascripts(self::RAW);
797
    $this->slots       = $response->getSlots();
798
 
799
    // HTTP protocol must be from the current request
800
    // this fix is not nice but that's the only way to fix it and keep BC (see #9254)
801
    $this->options['http_protocol'] = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
802
  }
803
 
804
  /**
805
   * Merges all properties from a given sfWebResponse object to the current one.
806
   *
807
   * @param sfWebResponse $response  An sfWebResponse instance
808
   */
809
  public function merge(sfWebResponse $response)
810
  {
811
    foreach ($this->getPositions() as $position)
812
    {
813
      $this->javascripts[$position] = array_merge($this->getJavascripts($position), $response->getJavascripts($position));
814
      $this->stylesheets[$position] = array_merge($this->getStylesheets($position), $response->getStylesheets($position));
815
    }
816
 
817
    $this->slots = array_merge($this->getSlots(), $response->getSlots());
818
  }
819
 
820
  /**
821
   * @see sfResponse
822
   */
823
  public function serialize()
824
  {
825
    return serialize(array($this->content, $this->statusCode, $this->statusText, $this->options, $this->headerOnly, $this->headers, $this->metas, $this->httpMetas, $this->stylesheets, $this->javascripts, $this->slots));
826
  }
827
 
828
  /**
829
   * @see sfResponse
830
   */
831
  public function unserialize($serialized)
832
  {
833
    list($this->content, $this->statusCode, $this->statusText, $this->options, $this->headerOnly, $this->headers, $this->metas, $this->httpMetas, $this->stylesheets, $this->javascripts, $this->slots) = unserialize($serialized);
834
  }
835
 
836
  /**
837
   * Validate a position name.
838
   *
839
   * @param  string $position
840
   *
841
   * @throws InvalidArgumentException if the position is not available
842
   */
843
  protected function validatePosition($position)
844
  {
845
    if (!in_array($position, $this->positions, true))
846
    {
847
      throw new InvalidArgumentException(sprintf('The position "%s" does not exist (available positions: %s).', $position, implode(', ', $this->positions)));
848
    }
849
  }
850
 
851
  /**
852
   * Fixes the content type by adding the charset for text content types.
853
   *
854
   * @param  string $contentType  The content type
855
   *
856
   * @return string The content type with the charset if needed
857
   */
858
  protected function fixContentType($contentType)
859
  {
860
    // add charset if needed (only on text content)
861
    if (false === stripos($contentType, 'charset') && (0 === stripos($contentType, 'text/') || strlen($contentType) - 3 === strripos($contentType, 'xml')))
862
    {
863
      $contentType .= '; charset='.$this->options['charset'];
864
    }
865
 
866
    // change the charset for the response
867
    if (preg_match('/charset\s*=\s*(.+)\s*$/', $contentType, $match))
868
    {
869
      $this->options['charset'] = $match[1];
870
    }
871
 
872
    return $contentType;
873
  }
874
}