Hoa central
Response.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Http\Response;
38 
39 use Hoa\Core;
40 use Hoa\Http;
41 use Hoa\Stream;
42 
53 class Response
54  extends Http
55  implements Stream\IStream\Out,
57 {
63  const STATUS_CONTINUE = '100 Continue';
64 
70  const STATUS_SWITCHING_PROTOCOLS = '101 Switching Protocols';
71 
77  const STATUS_PROCESSING = '102 Processing';
78 
84  const STATUS_OK = '200 Ok';
85 
91  const STATUS_CREATED = '201 Created';
92 
98  const STATUS_ACCEPTED = '202 Accepted';
99 
105  const STATUS_NON_AUTHORITATIVE_INFORMATION = '203 Non Authoritative Information';
106 
112  const STATUS_NO_CONTENT = '204 No Content';
113 
119  const STATUS_RESET_CONTENT = '205 Reset Content';
120 
126  const STATUS_PARTIAL_CONTENT = '206 Partial Content';
127 
133  const STATUS_MULTI_STATUS = '207 Multi-Status';
134 
140  const STATUS_ALREADY_REPORTED = '208 Already Reported';
141 
147  const STATUS_IM_USED = '226 IM Used';
148 
154  const STATUS_MULTIPLE_CHOICES = '300 Multiple Choices';
155 
161  const STATUS_MOVED_PERMANENTLY = '301 Moved Permanently';
162 
168  const STATUS_FOUND = '302 Found';
169 
175  const STATUS_SEE_OTHER = '303 See Other';
176 
182  const STATUS_NOT_MODIFIED = '304 Not Modified';
183 
189  const STATUS_USE_PROXY = '305 Use Proxy';
190 
196  const STATUS_TEMPORARY_REDIRECT = '307 Temporary Redirect';
197 
203  const STATUS_PERMANENT_REDIRECT = '308 Permanent Redirect';
204 
210  const STATUS_BAD_REQUEST = '400 Bad Request';
211 
217  const STATUS_UNAUTHORIZED = '401 Unauthorized';
218 
224  const STATUS_PAYMENT_REQUIRED = '402 Payment Required';
225 
231  const STATUS_FORBIDDEN = '403 Forbidden';
232 
238  const STATUS_NOT_FOUND = '404 Not Found';
239 
245  const STATUS_METHOD_NOT_ALLOWED = '405 Method Not Allowed';
246 
252  const STATUS_NOT_ACCEPTABLE = '406 Not Acceptable';
253 
259  const STATUS_PROXY_AUTHENTIFICATION_REQUIRED = '407 Proxy Authentification Required';
260 
266  const STATUS_REQUEST_TIME_OUT = '408 Request Timeout';
267 
273  const STATUS_CONFLICT = '409 Conflict';
274 
280  const STATUS_GONE = '410 Gone';
281 
287  const STATUS_LENGTH_REQUIRED = '411 Length Required';
288 
294  const STATUS_PRECONDITION_FAILED = '412 Precondition Failed';
295 
301  const STATUS_REQUEST_ENTITY_TOO_LARGE = '413 Request Entity Too Large';
302 
308  const STATUS_REQUEST_URI_TOO_LARGE = '414 Request URI Too Large';
309 
315  const STATUS_UNSUPPORTED_MEDIA_TYPE = '415 Unsupported Media Type';
316 
322  const STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = '416 Requested Range Not Satisfiable';
323 
329  const STATUS_EXPECTATION_FAILED = '417 Expectation Failed';
330 
336  const STATUS_IM_A_TEAPOT = '418 I\'m a teapot';
337 
343  const STATUS_UNPROCESSABLE_ENTITY = '422 Unprocessable Entity';
344 
350  const STATUS_LOCKED = '423 Locked';
351 
357  const STATUS_FAILED_DEPENDENCY = '424 Failed Dependency';
358 
364  const STATUS_UPGRADE_REQUIRED = '426 Upgrade Required';
365 
371  const STATUS_PRECONDITION_REQUIRED = '428 Precondition Required';
372 
378  const STATUS_TOO_MANY_REQUESTS = '429 Too Many Requests';
379 
385  const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = '431 Request Header Fields Too Large';
386 
392  const STATUS_INTERNAL_SERVER_ERROR = '500 Internal Server Error';
393 
399  const STATUS_NOT_IMPLEMENTED = '501 Not Implemented';
400 
406  const STATUS_BAD_GATEWAY = '502 Bad Gateway';
407 
413  const STATUS_SERVICE_UNAVAILABLE = '503 Service Unavailable';
414 
420  const STATUS_GATEWAY_TIME_OUT = '504 Gateway Time Out';
421 
427  const STATUS_HTTP_VERSION_NOT_SUPPORTED = '505 HTTP Version Not Supported';
428 
434  const STATUS_VARIANT_ALSO_NEGOTIATES = '506 Variant Also Negotiates';
435 
441  const STATUS_INSUFFICIENT_STORAGE = '507 Insufficient Storage';
442 
448  const STATUS_LOOP_DETECTED = '508 Loop Detected';
449 
455  const STATUS_NOT_EXTENDED = '510 Not Extended';
456 
462  const STATUS_NETWORK_AUTHENTIFICATION_REQUIRED = '511 Network Authentification Required';
463 
469  private $_status = [];
470 
476  private $_hash = null;
477 
484  private static $_stack = [];
485 
486 
487 
498  public function __construct($newBuffer = true, $callable = null, $size = null)
499  {
500  parent::__construct();
501  $this->_hash = spl_object_hash($this);
502 
503  if (true === $newBuffer) {
504  $this->newBuffer($callable, $size);
505  }
506 
507  if (empty($this->_status)) {
508  $reflection = new \ReflectionClass($this);
509 
510  foreach ($reflection->getConstants() as $value) {
511  $this->_status[$this->getStatus($value)] = $value;
512  }
513  }
514 
515  return;
516  }
517 
524  public function parse($packet)
525  {
526  $headers = explode("\r\n", $packet);
527  $status = array_shift($headers);
528  $this->setBody(null);
529 
530  foreach ($headers as $i => $header) {
531  if ('' == trim($header)) {
532  unset($headers[$i]);
533  $this->setBody(
534  trim(
535  implode("\r\n", array_splice($headers, $i))
536  )
537  );
538 
539  break;
540  }
541  }
542 
543  if (0 === preg_match('#^HTTP/(1\.(?:0|1))\s+(\d{3})#i', $status, $matches)) {
544  throw new \Hoa\Http\Exception(
545  'HTTP status is not well-formed: %s.',
546  0,
547  $status
548  );
549  }
550 
551  if (!isset($this->_status[$matches[2]])) {
552  throw new \Hoa\Http\Exception(
553  'Unknow HTTP status %d in %s.',
554  1,
555  [$matches[2], $status]
556  );
557  }
558 
559  $this->setHttpVersion((float) $matches[1]);
560  $this->_parse($headers);
561  $this['status'] = $this->_status[$matches[2]];
562 
563  return;
564  }
565 
571  public static function getStatus($status)
572  {
573  return (int) substr($status, 0, 3);
574  }
575 
584  public function sendStatus($status, $replace = true)
585  {
586  return $this->sendHeader('status', $status, $replace, $status);
587  }
588 
599  public function sendHeader(
600  $header,
601  $value,
602  $replace = true,
603  $status = null
604  ) {
605  if (0 === strcasecmp('status', $header) &&
606  false === self::$_fcgi) {
607  header(
608  'HTTP/1.1 ' . $value,
609  $replace,
610  static::getStatus($value)
611  );
612 
613  return;
614  }
615 
616  header(
617  $header . ': ' . $value,
618  $replace,
619  null !== $status ? static::getStatus($status) : null
620  );
621 
622  return;
623  }
624 
630  public function sendHeaders()
631  {
632  foreach ($this->_headers as $header => $value) {
633  $this->sendHeader($header, $value);
634  }
635 
636  return;
637  }
638 
644  public function getSentHeaders()
645  {
646  return implode("\r\n", headers_list());
647  }
648 
657  public function newBuffer($callable = null, $size = null)
658  {
659  $last = current(self::$_stack);
660  $hash = $this->getHash();
661 
662  if (false === $last || $hash != $last[0]) {
663  self::$_stack[] = [
664  0 => $hash,
665  1 => 1
666  ];
667  } else {
668  ++self::$_stack[key(self::$_stack)][1];
669  }
670 
671  end(self::$_stack);
672 
673  if (null === $callable) {
674  ob_start();
675  } else {
676  ob_start(xcallable($callable), null === $size ? 0 : $size);
677  }
678 
679  return $this->getBufferLevel();
680  }
681 
688  public function flush($force = false)
689  {
690  if (0 >= $this->getBufferSize()) {
691  return;
692  }
693 
694  ob_flush();
695 
696  if (true === $force) {
697  flush();
698  }
699 
700  return;
701  }
702 
709  public function deleteBuffer()
710  {
711  $key = key(self::$_stack);
712 
713  if ($this->getHash() != self::$_stack[$key][0]) {
714  throw new \Hoa\Http\Exception\CrossBufferization(
715  'Cannot delete this buffer because it was not opened by this ' .
716  'class (%s, %s).',
717  2,
718  [get_class($this), $this->getHash()]
719  );
720  }
721 
722  $out = ob_end_clean();
723 
724  if (false === $out) {
725  return false;
726  }
727 
728  --self::$_stack[$key][1];
729 
730  if (0 >= self::$_stack[$key][1]) {
731  unset(self::$_stack[$key]);
732  }
733 
734  return true;
735  }
736 
742  public function getBufferLevel()
743  {
744  return ob_get_level();
745  }
746 
752  public function getBufferSize()
753  {
754  return ob_get_length();
755  }
756 
765  public function write($string, $length)
766  {
767  if (0 > $length) {
768  throw new \Hoa\Http\Exception(
769  'Length must be greater than 0, given %d.',
770  3,
771  $length
772  );
773  }
774 
775  if (strlen($string) > $length) {
776  $string = substr($string, 0, $length);
777  }
778 
779  echo $string;
780 
781  return;
782  }
783 
790  public function writeString($string)
791  {
792  echo (string) $string;
793 
794  return;
795  }
796 
803  public function writeCharacter($character)
804  {
805  echo $character[0];
806 
807  return;
808  }
809 
816  public function writeBoolean($boolean)
817  {
818  echo (string) (bool) $boolean;
819 
820  return;
821  }
822 
829  public function writeInteger($integer)
830  {
831  echo (string) (int) $integer;
832 
833  return;
834  }
835 
842  public function writeFloat($float)
843  {
844  echo (string) (float) $float;
845 
846  return;
847  }
848 
855  public function writeArray(Array $array)
856  {
857  echo var_export($array, true);
858 
859  return;
860  }
861 
868  public function writeLine($line)
869  {
870  if (false !== $n = strpos($line, "\n")) {
871  $line = substr($line, 0, $n + 1);
872  }
873 
874  echo $line;
875 
876  return;
877  }
878 
885  public function writeAll($string)
886  {
887  echo $string;
888 
889  return;
890  }
891 
898  public function truncate($size)
899  {
900  if (0 === $size) {
901  ob_clean();
902 
903  return true;
904  }
905 
906  $bSize = $this->getBufferSize();
907 
908  if ($size >= $bSize) {
909  return true;
910  }
911 
912  echo substr(ob_get_clean(), 0, $size);
913 
914  return true;
915  }
916 
922  public function getHash()
923  {
924  return $this->_hash;
925  }
926 
932  public function __destruct()
933  {
934  $last = current(self::$_stack);
935 
936  if ($this->getHash() != $last[0]) {
937  return;
938  }
939 
940  for ($i = 0, $max = $last[1]; $i < $max; ++$i) {
941  $this->flush();
942 
943  if (0 < $this->getBufferLevel()) {
944  $this->deleteBuffer();
945  }
946  }
947 
948  return;
949  }
950 }
951 
955 Core\Consistency::flexEntity('Hoa\Http\Response\Response');
sendHeader($header, $value, $replace=true, $status=null)
Definition: Response.php:599
setBody($body)
Definition: Http.php:256
sendStatus($status, $replace=true)
Definition: Response.php:584
const STATUS_NETWORK_AUTHENTIFICATION_REQUIRED
Definition: Response.php:462
const STATUS_PROXY_AUTHENTIFICATION_REQUIRED
Definition: Response.php:259
const STATUS_HTTP_VERSION_NOT_SUPPORTED
Definition: Response.php:427
setHttpVersion($version)
Definition: Http.php:101
newBuffer($callable=null, $size=null)
Definition: Response.php:657
_parse(Array $headers)
Definition: Http.php:134
static getStatus($status)
Definition: Response.php:571
writeArray(Array $array)
Definition: Response.php:855
const STATUS_REQUESTED_RANGE_NOT_SATISFIABLE
Definition: Response.php:322
const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition: Response.php:385
const STATUS_NON_AUTHORITATIVE_INFORMATION
Definition: Response.php:105
write($string, $length)
Definition: Response.php:765
__construct($newBuffer=true, $callable=null, $size=null)
Definition: Response.php:498
writeCharacter($character)
Definition: Response.php:803