PK %RK8%
LICENSE.mdnu ٘ Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Laminas Foundation nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK %RF[ [ COPYRIGHT.mdnu ٘ Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC. (https://getlaminas.org/)
PK %RN N .travis.ymlnu ٘ language: php
cache:
directories:
- $HOME/.composer/cache
env:
global:
- COMPOSER_ARGS="--no-interaction"
- COVERAGE_DEPS="php-coveralls/php-coveralls"
- TESTS_LAMINAS_HTTP_CLIENT_ONLINE=true
- TESTS_LAMINAS_HTTP_CLIENT_BASEURI=http://127.0.0.1
- TESTS_LAMINAS_HTTP_CLIENT_HTTP_PROXY=127.0.0.1:8081
- TESTS_LAMINAS_HTTP_CLIENT_NOTRESPONDINGURI=http://127.1.1.0:1234
matrix:
fast_finish: true
include:
- php: 7.3
env:
- DEPS=lowest
- php: 7.3
env:
- DEPS=latest
- php: 7.4
env:
- DEPS=lowest
- php: 7.4
env:
- DEPS=latest
- CS_CHECK=true
- TEST_COVERAGE=true
- php: 8.0
env:
- DEPS=lowest
- COMPOSER_ARGS="--no-interaction --ignore-platform-req=php"
- php: 8.0
env:
- COMPOSER_ARGS="--no-interaction --ignore-platform-req=php"
- php: nightly
env:
- DEPS=lowest
- COMPOSER_ARGS="--no-interaction --ignore-platform-req=php"
- php: nightly
env:
- DEPS=latest
- COMPOSER_ARGS="--no-interaction --ignore-platform-req=php"
allow_failures:
- php: nightly
before_install:
- if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
install:
- travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
- if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi
- stty cols 120 && composer show
before_script:
# custom php.ini for PHP 5.6
- if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.6" ]]; then phpenv config-add .ci/php5.6.ini; fi
# install apache
- sudo apt-get update -qq
- sudo apt-get install apache2 libapache2-mod-fastcgi
# enable php-fpm
- sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
- sudo a2enmod rewrite actions fastcgi alias
- echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- sudo sed -i -e "s,www-data,travis,g" /etc/apache2/envvars
- sudo chown -R travis:travis /var/lib/apache2/fastcgi
- ~/.phpenv/versions/$(phpenv version-name)/sbin/php-fpm
# configure apache virtual hosts
- sudo cp -f .ci/site.conf /etc/apache2/sites-available/000-default.conf
- sudo sed -e "s?%TRAVIS_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-available/000-default.conf
- sudo sed -e "s?%PHP_VERSION%?$(phpenv version-name)?g" --in-place /etc/apache2/sites-available/000-default.conf
# enable TRACE
- sudo sed -e "s?TraceEnable Off?TraceEnable On?g" --in-place /etc/apache2/conf-available/security.conf
# configure proxy
- sudo a2enmod proxy proxy_http proxy_connect
- sudo cp -f .ci/proxy.conf /etc/apache2/sites-available/proxy.conf
- sudo a2ensite proxy
- sudo sed -i "s/Listen 80/Listen 80\nListen 8081/" /etc/apache2/ports.conf
- sudo service apache2 restart
script:
- if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi
- if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi
after_script:
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry php vendor/bin/php-coveralls -v ; fi
- sudo cat /var/log/apache2/error.log
- sudo cat /var/log/apache2/access.log
notifications:
email: false
PK %Rx= = .coveralls.ymlnu ٘ coverage_clover: clover.xml
json_path: coveralls-upload.json
PK %RMs
phpcs.xmlnu ٘
src
test
*/_files/*
PK %Rf
.ci/site.confnu ٘
DocumentRoot %TRAVIS_BUILD_DIR%/test/Client/_files
Options FollowSymLinks MultiViews ExecCGI
AllowOverride All
Require all granted
# Wire up Apache to use Travis CI's php-fpm.
AddHandler php%PHP_VERSION%-fcgi .php
Action php%PHP_VERSION%-fcgi /php%PHP_VERSION%-fcgi
Alias /php%PHP_VERSION%-fcgi /usr/lib/cgi-bin/php%PHP_VERSION%-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php%PHP_VERSION%-fcgi -host 127.0.0.1:9000 -pass-header Authorization
Require all granted
PK %R_ .ci/proxy.confnu ٘
ProxyRequests On
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
PK %R5! ! .ci/php5.6.ininu ٘ always_populate_raw_post_data=-1
PK %RF#% % README.mdnu ٘ # laminas-http
[![Build Status](https://travis-ci.com/laminas/laminas-http.svg?branch=master)](https://travis-ci.com/laminas/laminas-http)
[![Coverage Status](https://coveralls.io/repos/github/laminas/laminas-http/badge.svg?branch=master)](https://coveralls.io/github/laminas/laminas-http?branch=master)
laminas-http provides the HTTP message abstraction used by
[laminas-mvc](https://docs.laminas.dev/laminas-mvc/), and also provides an
extensible, adapter-driven HTTP client library.
This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as
it predates that specification. For PSR-7 support, please see our
[Diactoros component](https://docs.laminas.dev/laminas-diactoros/).
## Installation
Run the following to install this library:
```bash
$ composer require laminas/laminas-http
```
## Documentation
Browse the documentation online at https://docs.laminas.dev/laminas-http/
## Support
* [Issues](https://github.com/laminas/laminas-http/issues/)
* [Chat](https://laminas.dev/chat/)
* [Forum](https://discourse.laminas.dev/)
PK %R
.gitignorenu ٘ /clover.xml
/composer.lock
/coveralls-upload.json
/docs/html/
/laminas-mkdoc-theme.tgz
/laminas-mkdoc-theme/
/phpunit.xml
/vendor/
/.phpunit.result.cache
PK %R&; ; src/Headers.phpnu ٘ 2) {
throw new Exception\RuntimeException('Malformed header detected');
}
continue;
}
if ($emptyLine) {
throw new Exception\RuntimeException('Malformed header detected');
}
// check if a header name is present
if (preg_match('/^(?P[^()><@,;:\"\\/\[\]?={} \t]+):.*$/', $line, $matches)) {
if ($current) {
// a header name was present, then store the current complete line
$headers->headersKeys[] = static::createKey($current['name']);
$headers->headers[] = $current;
}
$current = [
'name' => $matches['name'],
'line' => trim($line),
];
continue;
}
if (preg_match("/^[ \t][^\r\n]*$/", $line, $matches)) {
// continuation: append to current line
$current['line'] .= trim($line);
continue;
}
// Line does not match header format!
throw new Exception\RuntimeException(sprintf(
'Line "%s" does not match header format!',
$line
));
}
if ($current) {
$headers->headersKeys[] = static::createKey($current['name']);
$headers->headers[] = $current;
}
return $headers;
}
/**
* Set an alternate implementation for the PluginClassLoader
*
* @param PluginClassLocator $pluginClassLoader
* @return $this
*/
public function setPluginClassLoader(PluginClassLocator $pluginClassLoader)
{
$this->pluginClassLoader = $pluginClassLoader;
return $this;
}
/**
* Return an instance of a PluginClassLocator, lazyload and inject map if necessary
*
* @return PluginClassLocator
*/
public function getPluginClassLoader()
{
if ($this->pluginClassLoader === null) {
$this->pluginClassLoader = new HeaderLoader();
}
return $this->pluginClassLoader;
}
/**
* Add many headers at once
*
* Expects an array (or Traversable object) of type/value pairs.
*
* @param array|Traversable $headers
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function addHeaders($headers)
{
if (! is_array($headers) && ! $headers instanceof Traversable) {
throw new Exception\InvalidArgumentException(sprintf(
'Expected array or Traversable; received "%s"',
(is_object($headers) ? get_class($headers) : gettype($headers))
));
}
foreach ($headers as $name => $value) {
if (is_int($name)) {
if (is_string($value)) {
$this->addHeaderLine($value);
} elseif (is_array($value) && count($value) == 1) {
$this->addHeaderLine(key($value), current($value));
} elseif (is_array($value) && count($value) == 2) {
$this->addHeaderLine($value[0], $value[1]);
} elseif ($value instanceof Header\HeaderInterface) {
$this->addHeader($value);
}
} elseif (is_string($name)) {
$this->addHeaderLine($name, $value);
}
}
return $this;
}
/**
* Add a raw header line, either in name => value, or as a single string 'name: value'
*
* This method allows for lazy-loading in that the parsing and instantiation of Header object
* will be delayed until they are retrieved by either get() or current()
*
* @throws Exception\InvalidArgumentException
* @param string $headerFieldNameOrLine
* @param string $fieldValue optional
* @return $this
*/
public function addHeaderLine($headerFieldNameOrLine, $fieldValue = null)
{
$matches = null;
if (preg_match('/^(?P[^()><@,;:\"\\/\[\]?=}{ \t]+):.*$/', $headerFieldNameOrLine, $matches)
&& $fieldValue === null) {
// is a header
$headerName = $matches['name'];
$headerKey = static::createKey($matches['name']);
$line = $headerFieldNameOrLine;
} elseif ($fieldValue === null) {
throw new Exception\InvalidArgumentException('A field name was provided without a field value');
} else {
$headerName = $headerFieldNameOrLine;
$headerKey = static::createKey($headerFieldNameOrLine);
if (is_array($fieldValue)) {
$fieldValue = implode('; ', $fieldValue);
}
$line = $headerFieldNameOrLine . ': ' . $fieldValue;
}
$this->headersKeys[] = $headerKey;
$this->headers[] = ['name' => $headerName, 'line' => $line];
return $this;
}
/**
* Add a Header to this container, for raw values @see addHeaderLine() and addHeaders()
*
* @param Header\HeaderInterface $header
* @return $this
*/
public function addHeader(Header\HeaderInterface $header)
{
$key = static::createKey($header->getFieldName());
$index = array_search($key, $this->headersKeys);
// No header by that key presently; append key and header to list.
if ($index === false) {
$this->headersKeys[] = $key;
$this->headers[] = $header;
return $this;
}
// Header exists, and is a multi-value header; append key and header to
// list (as multi-value headers are aggregated on retrieval)
$class = ($this->getPluginClassLoader()->load(str_replace('-', '', $key))) ?: Header\GenericHeader::class;
if (in_array(Header\MultipleHeaderInterface::class, class_implements($class, true))) {
$this->headersKeys[] = $key;
$this->headers[] = $header;
return $this;
}
// Otherwise, we replace the current instance.
$this->headers[$index] = $header;
return $this;
}
/**
* Remove a Header from the container
*
* @param Header\HeaderInterface $header
* @return bool
*/
public function removeHeader(Header\HeaderInterface $header)
{
$index = array_search($header, $this->headers, true);
if ($index !== false) {
unset($this->headersKeys[$index]);
unset($this->headers[$index]);
return true;
}
return false;
}
/**
* Clear all headers
*
* Removes all headers from queue
*
* @return $this
*/
public function clearHeaders()
{
$this->headers = $this->headersKeys = [];
return $this;
}
/**
* Get all headers of a certain name/type
*
* @param string $name
* @return bool|Header\HeaderInterface|ArrayIterator
*/
public function get($name)
{
$key = static::createKey($name);
if (! $this->has($name)) {
return false;
}
$class = ($this->getPluginClassLoader()->load(str_replace('-', '', $key))) ?: GenericHeader::class;
if (in_array(MultipleHeaderInterface::class, class_implements($class, true))) {
$headers = [];
foreach (array_keys($this->headersKeys, $key) as $index) {
if (is_array($this->headers[$index])) {
$this->lazyLoadHeader($index);
}
}
foreach (array_keys($this->headersKeys, $key) as $index) {
$headers[] = $this->headers[$index];
}
return new ArrayIterator($headers);
}
$index = array_search($key, $this->headersKeys);
if ($index === false) {
return false;
}
if (is_array($this->headers[$index])) {
return $this->lazyLoadHeader($index);
}
return $this->headers[$index];
}
/**
* Test for existence of a type of header
*
* @param string $name
* @return bool
*/
public function has($name)
{
return in_array(static::createKey($name), $this->headersKeys);
}
/**
* Advance the pointer for this object as an iterator
*
* @return void
*/
public function next()
{
next($this->headers);
}
/**
* Return the current key for this object as an iterator
*
* @return mixed
*/
public function key()
{
return (key($this->headers));
}
/**
* Is this iterator still valid?
*
* @return bool
*/
public function valid()
{
return (current($this->headers) !== false);
}
/**
* Reset the internal pointer for this object as an iterator
*
* @return void
*/
public function rewind()
{
reset($this->headers);
}
/**
* Return the current value for this iterator, lazy loading it if need be
*
* @return array|Header\HeaderInterface
*/
public function current()
{
$current = current($this->headers);
if (is_array($current)) {
$current = $this->lazyLoadHeader(key($this->headers));
}
return $current;
}
/**
* Return the number of headers in this contain, if all headers have not been parsed, actual count could
* increase if MultipleHeader objects exist in the Request/Response. If you need an exact count, iterate
*
* @return int count of currently known headers
*/
public function count()
{
return count($this->headers);
}
/**
* Render all headers at once
*
* This method handles the normal iteration of headers; it is up to the
* concrete classes to prepend with the appropriate status/request line.
*
* @return string
*/
public function toString()
{
$headers = '';
foreach ($this->toArray() as $fieldName => $fieldValue) {
if (is_array($fieldValue)) {
// Handle multi-value headers
foreach ($fieldValue as $value) {
$headers .= $fieldName . ': ' . $value . "\r\n";
}
continue;
}
// Handle single-value headers
$headers .= $fieldName . ': ' . $fieldValue . "\r\n";
}
return $headers;
}
/**
* Return the headers container as an array
*
* @todo determine how to produce single line headers, if they are supported
* @return array
*/
public function toArray()
{
$headers = [];
/* @var $header Header\HeaderInterface */
foreach ($this->headers as $index => $header) {
if (is_array($header)) {
$header = $this->lazyLoadHeader($index);
}
if ($header instanceof Header\MultipleHeaderInterface) {
$name = $header->getFieldName();
if (! isset($headers[$name])) {
$headers[$name] = [];
}
$headers[$name][] = $header->getFieldValue();
} else {
$headers[$header->getFieldName()] = $header->getFieldValue();
}
}
return $headers;
}
/**
* By calling this, it will force parsing and loading of all headers, after this count() will be accurate
*
* @return bool
*/
public function forceLoading()
{
foreach ($this as $item) {
// $item should now be loaded
}
return true;
}
/**
* @param $index
* @param bool $isGeneric If true, there is no need to parse $index and call the ClassLoader.
* @return mixed|void
*/
protected function lazyLoadHeader($index, $isGeneric = false)
{
$current = $this->headers[$index];
$key = $this->headersKeys[$index];
/* @var $class Header\HeaderInterface */
$class = $this->getPluginClassLoader()->load(str_replace('-', '', $key));
if ($isGeneric || ! $class) {
$class = GenericHeader::class;
}
try {
$headers = $class::fromString($current['line']);
} catch (Exception\InvalidArgumentException $exception) {
// Generic Header should throw an exception if it fails
if ($isGeneric) {
throw $exception;
}
// Retry one more time with GenericHeader
return $this->lazyLoadHeader($index, true);
}
if (is_array($headers)) {
$this->headers[$index] = $current = array_shift($headers);
foreach ($headers as $header) {
$this->headersKeys[] = $key;
$this->headers[] = $header;
}
return $current;
}
$this->headers[$index] = $current = $headers;
return $current;
}
/**
* Create array key from header name
*
* @param string $name
* @return string
*/
protected static function createKey($name)
{
return str_replace(['_', ' ', '.'], '-', strtolower($name));
}
}
PK %RMkw % src/Exception/OutOfRangeException.phpnu ٘ 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
// SUCCESS CODES
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-status',
208 => 'Already Reported',
226 => 'IM Used',
// REDIRECTION CODES
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Switch Proxy', // Deprecated
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
// CLIENT ERROR
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Time-out',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested range not satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a teapot',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Too Early',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
444 => 'Connection Closed Without Response',
451 => 'Unavailable For Legal Reasons',
499 => 'Client Closed Request',
// SERVER ERROR
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Time-out',
505 => 'HTTP Version not supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
599 => 'Network Connect Timeout Error',
];
/**
* @var int Status code
*/
protected $statusCode = 200;
/**
* @var string|null Null means it will be looked up from the $reasonPhrase list above
*/
protected $reasonPhrase;
/**
* Populate object from string
*
* @param string $string
* @return static
* @throws Exception\InvalidArgumentException
*/
public static function fromString($string)
{
$lines = explode("\r\n", $string);
if (! is_array($lines) || count($lines) === 1) {
$lines = explode("\n", $string);
}
$firstLine = array_shift($lines);
$response = new static();
$response->parseStatusLine($firstLine);
/**
* @link https://tools.ietf.org/html/rfc7231#section-6.2.1
*/
if ($response->statusCode === static::STATUS_CODE_100) {
$next = array_shift($lines); // take next line
$next = empty($next) ? array_shift($lines) : $next; // take next or skip if empty
$response->parseStatusLine($next);
}
if (count($lines) === 0) {
return $response;
}
$isHeader = true;
$headers = $content = [];
foreach ($lines as $line) {
if ($isHeader && $line === '') {
$isHeader = false;
continue;
}
if ($isHeader) {
if (preg_match("/[\r\n]/", $line)) {
throw new Exception\RuntimeException('CRLF injection detected');
}
$headers[] = $line;
continue;
}
if (empty($content)
&& preg_match('/^[a-z0-9!#$%&\'*+.^_`|~-]+:$/i', $line)
) {
throw new Exception\RuntimeException('CRLF injection detected');
}
$content[] = $line;
}
if ($headers) {
$response->headers = implode("\r\n", $headers);
}
if ($content) {
$response->setContent(implode("\r\n", $content));
}
return $response;
}
/**
* @param string $line
* @throws Exception\InvalidArgumentException
* @throws Exception\RuntimeException
*/
protected function parseStatusLine($line)
{
$regex = '/^HTTP\/(?P1\.[01]|2) (?P\d{3})(?:[ ]+(?P.*))?$/';
$matches = [];
if (! preg_match($regex, $line, $matches)) {
throw new Exception\InvalidArgumentException(
'A valid response status line was not found in the provided string'
);
}
$this->version = $matches['version'];
$this->setStatusCode($matches['status']);
$this->setReasonPhrase((isset($matches['reason']) ? $matches['reason'] : ''));
}
/**
* @return Header\SetCookie[]
*/
public function getCookie()
{
return $this->getHeaders()->get('Set-Cookie');
}
/**
* Set HTTP status code and (optionally) message
*
* @param int $code
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function setStatusCode($code)
{
if (! is_numeric($code)
|| is_float($code)
|| $code < static::MIN_STATUS_CODE_VALUE
|| $code > static::MAX_STATUS_CODE_VALUE
) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid status code "%s"; must be an integer between %d and %d, inclusive',
is_scalar($code) ? $code : gettype($code),
static::MIN_STATUS_CODE_VALUE,
static::MAX_STATUS_CODE_VALUE
));
}
return $this->saveStatusCode($code);
}
/**
* Retrieve HTTP status code
*
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* Set custom HTTP status code
*
* @param int $code
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function setCustomStatusCode($code)
{
if (! is_numeric($code)) {
$code = is_scalar($code) ? $code : gettype($code);
throw new Exception\InvalidArgumentException(sprintf(
'Invalid status code provided: "%s"',
$code
));
}
return $this->saveStatusCode($code);
}
/**
* Assign status code
*
* @param int $code
* @return $this
*/
protected function saveStatusCode($code)
{
$this->reasonPhrase = null;
$this->statusCode = (int) $code;
return $this;
}
/**
* @param string $reasonPhrase
* @return $this
*/
public function setReasonPhrase($reasonPhrase)
{
$this->reasonPhrase = trim($reasonPhrase);
return $this;
}
/**
* Get HTTP status message
*
* @return string
*/
public function getReasonPhrase()
{
if (null == $this->reasonPhrase && isset($this->recommendedReasonPhrases[$this->statusCode])) {
$this->reasonPhrase = $this->recommendedReasonPhrases[$this->statusCode];
}
return $this->reasonPhrase;
}
/**
* Get the body of the response
*
* @return string
*/
public function getBody()
{
$body = (string) $this->getContent();
$transferEncoding = $this->getHeaders()->get('Transfer-Encoding');
if (! empty($transferEncoding)) {
if (strtolower($transferEncoding->getFieldValue()) === 'chunked') {
$body = $this->decodeChunkedBody($body);
}
}
$contentEncoding = $this->getHeaders()->get('Content-Encoding');
if (! empty($contentEncoding)) {
$contentEncoding = $contentEncoding->getFieldValue();
if ($contentEncoding === 'gzip') {
$body = $this->decodeGzip($body);
} elseif ($contentEncoding === 'deflate') {
$body = $this->decodeDeflate($body);
}
}
return $body;
}
/**
* Does the status code indicate a client error?
*
* @return bool
*/
public function isClientError()
{
$code = $this->getStatusCode();
return ($code < 500 && $code >= 400);
}
/**
* Is the request forbidden due to ACLs?
*
* @return bool
*/
public function isForbidden()
{
return (403 === $this->getStatusCode());
}
/**
* Is the current status "informational"?
*
* @return bool
*/
public function isInformational()
{
$code = $this->getStatusCode();
return ($code >= 100 && $code < 200);
}
/**
* Does the status code indicate the resource is not found?
*
* @return bool
*/
public function isNotFound()
{
return (404 === $this->getStatusCode());
}
/**
* Does the status code indicate the resource is gone?
*
* @return bool
*/
public function isGone()
{
return (410 === $this->getStatusCode());
}
/**
* Do we have a normal, OK response?
*
* @return bool
*/
public function isOk()
{
return (200 === $this->getStatusCode());
}
/**
* Does the status code reflect a server error?
*
* @return bool
*/
public function isServerError()
{
$code = $this->getStatusCode();
return (500 <= $code && 600 > $code);
}
/**
* Do we have a redirect?
*
* @return bool
*/
public function isRedirect()
{
$code = $this->getStatusCode();
return (300 <= $code && 400 > $code);
}
/**
* Was the response successful?
*
* @return bool
*/
public function isSuccess()
{
$code = $this->getStatusCode();
return (200 <= $code && 300 > $code);
}
/**
* Render the status line header
*
* @return string
*/
public function renderStatusLine()
{
$status = sprintf(
'HTTP/%s %d %s',
$this->getVersion(),
$this->getStatusCode(),
$this->getReasonPhrase()
);
return trim($status);
}
/**
* Render entire response as HTTP response string
*
* @return string
*/
public function toString()
{
$str = $this->renderStatusLine() . "\r\n";
$str .= $this->getHeaders()->toString();
$str .= "\r\n";
$str .= $this->getContent();
return $str;
}
/**
* Decode a "chunked" transfer-encoded body and return the decoded text
*
* @param string $body
* @return string
* @throws Exception\RuntimeException
*/
protected function decodeChunkedBody($body)
{
$decBody = '';
$offset = 0;
while (true) {
if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m, 0, $offset)) {
if (trim(substr($body, $offset))) {
// Message was not consumed completely!
throw new Exception\RuntimeException(
'Error parsing body - doesn\'t seem to be a chunked message'
);
}
// Message was consumed completely
break;
}
$length = hexdec(trim($m[1]));
$cut = strlen($m[0]);
$decBody .= substr($body, $offset + $cut, $length);
$offset += $cut + $length + 2;
}
return $decBody;
}
/**
* Decode a gzip encoded message (when Content-encoding = gzip)
*
* Currently requires PHP with zlib support
*
* @param string $body
* @return string
* @throws Exception\RuntimeException
*/
protected function decodeGzip($body)
{
if (! function_exists('gzinflate')) {
throw new Exception\RuntimeException(
'zlib extension is required in order to decode "gzip" encoding'
);
}
if ($body === ''
|| ($this->getHeaders()->has('content-length')
&& (int) $this->getHeaders()->get('content-length')->getFieldValue() === 0)
) {
return '';
}
ErrorHandler::start();
$return = gzinflate(substr($body, 10));
$test = ErrorHandler::stop();
if ($test) {
throw new Exception\RuntimeException(
'Error occurred during gzip inflation',
0,
$test
);
}
return $return;
}
/**
* Decode a zlib deflated message (when Content-encoding = deflate)
*
* Currently requires PHP with zlib support
*
* @param string $body
* @return string
* @throws Exception\RuntimeException
*/
protected function decodeDeflate($body)
{
if (! function_exists('gzuncompress')) {
throw new Exception\RuntimeException(
'zlib extension is required in order to decode "deflate" encoding'
);
}
if ($this->getHeaders()->has('content-length')
&& 0 === (int) $this->getHeaders()->get('content-length')->getFieldValue()) {
return '';
}
/**
* Some servers (IIS ?) send a broken deflate response, without the
* RFC-required zlib header.
*
* We try to detect the zlib header, and if it does not exist we
* teat the body is plain DEFLATE content.
*
* This method was adapted from PEAR HTTP_Request2 by (c) Alexey Borzov
*
* @link https://getlaminas.org/issues/browse/Laminas-6040
*/
$zlibHeader = unpack('n', substr($body, 0, 2));
if ($zlibHeader[1] % 31 === 0) {
return gzuncompress($body);
}
return gzinflate($body);
}
}
PK %RcU src/Response/Stream.phpnu ٘ contentLength = $contentLength;
}
/**
* Get content length
*
* @return int|null
*/
public function getContentLength()
{
return $this->contentLength;
}
/**
* Get the response as stream
*
* @return resource
*/
public function getStream()
{
return $this->stream;
}
/**
* Set the response stream
*
* @param resource $stream
* @return $this
*/
public function setStream($stream)
{
$this->stream = $stream;
return $this;
}
/**
* Get the cleanup trigger
*
* @return bool
*/
public function getCleanup()
{
return $this->cleanup;
}
/**
* Set the cleanup trigger
*
* @param bool $cleanup
*/
public function setCleanup($cleanup = true)
{
$this->cleanup = $cleanup;
}
/**
* Get file name associated with the stream
*
* @return string
*/
public function getStreamName()
{
return $this->streamName;
}
/**
* Set file name associated with the stream
*
* @param string $streamName Name to set
* @return $this
*/
public function setStreamName($streamName)
{
$this->streamName = $streamName;
return $this;
}
/**
* Create a new Laminas\Http\Response\Stream object from a stream
*
* @param string $responseString
* @param resource $stream
* @return $this
* @throws Exception\InvalidArgumentException
* @throws Exception\OutOfRangeException
*/
public static function fromStream($responseString, $stream)
{
if (! is_resource($stream) || get_resource_type($stream) !== 'stream') {
throw new Exception\InvalidArgumentException('A valid stream is required');
}
$headerComplete = false;
$headersString = '';
$responseArray = [];
if ($responseString) {
$responseArray = explode("\n", $responseString);
}
while (! empty($responseArray)) {
$nextLine = array_shift($responseArray);
$headersString .= $nextLine . "\n";
$nextLineTrimmed = trim($nextLine);
if ($nextLineTrimmed == '') {
$headerComplete = true;
break;
}
}
if (! $headerComplete) {
while (false !== ($nextLine = fgets($stream))) {
$headersString .= trim($nextLine) . "\r\n";
if ($nextLine == "\r\n" || $nextLine == "\n") {
$headerComplete = true;
break;
}
}
}
if (! $headerComplete) {
throw new Exception\OutOfRangeException('End of header not found');
}
/** @var Stream $response */
$response = static::fromString($headersString);
if (is_resource($stream)) {
$response->setStream($stream);
}
if (! empty($responseArray)) {
$response->content = implode("\n", $responseArray);
}
$headers = $response->getHeaders();
foreach ($headers as $header) {
if ($header instanceof \Laminas\Http\Header\ContentLength) {
$response->setContentLength((int) $header->getFieldValue());
$contentLength = $response->getContentLength();
if (strlen($response->content) > $contentLength) {
throw new Exception\OutOfRangeException(sprintf(
'Too much content was extracted from the stream (%d instead of %d bytes)',
strlen($response->content),
$contentLength
));
}
break;
}
}
return $response;
}
/**
* Get the response body as string
*
* This method returns the body of the HTTP response (the content), as it
* should be in it's readable version - that is, after decoding it (if it
* was decoded), deflating it (if it was gzip compressed), etc.
*
* If you want to get the raw body (as transferred on wire) use
* $this->getRawBody() instead.
*
* @return string
*/
public function getBody()
{
if ($this->stream !== null) {
$this->readStream();
}
return parent::getBody();
}
/**
* Get the raw response body (as transferred "on wire") as string
*
* If the body is encoded (with Transfer-Encoding, not content-encoding -
* IE "chunked" body), gzip compressed, etc. it will not be decoded.
*
* @return string
*/
public function getRawBody()
{
if ($this->stream) {
$this->readStream();
}
return $this->content;
}
/**
* Read stream content and return it as string
*
* Function reads the remainder of the body from the stream and closes the stream.
*
* @return string
*/
protected function readStream()
{
$contentLength = $this->getContentLength();
if (null !== $contentLength) {
$bytes = $contentLength - $this->contentStreamed;
} else {
$bytes = -1; // Read the whole buffer
}
if (! is_resource($this->stream) || $bytes == 0) {
return '';
}
$this->content .= stream_get_contents($this->stream, $bytes);
$this->contentStreamed += strlen($this->content);
if ($this->getContentLength() == $this->contentStreamed) {
$this->stream = null;
}
}
/**
* Destructor
*/
public function __destruct()
{
if (is_resource($this->stream)) {
$this->stream = null; //Could be listened by others
}
if ($this->cleanup && is_string($this->streamName) && file_exists($this->streamName)) {
ErrorHandler::start(E_WARNING);
unlink($this->streamName);
ErrorHandler::stop();
}
}
}
PK %R&84 4 src/PhpEnvironment/Response.phpnu ٘ version) {
$this->version = $this->detectVersion();
}
return $this->version;
}
/**
* Detect the current used protocol version.
* If detection failed it falls back to version 1.0.
*
* @return string
*/
protected function detectVersion()
{
if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.1') {
return self::VERSION_11;
}
return self::VERSION_10;
}
/**
* @return bool
*/
public function headersSent()
{
return headers_sent();
}
/**
* @return bool
*/
public function contentSent()
{
return $this->contentSent;
}
/**
* @return void
*/
public function setHeadersSentHandler(callable $handler)
{
$this->headersSentHandler = $handler;
}
/**
* Send HTTP headers
*
* @return $this
*/
public function sendHeaders()
{
if ($this->headersSent()) {
if ($this->headersSentHandler) {
call_user_func($this->headersSentHandler, $this);
}
return $this;
}
$status = $this->renderStatusLine();
header($status);
/** @var \Laminas\Http\Header\HeaderInterface $header */
foreach ($this->getHeaders() as $header) {
if ($header instanceof MultipleHeaderInterface) {
header($header->toString(), false);
continue;
}
header($header->toString());
}
$this->headersSent = true;
return $this;
}
/**
* Send content
*
* @return $this
*/
public function sendContent()
{
if ($this->contentSent()) {
return $this;
}
echo $this->getContent();
$this->contentSent = true;
return $this;
}
/**
* Send HTTP response
*
* @return $this
*/
public function send()
{
$this->sendHeaders()
->sendContent();
return $this;
}
}
PK %RAڤ $ src/PhpEnvironment/RemoteAddress.phpnu ٘ useProxy = $useProxy;
return $this;
}
/**
* Checks proxy handling setting.
*
* @return bool Current setting value.
*/
public function getUseProxy()
{
return $this->useProxy;
}
/**
* Set list of trusted proxy addresses
*
* @param array $trustedProxies
* @return $this
*/
public function setTrustedProxies(array $trustedProxies)
{
$this->trustedProxies = $trustedProxies;
return $this;
}
/**
* Set the header to introspect for proxy IPs
*
* @param string $header
* @return $this
*/
public function setProxyHeader($header = 'X-Forwarded-For')
{
$this->proxyHeader = $this->normalizeProxyHeader($header);
return $this;
}
/**
* Returns client IP address.
*
* @return string IP address.
*/
public function getIpAddress()
{
$ip = $this->getIpAddressFromProxy();
if ($ip) {
return $ip;
}
// direct IP address
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return '';
}
/**
* Attempt to get the IP address for a proxied client
*
* @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
* @return false|string
*/
protected function getIpAddressFromProxy()
{
if (! $this->useProxy
|| (isset($_SERVER['REMOTE_ADDR']) && ! in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
) {
return false;
}
$header = $this->proxyHeader;
if (! isset($_SERVER[$header]) || empty($_SERVER[$header])) {
return false;
}
// Extract IPs
$ips = explode(',', $_SERVER[$header]);
// trim, so we can compare against trusted proxies properly
$ips = array_map('trim', $ips);
// remove trusted proxy IPs
$ips = array_diff($ips, $this->trustedProxies);
// Any left?
if (empty($ips)) {
return false;
}
// Since we've removed any known, trusted proxy servers, the right-most
// address represents the first IP we do not know about -- i.e., we do
// not know if it is a proxy server, or a client. As such, we treat it
// as the originating IP.
// @see http://en.wikipedia.org/wiki/X-Forwarded-For
$ip = array_pop($ips);
return $ip;
}
/**
* Normalize a header string
*
* Normalizes a header string to a format that is compatible with
* $_SERVER
*
* @param string $header
* @return string
*/
protected function normalizeProxyHeader($header)
{
$header = strtoupper($header);
$header = str_replace('-', '_', $header);
if (0 !== strpos($header, 'HTTP_')) {
$header = 'HTTP_' . $header;
}
return $header;
}
}
PK %RD D src/PhpEnvironment/Request.phpnu ٘ setAllowCustomMethods($allowCustomMethods);
$this->setEnv(new Parameters($_ENV));
if ($_GET) {
$this->setQuery(new Parameters($_GET));
}
if ($_POST) {
$this->setPost(new Parameters($_POST));
}
if ($_COOKIE) {
$this->setCookies(new Parameters($_COOKIE));
}
if ($_FILES) {
// convert PHP $_FILES superglobal
$files = $this->mapPhpFiles();
$this->setFiles(new Parameters($files));
}
$this->setServer(new Parameters($_SERVER));
}
/**
* Get raw request body
*
* @return string
*/
public function getContent()
{
if (empty($this->content)) {
$requestBody = file_get_contents('php://input');
if (strlen($requestBody) > 0) {
$this->content = $requestBody;
}
}
return $this->content;
}
/**
* Set cookies
*
* Instantiate and set cookies.
*
* @param $cookie
* @return $this
*/
public function setCookies($cookie)
{
$this->getHeaders()->addHeader(new Cookie((array) $cookie));
return $this;
}
/**
* Set the request URI.
*
* @param string $requestUri
* @return $this
*/
public function setRequestUri($requestUri)
{
$this->requestUri = $requestUri;
return $this;
}
/**
* Get the request URI.
*
* @return string
*/
public function getRequestUri()
{
if ($this->requestUri === null) {
$this->requestUri = $this->detectRequestUri();
}
return $this->requestUri;
}
/**
* Set the base URL.
*
* @param string $baseUrl
* @return $this
*/
public function setBaseUrl($baseUrl)
{
$this->baseUrl = rtrim($baseUrl, '/');
return $this;
}
/**
* Get the base URL.
*
* @return string
*/
public function getBaseUrl()
{
if ($this->baseUrl === null) {
$this->setBaseUrl($this->detectBaseUrl());
}
return $this->baseUrl;
}
/**
* Set the base path.
*
* @param string $basePath
* @return $this
*/
public function setBasePath($basePath)
{
$this->basePath = rtrim($basePath, '/');
return $this;
}
/**
* Get the base path.
*
* @return string
*/
public function getBasePath()
{
if ($this->basePath === null) {
$this->setBasePath($this->detectBasePath());
}
return $this->basePath;
}
/**
* Provide an alternate Parameter Container implementation for server parameters in this object,
* (this is NOT the primary API for value setting, for that see getServer())
*
* @param ParametersInterface $server
* @return $this
*/
public function setServer(ParametersInterface $server)
{
$this->serverParams = $server;
// This seems to be the only way to get the Authorization header on Apache
if (function_exists('apache_request_headers')) {
$apacheRequestHeaders = apache_request_headers();
if (! isset($this->serverParams['HTTP_AUTHORIZATION'])) {
if (isset($apacheRequestHeaders['Authorization'])) {
$this->serverParams->set('HTTP_AUTHORIZATION', $apacheRequestHeaders['Authorization']);
} elseif (isset($apacheRequestHeaders['authorization'])) {
$this->serverParams->set('HTTP_AUTHORIZATION', $apacheRequestHeaders['authorization']);
}
}
}
// set headers
$headers = [];
foreach ($server as $key => $value) {
if ($value || (! is_array($value) && strlen($value))) {
if (strpos($key, 'HTTP_') === 0) {
if (strpos($key, 'HTTP_COOKIE') === 0) {
// Cookies are handled using the $_COOKIE superglobal
continue;
}
$headers[strtr(ucwords(strtolower(strtr(substr($key, 5), '_', ' '))), ' ', '-')] = $value;
} elseif (strpos($key, 'CONTENT_') === 0) {
$name = substr($key, 8); // Remove "Content-"
$headers['Content-' . (($name == 'MD5') ? $name : ucfirst(strtolower($name)))] = $value;
}
}
}
$this->getHeaders()->addHeaders($headers);
// set method
if (isset($this->serverParams['REQUEST_METHOD'])) {
$this->setMethod($this->serverParams['REQUEST_METHOD']);
}
// set HTTP version
if (isset($this->serverParams['SERVER_PROTOCOL'])
&& strpos($this->serverParams['SERVER_PROTOCOL'], self::VERSION_10) !== false
) {
$this->setVersion(self::VERSION_10);
}
// set URI
$uri = new HttpUri();
// URI scheme
if ((! empty($this->serverParams['HTTPS']) && strtolower($this->serverParams['HTTPS']) !== 'off')
|| (! empty($this->serverParams['HTTP_X_FORWARDED_PROTO'])
&& $this->serverParams['HTTP_X_FORWARDED_PROTO'] == 'https')
) {
$scheme = 'https';
} else {
$scheme = 'http';
}
$uri->setScheme($scheme);
// URI host & port
$host = null;
$port = null;
// Set the host
$headerHost = $this->getHeaders()->get('host');
if ($headerHost) {
$host = $headerHost->getFieldValue();
// works for regname, IPv4 & IPv6
if (preg_match('|\:(\d+)$|', $host, $matches)) {
$host = substr($host, 0, -1 * (strlen($matches[1]) + 1));
$port = (int) $matches[1];
}
// set up a validator that check if the hostname is legal (not spoofed)
$hostnameValidator = new HostnameValidator([
'allow' => HostnameValidator::ALLOW_ALL,
'useIdnCheck' => false,
'useTldCheck' => false,
]);
// If invalid. Reset the host & port
if (! $hostnameValidator->isValid($host)) {
$host = null;
$port = null;
}
}
if (! $host && isset($this->serverParams['SERVER_NAME'])) {
$host = $this->serverParams['SERVER_NAME'];
if (isset($this->serverParams['SERVER_PORT'])) {
$port = (int) $this->serverParams['SERVER_PORT'];
}
// Check for missinterpreted IPv6-Address
// Reported at least for Safari on Windows
if (isset($this->serverParams['SERVER_ADDR']) && preg_match('/^\[[0-9a-fA-F\:]+\]$/', $host)) {
$host = '[' . $this->serverParams['SERVER_ADDR'] . ']';
if ($port . ']' == substr($host, strrpos($host, ':') + 1)) {
// The last digit of the IPv6-Address has been taken as port
// Unset the port so the default port can be used
$port = null;
}
}
}
$uri->setHost($host);
$uri->setPort($port);
// URI path
$requestUri = $this->getRequestUri();
if (($qpos = strpos($requestUri, '?')) !== false) {
$requestUri = substr($requestUri, 0, $qpos);
}
$uri->setPath($requestUri);
// URI query
if (isset($this->serverParams['QUERY_STRING'])) {
$uri->setQuery($this->serverParams['QUERY_STRING']);
}
$this->setUri($uri);
return $this;
}
/**
* Return the parameter container responsible for server parameters or a single parameter value.
*
* @param string|null $name Parameter name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the parameter is missing.
* @see http://www.faqs.org/rfcs/rfc3875.html
* @return \Laminas\Stdlib\ParametersInterface|mixed
*/
public function getServer($name = null, $default = null)
{
if ($this->serverParams === null) {
$this->serverParams = new Parameters();
}
if ($name === null) {
return $this->serverParams;
}
return $this->serverParams->get($name, $default);
}
/**
* Provide an alternate Parameter Container implementation for env parameters in this object,
* (this is NOT the primary API for value setting, for that see env())
*
* @param ParametersInterface $env
* @return $this
*/
public function setEnv(ParametersInterface $env)
{
$this->envParams = $env;
return $this;
}
/**
* Return the parameter container responsible for env parameters or a single parameter value.
*
* @param string|null $name Parameter name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the parameter is missing.
* @return \Laminas\Stdlib\ParametersInterface|mixed
*/
public function getEnv($name = null, $default = null)
{
if ($this->envParams === null) {
$this->envParams = new Parameters();
}
if ($name === null) {
return $this->envParams;
}
return $this->envParams->get($name, $default);
}
/**
* Convert PHP superglobal $_FILES into more sane parameter=value structure
* This handles form file input with brackets (name=files[])
*
* @return array
*/
protected function mapPhpFiles()
{
$files = [];
foreach ($_FILES as $fileName => $fileParams) {
$files[$fileName] = [];
foreach ($fileParams as $param => $data) {
if (! is_array($data)) {
$files[$fileName][$param] = $data;
} else {
foreach ($data as $i => $v) {
$this->mapPhpFileParam($files[$fileName], $param, $i, $v);
}
}
}
}
return $files;
}
/**
* @param array $array
* @param string $paramName
* @param int|string $index
* @param string|array $value
*/
protected function mapPhpFileParam(&$array, $paramName, $index, $value)
{
if (! is_array($value)) {
$array[$index][$paramName] = $value;
} else {
foreach ($value as $i => $v) {
$this->mapPhpFileParam($array[$index], $paramName, $i, $v);
}
}
}
/**
* Detect the base URI for the request
*
* Looks at a variety of criteria in order to attempt to autodetect a base
* URI, including rewrite URIs, proxy URIs, etc.
*
* @return string
*/
protected function detectRequestUri()
{
$requestUri = null;
$server = $this->getServer();
// IIS7 with URL Rewrite: make sure we get the unencoded url
// (double slash problem).
$iisUrlRewritten = $server->get('IIS_WasUrlRewritten');
$unencodedUrl = $server->get('UNENCODED_URL', '');
if ('1' == $iisUrlRewritten && '' !== $unencodedUrl) {
return $unencodedUrl;
}
$requestUri = $server->get('REQUEST_URI');
// HTTP proxy requests setup request URI with scheme and host [and port]
// + the URL path, only use URL path.
if ($requestUri !== null) {
return preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
}
// IIS 5.0, PHP as CGI.
$origPathInfo = $server->get('ORIG_PATH_INFO');
if ($origPathInfo !== null) {
$queryString = $server->get('QUERY_STRING', '');
if ($queryString !== '') {
$origPathInfo .= '?' . $queryString;
}
return $origPathInfo;
}
return '/';
}
/**
* Auto-detect the base path from the request environment
*
* Uses a variety of criteria in order to detect the base URL of the request
* (i.e., anything additional to the document root).
*
* @return string
*/
protected function detectBaseUrl()
{
$filename = $this->getServer()->get('SCRIPT_FILENAME', '');
$scriptName = $this->getServer()->get('SCRIPT_NAME');
$phpSelf = $this->getServer()->get('PHP_SELF');
$origScriptName = $this->getServer()->get('ORIG_SCRIPT_NAME');
if ($scriptName !== null && basename($scriptName) === $filename) {
$baseUrl = $scriptName;
} elseif ($phpSelf !== null && basename($phpSelf) === $filename) {
$baseUrl = $phpSelf;
} elseif ($origScriptName !== null && basename($origScriptName) === $filename) {
// 1and1 shared hosting compatibility.
$baseUrl = $origScriptName;
} else {
// Backtrack up the SCRIPT_FILENAME to find the portion
// matching PHP_SELF.
// Only for CLI requests argv[0] contains script filename
// @see https://www.php.net/manual/en/reserved.variables.server.php
if (PHP_SAPI === 'cli') {
$argv = $this->getServer()->get('argv', []);
if (isset($argv[0]) && is_string($argv[0]) && $argv[0] !== '' && strpos($filename, $argv[0]) === 0) {
$filename = substr($filename, strlen($argv[0]));
}
}
$baseUrl = '/';
$basename = basename($filename);
if ($basename) {
$path = ($phpSelf ? trim($phpSelf, '/') : '');
$basePos = strpos($path, $basename) ?: 0;
$baseUrl .= substr($path, 0, $basePos) . $basename;
}
}
// If the baseUrl is empty, then simply return it.
if (empty($baseUrl)) {
return '';
}
// Does the base URL have anything in common with the request URI?
$requestUri = $this->getRequestUri();
// Full base URL matches.
if (0 === strpos($requestUri, $baseUrl)) {
return $baseUrl;
}
// Directory portion of base path matches.
$baseDir = str_replace('\\', '/', dirname($baseUrl));
if (0 === strpos($requestUri, $baseDir)) {
return $baseDir;
}
$truncatedRequestUri = $requestUri;
if (false !== ($pos = strpos($requestUri, '?'))) {
$truncatedRequestUri = substr($requestUri, 0, $pos);
}
$basename = basename($baseUrl);
// No match whatsoever
if (empty($basename) || false === strpos($truncatedRequestUri, $basename)) {
return '';
}
// If using mod_rewrite or ISAPI_Rewrite strip the script filename
// out of the base path. $pos !== 0 makes sure it is not matching a
// value from PATH_INFO or QUERY_STRING.
if (strlen($requestUri) >= strlen($baseUrl)
&& (false !== ($pos = strpos($requestUri, $baseUrl)) && $pos !== 0)
) {
$baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
}
return $baseUrl;
}
/**
* Autodetect the base path of the request
*
* Uses several criteria to determine the base path of the request.
*
* @return string
*/
protected function detectBasePath()
{
$baseUrl = $this->getBaseUrl();
// Empty base url detected
if ($baseUrl === '') {
return '';
}
$filename = basename($this->getServer()->get('SCRIPT_FILENAME', ''));
// basename() matches the script filename; return the directory
if (basename($baseUrl) === $filename) {
return str_replace('\\', '/', dirname($baseUrl));
}
// Base path is identical to base URL
return $baseUrl;
}
}
PK %R=Բ Բ src/Client.phpnu ٘ 5,
'strictredirects' => false,
'useragent' => 'Laminas_Http_Client',
'timeout' => 10,
'connecttimeout' => null,
'adapter' => Socket::class,
'httpversion' => Request::VERSION_11,
'storeresponse' => true,
'keepalive' => false,
'outputstream' => false,
'encodecookies' => true,
'argseparator' => null,
'rfc3986strict' => false,
'sslcafile' => null,
'sslcapath' => null,
];
/**
* Fileinfo magic database resource
*
* This variable is populated the first time _detectFileMimeType is called
* and is then reused on every call to this method
*
* @var resource
*/
protected static $fileInfoDb;
/**
* Constructor
*
* @param string $uri
* @param array|Traversable $options
*/
public function __construct($uri = null, $options = null)
{
if ($uri !== null) {
$this->setUri($uri);
}
if ($options !== null) {
$this->setOptions($options);
}
}
/**
* Set configuration parameters for this HTTP client
*
* @param array|Traversable $options
* @return $this
* @throws Client\Exception\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (! is_array($options)) {
throw new Client\Exception\InvalidArgumentException('Config parameter is not valid');
}
/** Config Key Normalization */
foreach ($options as $k => $v) {
$this->config[str_replace(['-', '_', ' ', '.'], '', strtolower($k))] = $v; // replace w/ normalized
}
// Pass configuration options to the adapter if it exists
if ($this->adapter instanceof Client\Adapter\AdapterInterface) {
$this->adapter->setOptions($options);
}
return $this;
}
/**
* Load the connection adapter
*
* While this method is not called more than one for a client, it is
* separated from ->request() to preserve logic and readability
*
* @param Client\Adapter\AdapterInterface|string $adapter
* @return $this
* @throws Client\Exception\InvalidArgumentException
*/
public function setAdapter($adapter)
{
if (is_string($adapter)) {
if (! class_exists($adapter)) {
throw new Client\Exception\InvalidArgumentException(
'Unable to locate adapter class "' . $adapter . '"'
);
}
$adapter = new $adapter;
}
if (! $adapter instanceof Client\Adapter\AdapterInterface) {
throw new Client\Exception\InvalidArgumentException('Passed adapter is not a HTTP connection adapter');
}
$this->adapter = $adapter;
$config = $this->config;
unset($config['adapter']);
$this->adapter->setOptions($config);
return $this;
}
/**
* Load the connection adapter
*
* @return Client\Adapter\AdapterInterface
*/
public function getAdapter()
{
if (! $this->adapter) {
$this->setAdapter($this->config['adapter']);
}
return $this->adapter;
}
/**
* Set request
*
* @param Request $request
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Get Request
*
* @return Request
*/
public function getRequest()
{
if (empty($this->request)) {
$this->request = new Request();
$this->request->setAllowCustomMethods(false);
}
return $this->request;
}
/**
* Set response
*
* @param Response $response
* @return $this
*/
public function setResponse(Response $response)
{
$this->response = $response;
return $this;
}
/**
* Get Response
*
* @return Response
*/
public function getResponse()
{
if (empty($this->response)) {
$this->response = new Response();
}
return $this->response;
}
/**
* Get the last request (as a string)
*
* @return string
*/
public function getLastRawRequest()
{
return $this->lastRawRequest;
}
/**
* Get the last response (as a string)
*
* @return string
*/
public function getLastRawResponse()
{
return $this->lastRawResponse;
}
/**
* Get the redirections count
*
* @return int
*/
public function getRedirectionsCount()
{
return $this->redirectCounter;
}
/**
* Set Uri (to the request)
*
* @param string|Http $uri
* @return $this
*/
public function setUri($uri)
{
if (! empty($uri)) {
// remember host of last request
$lastHost = $this->getRequest()->getUri()->getHost();
$this->getRequest()->setUri($uri);
// if host changed, the HTTP authentication should be cleared for security
// reasons, see #4215 for a discussion - currently authentication is also
// cleared for peer subdomains due to technical limits
$nextHost = $this->getRequest()->getUri()->getHost();
if (! preg_match('/' . preg_quote($lastHost, '/') . '$/i', $nextHost)) {
$this->clearAuth();
}
$uri = $this->getUri();
$user = $uri->getUser();
$password = $uri->getPassword();
// Set auth if username and password has been specified in the uri
if ($user && $password) {
$this->setAuth($user, $password);
}
// We have no ports, set the defaults
if (! $uri->getPort() && $uri->isAbsolute()) {
$uri->setPort($uri->getScheme() === 'https' ? 443 : 80);
}
}
return $this;
}
/**
* Get uri (from the request)
*
* @return Http
*/
public function getUri()
{
return $this->getRequest()->getUri();
}
/**
* Set the HTTP method (to the request)
*
* @param string $method
* @return $this
*/
public function setMethod($method)
{
$method = $this->getRequest()->setMethod($method)->getMethod();
if (empty($this->encType)
&& in_array(
$method,
[
Request::METHOD_POST,
Request::METHOD_PUT,
Request::METHOD_DELETE,
Request::METHOD_PATCH,
Request::METHOD_OPTIONS,
],
true
)
) {
$this->setEncType(self::ENC_URLENCODED);
}
return $this;
}
/**
* Get the HTTP method
*
* @return string
*/
public function getMethod()
{
return $this->getRequest()->getMethod();
}
/**
* Set the query string argument separator
*
* @param string $argSeparator
* @return $this
*/
public function setArgSeparator($argSeparator)
{
$this->setOptions(['argseparator' => $argSeparator]);
return $this;
}
/**
* Get the query string argument separator
*
* @return string
*/
public function getArgSeparator()
{
$argSeparator = $this->config['argseparator'];
if (empty($argSeparator)) {
$argSeparator = ini_get('arg_separator.output');
$this->setArgSeparator($argSeparator);
}
return $argSeparator;
}
/**
* Set the encoding type and the boundary (if any)
*
* @param string $encType
* @param string $boundary
* @return $this
*/
public function setEncType($encType, $boundary = null)
{
if (null === $encType || empty($encType)) {
$this->encType = null;
return $this;
}
if (! empty($boundary)) {
$encType .= sprintf('; boundary=%s', $boundary);
}
$this->encType = $encType;
return $this;
}
/**
* Get the encoding type
*
* @return string
*/
public function getEncType()
{
return $this->encType;
}
/**
* Set raw body (for advanced use cases)
*
* @param string $body
* @return $this
*/
public function setRawBody($body)
{
$this->getRequest()->setContent($body);
return $this;
}
/**
* Set the POST parameters
*
* @param array $post
* @return $this
*/
public function setParameterPost(array $post)
{
$this->getRequest()->getPost()->fromArray($post);
return $this;
}
/**
* Set the GET parameters
*
* @param array $query
* @return $this
*/
public function setParameterGet(array $query)
{
$this->getRequest()->getQuery()->fromArray($query);
return $this;
}
/**
* Reset all the HTTP parameters (request, response, etc)
*
* @param bool $clearCookies Also clear all valid cookies? (defaults to false)
* @param bool $clearAuth Also clear http authentication? (defaults to true)
* @return $this
*/
public function resetParameters($clearCookies = false /*, $clearAuth = true */)
{
$clearAuth = true;
if (func_num_args() > 1) {
$clearAuth = func_get_arg(1);
}
$uri = $this->getUri();
$this->streamName = null;
$this->encType = null;
$this->request = null;
$this->response = null;
$this->lastRawRequest = null;
$this->lastRawResponse = null;
$this->setUri($uri);
if ($clearCookies) {
$this->clearCookies();
}
if ($clearAuth) {
$this->clearAuth();
}
return $this;
}
/**
* Return the current cookies
*
* @return array
*/
public function getCookies()
{
return $this->cookies;
}
/**
* Get the cookie Id (name+domain+path)
*
* @param Header\SetCookie|Header\Cookie $cookie
* @return string|bool
*/
protected function getCookieId($cookie)
{
if (($cookie instanceof Header\SetCookie) || ($cookie instanceof Header\Cookie)) {
return $cookie->getName() . $cookie->getDomain() . $cookie->getPath();
}
return false;
}
/**
* Add a cookie
*
* @param array|ArrayIterator|Header\SetCookie|string $cookie
* @param string $value
* @param string $expire
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httponly
* @param string $maxAge
* @param string $version
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function addCookie(
$cookie,
$value = null,
$expire = null,
$path = null,
$domain = null,
$secure = false,
$httponly = true,
$maxAge = null,
$version = null
) {
if (is_array($cookie) || $cookie instanceof ArrayIterator) {
foreach ($cookie as $setCookie) {
if ($setCookie instanceof Header\SetCookie) {
$this->cookies[$this->getCookieId($setCookie)] = $setCookie;
} else {
throw new Exception\InvalidArgumentException('The cookie parameter is not a valid Set-Cookie type');
}
}
} elseif (is_string($cookie) && $value !== null) {
$setCookie = new Header\SetCookie(
$cookie,
$value,
$expire,
$path,
$domain,
$secure,
$httponly,
$maxAge,
$version
);
$this->cookies[$this->getCookieId($setCookie)] = $setCookie;
} elseif ($cookie instanceof Header\SetCookie) {
$this->cookies[$this->getCookieId($cookie)] = $cookie;
} else {
throw new Exception\InvalidArgumentException('Invalid parameter type passed as Cookie');
}
return $this;
}
/**
* Set an array of cookies
*
* @param array|SetCookie[] $cookies Cookies as name=>value pairs or instances of SetCookie.
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function setCookies($cookies)
{
if (is_array($cookies)) {
$this->clearCookies();
foreach ($cookies as $name => $value) {
if ($value instanceof SetCookie) {
$this->addCookie($value);
} else {
$this->addCookie($name, $value);
}
}
} else {
throw new Exception\InvalidArgumentException('Invalid cookies passed as parameter, it must be an array');
}
return $this;
}
/**
* Clear all the cookies
*/
public function clearCookies()
{
$this->cookies = [];
}
/**
* Set the headers (for the request)
*
* @param Headers|array $headers
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function setHeaders($headers)
{
if (is_array($headers)) {
$newHeaders = new Headers();
$newHeaders->addHeaders($headers);
$this->getRequest()->setHeaders($newHeaders);
} elseif ($headers instanceof Headers) {
$this->getRequest()->setHeaders($headers);
} else {
throw new Exception\InvalidArgumentException('Invalid parameter headers passed');
}
return $this;
}
/**
* Check if exists the header type specified
*
* @param string $name
* @return bool
*/
public function hasHeader($name)
{
$headers = $this->getRequest()->getHeaders();
if ($headers instanceof Headers) {
return $headers->has($name);
}
return false;
}
/**
* Get the header value of the request
*
* @param string $name
* @return string|bool
*/
public function getHeader($name)
{
$headers = $this->getRequest()->getHeaders();
if ($headers instanceof Headers) {
if ($headers->get($name)) {
return $headers->get($name)->getFieldValue();
}
}
return false;
}
/**
* Set streaming for received data
*
* @param string|bool $streamfile Stream file, true for temp file, false/null for no streaming
* @return $this
*/
public function setStream($streamfile = true)
{
$this->setOptions(['outputstream' => $streamfile]);
return $this;
}
/**
* Get status of streaming for received data
* @return bool|string
*/
public function getStream()
{
if (null !== $this->streamName) {
return $this->streamName;
}
return $this->config['outputstream'];
}
/**
* Create temporary stream
*
* @return resource
* @throws Exception\RuntimeException
*/
protected function openTempStream()
{
$this->streamName = $this->config['outputstream'];
if (! is_string($this->streamName)) {
// If name is not given, create temp name
$this->streamName = tempnam(
isset($this->config['streamtmpdir']) ? $this->config['streamtmpdir'] : sys_get_temp_dir(),
Client::class
);
}
ErrorHandler::start();
$fp = fopen($this->streamName, 'w+b');
$error = ErrorHandler::stop();
if (false === $fp) {
if ($this->adapter instanceof Client\Adapter\AdapterInterface) {
$this->adapter->close();
}
throw new Exception\RuntimeException(sprintf('Could not open temp file %s', $this->streamName), 0, $error);
}
return $fp;
}
/**
* Create a HTTP authentication "Authorization:" header according to the
* specified user, password and authentication method.
*
* @param string $user
* @param string $password
* @param string $type
* @throws Exception\InvalidArgumentException
* @return $this
*/
public function setAuth($user, $password, $type = self::AUTH_BASIC)
{
if (! defined('static::AUTH_' . strtoupper($type))) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid or not supported authentication type: \'%s\'',
$type
));
}
if (empty($user)) {
throw new Exception\InvalidArgumentException('The username cannot be empty');
}
$this->auth = [
'user' => $user,
'password' => $password,
'type' => $type,
];
return $this;
}
/**
* Clear http authentication
*/
public function clearAuth()
{
$this->auth = [];
}
/**
* Calculate the response value according to the HTTP authentication type
*
* @see http://www.faqs.org/rfcs/rfc2617.html
* @param string $user
* @param string $password
* @param string $type
* @param array $digest
* @param null|string $entityBody
* @throws Exception\InvalidArgumentException
* @return string|bool
*/
protected function calcAuthDigest($user, $password, $type = self::AUTH_BASIC, $digest = [], $entityBody = null)
{
if (! defined('self::AUTH_' . strtoupper($type))) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid or not supported authentication type: \'%s\'',
$type
));
}
$response = false;
switch (strtolower($type)) {
case self::AUTH_BASIC:
// In basic authentication, the user name cannot contain ":"
if (strpos($user, ':') !== false) {
throw new Exception\InvalidArgumentException(
'The user name cannot contain \':\' in Basic HTTP authentication'
);
}
$response = base64_encode($user . ':' . $password);
break;
case self::AUTH_DIGEST:
if (empty($digest)) {
throw new Exception\InvalidArgumentException('The digest cannot be empty');
}
foreach ($digest as $key => $value) {
if (! defined('self::DIGEST_' . strtoupper($key))) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid or not supported digest authentication parameter: \'%s\'',
$key
));
}
}
$ha1 = md5($user . ':' . $digest['realm'] . ':' . $password);
if (empty($digest['qop']) || strtolower($digest['qop']) == 'auth') {
$ha2 = md5($this->getMethod() . ':' . $this->getUri()->getPath());
} elseif (strtolower($digest['qop']) == 'auth-int') {
if (empty($entityBody)) {
throw new Exception\InvalidArgumentException(
'I cannot use the auth-int digest authentication without the entity body'
);
}
$ha2 = md5($this->getMethod() . ':' . $this->getUri()->getPath() . ':' . md5($entityBody));
}
if (empty($digest['qop'])) {
$response = md5($ha1 . ':' . $digest['nonce'] . ':' . $ha2);
} else {
$response = md5($ha1 . ':' . $digest['nonce'] . ':' . $digest['nc']
. ':' . $digest['cnonce'] . ':' . $digest['qoc'] . ':' . $ha2);
}
break;
}
return $response;
}
/**
* Dispatch
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function dispatch(RequestInterface $request, ResponseInterface $response = null)
{
return $this->send($request);
}
/**
* Send HTTP request
*
* @param Request|null $request
* @return Response
* @throws Exception\RuntimeException
* @throws Client\Exception\RuntimeException
*/
public function send(Request $request = null)
{
if ($request !== null) {
$this->setRequest($request);
}
$this->redirectCounter = 0;
$adapter = $this->getAdapter();
// Send the first request. If redirected, continue.
do {
// uri
$uri = $this->getUri();
// query
$query = $this->getRequest()->getQuery();
if (! empty($query)) {
$queryArray = $query->toArray();
if (! empty($queryArray)) {
$newUri = $uri->toString();
$queryString = http_build_query($queryArray, null, $this->getArgSeparator());
if ($this->config['rfc3986strict']) {
$queryString = str_replace('+', '%20', $queryString);
}
if (strpos($newUri, '?') !== false) {
$newUri .= $this->getArgSeparator() . $queryString;
} else {
$newUri .= '?' . $queryString;
}
$uri = new Http($newUri);
}
}
// If we have no ports, set the defaults
if (! $uri->getPort() && $uri->isAbsolute()) {
$uri->setPort($uri->getScheme() === 'https' ? 443 : 80);
}
// method
$method = $this->getRequest()->getMethod();
// this is so the correct Encoding Type is set
$this->setMethod($method);
// body
$body = $this->prepareBody();
// headers
$headers = $this->prepareHeaders($body, $uri);
$secure = $uri->getScheme() == 'https';
// cookies
$cookie = $this->prepareCookies($uri->getHost(), $uri->getPath(), $secure);
if ($cookie->getFieldValue()) {
$headers['Cookie'] = $cookie->getFieldValue();
}
// check that adapter supports streaming before using it
if (is_resource($body) && ! ($adapter instanceof Client\Adapter\StreamInterface)) {
throw new Client\Exception\RuntimeException('Adapter does not support streaming');
}
$this->streamHandle = null;
// calling protected method to allow extending classes
// to wrap the interaction with the adapter
$response = $this->doRequest($uri, $method, $secure, $headers, $body);
$stream = $this->streamHandle;
$this->streamHandle = null;
if (! $response) {
if ($stream !== null) {
fclose($stream);
}
throw new Exception\RuntimeException('Unable to read response, or response is empty');
}
if ($this->config['storeresponse']) {
$this->lastRawResponse = $response;
} else {
$this->lastRawResponse = null;
}
if ($this->config['outputstream']) {
if ($stream === null) {
$stream = $this->getStream();
if (! is_resource($stream) && is_string($stream)) {
$stream = fopen($stream, 'r');
}
}
$streamMetaData = stream_get_meta_data($stream);
if ($streamMetaData['seekable']) {
rewind($stream);
}
// cleanup the adapter
$adapter->setOutputStream(null);
$response = Response\Stream::fromStream($response, $stream);
$response->setStreamName($this->streamName);
if (! is_string($this->config['outputstream'])) {
// we used temp name, will need to clean up
$response->setCleanup(true);
}
} else {
$response = $this->getResponse()->fromString($response);
}
// Get the cookies from response (if any)
$setCookies = $response->getCookie();
if (! empty($setCookies)) {
$this->addCookie($setCookies);
}
// If we got redirected, look for the Location header
if ($response->isRedirect() && ($response->getHeaders()->has('Location'))) {
// Avoid problems with buggy servers that add whitespace at the
// end of some headers
$location = trim($response->getHeaders()->get('Location')->getFieldValue());
// Check whether we send the exact same request again, or drop the parameters
// and send a GET request
if ($response->getStatusCode() == 303
|| ((! $this->config['strictredirects'])
&& ($response->getStatusCode() == 302 || $response->getStatusCode() == 301))
) {
$this->resetParameters(false, false);
$this->setMethod(Request::METHOD_GET);
}
// If we got a well formed absolute URI
if (($scheme = substr($location, 0, 6))
&& ($scheme == 'http:/' || $scheme == 'https:')
) {
// setURI() clears parameters if host changed, see #4215
$this->setUri($location);
} else {
// Split into path and query and set the query
if (strpos($location, '?') !== false) {
list($location, $query) = explode('?', $location, 2);
} else {
$query = '';
}
$this->getUri()->setQuery($query);
// Else, if we got just an absolute path, set it
if (strpos($location, '/') === 0) {
$this->getUri()->setPath($location);
// Else, assume we have a relative path
} else {
// Get the current path directory, removing any trailing slashes
$path = $this->getUri()->getPath();
$path = rtrim(substr($path, 0, strrpos($path, '/')), '/');
$this->getUri()->setPath($path . '/' . $location);
}
}
++$this->redirectCounter;
} else {
// If we didn't get any location, stop redirecting
break;
}
} while ($this->redirectCounter <= $this->config['maxredirects']);
$this->response = $response;
return $response;
}
/**
* Fully reset the HTTP client (auth, cookies, request, response, etc.)
*
* @return $this
*/
public function reset()
{
$this->resetParameters();
$this->clearAuth();
$this->clearCookies();
return $this;
}
/**
* Set a file to upload (using a POST request)
*
* Can be used in two ways:
*
* 1. $data is null (default): $filename is treated as the name if a local file which
* will be read and sent. Will try to guess the content type using mime_content_type().
* 2. $data is set - $filename is sent as the file name, but $data is sent as the file
* contents and no file is read from the file system. In this case, you need to
* manually set the Content-Type ($ctype) or it will default to
* application/octet-stream.
*
* @param string $filename Name of file to upload, or name to save as
* @param string $formname Name of form element to send as
* @param string $data Data to send (if null, $filename is read and sent)
* @param string $ctype Content type to use (if $data is set and $ctype is
* null, will be application/octet-stream)
* @return $this
* @throws Exception\RuntimeException
*/
public function setFileUpload($filename, $formname, $data = null, $ctype = null)
{
if ($data === null) {
ErrorHandler::start();
$data = file_get_contents($filename);
$error = ErrorHandler::stop();
if ($data === false) {
throw new Exception\RuntimeException(sprintf(
'Unable to read file \'%s\' for upload',
$filename
), 0, $error);
}
if (! $ctype) {
$ctype = $this->detectFileMimeType($filename);
}
}
$this->getRequest()->getFiles()->set($filename, [
'formname' => $formname,
'filename' => basename($filename),
'ctype' => $ctype,
'data' => $data,
]);
return $this;
}
/**
* Remove a file to upload
*
* @param string $filename
* @return bool
*/
public function removeFileUpload($filename)
{
$file = $this->getRequest()->getFiles()->get($filename);
if (! empty($file)) {
$this->getRequest()->getFiles()->set($filename, null);
return true;
}
return false;
}
/**
* Prepare Cookies
*
* @param string $domain
* @param string $path
* @param bool $secure
* @return Header\Cookie|bool
*/
protected function prepareCookies($domain, $path, $secure)
{
$validCookies = [];
if (! empty($this->cookies)) {
foreach ($this->cookies as $id => $cookie) {
if ($cookie->isExpired()) {
unset($this->cookies[$id]);
continue;
}
if ($cookie->isValidForRequest($domain, $path, $secure)) {
// OAM hack some domains try to set the cookie multiple times
$validCookies[$cookie->getName()] = $cookie;
}
}
}
$cookies = Header\Cookie::fromSetCookieArray($validCookies);
$cookies->setEncodeValue($this->config['encodecookies']);
return $cookies;
}
/**
* Prepare the request headers
*
* @param resource|string $body
* @param Http $uri
* @return array
* @throws Exception\RuntimeException
*/
protected function prepareHeaders($body, $uri)
{
$headers = [];
// Set the host header
if ($this->config['httpversion'] == Request::VERSION_11) {
$host = $uri->getHost();
// If the port is not default, add it
if (! (($uri->getScheme() == 'http' && $uri->getPort() == 80)
|| ($uri->getScheme() == 'https' && $uri->getPort() == 443))
) {
$host .= ':' . $uri->getPort();
}
$headers['Host'] = $host;
}
// Set the connection header
if (! $this->getRequest()->getHeaders()->has('Connection')) {
if (! $this->config['keepalive']) {
$headers['Connection'] = 'close';
}
}
// Set the Accept-encoding header if not set - depending on whether
// zlib is available or not.
if (! $this->getRequest()->getHeaders()->has('Accept-Encoding')) {
if (empty($this->config['outputstream']) && function_exists('gzinflate')) {
$headers['Accept-Encoding'] = 'gzip, deflate';
} else {
$headers['Accept-Encoding'] = 'identity';
}
}
// Set the user agent header
if (! $this->getRequest()->getHeaders()->has('User-Agent') && isset($this->config['useragent'])) {
$headers['User-Agent'] = $this->config['useragent'];
}
// Set HTTP authentication if needed
if (! empty($this->auth)) {
switch ($this->auth['type']) {
case self::AUTH_BASIC:
$auth = $this->calcAuthDigest($this->auth['user'], $this->auth['password'], $this->auth['type']);
if ($auth !== false) {
$headers['Authorization'] = 'Basic ' . $auth;
}
break;
case self::AUTH_DIGEST:
if (! $this->adapter instanceof Client\Adapter\Curl) {
throw new Exception\RuntimeException(sprintf(
'The digest authentication is only available for curl adapters (%s)',
Curl::class
));
}
$this->adapter->setCurlOption(CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
$this->adapter->setCurlOption(CURLOPT_USERPWD, $this->auth['user'] . ':' . $this->auth['password']);
}
}
// Content-type
$encType = $this->getEncType();
if (! empty($encType)) {
$headers['Content-Type'] = $encType;
}
if (! empty($body)) {
if (is_resource($body)) {
$fstat = fstat($body);
$headers['Content-Length'] = $fstat['size'];
} else {
$headers['Content-Length'] = strlen($body);
}
}
// Merge the headers of the request (if any)
// here we need right 'http field' and not lowercase letters
$requestHeaders = $this->getRequest()->getHeaders();
foreach ($requestHeaders as $requestHeaderElement) {
$headers[$requestHeaderElement->getFieldName()] = $requestHeaderElement->getFieldValue();
}
return $headers;
}
/**
* Prepare the request body (for PATCH, POST and PUT requests)
*
* @return string
* @throws \Laminas\Http\Client\Exception\RuntimeException
*/
protected function prepareBody()
{
// According to RFC2616, a TRACE request should not have a body.
if ($this->getRequest()->isTrace()) {
return '';
}
$rawBody = $this->getRequest()->getContent();
if (! empty($rawBody)) {
return $rawBody;
}
$body = '';
$hasFiles = false;
if (! $this->getRequest()->getHeaders()->has('Content-Type')) {
$hasFiles = ! empty($this->getRequest()->getFiles()->toArray());
// If we have files to upload, force encType to multipart/form-data
if ($hasFiles) {
$this->setEncType(self::ENC_FORMDATA);
}
} else {
$this->setEncType($this->getHeader('Content-Type'));
}
// If we have POST parameters or files, encode and add them to the body
if (! empty($this->getRequest()->getPost()->toArray()) || $hasFiles) {
if (stripos($this->getEncType(), self::ENC_FORMDATA) === 0) {
$boundary = '---ZENDHTTPCLIENT-' . md5(microtime());
$this->setEncType(self::ENC_FORMDATA, $boundary);
// Get POST parameters and encode them
$params = self::flattenParametersArray($this->getRequest()->getPost()->toArray());
foreach ($params as $pp) {
$body .= $this->encodeFormData($boundary, $pp[0], $pp[1]);
}
// Encode files
foreach ($this->getRequest()->getFiles()->toArray() as $file) {
$fhead = ['Content-Type' => $file['ctype']];
$body .= $this->encodeFormData(
$boundary,
$file['formname'],
$file['data'],
$file['filename'],
$fhead
);
}
$body .= '--' . $boundary . '--' . "\r\n";
} elseif (stripos($this->getEncType(), self::ENC_URLENCODED) === 0) {
// Encode body as application/x-www-form-urlencoded
$body = http_build_query($this->getRequest()->getPost()->toArray(), null, '&');
} else {
throw new Client\Exception\RuntimeException(sprintf(
'Cannot handle content type \'%s\' automatically',
$this->encType
));
}
}
return $body;
}
/**
* Attempt to detect the MIME type of a file using available extensions
*
* This method will try to detect the MIME type of a file. If the fileinfo
* extension is available, it will be used. If not, the mime_magic
* extension which is deprecated but is still available in many PHP setups
* will be tried.
*
* If neither extension is available, the default application/octet-stream
* MIME type will be returned
*
* @param string $file File path
* @return string MIME type
*/
protected function detectFileMimeType($file)
{
$type = null;
// First try with fileinfo functions
if (function_exists('finfo_open')) {
if (static::$fileInfoDb === null) {
ErrorHandler::start();
static::$fileInfoDb = finfo_open(FILEINFO_MIME);
ErrorHandler::stop();
}
if (static::$fileInfoDb) {
$type = finfo_file(static::$fileInfoDb, $file);
}
} elseif (function_exists('mime_content_type')) {
$type = mime_content_type($file);
}
// Fallback to the default application/octet-stream
if (! $type) {
$type = 'application/octet-stream';
}
return $type;
}
/**
* Encode data to a multipart/form-data part suitable for a POST request.
*
* @param string $boundary
* @param string $name
* @param mixed $value
* @param string $filename
* @param array $headers Associative array of optional headers @example ("Content-Transfer-Encoding" => "binary")
* @return string
*/
public function encodeFormData($boundary, $name, $value, $filename = null, $headers = [])
{
$ret = '--' . $boundary . "\r\n"
. 'Content-Disposition: form-data; name="' . $name . '"';
if ($filename) {
$ret .= '; filename="' . $filename . '"';
}
$ret .= "\r\n";
foreach ($headers as $hname => $hvalue) {
$ret .= $hname . ': ' . $hvalue . "\r\n";
}
$ret .= "\r\n";
$ret .= $value . "\r\n";
return $ret;
}
/**
* Convert an array of parameters into a flat array of (key, value) pairs
*
* Will flatten a potentially multi-dimentional array of parameters (such
* as POST parameters) into a flat array of (key, value) paris. In case
* of multi-dimentional arrays, square brackets ([]) will be added to the
* key to indicate an array.
*
* @since 1.9
*
* @param array $parray
* @param string $prefix
* @return array
*/
protected function flattenParametersArray($parray, $prefix = null)
{
if (! is_array($parray)) {
return $parray;
}
$parameters = [];
foreach ($parray as $name => $value) {
// Calculate array key
if ($prefix) {
if (is_int($name)) {
$key = $prefix . '[]';
} else {
$key = $prefix . sprintf('[%s]', $name);
}
} else {
$key = $name;
}
if (is_array($value)) {
$parameters = array_merge($parameters, $this->flattenParametersArray($value, $key));
} else {
$parameters[] = [$key, $value];
}
}
return $parameters;
}
/**
* Separating this from send method allows subclasses to wrap
* the interaction with the adapter
*
* @param Http $uri
* @param string $method
* @param bool $secure
* @param array $headers
* @param string $body
* @return string the raw response
* @throws Exception\RuntimeException
*/
protected function doRequest(Http $uri, $method, $secure = false, $headers = [], $body = '')
{
// Open the connection, send the request and read the response
$this->adapter->connect($uri->getHost(), $uri->getPort(), $secure);
if ($this->config['outputstream']) {
if ($this->adapter instanceof Client\Adapter\StreamInterface) {
$this->streamHandle = $this->openTempStream();
$this->adapter->setOutputStream($this->streamHandle);
} else {
throw new Exception\RuntimeException('Adapter does not support streaming');
}
}
// HTTP connection
$this->lastRawRequest = $this->adapter->write(
$method,
$uri,
$this->config['httpversion'],
$headers,
$body
);
return $this->adapter->read();
}
/**
* Create a HTTP authentication "Authorization:" header according to the
* specified user, password and authentication method.
*
* @see http://www.faqs.org/rfcs/rfc2617.html
* @param string $user
* @param string $password
* @param string $type
* @return string
* @throws Client\Exception\InvalidArgumentException
*/
public static function encodeAuthHeader($user, $password, $type = self::AUTH_BASIC)
{
switch ($type) {
case self::AUTH_BASIC:
// In basic authentication, the user name cannot contain ":"
if (strpos($user, ':') !== false) {
throw new Client\Exception\InvalidArgumentException(
'The user name cannot contain \':\' in \'Basic\' HTTP authentication'
);
}
return 'Basic ' . base64_encode($user . ':' . $password);
//case self::AUTH_DIGEST:
/**
* @todo Implement digest authentication
*/
// break;
default:
throw new Client\Exception\InvalidArgumentException(sprintf(
'Not a supported HTTP authentication type: \'%s\'',
$type
));
}
}
}
PK %RȔ5 5 src/HeaderLoader.phpnu ٘ Header\Accept::class,
'acceptcharset' => Header\AcceptCharset::class,
'acceptencoding' => Header\AcceptEncoding::class,
'acceptlanguage' => Header\AcceptLanguage::class,
'acceptranges' => Header\AcceptRanges::class,
'age' => Header\Age::class,
'allow' => Header\Allow::class,
'authenticationinfo' => Header\AuthenticationInfo::class,
'authorization' => Header\Authorization::class,
'cachecontrol' => Header\CacheControl::class,
'connection' => Header\Connection::class,
'contentdisposition' => Header\ContentDisposition::class,
'contentencoding' => Header\ContentEncoding::class,
'contentlanguage' => Header\ContentLanguage::class,
'contentlength' => Header\ContentLength::class,
'contentlocation' => Header\ContentLocation::class,
'contentmd5' => Header\ContentMD5::class,
'contentrange' => Header\ContentRange::class,
'contentsecuritypolicy' => Header\ContentSecurityPolicy::class,
'contenttransferencoding' => Header\ContentTransferEncoding::class,
'contenttype' => Header\ContentType::class,
'cookie' => Header\Cookie::class,
'date' => Header\Date::class,
'etag' => Header\Etag::class,
'expect' => Header\Expect::class,
'expires' => Header\Expires::class,
'featurepolicy' => Header\FeaturePolicy::class,
'from' => Header\From::class,
'host' => Header\Host::class,
'ifmatch' => Header\IfMatch::class,
'ifmodifiedsince' => Header\IfModifiedSince::class,
'ifnonematch' => Header\IfNoneMatch::class,
'ifrange' => Header\IfRange::class,
'ifunmodifiedsince' => Header\IfUnmodifiedSince::class,
'keepalive' => Header\KeepAlive::class,
'lastmodified' => Header\LastModified::class,
'location' => Header\Location::class,
'maxforwards' => Header\MaxForwards::class,
'origin' => Header\Origin::class,
'pragma' => Header\Pragma::class,
'proxyauthenticate' => Header\ProxyAuthenticate::class,
'proxyauthorization' => Header\ProxyAuthorization::class,
'range' => Header\Range::class,
'referer' => Header\Referer::class,
'refresh' => Header\Refresh::class,
'retryafter' => Header\RetryAfter::class,
'server' => Header\Server::class,
'setcookie' => Header\SetCookie::class,
'te' => Header\TE::class,
'trailer' => Header\Trailer::class,
'transferencoding' => Header\TransferEncoding::class,
'upgrade' => Header\Upgrade::class,
'useragent' => Header\UserAgent::class,
'vary' => Header\Vary::class,
'via' => Header\Via::class,
'warning' => Header\Warning::class,
'wwwauthenticate' => Header\WWWAuthenticate::class,
];
}
PK %R؎3\
\
src/AbstractMessage.phpnu ٘ version = $version;
return $this;
}
/**
* Return the HTTP version for this request
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Provide an alternate Parameter Container implementation for headers in this object,
* (this is NOT the primary API for value setting, for that see getHeaders())
*
* @see getHeaders()
* @param Headers $headers
* @return $this
*/
public function setHeaders(Headers $headers)
{
$this->headers = $headers;
return $this;
}
/**
* Return the header container responsible for headers
*
* @return Headers
*/
public function getHeaders()
{
if ($this->headers === null || is_string($this->headers)) {
// this is only here for fromString lazy loading
$this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
}
return $this->headers;
}
/**
* Allow PHP casting of this object
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}
PK %Re s,/ / src/Cookies.phpnu ٘ getDomain();
$path = $cookie->getPath();
if (! isset($this->cookies[$domain])) {
$this->cookies[$domain] = [];
}
if (! isset($this->cookies[$domain][$path])) {
$this->cookies[$domain][$path] = [];
}
$this->cookies[$domain][$path][$cookie->getName()] = $cookie;
$this->rawCookies[] = $cookie;
} else {
throw new Exception\InvalidArgumentException('Supplient argument is not a valid cookie string or object');
}
}
/**
* Parse an HTTP response, adding all the cookies set in that response
*
* @param Response $response
* @param Uri\Uri|string $refUri Requested URI
*/
public function addCookiesFromResponse(Response $response, $refUri)
{
$cookieHdrs = $response->getHeaders()->get('Set-Cookie');
if (is_array($cookieHdrs) || $cookieHdrs instanceof ArrayIterator) {
foreach ($cookieHdrs as $cookie) {
$this->addCookie($cookie, $refUri);
}
} elseif (is_string($cookieHdrs)) {
$this->addCookie($cookieHdrs, $refUri);
}
}
/**
* Get all cookies in the cookie jar as an array
*
* @param int $retAs Whether to return cookies as objects of \Laminas\Http\Header\SetCookie or as strings
* @return array|string
*/
public function getAllCookies($retAs = self::COOKIE_OBJECT)
{
$cookies = $this->_flattenCookiesArray($this->cookies, $retAs);
return $cookies;
}
/**
* Return an array of all cookies matching a specific request according to the request URI,
* whether session cookies should be sent or not, and the time to consider as "now" when
* checking cookie expiry time.
*
* @param string|Uri\Uri $uri URI to check against (secure, domain, path)
* @param bool $matchSessionCookies Whether to send session cookies
* @param int $retAs Whether to return cookies as objects of \Laminas\Http\Header\Cookie or as strings
* @param int $now Override the current time when checking for expiry time
* @throws Exception\InvalidArgumentException if invalid URI specified
* @return array|string
*/
public function getMatchingCookies(
$uri,
$matchSessionCookies = true,
$retAs = self::COOKIE_OBJECT,
$now = null
) {
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (! $uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException('Invalid URI string or object passed');
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; does not contain a host');
}
// First, reduce the array of cookies to only those matching domain and path
$cookies = $this->_matchDomain($host);
$cookies = $this->_matchPath($cookies, $uri->getPath());
$cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT);
// Next, run Cookie->match on all cookies to check secure, time and session matching
$ret = [];
foreach ($cookies as $cookie) {
if ($cookie->match($uri, $matchSessionCookies, $now)) {
$ret[] = $cookie;
}
}
// Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
$ret = $this->_flattenCookiesArray($ret, $retAs);
return $ret;
}
/**
* Get a specific cookie according to a URI and name
*
* @param Uri\Uri|string $uri The uri (domain and path) to match
* @param string $cookieName The cookie's name
* @param int $retAs Whether to return cookies as objects of \Laminas\Http\Header\SetCookie or as strings
* @throws Exception\InvalidArgumentException if invalid URI specified or invalid $retAs value
* @return SetCookie|string
*/
public function getCookie($uri, $cookieName, $retAs = self::COOKIE_OBJECT)
{
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (! $uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException('Invalid URI specified');
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; host missing');
}
// Get correct cookie path
$path = $uri->getPath();
$lastSlashPos = strrpos($path, '/') ?: 0;
$path = substr($path, 0, $lastSlashPos);
if (! $path) {
$path = '/';
}
if (isset($this->cookies[$uri->getHost()][$path][$cookieName])) {
$cookie = $this->cookies[$uri->getHost()][$path][$cookieName];
switch ($retAs) {
case self::COOKIE_OBJECT:
return $cookie;
case self::COOKIE_STRING_ARRAY:
case self::COOKIE_STRING_CONCAT:
return $cookie->__toString();
default:
throw new Exception\InvalidArgumentException(sprintf(
'Invalid value passed for $retAs: %s',
$retAs
));
}
}
return false;
}
/**
* Helper function to recursively flatten an array. Should be used when exporting the
* cookies array (or parts of it)
*
* @param \Laminas\Http\Header\SetCookie|array $ptr
* @param int $retAs What value to return
* @return array|string
*/
// @codingStandardsIgnoreStart
protected function _flattenCookiesArray($ptr, $retAs = self::COOKIE_OBJECT)
{
// @codingStandardsIgnoreEnd
if (is_array($ptr)) {
$ret = ($retAs == self::COOKIE_STRING_CONCAT ? '' : []);
foreach ($ptr as $item) {
if ($retAs == self::COOKIE_STRING_CONCAT) {
$ret .= $this->_flattenCookiesArray($item, $retAs);
} else {
$ret = array_merge($ret, $this->_flattenCookiesArray($item, $retAs));
}
}
return $ret;
} elseif ($ptr instanceof SetCookie) {
switch ($retAs) {
case self::COOKIE_STRING_ARRAY:
return [$ptr->__toString()];
case self::COOKIE_STRING_CONCAT:
return $ptr->__toString();
case self::COOKIE_OBJECT:
default:
return [$ptr];
}
}
return;
}
/**
* Return a subset of the cookies array matching a specific domain
*
* @param string $domain
* @return array
*/
// @codingStandardsIgnoreStart
protected function _matchDomain($domain)
{
// @codingStandardsIgnoreEnd
$ret = [];
foreach (array_keys($this->cookies) as $cdom) {
if (SetCookie::matchCookieDomain($cdom, $domain)) {
$ret[$cdom] = $this->cookies[$cdom];
}
}
return $ret;
}
/**
* Return a subset of a domain-matching cookies that also match a specified path
*
* @param array $domains
* @param string $path
* @return array
*/
// @codingStandardsIgnoreStart
protected function _matchPath($domains, $path)
{
// @codingStandardsIgnoreEnd
$ret = [];
foreach ($domains as $dom => $pathsArray) {
foreach (array_keys($pathsArray) as $cpath) {
if (SetCookie::matchCookiePath($cpath, $path)) {
if (! isset($ret[$dom])) {
$ret[$dom] = [];
}
$ret[$dom][$cpath] = $pathsArray[$cpath];
}
}
}
return $ret;
}
/**
* Create a new Cookies object and automatically load into it all the
* cookies set in a Response object. If $uri is set, it will be
* considered as the requested URI for setting default domain and path
* of the cookie.
*
* @param Response $response HTTP Response object
* @param Uri\Uri|string $refUri The requested URI
* @return static
* @todo Add the $uri functionality.
*/
public static function fromResponse(Response $response, $refUri)
{
$jar = new static();
$jar->addCookiesFromResponse($response, $refUri);
return $jar;
}
/**
* Tells if the array of cookies is empty
*
* @return bool
*/
public function isEmpty()
{
return count($this) == 0;
}
/**
* Empties the cookieJar of any cookie
*
* @return $this
*/
public function reset()
{
$this->cookies = $this->rawCookies = [];
return $this;
}
}
PK %R1w w src/ClientStatic.phpnu ٘ setUri($url);
$request->setMethod(Request::METHOD_GET);
if (! empty($query) && is_array($query)) {
$request->getQuery()->fromArray($query);
}
if (! empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (! empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
/**
* HTTP POST METHOD (static)
*
* @param string $url
* @param array $params
* @param array $headers
* @param mixed $body
* @param array|Traversable $clientOptions
* @throws Exception\InvalidArgumentException
* @return Response|bool
*/
public static function post($url, $params, $headers = [], $body = null, $clientOptions = null)
{
if (empty($url)) {
return false;
}
$request = new Request();
$request->setUri($url);
$request->setMethod(Request::METHOD_POST);
if (! empty($params) && is_array($params)) {
$request->getPost()->fromArray($params);
} else {
throw new Exception\InvalidArgumentException('The array of post parameters is empty');
}
if (! isset($headers['Content-Type'])) {
$headers['Content-Type'] = Client::ENC_URLENCODED;
}
if (! empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (! empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
}
PK %RZK)K: K: src/Request.phpnu ٘ setAllowCustomMethods($allowCustomMethods);
$lines = explode("\r\n", $string);
// first line must be Method/Uri/Version string
$matches = null;
$methods = $allowCustomMethods
? '[\w-]+'
: implode(
'|',
[
self::METHOD_OPTIONS,
self::METHOD_GET,
self::METHOD_HEAD,
self::METHOD_POST,
self::METHOD_PUT,
self::METHOD_DELETE,
self::METHOD_TRACE,
self::METHOD_CONNECT,
self::METHOD_PATCH,
]
);
$regex = '#^(?P' . $methods . ')\s(?P[^ ]*)(?:\sHTTP\/(?P\d+\.\d+)){0,1}#';
$firstLine = array_shift($lines);
if (! preg_match($regex, $firstLine, $matches)) {
throw new Exception\InvalidArgumentException(
'A valid request line was not found in the provided string'
);
}
$request->setMethod($matches['method']);
$request->setUri($matches['uri']);
$parsedUri = parse_url($matches['uri']);
if (array_key_exists('query', $parsedUri)) {
$parsedQuery = [];
parse_str($parsedUri['query'], $parsedQuery);
$request->setQuery(new Parameters($parsedQuery));
}
if (isset($matches['version'])) {
$request->setVersion($matches['version']);
}
if (empty($lines)) {
return $request;
}
$isHeader = true;
$headers = $rawBody = [];
while ($lines) {
$nextLine = array_shift($lines);
if ($nextLine == '') {
$isHeader = false;
continue;
}
if ($isHeader) {
if (preg_match("/[\r\n]/", $nextLine)) {
throw new Exception\RuntimeException('CRLF injection detected');
}
$headers[] = $nextLine;
continue;
}
if (empty($rawBody)
&& preg_match('/^[a-z0-9!#$%&\'*+.^_`|~-]+:$/i', $nextLine)
) {
throw new Exception\RuntimeException('CRLF injection detected');
}
$rawBody[] = $nextLine;
}
if ($headers) {
$request->headers = implode("\r\n", $headers);
}
if ($rawBody) {
$request->setContent(implode("\r\n", $rawBody));
}
return $request;
}
/**
* Set the method for this request
*
* @param string $method
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setMethod($method)
{
$method = strtoupper($method);
if (! defined('static::METHOD_' . $method) && ! $this->getAllowCustomMethods()) {
throw new Exception\InvalidArgumentException('Invalid HTTP method passed');
}
$this->method = $method;
return $this;
}
/**
* Return the method for this request
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set the URI/URL for this request, this can be a string or an instance of Laminas\Uri\Http
*
* @throws Exception\InvalidArgumentException
* @param string|HttpUri $uri
* @return $this
*/
public function setUri($uri)
{
if (is_string($uri)) {
try {
$uri = new HttpUri($uri);
} catch (UriException\InvalidUriPartException $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid URI passed as string (%s)', (string) $uri),
$e->getCode(),
$e
);
}
} elseif (! ($uri instanceof HttpUri)) {
throw new Exception\InvalidArgumentException(
'URI must be an instance of Laminas\Uri\Http or a string'
);
}
$this->uri = $uri;
return $this;
}
/**
* Return the URI for this request object
*
* @return HttpUri
*/
public function getUri()
{
if ($this->uri === null || is_string($this->uri)) {
$this->uri = new HttpUri($this->uri);
}
return $this->uri;
}
/**
* Return the URI for this request object as a string
*
* @return string
*/
public function getUriString()
{
if ($this->uri instanceof HttpUri) {
return $this->uri->toString();
}
return $this->uri;
}
/**
* Provide an alternate Parameter Container implementation for query parameters in this object,
* (this is NOT the primary API for value setting, for that see getQuery())
*
* @param ParametersInterface $query
* @return $this
*/
public function setQuery(ParametersInterface $query)
{
$this->queryParams = $query;
return $this;
}
/**
* Return the parameter container responsible for query parameters or a single query parameter
*
* @param string|null $name Parameter name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the parameter is missing.
* @return ParametersInterface|mixed
*/
public function getQuery($name = null, $default = null)
{
if ($this->queryParams === null) {
$this->queryParams = new Parameters();
}
if ($name === null) {
return $this->queryParams;
}
return $this->queryParams->get($name, $default);
}
/**
* Provide an alternate Parameter Container implementation for post parameters in this object,
* (this is NOT the primary API for value setting, for that see getPost())
*
* @param ParametersInterface $post
* @return $this
*/
public function setPost(ParametersInterface $post)
{
$this->postParams = $post;
return $this;
}
/**
* Return the parameter container responsible for post parameters or a single post parameter.
*
* @param string|null $name Parameter name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the parameter is missing.
* @return ParametersInterface|mixed
*/
public function getPost($name = null, $default = null)
{
if ($this->postParams === null) {
$this->postParams = new Parameters();
}
if ($name === null) {
return $this->postParams;
}
return $this->postParams->get($name, $default);
}
/**
* Return the Cookie header, this is the same as calling $request->getHeaders()->get('Cookie');
*
* @convenience $request->getHeaders()->get('Cookie');
* @return Header\Cookie|bool
*/
public function getCookie()
{
return $this->getHeaders()->get('Cookie');
}
/**
* Provide an alternate Parameter Container implementation for file parameters in this object,
* (this is NOT the primary API for value setting, for that see getFiles())
*
* @param ParametersInterface $files
* @return $this
*/
public function setFiles(ParametersInterface $files)
{
$this->fileParams = $files;
return $this;
}
/**
* Return the parameter container responsible for file parameters or a single file.
*
* @param string|null $name Parameter name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the parameter is missing.
* @return ParametersInterface|mixed
*/
public function getFiles($name = null, $default = null)
{
if ($this->fileParams === null) {
$this->fileParams = new Parameters();
}
if ($name === null) {
return $this->fileParams;
}
return $this->fileParams->get($name, $default);
}
/**
* Return the header container responsible for headers or all headers of a certain name/type
*
* @see \Laminas\Http\Headers::get()
* @param string|null $name Header name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the requested header is missing.
* @return \Laminas\Http\Headers|bool|\Laminas\Http\Header\HeaderInterface|\ArrayIterator
*/
public function getHeaders($name = null, $default = false)
{
if ($this->headers === null || is_string($this->headers)) {
// this is only here for fromString lazy loading
$this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
}
if ($name === null) {
return $this->headers;
}
if ($this->headers->has($name)) {
return $this->headers->get($name);
}
return $default;
}
/**
* Get all headers of a certain name/type.
*
* @see Request::getHeaders()
* @param string|null $name Header name to retrieve, or null to get the whole container.
* @param mixed|null $default Default value to use when the requested header is missing.
* @return \Laminas\Http\Headers|bool|\Laminas\Http\Header\HeaderInterface|\ArrayIterator
*/
public function getHeader($name, $default = false)
{
return $this->getHeaders($name, $default);
}
/**
* Is this an OPTIONS method request?
*
* @return bool
*/
public function isOptions()
{
return ($this->method === self::METHOD_OPTIONS);
}
/**
* Is this a PROPFIND method request?
*
* @return bool
*/
public function isPropFind()
{
return ($this->method === self::METHOD_PROPFIND);
}
/**
* Is this a GET method request?
*
* @return bool
*/
public function isGet()
{
return ($this->method === self::METHOD_GET);
}
/**
* Is this a HEAD method request?
*
* @return bool
*/
public function isHead()
{
return ($this->method === self::METHOD_HEAD);
}
/**
* Is this a POST method request?
*
* @return bool
*/
public function isPost()
{
return ($this->method === self::METHOD_POST);
}
/**
* Is this a PUT method request?
*
* @return bool
*/
public function isPut()
{
return ($this->method === self::METHOD_PUT);
}
/**
* Is this a DELETE method request?
*
* @return bool
*/
public function isDelete()
{
return ($this->method === self::METHOD_DELETE);
}
/**
* Is this a TRACE method request?
*
* @return bool
*/
public function isTrace()
{
return ($this->method === self::METHOD_TRACE);
}
/**
* Is this a CONNECT method request?
*
* @return bool
*/
public function isConnect()
{
return ($this->method === self::METHOD_CONNECT);
}
/**
* Is this a PATCH method request?
*
* @return bool
*/
public function isPatch()
{
return ($this->method === self::METHOD_PATCH);
}
/**
* Is the request a Javascript XMLHttpRequest?
*
* Should work with Prototype/Script.aculo.us, possibly others.
*
* @return bool
*/
public function isXmlHttpRequest()
{
$header = $this->getHeaders()->get('X_REQUESTED_WITH');
return false !== $header && $header->getFieldValue() == 'XMLHttpRequest';
}
/**
* Is this a Flash request?
*
* @return bool
*/
public function isFlashRequest()
{
$header = $this->getHeaders()->get('USER_AGENT');
return false !== $header && stristr($header->getFieldValue(), ' flash');
}
/**
* Return the formatted request line (first line) for this http request
*
* @return string
*/
public function renderRequestLine()
{
return $this->method . ' ' . (string) $this->uri . ' HTTP/' . $this->version;
}
/**
* @return string
*/
public function toString()
{
$str = $this->renderRequestLine() . "\r\n";
$str .= $this->getHeaders()->toString();
$str .= "\r\n";
$str .= $this->getContent();
return $str;
}
/**
* @return bool
*/
public function getAllowCustomMethods()
{
return $this->allowCustomMethods;
}
/**
* @param bool $strictMethods
*/
public function setAllowCustomMethods($strictMethods)
{
$this->allowCustomMethods = (bool) $strictMethods;
}
}
PK %RQ src/Header/Age.phpnu ٘ setDeltaSeconds($deltaSeconds);
}
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Age';
}
/**
* Get header value (number of seconds)
*
* @return string
*/
public function getFieldValue()
{
return (string) $this->getDeltaSeconds();
}
/**
* Set number of seconds
*
* @param int $delta
* @return $this
*/
public function setDeltaSeconds($delta)
{
if (! is_int($delta) && ! is_numeric($delta)) {
throw new Exception\InvalidArgumentException('Invalid delta provided');
}
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Return header line
* In case of overflow RFC states to set value of 2147483648 (2^31)
*
* @return string
*/
public function toString()
{
return 'Age: ' . (($this->deltaSeconds >= PHP_INT_MAX) ? '2147483648' : $this->deltaSeconds);
}
}
PK %RNЏ src/Header/Range.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Range';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Range: ' . $this->getFieldValue();
}
}
PK %RĦ"˃ . src/Header/ContentSecurityPolicyReportOnly.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Max-Forwards';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Max-Forwards: ' . $this->getFieldValue();
}
}
PK %R^j ! src/Header/ContentDisposition.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Content-Disposition';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Content-Disposition: ' . $this->getFieldValue();
}
}
PK %RNX $ src/Header/ContentSecurityPolicy.phpnu ٘ directives;
}
/**
* Sets the directive to consist of the source list
*
* Reverses http://www.w3.org/TR/CSP/#parsing-1
*
* @param string $name The directive name.
* @param array $sources The source list.
* @return $this
* @throws Exception\InvalidArgumentException If the name is not a valid directive name.
*/
public function setDirective($name, array $sources)
{
if (! in_array($name, $this->validDirectiveNames, true)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid directive name; received "%s"',
__METHOD__,
(string) $name
));
}
if ($name === 'block-all-mixed-content'
|| $name === 'upgrade-insecure-requests'
) {
if ($sources) {
throw new Exception\InvalidArgumentException(sprintf(
'Received value for %s directive; none expected',
$name
));
}
$this->directives[$name] = '';
return $this;
}
if (empty($sources)) {
if ('report-uri' === $name) {
if (isset($this->directives[$name])) {
unset($this->directives[$name]);
}
return $this;
}
$this->directives[$name] = "'none'";
return $this;
}
array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
$this->directives[$name] = implode(' ', $sources);
return $this;
}
/**
* Create Content Security Policy header from a given header line
*
* @param string $headerLine The header line to parse.
* @return static
* @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
*/
public static function fromString($headerLine)
{
$header = new static();
$headerName = $header->getFieldName();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// Ensure the proper header name
if (strcasecmp($name, $headerName) != 0) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for %s string: "%s"',
$headerName,
$name
));
}
// As per http://www.w3.org/TR/CSP/#parsing
$tokens = explode(';', $value);
foreach ($tokens as $token) {
$token = trim($token);
if ($token) {
list($directiveName, $directiveValue) = array_pad(explode(' ', $token, 2), 2, null);
if (! isset($header->directives[$directiveName])) {
$header->setDirective(
$directiveName,
$directiveValue === null ? [] : [$directiveValue]
);
}
}
}
return $header;
}
/**
* Get the header name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Security-Policy';
}
/**
* Get the header value
*
* @return string
*/
public function getFieldValue()
{
$directives = [];
foreach ($this->directives as $name => $value) {
$directives[] = sprintf('%s %s;', $name, $value);
}
return str_replace(' ;', ';', implode(' ', $directives));
}
/**
* Return the header as a string
*
* @return string
*/
public function toString()
{
return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
}
public function toStringMultipleHeaders(array $headers)
{
$strings = [$this->toString()];
foreach ($headers as $header) {
if (! $header instanceof ContentSecurityPolicy) {
throw new Exception\RuntimeException(
'The ContentSecurityPolicy multiple header implementation can only'
. ' accept an array of ContentSecurityPolicy headers'
);
}
$strings[] = $header->toString();
}
return implode("\r\n", $strings) . "\r\n";
}
}
PK %R$ ) src/Header/Exception/RuntimeException.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Content-Range';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Content-Range: ' . $this->getFieldValue();
}
}
PK %R8r r src/Header/IfModifiedSince.phpnu ٘ getFieldValue();
}
/**
* Add a media type, with the given priority
*
* @param string $type
* @param int|float $priority
* @param array $params
* @return $this
*/
public function addMediaType($type, $priority = 1, array $params = [])
{
return $this->addType($type, $priority, $params);
}
/**
* Does the header have the requested media type?
*
* @param string $type
* @return bool
*/
public function hasMediaType($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return FieldValuePart\AcceptFieldValuePart
* @see \Laminas\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '/')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim($fieldValuePart);
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '/')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '/') + 1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$pos = strpos($subtype, '+');
if (false !== $pos) {
$format = trim(substr($subtype, $pos + 1));
$subtype = trim(substr($subtype, 0, $pos));
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw),
];
return new FieldValuePart\AcceptFieldValuePart((object) $aggregated);
}
}
PK %Rwhx x src/Header/IfUnmodifiedSince.phpnu ٘ setFieldName($fieldName);
}
if ($fieldValue !== null) {
$this->setFieldValue($fieldValue);
}
}
/**
* Set header field name
*
* @param string $fieldName
* @return $this
* @throws Exception\InvalidArgumentException If the name does not match with RFC 2616 format.
*/
public function setFieldName($fieldName)
{
if (! is_string($fieldName) || empty($fieldName)) {
throw new Exception\InvalidArgumentException('Header name must be a string');
}
/*
* Following RFC 7230 section 3.2
*
* header-field = field-name ":" [ field-value ]
* field-name = token
* token = 1*tchar
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
* "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
*/
if (! preg_match('/^[!#$%&\'*+\-\.\^_`|~0-9a-zA-Z]+$/', $fieldName)) {
throw new Exception\InvalidArgumentException(
'Header name must be a valid RFC 7230 (section 3.2) field-name.'
);
}
$this->fieldName = $fieldName;
return $this;
}
/**
* Retrieve header field name
*
* @return string
*/
public function getFieldName()
{
return $this->fieldName;
}
/**
* Set header field value
*
* @param string $fieldValue
* @return $this
*/
public function setFieldValue($fieldValue)
{
$fieldValue = (string) $fieldValue;
HeaderValue::assertValid($fieldValue);
if (preg_match('/^\s+$/', $fieldValue)) {
$fieldValue = '';
}
$this->fieldValue = $fieldValue;
return $this;
}
/**
* Retrieve header field value
*
* @return string
*/
public function getFieldValue()
{
return $this->fieldValue;
}
/**
* Cast to string as a well formed HTTP header line
*
* Returns in form of "NAME: VALUE\r\n"
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getFieldValue();
}
}
PK %R/ src/Header/From.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'From';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'From: ' . $this->getFieldValue();
}
}
PK %R src/Header/IfNoneMatch.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'If-None-Match';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'If-None-Match: ' . $this->getFieldValue();
}
}
PK %R'%` ` src/Header/Location.phpnu ٘ getInternalValues()->type;
}
}
PK %R۴ 9 src/Header/Accept/FieldValuePart/AcceptFieldValuePart.phpnu ٘ getInternalValues()->subtype;
}
/**
* @return string
*/
public function getSubtypeRaw()
{
return $this->getInternalValues()->subtypeRaw;
}
/**
* @return string
*/
public function getFormat()
{
return $this->getInternalValues()->format;
}
}
PK %R({| | : src/Header/Accept/FieldValuePart/CharsetFieldValuePart.phpnu ٘ getInternalValues()->type;
}
}
PK %R! ! ; src/Header/Accept/FieldValuePart/LanguageFieldValuePart.phpnu ٘ getInternalValues()->typeString;
}
public function getPrimaryTag()
{
return $this->getInternalValues()->type;
}
public function getSubTag()
{
return $this->getInternalValues()->subtype;
}
}
PK %Rמ) ) ; src/Header/Accept/FieldValuePart/AbstractFieldValuePart.phpnu ٘ internalValues = $internalValues;
}
/**
* Set a Field Value Part this Field Value Part matched against.
*
* @param AbstractFieldValuePart $matchedAgainst
* @return $this
*/
public function setMatchedAgainst(AbstractFieldValuePart $matchedAgainst)
{
$this->matchedAgainst = $matchedAgainst;
return $this;
}
/**
* Get a Field Value Part this Field Value Part matched against.
*
* @return AbstractFieldValuePart|null
*/
public function getMatchedAgainst()
{
return $this->matchedAgainst;
}
/**
* @return object
*/
protected function getInternalValues()
{
return $this->internalValues;
}
/**
* @return string $typeString
*/
public function getTypeString()
{
return $this->getInternalValues()->typeString;
}
/**
* @return float $priority
*/
public function getPriority()
{
return (float) $this->getInternalValues()->priority;
}
/**
* @return \stdClass $params
*/
public function getParams()
{
return (object) $this->getInternalValues()->params;
}
/**
* @return string $raw
*/
public function getRaw()
{
return $this->getInternalValues()->raw;
}
/**
* @param mixed $key
* @return mixed
*/
public function __get($key)
{
return $this->getInternalValues()->$key;
}
}
PK %RM M src/Header/Date.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Upgrade';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Upgrade: ' . $this->getFieldValue();
}
}
PK %RwU src/Header/Referer.phpnu ٘ uri->setFragment(null);
return $this;
}
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Referer';
}
}
PK %R$| | src/Header/Origin.phpnu ٘ isValid()) {
throw new Exception\InvalidArgumentException('Invalid header value for Origin key: "' . $name . '"');
}
return new static($value);
}
/**
* @param string|null $value
*/
public function __construct($value = null)
{
if ($value !== null) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Origin';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Origin: ' . $this->getFieldValue();
}
}
PK %RX X src/Header/RetryAfter.phpnu ٘ getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
if (is_numeric($date)) {
$dateHeader->setDeltaSeconds($date);
} else {
$dateHeader->setDate($date);
}
return $dateHeader;
}
/**
* Set number of seconds
*
* @param int $delta
* @return $this
*/
public function setDeltaSeconds($delta)
{
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Retry-After';
}
/**
* Returns date if it's set, or number of seconds
*
* @return int|string
*/
public function getFieldValue()
{
return ($this->date === null) ? $this->deltaSeconds : $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Retry-After: ' . $this->getFieldValue();
}
}
PK %RsĐ src/Header/Warning.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Warning';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Warning: ' . $this->getFieldValue();
}
}
PK %R src/Header/Allow.phpnu ٘ false,
Request::METHOD_GET => true,
Request::METHOD_HEAD => false,
Request::METHOD_POST => true,
Request::METHOD_PUT => false,
Request::METHOD_DELETE => false,
Request::METHOD_TRACE => false,
Request::METHOD_CONNECT => false,
Request::METHOD_PATCH => false,
];
/**
* Create Allow header from header line
*
* @param string $headerLine
* @return static
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'allow') {
throw new Exception\InvalidArgumentException('Invalid header line for Allow string: "' . $name . '"');
}
$header = new static();
$header->disallowMethods(array_keys($header->getAllMethods()));
$header->allowMethods(explode(',', $value));
return $header;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Allow';
}
/**
* Get comma-separated list of allowed methods
*
* @return string
*/
public function getFieldValue()
{
return implode(', ', array_keys($this->methods, true, true));
}
/**
* Get list of all defined methods
*
* @return array
*/
public function getAllMethods()
{
return $this->methods;
}
/**
* Get list of allowed methods
*
* @return array
*/
public function getAllowedMethods()
{
return array_keys($this->methods, true, true);
}
/**
* Allow methods or list of methods
*
* @param array|string $allowedMethods
* @return $this
*/
public function allowMethods($allowedMethods)
{
foreach ((array) $allowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to whitelist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = true;
}
return $this;
}
/**
* Disallow methods or list of methods
*
* @param array|string $disallowedMethods
* @return $this
*/
public function disallowMethods($disallowedMethods)
{
foreach ((array) $disallowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to blacklist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = false;
}
return $this;
}
/**
* Convenience alias for @see disallowMethods()
*
* @param array|string $disallowedMethods
* @return $this
*/
public function denyMethods($disallowedMethods)
{
return $this->disallowMethods($disallowedMethods);
}
/**
* Check whether method is allowed
*
* @param string $method
* @return bool
*/
public function isAllowedMethod($method)
{
$method = trim(strtoupper($method));
// disallow unknown method
if (! isset($this->methods[$method])) {
$this->methods[$method] = false;
}
return $this->methods[$method];
}
/**
* Return header as string
*
* @return string
*/
public function toString()
{
return 'Allow: ' . $this->getFieldValue();
}
}
PK %R]LS src/Header/AbstractDate.phpnu ٘ 'D, d M Y H:i:s \G\M\T',
self::DATE_RFC1036 => 'D, d M y H:i:s \G\M\T',
self::DATE_ANSIC => 'D M j H:i:s Y',
];
/**
* Create date-based header from string
*
* @param string $headerLine
* @return static
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$dateHeader = new static();
list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate($date);
return $dateHeader;
}
/**
* Create date-based header from strtotime()-compatible string
*
* @param int|string $time
* @return static
* @throws Exception\InvalidArgumentException
*/
public static function fromTimeString($time)
{
return static::fromTimestamp(strtotime($time));
}
/**
* Create date-based header from Unix timestamp
*
* @param int $time
* @return static
* @throws Exception\InvalidArgumentException
*/
public static function fromTimestamp($time)
{
$dateHeader = new static();
if (! $time || ! is_numeric($time)) {
throw new Exception\InvalidArgumentException(
'Invalid time for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate(new DateTime('@' . $time));
return $dateHeader;
}
/**
* Set date output format
*
* @param int $format
* @throws Exception\InvalidArgumentException
*/
public static function setDateFormat($format)
{
if (! isset(static::$dateFormats[$format])) {
throw new Exception\InvalidArgumentException(sprintf(
'No constant defined for provided date format: %s',
$format
));
}
static::$dateFormat = static::$dateFormats[$format];
}
/**
* Return current date output format
*
* @return string
*/
public static function getDateFormat()
{
return static::$dateFormat;
}
/**
* Set the date for this header, this can be a string or an instance of \DateTime
*
* @param string|DateTime $date
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setDate($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (! ($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$date->setTimezone(new DateTimeZone('GMT'));
$this->date = $date;
return $this;
}
/**
* Return date for this header
*
* @return string
*/
public function getDate()
{
return $this->date()->format(static::$dateFormat);
}
/**
* Return date for this header as an instance of \DateTime
*
* @return DateTime
*/
public function date()
{
if ($this->date === null) {
$this->date = new DateTime(null, new DateTimeZone('GMT'));
}
return $this->date;
}
/**
* Compare provided date to date for this header
* Returns < 0 if date in header is less than $date; > 0 if it's greater, and 0 if they are equal.
* @see \strcmp()
*
* @param string|DateTime $date
* @return int
* @throws Exception\InvalidArgumentException
*/
public function compareTo($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid Date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (! ($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$dateTimestamp = $date->getTimestamp();
$thisTimestamp = $this->date()->getTimestamp();
return ($thisTimestamp === $dateTimestamp) ? 0 : (($thisTimestamp > $dateTimestamp) ? 1 : -1);
}
/**
* Get header value as formatted date
*
* @return string
*/
public function getFieldValue()
{
return $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getDate();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}
PK %RIM M src/Header/HeaderInterface.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'User-Agent';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'User-Agent: ' . $this->getFieldValue();
}
}
PK %Rt src/Header/AbstractLocation.phpnu ٘ getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $locationHeader->getFieldName() . '" header string'
);
}
HeaderValue::assertValid($uri);
$locationHeader->setUri(trim($uri));
return $locationHeader;
}
/**
* Set the URI/URL for this header, this can be a string or an instance of Laminas\Uri\Http
*
* @param string|UriInterface $uri
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setUri($uri)
{
if (is_string($uri)) {
try {
$uri = UriFactory::factory($uri);
} catch (UriException\InvalidUriPartException $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid URI passed as string (%s)', (string) $uri),
$e->getCode(),
$e
);
} catch (UriException\InvalidArgumentException $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid URI passed as string (%s)', (string) $uri),
$e->getCode(),
$e
);
}
} elseif (! ($uri instanceof UriInterface)) {
throw new Exception\InvalidArgumentException('URI must be an instance of Laminas\Uri\Http or a string');
}
$this->uri = $uri;
return $this;
}
/**
* Return the URI for this header
*
* @return string
*/
public function getUri()
{
if ($this->uri instanceof UriInterface) {
return $this->uri->toString();
}
return $this->uri;
}
/**
* Return the URI for this header as an instance of Laminas\Uri\Http
*
* @return UriInterface
*/
public function uri()
{
if ($this->uri === null || is_string($this->uri)) {
$this->uri = UriFactory::factory($this->uri);
}
return $this->uri;
}
/**
* Get header value as URI string
*
* @return string
*/
public function getFieldValue()
{
return $this->getUri();
}
/**
* Output header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getUri();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}
PK %R58N src/Header/Vary.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Vary';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Vary: ' . $this->getFieldValue();
}
}
PK %R' ' src/Header/ContentType.phpnu ٘ 0) {
$parameters = [];
foreach ($parts as $parameter) {
$parameter = trim($parameter);
if (! preg_match('/^(?P[^\s\=]+)\="?(?P[^\s\"]*)"?$/', $parameter, $matches)) {
continue;
}
$parameters[$matches['key']] = $matches['value'];
}
$header->setParameters($parameters);
}
return $header;
}
public function __construct($value = null, $mediaType = null)
{
if ($value !== null) {
HeaderValue::assertValid($value);
$this->value = $value;
}
$this->mediaType = $mediaType;
}
/**
* Determine if the mediatype value in this header matches the provided criteria
*
* @param array|string $matchAgainst
* @return string|bool Matched value or false
*/
public function match($matchAgainst)
{
if (is_string($matchAgainst)) {
$matchAgainst = $this->splitMediaTypesFromString($matchAgainst);
}
$mediaType = $this->getMediaType();
$left = $this->getMediaTypeObjectFromString($mediaType);
foreach ($matchAgainst as $matchType) {
$matchType = strtolower($matchType);
if ($mediaType == $matchType) {
return $matchType;
}
$right = $this->getMediaTypeObjectFromString($matchType);
// Is the right side a wildcard type?
if ($right->type == '*') {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
// Do the types match?
if ($right->type == $left->type) {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
}
return false;
}
/**
* Create a string representation of the header
*
* @return string
*/
public function toString()
{
return 'Content-Type: ' . $this->getFieldValue();
}
/**
* Get the field name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Type';
}
/**
* Get the field value
*
* @return string
*/
public function getFieldValue()
{
if (null !== $this->value) {
return (string) $this->value;
}
return $this->assembleValue();
}
/**
* Set the media type
*
* @param string $mediaType
* @return $this
*/
public function setMediaType($mediaType)
{
HeaderValue::assertValid($mediaType);
$this->mediaType = strtolower($mediaType);
$this->value = null;
return $this;
}
/**
* Get the media type
*
* @return string
*/
public function getMediaType()
{
return (string) $this->mediaType;
}
/**
* Set additional content-type parameters
*
* @param array $parameters
* @return $this
*/
public function setParameters(array $parameters)
{
foreach ($parameters as $key => $value) {
HeaderValue::assertValid($key);
HeaderValue::assertValid($value);
}
$this->parameters = array_merge($this->parameters, $parameters);
$this->value = null;
return $this;
}
/**
* Get any additional content-type parameters currently set
*
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Set the content-type character set encoding
*
* @param string $charset
* @return $this
*/
public function setCharset($charset)
{
HeaderValue::assertValid($charset);
$this->parameters['charset'] = $charset;
$this->value = null;
return $this;
}
/**
* Get the content-type character set encoding, if any
*
* @return null|string
*/
public function getCharset()
{
if (isset($this->parameters['charset'])) {
return $this->parameters['charset'];
}
return null;
}
/**
* Assemble the value based on the media type and any available parameters
*
* @return string
*/
protected function assembleValue()
{
$mediaType = $this->getMediaType();
if (empty($this->parameters)) {
return $mediaType;
}
$parameters = [];
foreach ($this->parameters as $key => $value) {
$parameters[] = sprintf('%s=%s', $key, $value);
}
return sprintf('%s; %s', $mediaType, implode('; ', $parameters));
}
/**
* Split comma-separated media types into an array
*
* @param string $criteria
* @return array
*/
protected function splitMediaTypesFromString($criteria)
{
$mediaTypes = explode(',', $criteria);
array_walk(
$mediaTypes,
function (&$value) {
$value = trim($value);
}
);
return $mediaTypes;
}
/**
* Split a mediatype string into an object with the following parts:
*
* - type
* - subtype
* - format
*
* @param string $string
* @return stdClass
*/
protected function getMediaTypeObjectFromString($string)
{
if (! is_string($string)) {
throw new Exception\InvalidArgumentException(sprintf(
'Non-string mediatype "%s" provided',
(is_object($string) ? get_class($string) : gettype($string))
));
}
$parts = explode('/', $string, 2);
if (1 == count($parts)) {
throw new Exception\DomainException(sprintf(
'Invalid mediatype "%s" provided',
$string
));
}
$type = array_shift($parts);
$subtype = array_shift($parts);
$format = $subtype;
if (false !== strpos($subtype, '+')) {
$parts = explode('+', $subtype, 2);
$subtype = array_shift($parts);
$format = array_shift($parts);
}
$mediaType = (object) [
'type' => $type,
'subtype' => $subtype,
'format' => $format,
];
return $mediaType;
}
/**
* Validate a subtype
*
* @param stdClass $right
* @param stdClass $left
* @return bool
*/
protected function validateSubtype($right, $left)
{
// Is the right side a wildcard subtype?
if ($right->subtype == '*') {
return $this->validateFormat($right, $left);
}
// Do the right side and left side subtypes match?
if ($right->subtype == $left->subtype) {
return $this->validateFormat($right, $left);
}
// Is the right side a partial wildcard?
if ('*' == substr($right->subtype, -1)) {
// validate partial-wildcard subtype
if (! $this->validatePartialWildcard($right->subtype, $left->subtype)) {
return false;
}
// Finally, verify format is valid
return $this->validateFormat($right, $left);
}
// Does the right side subtype match the left side format?
if ($right->subtype == $left->format) {
return true;
}
// At this point, there is no valid match
return false;
}
/**
* Validate the format
*
* Validate that the right side format matches what the left side defines.
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validateFormat($right, $left)
{
if ($right->format && $left->format) {
if ($right->format == '*') {
return true;
}
if ($right->format == $left->format) {
return true;
}
return false;
}
return true;
}
/**
* Validate a partial wildcard (i.e., string ending in '*')
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validatePartialWildcard($right, $left)
{
$requiredSegment = substr($right, 0, strlen($right) - 1);
if ($requiredSegment == $left) {
return true;
}
if (strlen($requiredSegment) >= strlen($left)) {
return false;
}
if (0 === strpos($left, $requiredSegment)) {
return true;
}
return false;
}
}
PK %R贩N N src/Header/SetCookie.phpnu ٘ self::SAME_SITE_STRICT,
'lax' => self::SAME_SITE_LAX,
'none' => self::SAME_SITE_NONE,
];
/**
* Cookie name
*
* @var string|null
*/
protected $name;
/**
* Cookie value
*
* @var string|null
*/
protected $value;
/**
* Version
*
* @var int|null
*/
protected $version;
/**
* Max Age
*
* @var int|null
*/
protected $maxAge;
/**
* Cookie expiry date
*
* @var int|null
*/
protected $expires;
/**
* Cookie domain
*
* @var string|null
*/
protected $domain;
/**
* Cookie path
*
* @var string|null
*/
protected $path;
/**
* Whether the cookie is secure or not
*
* @var bool|null
*/
protected $secure;
/**
* If the value need to be quoted or not
*
* @var bool
*/
protected $quoteFieldValue = false;
/**
* @var bool|null
*/
protected $httponly;
/**
* @var string|null
*/
protected $sameSite;
/**
* @var bool
*/
protected $encodeValue = true;
/**
* @static
* @throws Exception\InvalidArgumentException
* @param $headerLine
* @param bool $bypassHeaderFieldName
* @return array|SetCookie
*/
public static function fromString($headerLine, $bypassHeaderFieldName = false)
{
static $setCookieProcessor = null;
if ($setCookieProcessor === null) {
$setCookieClass = get_called_class();
$setCookieProcessor = function ($headerLine) use ($setCookieClass) {
/** @var SetCookie $header */
$header = new $setCookieClass();
$keyValuePairs = preg_split('#;\s*#', $headerLine);
foreach ($keyValuePairs as $keyValue) {
if (preg_match('#^(?P[^=]+)=\s*("?)(?P[^"]*)\2#', $keyValue, $matches)) {
$headerKey = $matches['headerKey'];
$headerValue = $matches['headerValue'];
} else {
$headerKey = $keyValue;
$headerValue = null;
}
// First K=V pair is always the cookie name and value
if ($header->getName() === null) {
$header->setName($headerKey);
$header->setValue(urldecode($headerValue));
// set no encode value if raw and encoded values are the same
if (urldecode($headerValue) === $headerValue) {
$header->setEncodeValue(false);
}
continue;
}
// Process the remaining elements
switch (str_replace(['-', '_'], '', strtolower($headerKey))) {
case 'expires':
$header->setExpires($headerValue);
break;
case 'domain':
$header->setDomain($headerValue);
break;
case 'path':
$header->setPath($headerValue);
break;
case 'secure':
$header->setSecure(true);
break;
case 'httponly':
$header->setHttponly(true);
break;
case 'version':
$header->setVersion((int) $headerValue);
break;
case 'maxage':
$header->setMaxAge($headerValue);
break;
case 'samesite':
$header->setSameSite($headerValue);
break;
default:
// Intentionally omitted
}
}
return $header;
};
}
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
HeaderValue::assertValid($value);
// some sites return set-cookie::value, this is to get rid of the second :
$name = strtolower($name) == 'set-cookie:' ? 'set-cookie' : $name;
// check to ensure proper header type for this factory
if (strtolower($name) !== 'set-cookie') {
throw new Exception\InvalidArgumentException('Invalid header line for Set-Cookie string: "' . $name . '"');
}
$multipleHeaders = preg_split('#(?type = 'Cookie';
$this->setName($name)
->setValue($value)
->setVersion($version)
->setMaxAge($maxAge)
->setDomain($domain)
->setExpires($expires)
->setPath($path)
->setSecure($secure)
->setHttpOnly($httponly)
->setSameSite($sameSite);
}
/**
* @return bool
*/
public function getEncodeValue()
{
return $this->encodeValue;
}
/**
* @param bool $encodeValue
*/
public function setEncodeValue($encodeValue)
{
$this->encodeValue = (bool) $encodeValue;
}
/**
* @return string 'Set-Cookie'
*/
public function getFieldName()
{
return 'Set-Cookie';
}
/**
* @throws Exception\RuntimeException
* @return string
*/
public function getFieldValue()
{
if ($this->getName() == '') {
return '';
}
$value = $this->encodeValue ? urlencode($this->getValue()) : $this->getValue();
if ($this->hasQuoteFieldValue()) {
$value = '"' . $value . '"';
}
$fieldValue = $this->getName() . '=' . $value;
$version = $this->getVersion();
if ($version !== null) {
$fieldValue .= '; Version=' . $version;
}
$maxAge = $this->getMaxAge();
if ($maxAge !== null) {
$fieldValue .= '; Max-Age=' . $maxAge;
}
$expires = $this->getExpires();
if ($expires) {
$fieldValue .= '; Expires=' . $expires;
}
$domain = $this->getDomain();
if ($domain) {
$fieldValue .= '; Domain=' . $domain;
}
$path = $this->getPath();
if ($path) {
$fieldValue .= '; Path=' . $path;
}
if ($this->isSecure()) {
$fieldValue .= '; Secure';
}
if ($this->isHttponly()) {
$fieldValue .= '; HttpOnly';
}
$sameSite = $this->getSameSite();
if ($sameSite !== null && array_key_exists(strtolower($sameSite), self::SAME_SITE_ALLOWED_VALUES)) {
$fieldValue .= '; SameSite=' . $sameSite;
}
return $fieldValue;
}
/**
* @param string|null $name
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setName($name)
{
HeaderValue::assertValid($name);
$this->name = $name;
return $this;
}
/**
* @return string|null
*/
public function getName()
{
return $this->name;
}
/**
* @param string|null $value
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return string|null
*/
public function getValue()
{
return $this->value;
}
/**
* @param int|null $version
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setVersion($version)
{
if ($version !== null && ! is_int($version)) {
throw new Exception\InvalidArgumentException('Invalid Version number specified');
}
$this->version = $version;
return $this;
}
/**
* @return int|null
*/
public function getVersion()
{
return $this->version;
}
/**
* @param int $maxAge
* @return $this
*/
public function setMaxAge($maxAge)
{
if ($maxAge === null || ! is_numeric($maxAge)) {
return $this;
}
$this->maxAge = max(0, (int) $maxAge);
return $this;
}
/**
* @return int|null
*/
public function getMaxAge()
{
return $this->maxAge;
}
/**
* @param int|string|DateTime|null $expires
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setExpires($expires)
{
if ($expires === null) {
$this->expires = null;
return $this;
}
if ($expires instanceof DateTime) {
$expires = $expires->format(DateTime::COOKIE);
}
$tsExpires = $expires;
if (is_string($expires)) {
$tsExpires = strtotime($expires);
// if $tsExpires is invalid and PHP is compiled as 32bit. Check if it fail reason is the 2038 bug
if (! is_int($tsExpires) && PHP_INT_SIZE === 4) {
$dateTime = new DateTime($expires);
if ($dateTime->format('Y') > 2038) {
$tsExpires = PHP_INT_MAX;
}
}
}
if (! is_int($tsExpires) || $tsExpires < 0) {
throw new Exception\InvalidArgumentException('Invalid expires time specified');
}
$this->expires = $tsExpires;
return $this;
}
/**
* @param bool $inSeconds
* @return int|string|null
*/
public function getExpires($inSeconds = false)
{
if ($this->expires === null) {
return null;
}
if ($inSeconds) {
return $this->expires;
}
return gmdate('D, d-M-Y H:i:s', $this->expires) . ' GMT';
}
/**
* @param string|null $domain
* @return $this
*/
public function setDomain($domain)
{
HeaderValue::assertValid($domain);
$this->domain = $domain;
return $this;
}
/**
* @return string|null
*/
public function getDomain()
{
return $this->domain;
}
/**
* @param string|null $path
* @return $this
*/
public function setPath($path)
{
HeaderValue::assertValid($path);
$this->path = $path;
return $this;
}
/**
* @return string|null
*/
public function getPath()
{
return $this->path;
}
/**
* @param bool|null $secure
* @return $this
*/
public function setSecure($secure)
{
if (null !== $secure) {
$secure = (bool) $secure;
}
$this->secure = $secure;
return $this;
}
/**
* Set whether the value for this cookie should be quoted
*
* @param bool $quotedValue
* @return $this
*/
public function setQuoteFieldValue($quotedValue)
{
$this->quoteFieldValue = (bool) $quotedValue;
return $this;
}
/**
* @return bool|null
*/
public function isSecure()
{
return $this->secure;
}
/**
* @param bool|null $httponly
* @return $this
*/
public function setHttponly($httponly)
{
if (null !== $httponly) {
$httponly = (bool) $httponly;
}
$this->httponly = $httponly;
return $this;
}
/**
* @return bool|null
*/
public function isHttponly()
{
return $this->httponly;
}
/**
* Check whether the cookie has expired
*
* Always returns false if the cookie is a session cookie (has no expiry time)
*
* @param int|null $now Timestamp to consider as "now"
* @return bool
*/
public function isExpired($now = null)
{
if ($now === null) {
$now = time();
}
if (is_int($this->expires) && $this->expires < $now) {
return true;
}
return false;
}
/**
* Check whether the cookie is a session cookie (has no expiry time set)
*
* @return bool
*/
public function isSessionCookie()
{
return ($this->expires === null);
}
/**
* @return string|null
*/
public function getSameSite()
{
return $this->sameSite;
}
/**
* @param string|null $sameSite
* @return $this
* @throws Exception\InvalidArgumentException
*/
public function setSameSite($sameSite)
{
if ($sameSite === null) {
$this->sameSite = null;
return $this;
}
if (! array_key_exists(strtolower($sameSite), self::SAME_SITE_ALLOWED_VALUES)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid value provided for SameSite directive: "%s"; expected one of: Strict, Lax or None',
is_scalar($sameSite) ? $sameSite : gettype($sameSite)
));
}
$this->sameSite = self::SAME_SITE_ALLOWED_VALUES[strtolower($sameSite)];
return $this;
}
/**
* Check whether the value for this cookie should be quoted
*
* @return bool
*/
public function hasQuoteFieldValue()
{
return $this->quoteFieldValue;
}
/**
* @param string $requestDomain
* @param string $path
* @param bool $isSecure
* @return bool
*/
public function isValidForRequest($requestDomain, $path, $isSecure = false)
{
if ($this->getDomain() && (strrpos($requestDomain, $this->getDomain()) === false)) {
return false;
}
if ($this->getPath() && (strpos($path, $this->getPath()) !== 0)) {
return false;
}
if ($this->secure && $this->isSecure() !== $isSecure) {
return false;
}
return true;
}
/**
* Checks whether the cookie should be sent or not in a specific scenario
*
* @param string|\Laminas\Uri\Uri $uri URI to check against (secure, domain, path)
* @param bool $matchSessionCookies Whether to send session cookies
* @param int|null $now Override the current time when checking for expiry time
* @return bool
* @throws Exception\InvalidArgumentException If URI does not have HTTP or HTTPS scheme.
*/
public function match($uri, $matchSessionCookies = true, $now = null)
{
if (is_string($uri)) {
$uri = UriFactory::factory($uri);
}
// Make sure we have a valid Laminas_Uri_Http object
if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() == 'https'))) {
throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI');
}
// Check that the cookie is secure (if required) and not expired
if ($this->secure && $uri->getScheme() != 'https') {
return false;
}
if ($this->isExpired($now)) {
return false;
}
if ($this->isSessionCookie() && ! $matchSessionCookies) {
return false;
}
// Check if the domain matches
if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
return false;
}
// Check that path matches using prefix match
if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
return false;
}
// If we didn't die until now, return true.
return true;
}
/**
* Check if a cookie's domain matches a host name.
*
* Used by Laminas\Http\Cookies for cookie matching
*
* @param string $cookieDomain
* @param string $host
* @return bool
*/
public static function matchCookieDomain($cookieDomain, $host)
{
$cookieDomain = strtolower($cookieDomain);
$host = strtolower($host);
// Check for either exact match or suffix match
return $cookieDomain == $host
|| preg_match('/' . preg_quote($cookieDomain) . '$/', $host);
}
/**
* Check if a cookie's path matches a URL path
*
* Used by Laminas\Http\Cookies for cookie matching
*
* @param string $cookiePath
* @param string $path
* @return bool
*/
public static function matchCookiePath($cookiePath, $path)
{
return (strpos($path, $cookiePath) === 0);
}
/**
* @return string
*/
public function toString()
{
return 'Set-Cookie: ' . $this->getFieldValue();
}
/**
* @param array $headers
* @return string
* @throws Exception\RuntimeException
*/
public function toStringMultipleHeaders(array $headers)
{
$headerLine = $this->toString();
/* @var $header SetCookie */
foreach ($headers as $header) {
if (! $header instanceof SetCookie) {
throw new Exception\RuntimeException(
'The SetCookie multiple header implementation can only accept an array of SetCookie headers'
);
}
$headerLine .= "\n" . $header->toString();
}
return $headerLine;
}
}
PK %Rt src/Header/Cookie.phpnu ٘ getName(), $nvPairs)) {
throw new Exception\InvalidArgumentException(sprintf(
'Two cookies with the same name were provided to %s',
__METHOD__
));
}
$nvPairs[$setCookie->getName()] = $setCookie->getValue();
}
return new static($nvPairs);
}
public static function fromString($headerLine)
{
$header = new static();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'cookie') {
throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
}
$nvPairs = preg_split('#;\s*#', $value);
$arrayInfo = [];
foreach ($nvPairs as $nvPair) {
$parts = explode('=', $nvPair, 2);
if (count($parts) != 2) {
throw new Exception\RuntimeException('Malformed Cookie header found');
}
list($name, $value) = $parts;
$arrayInfo[$name] = urldecode($value);
}
$header->exchangeArray($arrayInfo);
return $header;
}
public function __construct(array $array = [])
{
parent::__construct($array, ArrayObject::ARRAY_AS_PROPS);
}
/**
* @param bool $encodeValue
*
* @return $this
*/
public function setEncodeValue($encodeValue)
{
$this->encodeValue = (bool) $encodeValue;
return $this;
}
/**
* @return bool
*/
public function getEncodeValue()
{
return $this->encodeValue;
}
public function getFieldName()
{
return 'Cookie';
}
public function getFieldValue()
{
$nvPairs = [];
foreach ($this->flattenCookies($this) as $name => $value) {
$nvPairs[] = $name . '=' . (($this->encodeValue) ? urlencode($value) : $value);
}
return implode('; ', $nvPairs);
}
protected function flattenCookies($data, $prefix = null)
{
$result = [];
foreach ($data as $key => $value) {
$key = $prefix ? $prefix . '[' . $key . ']' : $key;
if (is_array($value)) {
$result = array_merge($result, $this->flattenCookies($value, $key));
} else {
$result[$key] = $value;
}
}
return $result;
}
public function toString()
{
return 'Cookie: ' . $this->getFieldValue();
}
/**
* Get the cookie as a string, suitable for sending as a "Cookie" header in an
* HTTP request
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}
PK %RJg@ @ src/Header/AcceptCharset.phpnu ٘ getFieldValue();
}
/**
* Add a charset, with the given priority
*
* @param string $type
* @param int|float $priority
* @return $this
*/
public function addCharset($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested charset?
*
* @param string $type
* @return bool
*/
public function hasCharset($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Laminas\Http\Header\Accept\FieldValuePart\CharsetFieldValuePart
* @see \Laminas\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$internalValues = parent::parseFieldValuePart($fieldValuePart);
return new FieldValuePart\CharsetFieldValuePart($internalValues);
}
}
PK %R~ ~ src/Header/TE.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'TE';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'TE: ' . $this->getFieldValue();
}
}
PK %Rh src/Header/FeaturePolicy.phpnu ٘ directives;
}
/**
* Sets the directive to consist of the source list
*
* @param string $name The directive name.
* @param string[] $sources The source list.
* @return $this
* @throws Exception\InvalidArgumentException If the name is not a valid directive name.
*/
public function setDirective($name, array $sources)
{
if (! in_array($name, $this->validDirectiveNames, true)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid directive name; received "%s"',
__METHOD__,
(string) $name
));
}
if (empty($sources)) {
$this->directives[$name] = "'none'";
return $this;
}
array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
$this->directives[$name] = implode(' ', $sources);
return $this;
}
/**
* Create Feature Policy header from a given header line
*
* @param string $headerLine The header line to parse.
* @return static
* @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
*/
public static function fromString($headerLine)
{
$header = new static();
$headerName = $header->getFieldName();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// Ensure the proper header name
if (strcasecmp($name, $headerName) !== 0) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for %s string: "%s"',
$headerName,
$name
));
}
// As per https://w3c.github.io/webappsec-feature-policy/#algo-parse-policy-directive
$tokens = explode(';', $value);
foreach ($tokens as $token) {
$token = trim($token);
if ($token) {
list($directiveName, $directiveValue) = array_pad(explode(' ', $token, 2), 2, null);
if (! isset($header->directives[$directiveName])) {
$header->setDirective(
$directiveName,
$directiveValue === null ? [] : [$directiveValue]
);
}
}
}
return $header;
}
/**
* Get the header name
*
* @return string
*/
public function getFieldName()
{
return 'Feature-Policy';
}
/**
* Get the header value
*
* @return string
*/
public function getFieldValue()
{
$directives = [];
foreach ($this->directives as $name => $value) {
$directives[] = sprintf('%s %s;', $name, $value);
}
return implode(' ', $directives);
}
/**
* Return the header as a string
*
* @return string
*/
public function toString()
{
return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
}
}
PK %RK~ src/Header/WWWAuthenticate.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'WWW-Authenticate';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'WWW-Authenticate: ' . $this->getFieldValue();
}
public function toStringMultipleHeaders(array $headers)
{
$strings = [$this->toString()];
foreach ($headers as $header) {
if (! $header instanceof WWWAuthenticate) {
throw new Exception\RuntimeException(
'The WWWAuthenticate multiple header implementation can only'
. ' accept an array of WWWAuthenticate headers'
);
}
$strings[] = $header->toString();
}
return implode("\r\n", $strings);
}
}
PK %Rlg g src/Header/LastModified.phpnu ٘ getFieldValue();
}
/**
* Add a language, with the given priority
*
* @param string $type
* @param int|float $priority
* @return $this
*/
public function addLanguage($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested language?
*
* @param string $type
* @return bool
*/
public function hasLanguage($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Laminas\Http\Header\Accept\FieldValuePart\LanguageFieldValuePart
* @see \Laminas\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '-')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim(substr($fieldValuePart, 0));
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = $type = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '-')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '-') + 1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw),
];
return new FieldValuePart\LanguageFieldValuePart((object) $aggregated);
}
}
PK %Rzt ! src/Header/ProxyAuthorization.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Proxy-Authorization';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Proxy-Authorization: ' . $this->getFieldValue();
}
}
PK %RC src/Header/ContentEncoding.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Content-Encoding';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Content-Encoding: ' . $this->getFieldValue();
}
}
PK %R}J
J
src/Header/Connection.phpnu ٘ setValue(trim($value));
return $header;
}
/**
* Set Connection header to define persistent connection
*
* @param bool $flag
* @return $this
*/
public function setPersistent($flag)
{
$this->value = (bool) $flag
? self::CONNECTION_KEEP_ALIVE
: self::CONNECTION_CLOSE;
return $this;
}
/**
* Get whether this connection is persistent
*
* @return bool
*/
public function isPersistent()
{
return ($this->value === self::CONNECTION_KEEP_ALIVE);
}
/**
* Set arbitrary header value
* RFC allows any token as value, 'close' and 'keep-alive' are commonly used
*
* @param string $value
* @return $this
*/
public function setValue($value)
{
HeaderValue::assertValid($value);
$this->value = strtolower($value);
return $this;
}
/**
* Connection header name
*
* @return string
*/
public function getFieldName()
{
return 'Connection';
}
/**
* Connection header value
*
* @return string
*/
public function getFieldValue()
{
return $this->value;
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Connection: ' . $this->getFieldValue();
}
}
PK %R՝ src/Header/Pragma.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Pragma';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Pragma: ' . $this->getFieldValue();
}
}
PK %Rqکk k src/Header/Refresh.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Refresh';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Refresh: ' . $this->getFieldValue();
}
}
PK %R0 0 src/Header/CacheControl.phpnu ٘ $value) {
$header->addDirective($key, $value);
}
return $header;
}
/**
* Required from HeaderDescription interface
*
* @return string
*/
public function getFieldName()
{
return 'Cache-Control';
}
/**
* Checks if the internal directives array is empty
*
* @return bool
*/
public function isEmpty()
{
return empty($this->directives);
}
/**
* Add a directive
* For directives like 'max-age=60', $value = '60'
* For directives like 'private', use the default $value = true
*
* @param string $key
* @param string|bool $value
* @return $this
*/
public function addDirective($key, $value = true)
{
HeaderValue::assertValid($key);
if (! is_bool($value)) {
HeaderValue::assertValid($value);
}
$this->directives[$key] = $value;
return $this;
}
/**
* Check the internal directives array for a directive
*
* @param string $key
* @return bool
*/
public function hasDirective($key)
{
return array_key_exists($key, $this->directives);
}
/**
* Fetch the value of a directive from the internal directive array
*
* @param string $key
* @return string|null
*/
public function getDirective($key)
{
return array_key_exists($key, $this->directives) ? $this->directives[$key] : null;
}
/**
* Remove a directive
*
* @param string $key
* @return $this
*/
public function removeDirective($key)
{
unset($this->directives[$key]);
return $this;
}
/**
* Assembles the directives into a comma-delimited string
*
* @return string
*/
public function getFieldValue()
{
$parts = [];
ksort($this->directives);
foreach ($this->directives as $key => $value) {
if (true === $value) {
$parts[] = $key;
} else {
if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
$value = '"' . $value . '"';
}
$parts[] = $key . '=' . $value;
}
}
return implode(', ', $parts);
}
/**
* Returns a string representation of the HTTP Cache-Control header
*
* @return string
*/
public function toString()
{
return 'Cache-Control: ' . $this->getFieldValue();
}
/**
* Internal function for parsing the value part of a
* HTTP Cache-Control header
*
* @param string $value
* @throws Exception\InvalidArgumentException
* @return array
*/
protected static function parseValue($value)
{
$value = trim($value);
$directives = [];
// handle empty string early so we don't need a separate start state
if ($value == '') {
return $directives;
}
$lastMatch = null;
state_directive:
switch (static::match(['[a-zA-Z][a-zA-Z_-]*'], $value, $lastMatch)) {
case 0:
$directive = $lastMatch;
goto state_value;
// intentional fall-through
default:
throw new Exception\InvalidArgumentException('expected DIRECTIVE');
}
state_value:
switch (static::match(['="[^"]*"', '=[^",\s;]*'], $value, $lastMatch)) {
case 0:
$directives[$directive] = substr($lastMatch, 2, -1);
goto state_separator;
// intentional fall-through
case 1:
$directives[$directive] = rtrim(substr($lastMatch, 1));
goto state_separator;
// intentional fall-through
default:
$directives[$directive] = true;
goto state_separator;
}
state_separator:
switch (static::match(['\s*,\s*', '$'], $value, $lastMatch)) {
case 0:
goto state_directive;
// intentional fall-through
case 1:
return $directives;
default:
throw new Exception\InvalidArgumentException('expected SEPARATOR or END');
}
}
/**
* Internal function used by parseValue to match tokens
*
* @param array $tokens
* @param string $string
* @param string $lastMatch
* @return int
*/
protected static function match($tokens, &$string, &$lastMatch)
{
// Ensure we have a string
$value = (string) $string;
foreach ($tokens as $i => $token) {
if (preg_match('/^' . $token . '/', $value, $matches)) {
$lastMatch = $matches[0];
$string = substr($value, strlen($matches[0]));
return $i;
}
}
return -1;
}
}
PK %R;H src/Header/Via.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Via';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Via: ' . $this->getFieldValue();
}
}
PK %RUԛ src/Header/IfMatch.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'If-Match';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'If-Match: ' . $this->getFieldValue();
}
}
PK %R\ src/Header/IfRange.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'If-Range';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'If-Range: ' . $this->getFieldValue();
}
}
PK %RTdc src/Header/Server.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Server';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Server: ' . $this->getFieldValue();
}
}
PK %R|w w src/Header/ContentLocation.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Content-Language';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Content-Language: ' . $this->getFieldValue();
}
}
PK %RxP P src/Header/AcceptRanges.phpnu ٘ setRangeUnit($rangeUnit);
}
}
public function getFieldName()
{
return 'Accept-Ranges';
}
public function getFieldValue()
{
return $this->getRangeUnit();
}
public function setRangeUnit($rangeUnit)
{
HeaderValue::assertValid($rangeUnit);
$this->rangeUnit = $rangeUnit;
return $this;
}
public function getRangeUnit()
{
return (string) $this->rangeUnit;
}
public function toString()
{
return 'Accept-Ranges: ' . $this->getFieldValue();
}
}
PK %Rj2_ src/Header/Etag.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Etag';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Etag: ' . $this->getFieldValue();
}
}
PK %R` ! src/Header/GenericMultiHeader.phpnu ٘ getFieldName();
$values = [$this->getFieldValue()];
foreach ($headers as $header) {
if (! $header instanceof static) {
throw new Exception\InvalidArgumentException(
'This method toStringMultipleHeaders was expecting an array of headers of the same type'
);
}
$values[] = $header->getFieldValue();
}
return $name . ': ' . implode(',', $values) . "\r\n";
}
}
PK %Rw8 src/Header/TransferEncoding.phpnu ٘ value = $value;
}
}
public function getFieldName()
{
return 'Transfer-Encoding';
}
public function getFieldValue()
{
return (string) $this->value;
}
public function toString()
{
return 'Transfer-Encoding: ' . $this->getFieldValue();
}
}
PK %R[=e &