newBuffer(). * Following arguments are for this * method. * @param mixed $callable Callable. * @param int $size Size. */ public function __construct($newBuffer = true, $callable = null, $size = null) { parent::__construct(); $this->_hash = spl_object_hash($this); if (true === $newBuffer) { $this->newBuffer($callable, $size); } if (empty($this->_status)) { $reflection = new \ReflectionClass($this); foreach ($reflection->getConstants() as $value) { $this->_status[$this->getStatus($value)] = $value; } } return; } /** * Parse a HTTP packet. * * @param string $packet HTTP packet. * @return void * @throws \Hoa\Http\Exception */ public function parse($packet) { $headers = explode("\r\n", $packet); $status = array_shift($headers); $this->setBody(null); foreach ($headers as $i => $header) { if ('' == trim($header)) { unset($headers[$i]); $this->setBody( trim( implode("\r\n", array_splice($headers, $i)) ) ); break; } } if (0 === preg_match('#^HTTP/(1\.(?:0|1))\s+(\d{3})#i', $status, $matches)) { throw new Http\Exception( 'HTTP status is not well-formed: %s.', 0, $status ); } if (!isset($this->_status[$matches[2]])) { throw new Http\Exception( 'Unknown HTTP status %d in %s.', 1, [$matches[2], $status] ); } $this->setHttpVersion((float) $matches[1]); $this->_parse($headers); $this['status'] = $this->_status[$matches[2]]; return; } /** * Get real status from static::STATUS_* constants. * * @param string $status Status. * @return int */ public static function getStatus($status) { return (int) substr($status, 0, 3); } /** * Send a new status. * * @param string $status Status. Please, see static::STATUS_* * constants. * @param bool $replace Whether replace an existing sent header. * @return void */ public function sendStatus($status, $replace = true) { return $this->sendHeader('status', $status, $replace, $status); } /** * Send a new header. * * @param string $header Header. * @param string $value Value. * @param bool $replace Whether replace an existing sent header. * @param string $status Force a specific status. Please, see * static::STATUS_* constants. * @return void */ public function sendHeader( $header, $value, $replace = true, $status = null ) { if (0 === strcasecmp('status', $header) && false === self::$_fcgi) { header( 'HTTP/1.1 ' . $value, $replace, static::getStatus($value) ); return; } header( $header . ': ' . $value, $replace, null !== $status ? static::getStatus($status) : null ); return; } /** * Send all headers. * * @return void */ public function sendHeaders() { foreach ($this->_headers as $header => $value) { $this->sendHeader($header, $value); } return; } /** * Get send headers. * * @return string */ public function getSentHeaders() { return implode("\r\n", headers_list()); } /** * Start a new buffer. * The callable acts like a filter. * * @param mixed $callable Callable. * @param int $size Size. * @return int */ public function newBuffer($callable = null, $size = null) { $last = current(self::$_stack); $hash = $this->getHash(); if (false === $last || $hash != $last[0]) { self::$_stack[] = [ 0 => $hash, 1 => 1 ]; } else { ++self::$_stack[key(self::$_stack)][1]; } end(self::$_stack); if (null === $callable) { ob_start(); } else { ob_start(xcallable($callable), null === $size ? 0 : $size); } return $this->getBufferLevel(); } /** * Flush the buffer. * * @param bool $force Force to flush the output buffer. * @return void */ public function flush($force = false) { if (0 >= $this->getBufferSize()) { return; } ob_flush(); if (true === $force) { flush(); } return; } /** * Delete buffer. * * @return bool * @throws \Hoa\Http\Exception\CrossBufferization */ public function deleteBuffer() { $key = key(self::$_stack); if ($this->getHash() != self::$_stack[$key][0]) { throw new Http\Exception\CrossBufferization( 'Cannot delete this buffer because it was not opened by this ' . 'class (%s, %s).', 2, [get_class($this), $this->getHash()] ); } $out = ob_end_clean(); if (false === $out) { return false; } --self::$_stack[$key][1]; if (0 >= self::$_stack[$key][1]) { unset(self::$_stack[$key]); } return true; } /** * Get buffer level. * * @return int */ public function getBufferLevel() { return ob_get_level(); } /** * Get buffer size. * * @return int */ public function getBufferSize() { return ob_get_length(); } /** * Write n characters. * * @param string $string String. * @param int $length Length. * @return mixed * @throws \Hoa\Http\Exception */ public function write($string, $length) { if (0 > $length) { throw new Http\Exception( 'Length must be greater than 0, given %d.', 3, $length ); } if (strlen($string) > $length) { $string = substr($string, 0, $length); } echo $string; return; } /** * Write a string. * * @param string $string String. * @return mixed */ public function writeString($string) { echo (string) $string; return; } /** * Write a character. * * @param string $character Character. * @return mixed */ public function writeCharacter($character) { echo $character[0]; return; } /** * Write a boolean. * * @param bool $boolean Boolean. * @return mixed */ public function writeBoolean($boolean) { echo (string) (bool) $boolean; return; } /** * Write an integer. * * @param int $integer Integer. * @return mixed */ public function writeInteger($integer) { echo (string) (int) $integer; return; } /** * Write a float. * * @param float $float Float. * @return mixed */ public function writeFloat($float) { echo (string) (float) $float; return; } /** * Write an array. * * @param array $array Array. * @return mixed */ public function writeArray(array $array) { echo var_export($array, true); return; } /** * Write a line. * * @param string $line Line. * @return mixed */ public function writeLine($line) { if (false !== $n = strpos($line, "\n")) { $line = substr($line, 0, $n + 1); } echo $line; return; } /** * Write all, i.e. as much as possible. * * @param string $string String. * @return mixed */ public function writeAll($string) { echo $string; return; } /** * Truncate a file to a given length. * * @param int $size Size. * @return bool */ public function truncate($size) { if (0 === $size) { ob_clean(); return true; } $bSize = $this->getBufferSize(); if ($size >= $bSize) { return true; } echo substr(ob_get_clean(), 0, $size); return true; } /** * Get the current stream. * * @return resource */ public function getStream() { return fopen('php://stdout', 'w'); } /** * Get this object hash. * * @return string */ public function getHash() { return $this->_hash; } /** * Delete head buffer. * * @return void */ public function __destruct() { $last = current(self::$_stack); if ($this->getHash() != $last[0]) { return; } for ($i = 0, $max = $last[1]; $i < $max; ++$i) { $this->flush(); if (0 < $this->getBufferLevel()) { $this->deleteBuffer(); } } return; } } /** * Flex entity. */ Consistency::flexEntity('Hoa\Http\Response\Response');