vendor/guzzlehttp/psr7/src/Utils.php line 423

Open in your IDE?
  1. <?php
  2. namespace GuzzleHttp\Psr7;
  3. use Psr\Http\Message\RequestInterface;
  4. use Psr\Http\Message\ServerRequestInterface;
  5. use Psr\Http\Message\StreamInterface;
  6. use Psr\Http\Message\UriInterface;
  7. final class Utils
  8. {
  9.     /**
  10.      * Remove the items given by the keys, case insensitively from the data.
  11.      *
  12.      * @param iterable<string> $keys
  13.      *
  14.      * @return array
  15.      */
  16.     public static function caselessRemove($keys, array $data)
  17.     {
  18.         $result = [];
  19.         foreach ($keys as &$key) {
  20.             $key strtolower($key);
  21.         }
  22.         foreach ($data as $k => $v) {
  23.             if (!in_array(strtolower($k), $keys)) {
  24.                 $result[$k] = $v;
  25.             }
  26.         }
  27.         return $result;
  28.     }
  29.     /**
  30.      * Copy the contents of a stream into another stream until the given number
  31.      * of bytes have been read.
  32.      *
  33.      * @param StreamInterface $source Stream to read from
  34.      * @param StreamInterface $dest   Stream to write to
  35.      * @param int             $maxLen Maximum number of bytes to read. Pass -1
  36.      *                                to read the entire stream.
  37.      *
  38.      * @throws \RuntimeException on error.
  39.      */
  40.     public static function copyToStream(StreamInterface $sourceStreamInterface $dest$maxLen = -1)
  41.     {
  42.         $bufferSize 8192;
  43.         if ($maxLen === -1) {
  44.             while (!$source->eof()) {
  45.                 if (!$dest->write($source->read($bufferSize))) {
  46.                     break;
  47.                 }
  48.             }
  49.         } else {
  50.             $remaining $maxLen;
  51.             while ($remaining && !$source->eof()) {
  52.                 $buf $source->read(min($bufferSize$remaining));
  53.                 $len strlen($buf);
  54.                 if (!$len) {
  55.                     break;
  56.                 }
  57.                 $remaining -= $len;
  58.                 $dest->write($buf);
  59.             }
  60.         }
  61.     }
  62.     /**
  63.      * Copy the contents of a stream into a string until the given number of
  64.      * bytes have been read.
  65.      *
  66.      * @param StreamInterface $stream Stream to read
  67.      * @param int             $maxLen Maximum number of bytes to read. Pass -1
  68.      *                                to read the entire stream.
  69.      *
  70.      * @return string
  71.      *
  72.      * @throws \RuntimeException on error.
  73.      */
  74.     public static function copyToString(StreamInterface $stream$maxLen = -1)
  75.     {
  76.         $buffer '';
  77.         if ($maxLen === -1) {
  78.             while (!$stream->eof()) {
  79.                 $buf $stream->read(1048576);
  80.                 // Using a loose equality here to match on '' and false.
  81.                 if ($buf == null) {
  82.                     break;
  83.                 }
  84.                 $buffer .= $buf;
  85.             }
  86.             return $buffer;
  87.         }
  88.         $len 0;
  89.         while (!$stream->eof() && $len $maxLen) {
  90.             $buf $stream->read($maxLen $len);
  91.             // Using a loose equality here to match on '' and false.
  92.             if ($buf == null) {
  93.                 break;
  94.             }
  95.             $buffer .= $buf;
  96.             $len strlen($buffer);
  97.         }
  98.         return $buffer;
  99.     }
  100.     /**
  101.      * Calculate a hash of a stream.
  102.      *
  103.      * This method reads the entire stream to calculate a rolling hash, based
  104.      * on PHP's `hash_init` functions.
  105.      *
  106.      * @param StreamInterface $stream    Stream to calculate the hash for
  107.      * @param string          $algo      Hash algorithm (e.g. md5, crc32, etc)
  108.      * @param bool            $rawOutput Whether or not to use raw output
  109.      *
  110.      * @return string Returns the hash of the stream
  111.      *
  112.      * @throws \RuntimeException on error.
  113.      */
  114.     public static function hash(StreamInterface $stream$algo$rawOutput false)
  115.     {
  116.         $pos $stream->tell();
  117.         if ($pos 0) {
  118.             $stream->rewind();
  119.         }
  120.         $ctx hash_init($algo);
  121.         while (!$stream->eof()) {
  122.             hash_update($ctx$stream->read(1048576));
  123.         }
  124.         $out hash_final($ctx, (bool) $rawOutput);
  125.         $stream->seek($pos);
  126.         return $out;
  127.     }
  128.     /**
  129.      * Clone and modify a request with the given changes.
  130.      *
  131.      * This method is useful for reducing the number of clones needed to mutate
  132.      * a message.
  133.      *
  134.      * The changes can be one of:
  135.      * - method: (string) Changes the HTTP method.
  136.      * - set_headers: (array) Sets the given headers.
  137.      * - remove_headers: (array) Remove the given headers.
  138.      * - body: (mixed) Sets the given body.
  139.      * - uri: (UriInterface) Set the URI.
  140.      * - query: (string) Set the query string value of the URI.
  141.      * - version: (string) Set the protocol version.
  142.      *
  143.      * @param RequestInterface $request Request to clone and modify.
  144.      * @param array            $changes Changes to apply.
  145.      *
  146.      * @return RequestInterface
  147.      */
  148.     public static function modifyRequest(RequestInterface $request, array $changes)
  149.     {
  150.         if (!$changes) {
  151.             return $request;
  152.         }
  153.         $headers $request->getHeaders();
  154.         if (!isset($changes['uri'])) {
  155.             $uri $request->getUri();
  156.         } else {
  157.             // Remove the host header if one is on the URI
  158.             if ($host $changes['uri']->getHost()) {
  159.                 $changes['set_headers']['Host'] = $host;
  160.                 if ($port $changes['uri']->getPort()) {
  161.                     $standardPorts = ['http' => 80'https' => 443];
  162.                     $scheme $changes['uri']->getScheme();
  163.                     if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
  164.                         $changes['set_headers']['Host'] .= ':' $port;
  165.                     }
  166.                 }
  167.             }
  168.             $uri $changes['uri'];
  169.         }
  170.         if (!empty($changes['remove_headers'])) {
  171.             $headers self::caselessRemove($changes['remove_headers'], $headers);
  172.         }
  173.         if (!empty($changes['set_headers'])) {
  174.             $headers self::caselessRemove(array_keys($changes['set_headers']), $headers);
  175.             $headers $changes['set_headers'] + $headers;
  176.         }
  177.         if (isset($changes['query'])) {
  178.             $uri $uri->withQuery($changes['query']);
  179.         }
  180.         if ($request instanceof ServerRequestInterface) {
  181.             $new = (new ServerRequest(
  182.                 isset($changes['method']) ? $changes['method'] : $request->getMethod(),
  183.                 $uri,
  184.                 $headers,
  185.                 isset($changes['body']) ? $changes['body'] : $request->getBody(),
  186.                 isset($changes['version'])
  187.                     ? $changes['version']
  188.                     : $request->getProtocolVersion(),
  189.                 $request->getServerParams()
  190.             ))
  191.             ->withParsedBody($request->getParsedBody())
  192.             ->withQueryParams($request->getQueryParams())
  193.             ->withCookieParams($request->getCookieParams())
  194.             ->withUploadedFiles($request->getUploadedFiles());
  195.             foreach ($request->getAttributes() as $key => $value) {
  196.                 $new $new->withAttribute($key$value);
  197.             }
  198.             return $new;
  199.         }
  200.         return new Request(
  201.             isset($changes['method']) ? $changes['method'] : $request->getMethod(),
  202.             $uri,
  203.             $headers,
  204.             isset($changes['body']) ? $changes['body'] : $request->getBody(),
  205.             isset($changes['version'])
  206.                 ? $changes['version']
  207.                 : $request->getProtocolVersion()
  208.         );
  209.     }
  210.     /**
  211.      * Read a line from the stream up to the maximum allowed buffer length.
  212.      *
  213.      * @param StreamInterface $stream    Stream to read from
  214.      * @param int|null        $maxLength Maximum buffer length
  215.      *
  216.      * @return string
  217.      */
  218.     public static function readLine(StreamInterface $stream$maxLength null)
  219.     {
  220.         $buffer '';
  221.         $size 0;
  222.         while (!$stream->eof()) {
  223.             // Using a loose equality here to match on '' and false.
  224.             if (null == ($byte $stream->read(1))) {
  225.                 return $buffer;
  226.             }
  227.             $buffer .= $byte;
  228.             // Break when a new line is found or the max length - 1 is reached
  229.             if ($byte === "\n" || ++$size === $maxLength 1) {
  230.                 break;
  231.             }
  232.         }
  233.         return $buffer;
  234.     }
  235.     /**
  236.      * Create a new stream based on the input type.
  237.      *
  238.      * Options is an associative array that can contain the following keys:
  239.      * - metadata: Array of custom metadata.
  240.      * - size: Size of the stream.
  241.      *
  242.      * This method accepts the following `$resource` types:
  243.      * - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
  244.      * - `string`: Creates a stream object that uses the given string as the contents.
  245.      * - `resource`: Creates a stream object that wraps the given PHP stream resource.
  246.      * - `Iterator`: If the provided value implements `Iterator`, then a read-only
  247.      *   stream object will be created that wraps the given iterable. Each time the
  248.      *   stream is read from, data from the iterator will fill a buffer and will be
  249.      *   continuously called until the buffer is equal to the requested read size.
  250.      *   Subsequent read calls will first read from the buffer and then call `next`
  251.      *   on the underlying iterator until it is exhausted.
  252.      * - `object` with `__toString()`: If the object has the `__toString()` method,
  253.      *   the object will be cast to a string and then a stream will be returned that
  254.      *   uses the string value.
  255.      * - `NULL`: When `null` is passed, an empty stream object is returned.
  256.      * - `callable` When a callable is passed, a read-only stream object will be
  257.      *   created that invokes the given callable. The callable is invoked with the
  258.      *   number of suggested bytes to read. The callable can return any number of
  259.      *   bytes, but MUST return `false` when there is no more data to return. The
  260.      *   stream object that wraps the callable will invoke the callable until the
  261.      *   number of requested bytes are available. Any additional bytes will be
  262.      *   buffered and used in subsequent reads.
  263.      *
  264.      * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data
  265.      * @param array                                                                  $options  Additional options
  266.      *
  267.      * @return StreamInterface
  268.      *
  269.      * @throws \InvalidArgumentException if the $resource arg is not valid.
  270.      */
  271.     public static function streamFor($resource '', array $options = [])
  272.     {
  273.         if (is_scalar($resource)) {
  274.             $stream self::tryFopen('php://temp''r+');
  275.             if ($resource !== '') {
  276.                 fwrite($stream$resource);
  277.                 fseek($stream0);
  278.             }
  279.             return new Stream($stream$options);
  280.         }
  281.         switch (gettype($resource)) {
  282.             case 'resource':
  283.                 /*
  284.                  * The 'php://input' is a special stream with quirks and inconsistencies.
  285.                  * We avoid using that stream by reading it into php://temp
  286.                  */
  287.                 $metaData = \stream_get_meta_data($resource);
  288.                 if (isset($metaData['uri']) && $metaData['uri'] === 'php://input') {
  289.                     $stream self::tryFopen('php://temp''w+');
  290.                     fwrite($streamstream_get_contents($resource));
  291.                     fseek($stream0);
  292.                     $resource $stream;
  293.                 }
  294.                 return new Stream($resource$options);
  295.             case 'object':
  296.                 if ($resource instanceof StreamInterface) {
  297.                     return $resource;
  298.                 } elseif ($resource instanceof \Iterator) {
  299.                     return new PumpStream(function () use ($resource) {
  300.                         if (!$resource->valid()) {
  301.                             return false;
  302.                         }
  303.                         $result $resource->current();
  304.                         $resource->next();
  305.                         return $result;
  306.                     }, $options);
  307.                 } elseif (method_exists($resource'__toString')) {
  308.                     return Utils::streamFor((string) $resource$options);
  309.                 }
  310.                 break;
  311.             case 'NULL':
  312.                 return new Stream(self::tryFopen('php://temp''r+'), $options);
  313.         }
  314.         if (is_callable($resource)) {
  315.             return new PumpStream($resource$options);
  316.         }
  317.         throw new \InvalidArgumentException('Invalid resource type: ' gettype($resource));
  318.     }
  319.     /**
  320.      * Safely opens a PHP stream resource using a filename.
  321.      *
  322.      * When fopen fails, PHP normally raises a warning. This function adds an
  323.      * error handler that checks for errors and throws an exception instead.
  324.      *
  325.      * @param string $filename File to open
  326.      * @param string $mode     Mode used to open the file
  327.      *
  328.      * @return resource
  329.      *
  330.      * @throws \RuntimeException if the file cannot be opened
  331.      */
  332.     public static function tryFopen($filename$mode)
  333.     {
  334.         $ex null;
  335.         set_error_handler(function () use ($filename$mode, &$ex) {
  336.             $ex = new \RuntimeException(sprintf(
  337.                 'Unable to open "%s" using mode "%s": %s',
  338.                 $filename,
  339.                 $mode,
  340.                 func_get_args()[1]
  341.             ));
  342.             return true;
  343.         });
  344.         try {
  345.             $handle fopen($filename$mode);
  346.         } catch (\Throwable $e) {
  347.             $ex = new \RuntimeException(sprintf(
  348.                 'Unable to open "%s" using mode "%s": %s',
  349.                 $filename,
  350.                 $mode,
  351.                 $e->getMessage()
  352.             ), 0$e);
  353.         }
  354.         restore_error_handler();
  355.         if ($ex) {
  356.             /** @var $ex \RuntimeException */
  357.             throw $ex;
  358.         }
  359.         return $handle;
  360.     }
  361.     /**
  362.      * Returns a UriInterface for the given value.
  363.      *
  364.      * This function accepts a string or UriInterface and returns a
  365.      * UriInterface for the given value. If the value is already a
  366.      * UriInterface, it is returned as-is.
  367.      *
  368.      * @param string|UriInterface $uri
  369.      *
  370.      * @return UriInterface
  371.      *
  372.      * @throws \InvalidArgumentException
  373.      */
  374.     public static function uriFor($uri)
  375.     {
  376.         if ($uri instanceof UriInterface) {
  377.             return $uri;
  378.         }
  379.         if (is_string($uri)) {
  380.             return new Uri($uri);
  381.         }
  382.         throw new \InvalidArgumentException('URI must be a string or UriInterface');
  383.     }
  384. }