Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/cakephp/cakephp/src/Http/ResponseEmitter.php
<?php
/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @since         3.3.5
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 *
 * Parts of this file are derived from Zend-Diactoros
 *
 * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (https://www.zend.com/)
 * @license   https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
 */
namespace Cake\Http;

use
Cake\Core\Configure;
use
Cake\Log\Log;
use
Psr\Http\Message\ResponseInterface;
use
Zend\Diactoros\RelativeStream;
use
Zend\Diactoros\Response\EmitterInterface;

/**
 * Emits a Response to the PHP Server API.
 *
 * This emitter offers a few changes from the emitters offered by
 * diactoros:
 *
 * - It logs headers sent using CakePHP's logging tools.
 * - Cookies are emitted using setcookie() to not conflict with ext/session
 * - For fastcgi servers with PHP-FPM session_write_close() is called just
 *   before fastcgi_finish_request() to make sure session data is saved
 *   correctly (especially on slower session backends).
 */
class ResponseEmitter implements EmitterInterface
{
   
/**
     * {@inheritDoc}
     *
     * @param \Psr\Http\Message\ResponseInterface $response Response
     * @param int $maxBufferLength Max buffer length
     */
   
public function emit(ResponseInterface $response, $maxBufferLength = 8192)
    {
       
$file = $line = null;
        if (
headers_sent($file, $line)) {
           
$message = "Unable to emit headers. Headers sent in file=$file line=$line";
            if (
Configure::read('debug')) {
               
trigger_error($message, E_USER_WARNING);
            } else {
               
Log::warning($message);
            }
        }

       
$this->emitStatusLine($response);
       
$this->emitHeaders($response);
       
$this->flush();

       
$range = $this->parseContentRange($response->getHeaderLine('Content-Range'));
        if (
is_array($range)) {
           
$this->emitBodyRange($range, $response, $maxBufferLength);
        } else {
           
$this->emitBody($response, $maxBufferLength);
        }

        if (
function_exists('fastcgi_finish_request')) {
           
session_write_close();
           
fastcgi_finish_request();
        }
    }

   
/**
     * Emit the message body.
     *
     * @param \Psr\Http\Message\ResponseInterface $response The response to emit
     * @param int $maxBufferLength The chunk size to emit
     * @return void
     */
   
protected function emitBody(ResponseInterface $response, $maxBufferLength)
    {
        if (
in_array($response->getStatusCode(), [204, 304])) {
            return;
        }
       
$body = $response->getBody();

        if (!
$body->isSeekable()) {
            echo
$body;

            return;
        }

       
$body->rewind();
        while (!
$body->eof()) {
            echo
$body->read($maxBufferLength);
        }
    }

   
/**
     * Emit a range of the message body.
     *
     * @param array $range The range data to emit
     * @param \Psr\Http\Message\ResponseInterface $response The response to emit
     * @param int $maxBufferLength The chunk size to emit
     * @return void
     */
   
protected function emitBodyRange(array $range, ResponseInterface $response, $maxBufferLength)
    {
        list(
$unit, $first, $last, $length) = $range;

       
$body = $response->getBody();

        if (!
$body->isSeekable()) {
           
$contents = $body->getContents();
            echo
substr($contents, $first, $last - $first + 1);

            return;
        }

       
$body = new RelativeStream($body, $first);
       
$body->rewind();
       
$pos = 0;
       
$length = $last - $first + 1;
        while (!
$body->eof() && $pos < $length) {
            if ((
$pos + $maxBufferLength) > $length) {
                echo
$body->read($length - $pos);
                break;
            }

            echo
$body->read($maxBufferLength);
           
$pos = $body->tell();
        }
    }

   
/**
     * Emit the status line.
     *
     * Emits the status line using the protocol version and status code from
     * the response; if a reason phrase is available, it, too, is emitted.
     *
     * @param \Psr\Http\Message\ResponseInterface $response The response to emit
     * @return void
     */
   
protected function emitStatusLine(ResponseInterface $response)
    {
       
$reasonPhrase = $response->getReasonPhrase();
       
header(sprintf(
           
'HTTP/%s %d%s',
           
$response->getProtocolVersion(),
           
$response->getStatusCode(),
            (
$reasonPhrase ? ' ' . $reasonPhrase : '')
        ));
    }

   
/**
     * Emit response headers.
     *
     * Loops through each header, emitting each; if the header value
     * is an array with multiple values, ensures that each is sent
     * in such a way as to create aggregate headers (instead of replace
     * the previous).
     *
     * @param \Psr\Http\Message\ResponseInterface $response The response to emit
     * @return void
     */
   
protected function emitHeaders(ResponseInterface $response)
    {
       
$cookies = [];
        if (
method_exists($response, 'getCookies')) {
           
$cookies = $response->getCookies();
        }

        foreach (
$response->getHeaders() as $name => $values) {
            if (
strtolower($name) === 'set-cookie') {
               
$cookies = array_merge($cookies, $values);
                continue;
            }
           
$first = true;
            foreach (
$values as $value) {
               
header(sprintf(
                   
'%s: %s',
                   
$name,
                   
$value
               
), $first);
               
$first = false;
            }
        }

       
$this->emitCookies($cookies);
    }

   
/**
     * Emit cookies using setcookie()
     *
     * @param array $cookies An array of Set-Cookie headers.
     * @return void
     */
   
protected function emitCookies(array $cookies)
    {
        foreach (
$cookies as $cookie) {
            if (
is_array($cookie)) {
               
setcookie(
                   
$cookie['name'],
                   
$cookie['value'],
                   
$cookie['expire'],
                   
$cookie['path'],
                   
$cookie['domain'],
                   
$cookie['secure'],
                   
$cookie['httpOnly']
                );
                continue;
            }

            if (
strpos($cookie, '";"') !== false) {
               
$cookie = str_replace('";"', '{__cookie_replace__}', $cookie);
               
$parts = str_replace('{__cookie_replace__}', '";"', explode(';', $cookie));
            } else {
               
$parts = preg_split('/\;[ \t]*/', $cookie);
            }

            list(
$name, $value) = explode('=', array_shift($parts), 2);
           
$data = [
               
'name' => urldecode($name),
               
'value' => urldecode($value),
               
'expires' => 0,
               
'path' => '',
               
'domain' => '',
               
'secure' => false,
               
'httponly' => false,
            ];

            foreach (
$parts as $part) {
                if (
strpos($part, '=') !== false) {
                    list(
$key, $value) = explode('=', $part);
                } else {
                   
$key = $part;
                   
$value = true;
                }

               
$key = strtolower($key);
               
$data[$key] = $value;
            }
            if (!empty(
$data['expires'])) {
               
$data['expires'] = strtotime($data['expires']);
            }
           
setcookie(
               
$data['name'],
               
$data['value'],
               
$data['expires'],
               
$data['path'],
               
$data['domain'],
               
$data['secure'],
               
$data['httponly']
            );
        }
    }

   
/**
     * Loops through the output buffer, flushing each, before emitting
     * the response.
     *
     * @param int|null $maxBufferLevel Flush up to this buffer level.
     * @return void
     */
   
protected function flush($maxBufferLevel = null)
    {
        if (
null === $maxBufferLevel) {
           
$maxBufferLevel = ob_get_level();
        }

        while (
ob_get_level() > $maxBufferLevel) {
           
ob_end_flush();
        }
    }

   
/**
     * Parse content-range header
     * https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
     *
     * @param string $header The Content-Range header to parse.
     * @return array|false [unit, first, last, length]; returns false if no
     *     content range or an invalid content range is provided
     */
   
protected function parseContentRange($header)
    {
        if (
preg_match('/(?P<unit>[\w]+)\s+(?P<first>\d+)-(?P<last>\d+)\/(?P<length>\d+|\*)/', $header, $matches)) {
            return [
               
$matches['unit'],
                (int)
$matches['first'],
                (int)
$matches['last'],
               
$matches['length'] === '*' ? '*' : (int)$matches['length'],
            ];
        }

        return
false;
    }
}