├── .ci
├── php5.6.ini
├── proxy.conf
└── site.conf
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── composer.json
└── src
├── AbstractMessage.php
├── Client.php
├── Client
├── Adapter
│ ├── AdapterInterface.php
│ ├── Curl.php
│ ├── Exception
│ │ ├── ExceptionInterface.php
│ │ ├── InitializationException.php
│ │ ├── InvalidArgumentException.php
│ │ ├── OutOfRangeException.php
│ │ ├── RuntimeException.php
│ │ └── TimeoutException.php
│ ├── Proxy.php
│ ├── Socket.php
│ ├── StreamInterface.php
│ └── Test.php
└── Exception
│ ├── ExceptionInterface.php
│ ├── InvalidArgumentException.php
│ ├── OutOfRangeException.php
│ └── RuntimeException.php
├── ClientStatic.php
├── Cookies.php
├── Exception
├── ExceptionInterface.php
├── InvalidArgumentException.php
├── OutOfRangeException.php
└── RuntimeException.php
├── Header
├── AbstractAccept.php
├── AbstractDate.php
├── AbstractLocation.php
├── Accept.php
├── Accept
│ └── FieldValuePart
│ │ ├── AbstractFieldValuePart.php
│ │ ├── AcceptFieldValuePart.php
│ │ ├── CharsetFieldValuePart.php
│ │ ├── EncodingFieldValuePart.php
│ │ └── LanguageFieldValuePart.php
├── AcceptCharset.php
├── AcceptEncoding.php
├── AcceptLanguage.php
├── AcceptRanges.php
├── Age.php
├── Allow.php
├── AuthenticationInfo.php
├── Authorization.php
├── CacheControl.php
├── Connection.php
├── ContentDisposition.php
├── ContentEncoding.php
├── ContentLanguage.php
├── ContentLength.php
├── ContentLocation.php
├── ContentMD5.php
├── ContentRange.php
├── ContentSecurityPolicy.php
├── ContentTransferEncoding.php
├── ContentType.php
├── Cookie.php
├── Date.php
├── Etag.php
├── Exception
│ ├── DomainException.php
│ ├── ExceptionInterface.php
│ ├── InvalidArgumentException.php
│ └── RuntimeException.php
├── Expect.php
├── Expires.php
├── FeaturePolicy.php
├── From.php
├── GenericHeader.php
├── GenericMultiHeader.php
├── HeaderInterface.php
├── HeaderValue.php
├── Host.php
├── IfMatch.php
├── IfModifiedSince.php
├── IfNoneMatch.php
├── IfRange.php
├── IfUnmodifiedSince.php
├── KeepAlive.php
├── LastModified.php
├── Location.php
├── MaxForwards.php
├── MultipleHeaderInterface.php
├── Origin.php
├── Pragma.php
├── ProxyAuthenticate.php
├── ProxyAuthorization.php
├── Range.php
├── Referer.php
├── Refresh.php
├── RetryAfter.php
├── Server.php
├── SetCookie.php
├── TE.php
├── Trailer.php
├── TransferEncoding.php
├── Upgrade.php
├── UserAgent.php
├── Vary.php
├── Via.php
├── WWWAuthenticate.php
└── Warning.php
├── HeaderLoader.php
├── Headers.php
├── PhpEnvironment
├── RemoteAddress.php
├── Request.php
└── Response.php
├── Request.php
├── Response.php
└── Response
└── Stream.php
/.ci/php5.6.ini:
--------------------------------------------------------------------------------
1 | always_populate_raw_post_data=-1
2 |
--------------------------------------------------------------------------------
/.ci/proxy.conf:
--------------------------------------------------------------------------------
1 |
2 | ProxyRequests On
3 |
4 | ErrorLog ${APACHE_LOG_DIR}/error.log
5 | CustomLog ${APACHE_LOG_DIR}/access.log combined
6 |
7 |
--------------------------------------------------------------------------------
/.ci/site.conf:
--------------------------------------------------------------------------------
1 |
2 | DocumentRoot %TRAVIS_BUILD_DIR%/test/Client/_files
3 |
4 |
5 | Options FollowSymLinks MultiViews ExecCGI
6 | AllowOverride All
7 | Require all granted
8 |
9 |
10 | # Wire up Apache to use Travis CI's php-fpm.
11 |
12 | AddHandler php%PHP_VERSION%-fcgi .php
13 | Action php%PHP_VERSION%-fcgi /php%PHP_VERSION%-fcgi
14 | Alias /php%PHP_VERSION%-fcgi /usr/lib/cgi-bin/php%PHP_VERSION%-fcgi
15 | FastCgiExternalServer /usr/lib/cgi-bin/php%PHP_VERSION%-fcgi -host 127.0.0.1:9000 -pass-header Authorization
16 |
17 |
18 | Require all granted
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2005-2019, Zend Technologies USA, Inc.
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | - Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | - Redistributions in binary form must reproduce the above copyright notice, this
11 | list of conditions and the following disclaimer in the documentation and/or
12 | other materials provided with the distribution.
13 |
14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from this
16 | software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zend-http
2 |
3 | > ## Repository abandoned 2019-12-31
4 | >
5 | > This repository has moved to [laminas/laminas-http](https://github.com/laminas/laminas-http).
6 |
7 | [](https://secure.travis-ci.org/zendframework/zend-http)
8 | [](https://coveralls.io/github/zendframework/zend-http?branch=master)
9 |
10 | zend-http provides the HTTP message abstraction used by
11 | [zend-mvc](https://docs.zendframework.com/zend-mvc/), and also provides an
12 | extensible, adapter-driven HTTP client library.
13 |
14 | This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as
15 | it predates that specification. For PSR-7 support, please see our
16 | [Diactoros component](https://docs.zendframework.com/zend-diactoros/).
17 |
18 | ## Installation
19 |
20 | Run the following to install this library:
21 |
22 | ```bash
23 | $ composer require zendframework/zend-http
24 | ```
25 |
26 | ## Documentation
27 |
28 | Browse the documentation online at https://docs.zendframework.com/zend-http/
29 |
30 | ## Support
31 |
32 | * [Issues](https://github.com/zendframework/zend-http/issues/)
33 | * [Chat](https://zendframework-slack.herokuapp.com/)
34 | * [Forum](https://discourse.zendframework.com/)
35 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zendframework/zend-http",
3 | "description": "Provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests",
4 | "license": "BSD-3-Clause",
5 | "keywords": [
6 | "zf",
7 | "zend",
8 | "zendframework",
9 | "http",
10 | "HTTP client"
11 | ],
12 | "support": {
13 | "docs": "https://docs.zendframework.com/zend-http/",
14 | "issues": "https://github.com/zendframework/zend-http/issues",
15 | "source": "https://github.com/zendframework/zend-http",
16 | "rss": "https://github.com/zendframework/zend-http/releases.atom",
17 | "chat": "https://zendframework-slack.herokuapp.com",
18 | "forum": "https://discourse.zendframework.com/c/questions/components"
19 | },
20 | "require": {
21 | "php": "^5.6 || ^7.0",
22 | "zendframework/zend-loader": "^2.5.1",
23 | "zendframework/zend-stdlib": "^3.2.1",
24 | "zendframework/zend-uri": "^2.5.2",
25 | "zendframework/zend-validator": "^2.10.1"
26 | },
27 | "require-dev": {
28 | "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.3",
29 | "zendframework/zend-coding-standard": "~1.0.0",
30 | "zendframework/zend-config": "^3.1 || ^2.6"
31 | },
32 | "suggest": {
33 | "paragonie/certainty": "For automated management of cacert.pem"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Zend\\Http\\": "src/"
38 | }
39 | },
40 | "autoload-dev": {
41 | "psr-4": {
42 | "ZendTest\\Http\\": "test/"
43 | }
44 | },
45 | "config": {
46 | "sort-packages": true
47 | },
48 | "extra": {
49 | "branch-alias": {
50 | "dev-master": "2.11.x-dev",
51 | "dev-develop": "2.12.x-dev"
52 | }
53 | },
54 | "scripts": {
55 | "check": [
56 | "@cs-check",
57 | "@test"
58 | ],
59 | "cs-check": "phpcs",
60 | "cs-fix": "phpcbf",
61 | "test": "phpunit --colors=always",
62 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/AbstractMessage.php:
--------------------------------------------------------------------------------
1 | version = $version;
53 | return $this;
54 | }
55 |
56 | /**
57 | * Return the HTTP version for this request
58 | *
59 | * @return string
60 | */
61 | public function getVersion()
62 | {
63 | return $this->version;
64 | }
65 |
66 | /**
67 | * Provide an alternate Parameter Container implementation for headers in this object,
68 | * (this is NOT the primary API for value setting, for that see getHeaders())
69 | *
70 | * @see getHeaders()
71 | * @param Headers $headers
72 | * @return $this
73 | */
74 | public function setHeaders(Headers $headers)
75 | {
76 | $this->headers = $headers;
77 | return $this;
78 | }
79 |
80 | /**
81 | * Return the header container responsible for headers
82 | *
83 | * @return Headers
84 | */
85 | public function getHeaders()
86 | {
87 | if ($this->headers === null || is_string($this->headers)) {
88 | // this is only here for fromString lazy loading
89 | $this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
90 | }
91 |
92 | return $this->headers;
93 | }
94 |
95 | /**
96 | * Allow PHP casting of this object
97 | *
98 | * @return string
99 | */
100 | public function __toString()
101 | {
102 | return $this->toString();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Client/Adapter/AdapterInterface.php:
--------------------------------------------------------------------------------
1 | false,
35 | 'ssltransport' => 'ssl',
36 | 'sslcert' => null,
37 | 'sslpassphrase' => null,
38 | 'sslverifypeer' => true,
39 | 'sslcafile' => null,
40 | 'sslcapath' => null,
41 | 'sslallowselfsigned' => false,
42 | 'sslusecontext' => false,
43 | 'sslverifypeername' => true,
44 | 'proxy_host' => '',
45 | 'proxy_port' => 8080,
46 | 'proxy_user' => '',
47 | 'proxy_pass' => '',
48 | 'proxy_auth' => Client::AUTH_BASIC,
49 | ];
50 |
51 | /**
52 | * Whether HTTPS CONNECT was already negotiated with the proxy or not
53 | *
54 | * @var bool
55 | */
56 | protected $negotiated = false;
57 |
58 | /**
59 | * Set the configuration array for the adapter
60 | *
61 | * @param array $options
62 | */
63 | public function setOptions($options = [])
64 | {
65 | if ($options instanceof Traversable) {
66 | $options = ArrayUtils::iteratorToArray($options);
67 | }
68 | if (! is_array($options)) {
69 | throw new AdapterException\InvalidArgumentException(
70 | 'Array or Zend\Config object expected, got ' . gettype($options)
71 | );
72 | }
73 |
74 | //enforcing that the proxy keys are set in the form proxy_*
75 | foreach ($options as $k => $v) {
76 | if (preg_match('/^proxy[a-z]+/', $k)) {
77 | $options['proxy_' . substr($k, 5, strlen($k))] = $v;
78 | unset($options[$k]);
79 | }
80 | }
81 |
82 | parent::setOptions($options);
83 | }
84 |
85 | /**
86 | * Connect to the remote server
87 | *
88 | * Will try to connect to the proxy server. If no proxy was set, will
89 | * fall back to the target server (behave like regular Socket adapter)
90 | *
91 | * @param string $host
92 | * @param int $port
93 | * @param bool $secure
94 | * @throws AdapterException\RuntimeException
95 | */
96 | public function connect($host, $port = 80, $secure = false)
97 | {
98 | // If no proxy is set, fall back to Socket adapter
99 | if (! $this->config['proxy_host']) {
100 | parent::connect($host, $port, $secure);
101 | return;
102 | }
103 |
104 | /* Url might require stream context even if proxy connection doesn't */
105 | if ($secure) {
106 | $this->config['sslusecontext'] = true;
107 | $this->setSslCryptoMethod = false;
108 | }
109 |
110 | // Connect (a non-secure connection) to the proxy server
111 | parent::connect(
112 | $this->config['proxy_host'],
113 | $this->config['proxy_port'],
114 | false
115 | );
116 | }
117 |
118 | /**
119 | * Send request to the proxy server
120 | *
121 | * @param string $method
122 | * @param \Zend\Uri\Uri $uri
123 | * @param string $httpVer
124 | * @param array $headers
125 | * @param string $body
126 | * @throws AdapterException\RuntimeException
127 | * @return string Request as string
128 | */
129 | public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
130 | {
131 | // If no proxy is set, fall back to default Socket adapter
132 | if (! $this->config['proxy_host']) {
133 | return parent::write($method, $uri, $httpVer, $headers, $body);
134 | }
135 |
136 | // Make sure we're properly connected
137 | if (! $this->socket) {
138 | throw new AdapterException\RuntimeException('Trying to write but we are not connected');
139 | }
140 |
141 | $host = $this->config['proxy_host'];
142 | $port = $this->config['proxy_port'];
143 |
144 | $isSecure = strtolower($uri->getScheme()) === 'https';
145 | $connectedHost = ($isSecure ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
146 |
147 | if ($this->connectedTo[1] !== $port || $this->connectedTo[0] !== $connectedHost) {
148 | throw new AdapterException\RuntimeException(
149 | 'Trying to write but we are connected to the wrong proxy server'
150 | );
151 | }
152 |
153 | // Add Proxy-Authorization header
154 | if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
155 | $headers['proxy-authorization'] = Client::encodeAuthHeader(
156 | $this->config['proxy_user'],
157 | $this->config['proxy_pass'],
158 | $this->config['proxy_auth']
159 | );
160 | }
161 |
162 | // if we are proxying HTTPS, preform CONNECT handshake with the proxy
163 | if ($isSecure && ! $this->negotiated) {
164 | $this->connectHandshake($uri->getHost(), $uri->getPort(), $httpVer, $headers);
165 | $this->negotiated = true;
166 | }
167 |
168 | // Save request method for later
169 | $this->method = $method;
170 |
171 | if ($uri->getUserInfo()) {
172 | $headers['Authorization'] = 'Basic ' . base64_encode($uri->getUserInfo());
173 | }
174 |
175 | $path = $uri->getPath();
176 | $query = $uri->getQuery();
177 | $path .= $query ? '?' . $query : '';
178 |
179 | if (! $this->negotiated) {
180 | $path = $uri->getScheme() . '://' . $uri->getHost() . $path;
181 | }
182 |
183 | // Build request headers
184 | $request = sprintf('%s %s HTTP/%s%s', $method, $path, $httpVer, "\r\n");
185 |
186 | // Add all headers to the request string
187 | foreach ($headers as $k => $v) {
188 | if (is_string($k)) {
189 | $v = $k . ': ' . $v;
190 | }
191 | $request .= $v . "\r\n";
192 | }
193 |
194 | if (is_resource($body)) {
195 | $request .= "\r\n";
196 | } else {
197 | // Add the request body
198 | $request .= "\r\n" . $body;
199 | }
200 |
201 | // Send the request
202 | ErrorHandler::start();
203 | $test = fwrite($this->socket, $request);
204 | $error = ErrorHandler::stop();
205 | if ($test === false) {
206 | throw new AdapterException\RuntimeException('Error writing request to proxy server', 0, $error);
207 | }
208 |
209 | if (is_resource($body)) {
210 | if (stream_copy_to_stream($body, $this->socket) == 0) {
211 | throw new AdapterException\RuntimeException('Error writing request to server');
212 | }
213 | }
214 |
215 | return $request;
216 | }
217 |
218 | /**
219 | * Preform handshaking with HTTPS proxy using CONNECT method
220 | *
221 | * @param string $host
222 | * @param int $port
223 | * @param string $httpVer
224 | * @param array $headers
225 | * @throws AdapterException\RuntimeException
226 | */
227 | protected function connectHandshake($host, $port = 443, $httpVer = '1.1', array &$headers = [])
228 | {
229 | $request = 'CONNECT ' . $host . ':' . $port . ' HTTP/' . $httpVer . "\r\n"
230 | . 'Host: ' . $host . "\r\n";
231 |
232 | // Add the user-agent header
233 | if (isset($this->config['useragent'])) {
234 | $request .= 'User-agent: ' . $this->config['useragent'] . "\r\n";
235 | }
236 |
237 | // If the proxy-authorization header is set, send it to proxy but remove
238 | // it from headers sent to target host
239 | if (isset($headers['proxy-authorization'])) {
240 | $request .= 'Proxy-authorization: ' . $headers['proxy-authorization'] . "\r\n";
241 | unset($headers['proxy-authorization']);
242 | }
243 |
244 | $request .= "\r\n";
245 |
246 | // Send the request
247 | ErrorHandler::start();
248 | $test = fwrite($this->socket, $request);
249 | $error = ErrorHandler::stop();
250 | if (! $test) {
251 | throw new AdapterException\RuntimeException('Error writing request to proxy server', 0, $error);
252 | }
253 |
254 | // Read response headers only
255 | $response = '';
256 | $gotStatus = false;
257 | ErrorHandler::start();
258 | while ($line = fgets($this->socket)) {
259 | $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
260 | if ($gotStatus) {
261 | $response .= $line;
262 | if (! rtrim($line)) {
263 | break;
264 | }
265 | }
266 | }
267 | ErrorHandler::stop();
268 |
269 | // Check that the response from the proxy is 200
270 | if (Response::fromString($response)->getStatusCode() != 200) {
271 | throw new AdapterException\RuntimeException(sprintf(
272 | 'Unable to connect to HTTPS proxy. Server response: %s',
273 | $response
274 | ));
275 | }
276 |
277 | // If all is good, switch socket to secure mode. We have to fall back
278 | // through the different modes
279 | $modes = [
280 | STREAM_CRYPTO_METHOD_TLS_CLIENT,
281 | STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
282 | STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
283 | STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
284 | ];
285 |
286 | $success = false;
287 | foreach ($modes as $mode) {
288 | $success = stream_socket_enable_crypto($this->socket, true, $mode);
289 | if ($success) {
290 | break;
291 | }
292 | }
293 |
294 | if (! $success) {
295 | throw new AdapterException\RuntimeException(
296 | 'Unable to connect to HTTPS server through proxy: could not negotiate secure connection.'
297 | );
298 | }
299 | }
300 |
301 | /**
302 | * Close the connection to the server
303 | */
304 | public function close()
305 | {
306 | parent::close();
307 | $this->negotiated = false;
308 | }
309 |
310 | /**
311 | * Destructor: make sure the socket is disconnected
312 | */
313 | public function __destruct()
314 | {
315 | if ($this->socket) {
316 | $this->close();
317 | }
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/src/Client/Adapter/StreamInterface.php:
--------------------------------------------------------------------------------
1 | nextRequestWillFail = (bool) $flag;
69 |
70 | return $this;
71 | }
72 |
73 | /**
74 | * Set the configuration array for the adapter
75 | *
76 | * @param array|Traversable $options
77 | * @throws Exception\InvalidArgumentException
78 | */
79 | public function setOptions($options = [])
80 | {
81 | if ($options instanceof Traversable) {
82 | $options = ArrayUtils::iteratorToArray($options);
83 | }
84 |
85 | if (! is_array($options)) {
86 | throw new Exception\InvalidArgumentException(
87 | 'Array or Traversable object expected, got ' . gettype($options)
88 | );
89 | }
90 |
91 | foreach ($options as $k => $v) {
92 | $this->config[strtolower($k)] = $v;
93 | }
94 | }
95 |
96 |
97 | /**
98 | * Connect to the remote server
99 | *
100 | * @param string $host
101 | * @param int $port
102 | * @param bool $secure
103 | * @throws Exception\RuntimeException
104 | */
105 | public function connect($host, $port = 80, $secure = false)
106 | {
107 | if ($this->nextRequestWillFail) {
108 | $this->nextRequestWillFail = false;
109 | throw new Exception\RuntimeException('Request failed');
110 | }
111 | }
112 |
113 | /**
114 | * Send request to the remote server
115 | *
116 | * @param string $method
117 | * @param \Zend\Uri\Uri $uri
118 | * @param string $httpVer
119 | * @param array $headers
120 | * @param string $body
121 | * @return string Request as string
122 | */
123 | public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
124 | {
125 | // Build request headers
126 | $path = $uri->getPath();
127 | if (empty($path)) {
128 | $path = '/';
129 | }
130 | $query = $uri->getQuery();
131 | $path .= $query ? '?' . $query : '';
132 | $request = $method . ' ' . $path . ' HTTP/' . $httpVer . "\r\n";
133 | foreach ($headers as $k => $v) {
134 | if (is_string($k)) {
135 | $v = $k . ': ' . $v;
136 | }
137 | $request .= $v . "\r\n";
138 | }
139 |
140 | // Add the request body
141 | $request .= "\r\n" . $body;
142 |
143 | // Do nothing - just return the request as string
144 |
145 | return $request;
146 | }
147 |
148 | /**
149 | * Return the response set in $this->setResponse()
150 | *
151 | * @return string
152 | */
153 | public function read()
154 | {
155 | if ($this->responseIndex >= count($this->responses)) {
156 | $this->responseIndex = 0;
157 | }
158 | return $this->responses[$this->responseIndex++];
159 | }
160 |
161 | /**
162 | * Close the connection (dummy)
163 | *
164 | */
165 | public function close()
166 | {
167 | }
168 |
169 | /**
170 | * Set the HTTP response(s) to be returned by this adapter
171 | *
172 | * @param \Zend\Http\Response|array|string $response
173 | */
174 | public function setResponse($response)
175 | {
176 | if ($response instanceof Response) {
177 | $response = $response->toString();
178 | }
179 |
180 | $this->responses = (array) $response;
181 | $this->responseIndex = 0;
182 | }
183 |
184 | /**
185 | * Add another response to the response buffer.
186 | *
187 | * @param string|Response $response
188 | */
189 | public function addResponse($response)
190 | {
191 | if ($response instanceof Response) {
192 | $response = $response->toString();
193 | }
194 |
195 | $this->responses[] = $response;
196 | }
197 |
198 | /**
199 | * Sets the position of the response buffer. Selects which
200 | * response will be returned on the next call to read().
201 | *
202 | * @param int $index
203 | * @throws Exception\OutOfRangeException
204 | */
205 | public function setResponseIndex($index)
206 | {
207 | if ($index < 0 || $index >= count($this->responses)) {
208 | throw new Exception\OutOfRangeException(
209 | 'Index out of range of response buffer size'
210 | );
211 | }
212 | $this->responseIndex = $index;
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/Client/Exception/ExceptionInterface.php:
--------------------------------------------------------------------------------
1 | setUri($url);
52 | $request->setMethod(Request::METHOD_GET);
53 |
54 | if (! empty($query) && is_array($query)) {
55 | $request->getQuery()->fromArray($query);
56 | }
57 |
58 | if (! empty($headers) && is_array($headers)) {
59 | $request->getHeaders()->addHeaders($headers);
60 | }
61 |
62 | if (! empty($body)) {
63 | $request->setContent($body);
64 | }
65 |
66 | return static::getStaticClient($clientOptions)->send($request);
67 | }
68 |
69 | /**
70 | * HTTP POST METHOD (static)
71 | *
72 | * @param string $url
73 | * @param array $params
74 | * @param array $headers
75 | * @param mixed $body
76 | * @param array|Traversable $clientOptions
77 | * @throws Exception\InvalidArgumentException
78 | * @return Response|bool
79 | */
80 | public static function post($url, $params, $headers = [], $body = null, $clientOptions = null)
81 | {
82 | if (empty($url)) {
83 | return false;
84 | }
85 |
86 | $request = new Request();
87 | $request->setUri($url);
88 | $request->setMethod(Request::METHOD_POST);
89 |
90 | if (! empty($params) && is_array($params)) {
91 | $request->getPost()->fromArray($params);
92 | } else {
93 | throw new Exception\InvalidArgumentException('The array of post parameters is empty');
94 | }
95 |
96 | if (! isset($headers['Content-Type'])) {
97 | $headers['Content-Type'] = Client::ENC_URLENCODED;
98 | }
99 |
100 | if (! empty($headers) && is_array($headers)) {
101 | $request->getHeaders()->addHeaders($headers);
102 | }
103 |
104 | if (! empty($body)) {
105 | $request->setContent($body);
106 | }
107 |
108 | return static::getStaticClient($clientOptions)->send($request);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Exception/ExceptionInterface.php:
--------------------------------------------------------------------------------
1 | 'D, d M Y H:i:s \G\M\T',
60 | self::DATE_RFC1036 => 'D, d M y H:i:s \G\M\T',
61 | self::DATE_ANSIC => 'D M j H:i:s Y',
62 | ];
63 |
64 | /**
65 | * Create date-based header from string
66 | *
67 | * @param string $headerLine
68 | * @return static
69 | * @throws Exception\InvalidArgumentException
70 | */
71 | public static function fromString($headerLine)
72 | {
73 | $dateHeader = new static();
74 |
75 | list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
76 |
77 | // check to ensure proper header type for this factory
78 | if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
79 | throw new Exception\InvalidArgumentException(
80 | 'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
81 | );
82 | }
83 |
84 | $dateHeader->setDate($date);
85 |
86 | return $dateHeader;
87 | }
88 |
89 | /**
90 | * Create date-based header from strtotime()-compatible string
91 | *
92 | * @param int|string $time
93 | * @return static
94 | * @throws Exception\InvalidArgumentException
95 | */
96 | public static function fromTimeString($time)
97 | {
98 | return static::fromTimestamp(strtotime($time));
99 | }
100 |
101 | /**
102 | * Create date-based header from Unix timestamp
103 | *
104 | * @param int $time
105 | * @return static
106 | * @throws Exception\InvalidArgumentException
107 | */
108 | public static function fromTimestamp($time)
109 | {
110 | $dateHeader = new static();
111 |
112 | if (! $time || ! is_numeric($time)) {
113 | throw new Exception\InvalidArgumentException(
114 | 'Invalid time for "' . $dateHeader->getFieldName() . '" header string'
115 | );
116 | }
117 |
118 | $dateHeader->setDate(new DateTime('@' . $time));
119 |
120 | return $dateHeader;
121 | }
122 |
123 | /**
124 | * Set date output format
125 | *
126 | * @param int $format
127 | * @throws Exception\InvalidArgumentException
128 | */
129 | public static function setDateFormat($format)
130 | {
131 | if (! isset(static::$dateFormats[$format])) {
132 | throw new Exception\InvalidArgumentException(sprintf(
133 | 'No constant defined for provided date format: %s',
134 | $format
135 | ));
136 | }
137 |
138 | static::$dateFormat = static::$dateFormats[$format];
139 | }
140 |
141 | /**
142 | * Return current date output format
143 | *
144 | * @return string
145 | */
146 | public static function getDateFormat()
147 | {
148 | return static::$dateFormat;
149 | }
150 |
151 | /**
152 | * Set the date for this header, this can be a string or an instance of \DateTime
153 | *
154 | * @param string|DateTime $date
155 | * @return $this
156 | * @throws Exception\InvalidArgumentException
157 | */
158 | public function setDate($date)
159 | {
160 | if (is_string($date)) {
161 | try {
162 | $date = new DateTime($date, new DateTimeZone('GMT'));
163 | } catch (\Exception $e) {
164 | throw new Exception\InvalidArgumentException(
165 | sprintf('Invalid date passed as string (%s)', (string) $date),
166 | $e->getCode(),
167 | $e
168 | );
169 | }
170 | } elseif (! ($date instanceof DateTime)) {
171 | throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
172 | }
173 |
174 | $date->setTimezone(new DateTimeZone('GMT'));
175 | $this->date = $date;
176 |
177 | return $this;
178 | }
179 |
180 | /**
181 | * Return date for this header
182 | *
183 | * @return string
184 | */
185 | public function getDate()
186 | {
187 | return $this->date()->format(static::$dateFormat);
188 | }
189 |
190 | /**
191 | * Return date for this header as an instance of \DateTime
192 | *
193 | * @return DateTime
194 | */
195 | public function date()
196 | {
197 | if ($this->date === null) {
198 | $this->date = new DateTime(null, new DateTimeZone('GMT'));
199 | }
200 | return $this->date;
201 | }
202 |
203 | /**
204 | * Compare provided date to date for this header
205 | * Returns < 0 if date in header is less than $date; > 0 if it's greater, and 0 if they are equal.
206 | * @see \strcmp()
207 | *
208 | * @param string|DateTime $date
209 | * @return int
210 | * @throws Exception\InvalidArgumentException
211 | */
212 | public function compareTo($date)
213 | {
214 | if (is_string($date)) {
215 | try {
216 | $date = new DateTime($date, new DateTimeZone('GMT'));
217 | } catch (\Exception $e) {
218 | throw new Exception\InvalidArgumentException(
219 | sprintf('Invalid Date passed as string (%s)', (string) $date),
220 | $e->getCode(),
221 | $e
222 | );
223 | }
224 | } elseif (! ($date instanceof DateTime)) {
225 | throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
226 | }
227 |
228 | $dateTimestamp = $date->getTimestamp();
229 | $thisTimestamp = $this->date()->getTimestamp();
230 |
231 | return ($thisTimestamp === $dateTimestamp) ? 0 : (($thisTimestamp > $dateTimestamp) ? 1 : -1);
232 | }
233 |
234 | /**
235 | * Get header value as formatted date
236 | *
237 | * @return string
238 | */
239 | public function getFieldValue()
240 | {
241 | return $this->getDate();
242 | }
243 |
244 | /**
245 | * Return header line
246 | *
247 | * @return string
248 | */
249 | public function toString()
250 | {
251 | return $this->getFieldName() . ': ' . $this->getDate();
252 | }
253 |
254 | /**
255 | * Allow casting to string
256 | *
257 | * @return string
258 | */
259 | public function __toString()
260 | {
261 | return $this->toString();
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/src/Header/AbstractLocation.php:
--------------------------------------------------------------------------------
1 | getFieldName())) {
50 | throw new Exception\InvalidArgumentException(
51 | 'Invalid header line for "' . $locationHeader->getFieldName() . '" header string'
52 | );
53 | }
54 |
55 | HeaderValue::assertValid($uri);
56 | $locationHeader->setUri(trim($uri));
57 |
58 | return $locationHeader;
59 | }
60 |
61 | /**
62 | * Set the URI/URL for this header, this can be a string or an instance of Zend\Uri\Http
63 | *
64 | * @param string|UriInterface $uri
65 | * @return $this
66 | * @throws Exception\InvalidArgumentException
67 | */
68 | public function setUri($uri)
69 | {
70 | if (is_string($uri)) {
71 | try {
72 | $uri = UriFactory::factory($uri);
73 | } catch (UriException\InvalidUriPartException $e) {
74 | throw new Exception\InvalidArgumentException(
75 | sprintf('Invalid URI passed as string (%s)', (string) $uri),
76 | $e->getCode(),
77 | $e
78 | );
79 | } catch (UriException\InvalidArgumentException $e) {
80 | throw new Exception\InvalidArgumentException(
81 | sprintf('Invalid URI passed as string (%s)', (string) $uri),
82 | $e->getCode(),
83 | $e
84 | );
85 | }
86 | } elseif (! ($uri instanceof UriInterface)) {
87 | throw new Exception\InvalidArgumentException('URI must be an instance of Zend\Uri\Http or a string');
88 | }
89 | $this->uri = $uri;
90 |
91 | return $this;
92 | }
93 |
94 | /**
95 | * Return the URI for this header
96 | *
97 | * @return string
98 | */
99 | public function getUri()
100 | {
101 | if ($this->uri instanceof UriInterface) {
102 | return $this->uri->toString();
103 | }
104 | return $this->uri;
105 | }
106 |
107 | /**
108 | * Return the URI for this header as an instance of Zend\Uri\Http
109 | *
110 | * @return UriInterface
111 | */
112 | public function uri()
113 | {
114 | if ($this->uri === null || is_string($this->uri)) {
115 | $this->uri = UriFactory::factory($this->uri);
116 | }
117 | return $this->uri;
118 | }
119 |
120 | /**
121 | * Get header value as URI string
122 | *
123 | * @return string
124 | */
125 | public function getFieldValue()
126 | {
127 | return $this->getUri();
128 | }
129 |
130 | /**
131 | * Output header line
132 | *
133 | * @return string
134 | */
135 | public function toString()
136 | {
137 | return $this->getFieldName() . ': ' . $this->getUri();
138 | }
139 |
140 | /**
141 | * Allow casting to string
142 | *
143 | * @return string
144 | */
145 | public function __toString()
146 | {
147 | return $this->toString();
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/Header/Accept.php:
--------------------------------------------------------------------------------
1 | getFieldValue();
42 | }
43 |
44 | /**
45 | * Add a media type, with the given priority
46 | *
47 | * @param string $type
48 | * @param int|float $priority
49 | * @param array $params
50 | * @return $this
51 | */
52 | public function addMediaType($type, $priority = 1, array $params = [])
53 | {
54 | return $this->addType($type, $priority, $params);
55 | }
56 |
57 | /**
58 | * Does the header have the requested media type?
59 | *
60 | * @param string $type
61 | * @return bool
62 | */
63 | public function hasMediaType($type)
64 | {
65 | return $this->hasType($type);
66 | }
67 |
68 | /**
69 | * Parse the keys contained in the header line
70 | *
71 | * @param string $fieldValuePart
72 | * @return FieldValuePart\AcceptFieldValuePart
73 | * @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
74 | */
75 | protected function parseFieldValuePart($fieldValuePart)
76 | {
77 | $raw = $fieldValuePart;
78 | if ($pos = strpos($fieldValuePart, '/')) {
79 | $type = trim(substr($fieldValuePart, 0, $pos));
80 | } else {
81 | $type = trim($fieldValuePart);
82 | }
83 |
84 | $params = $this->getParametersFromFieldValuePart($fieldValuePart);
85 |
86 | if ($pos = strpos($fieldValuePart, ';')) {
87 | $fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
88 | }
89 |
90 | if (strpos($fieldValuePart, '/')) {
91 | $subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '/') + 1));
92 | } else {
93 | $subtypeWhole = '';
94 | $format = '*';
95 | $subtype = '*';
96 | }
97 |
98 | $pos = strpos($subtype, '+');
99 | if (false !== $pos) {
100 | $format = trim(substr($subtype, $pos + 1));
101 | $subtype = trim(substr($subtype, 0, $pos));
102 | }
103 |
104 | $aggregated = [
105 | 'typeString' => trim($fieldValuePart),
106 | 'type' => $type,
107 | 'subtype' => $subtype,
108 | 'subtypeRaw' => $subtypeWhole,
109 | 'format' => $format,
110 | 'priority' => isset($params['q']) ? $params['q'] : 1,
111 | 'params' => $params,
112 | 'raw' => trim($raw),
113 | ];
114 |
115 | return new FieldValuePart\AcceptFieldValuePart((object) $aggregated);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/Header/Accept/FieldValuePart/AbstractFieldValuePart.php:
--------------------------------------------------------------------------------
1 | internalValues = $internalValues;
35 | }
36 |
37 | /**
38 | * Set a Field Value Part this Field Value Part matched against.
39 | *
40 | * @param AbstractFieldValuePart $matchedAgainst
41 | * @return $this
42 | */
43 | public function setMatchedAgainst(AbstractFieldValuePart $matchedAgainst)
44 | {
45 | $this->matchedAgainst = $matchedAgainst;
46 | return $this;
47 | }
48 |
49 | /**
50 | * Get a Field Value Part this Field Value Part matched against.
51 | *
52 | * @return AbstractFieldValuePart|null
53 | */
54 | public function getMatchedAgainst()
55 | {
56 | return $this->matchedAgainst;
57 | }
58 |
59 | /**
60 | * @return object
61 | */
62 | protected function getInternalValues()
63 | {
64 | return $this->internalValues;
65 | }
66 |
67 | /**
68 | * @return string $typeString
69 | */
70 | public function getTypeString()
71 | {
72 | return $this->getInternalValues()->typeString;
73 | }
74 |
75 | /**
76 | * @return float $priority
77 | */
78 | public function getPriority()
79 | {
80 | return (float) $this->getInternalValues()->priority;
81 | }
82 |
83 | /**
84 | * @return \stdClass $params
85 | */
86 | public function getParams()
87 | {
88 | return (object) $this->getInternalValues()->params;
89 | }
90 |
91 | /**
92 | * @return string $raw
93 | */
94 | public function getRaw()
95 | {
96 | return $this->getInternalValues()->raw;
97 | }
98 |
99 | /**
100 | * @param mixed $key
101 | * @return mixed
102 | */
103 | public function __get($key)
104 | {
105 | return $this->getInternalValues()->$key;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/Header/Accept/FieldValuePart/AcceptFieldValuePart.php:
--------------------------------------------------------------------------------
1 | getInternalValues()->subtype;
23 | }
24 |
25 | /**
26 | * @return string
27 | */
28 | public function getSubtypeRaw()
29 | {
30 | return $this->getInternalValues()->subtypeRaw;
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getFormat()
37 | {
38 | return $this->getInternalValues()->format;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Header/Accept/FieldValuePart/CharsetFieldValuePart.php:
--------------------------------------------------------------------------------
1 | getInternalValues()->type;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Header/Accept/FieldValuePart/EncodingFieldValuePart.php:
--------------------------------------------------------------------------------
1 | getInternalValues()->type;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Header/Accept/FieldValuePart/LanguageFieldValuePart.php:
--------------------------------------------------------------------------------
1 | getInternalValues()->typeString;
20 | }
21 |
22 | public function getPrimaryTag()
23 | {
24 | return $this->getInternalValues()->type;
25 | }
26 |
27 | public function getSubTag()
28 | {
29 | return $this->getInternalValues()->subtype;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Header/AcceptCharset.php:
--------------------------------------------------------------------------------
1 | getFieldValue();
39 | }
40 |
41 | /**
42 | * Add a charset, with the given priority
43 | *
44 | * @param string $type
45 | * @param int|float $priority
46 | * @return $this
47 | */
48 | public function addCharset($type, $priority = 1)
49 | {
50 | return $this->addType($type, $priority);
51 | }
52 |
53 | /**
54 | * Does the header have the requested charset?
55 | *
56 | * @param string $type
57 | * @return bool
58 | */
59 | public function hasCharset($type)
60 | {
61 | return $this->hasType($type);
62 | }
63 |
64 | /**
65 | * Parse the keys contained in the header line
66 | *
67 | * @param string $fieldValuePart
68 | * @return \Zend\Http\Header\Accept\FieldValuePart\CharsetFieldValuePart
69 | * @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
70 | */
71 | protected function parseFieldValuePart($fieldValuePart)
72 | {
73 | $internalValues = parent::parseFieldValuePart($fieldValuePart);
74 |
75 | return new FieldValuePart\CharsetFieldValuePart($internalValues);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Header/AcceptEncoding.php:
--------------------------------------------------------------------------------
1 | getFieldValue();
39 | }
40 |
41 | /**
42 | * Add an encoding, with the given priority
43 | *
44 | * @param string $type
45 | * @param int|float $priority
46 | * @return $this
47 | */
48 | public function addEncoding($type, $priority = 1)
49 | {
50 | return $this->addType($type, $priority);
51 | }
52 |
53 | /**
54 | * Does the header have the requested encoding?
55 | *
56 | * @param string $type
57 | * @return bool
58 | */
59 | public function hasEncoding($type)
60 | {
61 | return $this->hasType($type);
62 | }
63 |
64 | /**
65 | * Parse the keys contained in the header line
66 | *
67 | * @param string $fieldValuePart
68 | * @return \Zend\Http\Header\Accept\FieldValuePart\EncodingFieldValuePart
69 | * @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
70 | */
71 | protected function parseFieldValuePart($fieldValuePart)
72 | {
73 | $internalValues = parent::parseFieldValuePart($fieldValuePart);
74 |
75 | return new FieldValuePart\EncodingFieldValuePart($internalValues);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Header/AcceptLanguage.php:
--------------------------------------------------------------------------------
1 | getFieldValue();
39 | }
40 |
41 | /**
42 | * Add a language, with the given priority
43 | *
44 | * @param string $type
45 | * @param int|float $priority
46 | * @return $this
47 | */
48 | public function addLanguage($type, $priority = 1)
49 | {
50 | return $this->addType($type, $priority);
51 | }
52 |
53 | /**
54 | * Does the header have the requested language?
55 | *
56 | * @param string $type
57 | * @return bool
58 | */
59 | public function hasLanguage($type)
60 | {
61 | return $this->hasType($type);
62 | }
63 |
64 | /**
65 | * Parse the keys contained in the header line
66 | *
67 | * @param string $fieldValuePart
68 | * @return \Zend\Http\Header\Accept\FieldValuePart\LanguageFieldValuePart
69 | * @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
70 | */
71 | protected function parseFieldValuePart($fieldValuePart)
72 | {
73 | $raw = $fieldValuePart;
74 | if ($pos = strpos($fieldValuePart, '-')) {
75 | $type = trim(substr($fieldValuePart, 0, $pos));
76 | } else {
77 | $type = trim(substr($fieldValuePart, 0));
78 | }
79 |
80 | $params = $this->getParametersFromFieldValuePart($fieldValuePart);
81 |
82 | if ($pos = strpos($fieldValuePart, ';')) {
83 | $fieldValuePart = $type = trim(substr($fieldValuePart, 0, $pos));
84 | }
85 |
86 | if (strpos($fieldValuePart, '-')) {
87 | $subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '-') + 1));
88 | } else {
89 | $subtypeWhole = '';
90 | $format = '*';
91 | $subtype = '*';
92 | }
93 |
94 | $aggregated = [
95 | 'typeString' => trim($fieldValuePart),
96 | 'type' => $type,
97 | 'subtype' => $subtype,
98 | 'subtypeRaw' => $subtypeWhole,
99 | 'format' => $format,
100 | 'priority' => isset($params['q']) ? $params['q'] : 1,
101 | 'params' => $params,
102 | 'raw' => trim($raw),
103 | ];
104 |
105 | return new FieldValuePart\LanguageFieldValuePart((object) $aggregated);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/Header/AcceptRanges.php:
--------------------------------------------------------------------------------
1 | setRangeUnit($rangeUnit);
37 | }
38 | }
39 |
40 | public function getFieldName()
41 | {
42 | return 'Accept-Ranges';
43 | }
44 |
45 | public function getFieldValue()
46 | {
47 | return $this->getRangeUnit();
48 | }
49 |
50 | public function setRangeUnit($rangeUnit)
51 | {
52 | HeaderValue::assertValid($rangeUnit);
53 | $this->rangeUnit = $rangeUnit;
54 | return $this;
55 | }
56 |
57 | public function getRangeUnit()
58 | {
59 | return (string) $this->rangeUnit;
60 | }
61 |
62 | public function toString()
63 | {
64 | return 'Accept-Ranges: ' . $this->getFieldValue();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Header/Age.php:
--------------------------------------------------------------------------------
1 | setDeltaSeconds($deltaSeconds);
47 | }
48 | }
49 |
50 | /**
51 | * Get header name
52 | *
53 | * @return string
54 | */
55 | public function getFieldName()
56 | {
57 | return 'Age';
58 | }
59 |
60 | /**
61 | * Get header value (number of seconds)
62 | *
63 | * @return string
64 | */
65 | public function getFieldValue()
66 | {
67 | return (string) $this->getDeltaSeconds();
68 | }
69 |
70 | /**
71 | * Set number of seconds
72 | *
73 | * @param int $delta
74 | * @return $this
75 | */
76 | public function setDeltaSeconds($delta)
77 | {
78 | if (! is_int($delta) && ! is_numeric($delta)) {
79 | throw new Exception\InvalidArgumentException('Invalid delta provided');
80 | }
81 | $this->deltaSeconds = (int) $delta;
82 | return $this;
83 | }
84 |
85 | /**
86 | * Get number of seconds
87 | *
88 | * @return int
89 | */
90 | public function getDeltaSeconds()
91 | {
92 | return $this->deltaSeconds;
93 | }
94 |
95 | /**
96 | * Return header line
97 | * In case of overflow RFC states to set value of 2147483648 (2^31)
98 | *
99 | * @return string
100 | */
101 | public function toString()
102 | {
103 | return 'Age: ' . (($this->deltaSeconds >= PHP_INT_MAX) ? '2147483648' : $this->deltaSeconds);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Header/Allow.php:
--------------------------------------------------------------------------------
1 | false,
28 | Request::METHOD_GET => true,
29 | Request::METHOD_HEAD => false,
30 | Request::METHOD_POST => true,
31 | Request::METHOD_PUT => false,
32 | Request::METHOD_DELETE => false,
33 | Request::METHOD_TRACE => false,
34 | Request::METHOD_CONNECT => false,
35 | Request::METHOD_PATCH => false,
36 | ];
37 |
38 | /**
39 | * Create Allow header from header line
40 | *
41 | * @param string $headerLine
42 | * @return static
43 | * @throws Exception\InvalidArgumentException
44 | */
45 | public static function fromString($headerLine)
46 | {
47 | list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
48 |
49 | // check to ensure proper header type for this factory
50 | if (strtolower($name) !== 'allow') {
51 | throw new Exception\InvalidArgumentException('Invalid header line for Allow string: "' . $name . '"');
52 | }
53 |
54 | $header = new static();
55 | $header->disallowMethods(array_keys($header->getAllMethods()));
56 | $header->allowMethods(explode(',', $value));
57 |
58 | return $header;
59 | }
60 |
61 | /**
62 | * Get header name
63 | *
64 | * @return string
65 | */
66 | public function getFieldName()
67 | {
68 | return 'Allow';
69 | }
70 |
71 | /**
72 | * Get comma-separated list of allowed methods
73 | *
74 | * @return string
75 | */
76 | public function getFieldValue()
77 | {
78 | return implode(', ', array_keys($this->methods, true, true));
79 | }
80 |
81 | /**
82 | * Get list of all defined methods
83 | *
84 | * @return array
85 | */
86 | public function getAllMethods()
87 | {
88 | return $this->methods;
89 | }
90 |
91 | /**
92 | * Get list of allowed methods
93 | *
94 | * @return array
95 | */
96 | public function getAllowedMethods()
97 | {
98 | return array_keys($this->methods, true, true);
99 | }
100 |
101 | /**
102 | * Allow methods or list of methods
103 | *
104 | * @param array|string $allowedMethods
105 | * @return $this
106 | */
107 | public function allowMethods($allowedMethods)
108 | {
109 | foreach ((array) $allowedMethods as $method) {
110 | $method = trim(strtoupper($method));
111 | if (preg_match('/\s/', $method)) {
112 | throw new Exception\InvalidArgumentException(sprintf(
113 | 'Unable to whitelist method; "%s" is not a valid method',
114 | $method
115 | ));
116 | }
117 | $this->methods[$method] = true;
118 | }
119 |
120 | return $this;
121 | }
122 |
123 | /**
124 | * Disallow methods or list of methods
125 | *
126 | * @param array|string $disallowedMethods
127 | * @return $this
128 | */
129 | public function disallowMethods($disallowedMethods)
130 | {
131 | foreach ((array) $disallowedMethods as $method) {
132 | $method = trim(strtoupper($method));
133 | if (preg_match('/\s/', $method)) {
134 | throw new Exception\InvalidArgumentException(sprintf(
135 | 'Unable to blacklist method; "%s" is not a valid method',
136 | $method
137 | ));
138 | }
139 | $this->methods[$method] = false;
140 | }
141 |
142 | return $this;
143 | }
144 |
145 | /**
146 | * Convenience alias for @see disallowMethods()
147 | *
148 | * @param array|string $disallowedMethods
149 | * @return $this
150 | */
151 | public function denyMethods($disallowedMethods)
152 | {
153 | return $this->disallowMethods($disallowedMethods);
154 | }
155 |
156 | /**
157 | * Check whether method is allowed
158 | *
159 | * @param string $method
160 | * @return bool
161 | */
162 | public function isAllowedMethod($method)
163 | {
164 | $method = trim(strtoupper($method));
165 |
166 | // disallow unknown method
167 | if (! isset($this->methods[$method])) {
168 | $this->methods[$method] = false;
169 | }
170 |
171 | return $this->methods[$method];
172 | }
173 |
174 | /**
175 | * Return header as string
176 | *
177 | * @return string
178 | */
179 | public function toString()
180 | {
181 | return 'Allow: ' . $this->getFieldValue();
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/Header/AuthenticationInfo.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Authentication-Info';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Authentication-Info: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/Authorization.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Authorization';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Authorization: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/CacheControl.php:
--------------------------------------------------------------------------------
1 | $value) {
53 | $header->addDirective($key, $value);
54 | }
55 |
56 | return $header;
57 | }
58 |
59 | /**
60 | * Required from HeaderDescription interface
61 | *
62 | * @return string
63 | */
64 | public function getFieldName()
65 | {
66 | return 'Cache-Control';
67 | }
68 |
69 | /**
70 | * Checks if the internal directives array is empty
71 | *
72 | * @return bool
73 | */
74 | public function isEmpty()
75 | {
76 | return empty($this->directives);
77 | }
78 |
79 | /**
80 | * Add a directive
81 | * For directives like 'max-age=60', $value = '60'
82 | * For directives like 'private', use the default $value = true
83 | *
84 | * @param string $key
85 | * @param string|bool $value
86 | * @return $this
87 | */
88 | public function addDirective($key, $value = true)
89 | {
90 | HeaderValue::assertValid($key);
91 | if (! is_bool($value)) {
92 | HeaderValue::assertValid($value);
93 | }
94 | $this->directives[$key] = $value;
95 | return $this;
96 | }
97 |
98 | /**
99 | * Check the internal directives array for a directive
100 | *
101 | * @param string $key
102 | * @return bool
103 | */
104 | public function hasDirective($key)
105 | {
106 | return array_key_exists($key, $this->directives);
107 | }
108 |
109 | /**
110 | * Fetch the value of a directive from the internal directive array
111 | *
112 | * @param string $key
113 | * @return string|null
114 | */
115 | public function getDirective($key)
116 | {
117 | return array_key_exists($key, $this->directives) ? $this->directives[$key] : null;
118 | }
119 |
120 | /**
121 | * Remove a directive
122 | *
123 | * @param string $key
124 | * @return $this
125 | */
126 | public function removeDirective($key)
127 | {
128 | unset($this->directives[$key]);
129 | return $this;
130 | }
131 |
132 | /**
133 | * Assembles the directives into a comma-delimited string
134 | *
135 | * @return string
136 | */
137 | public function getFieldValue()
138 | {
139 | $parts = [];
140 | ksort($this->directives);
141 | foreach ($this->directives as $key => $value) {
142 | if (true === $value) {
143 | $parts[] = $key;
144 | } else {
145 | if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
146 | $value = '"' . $value . '"';
147 | }
148 | $parts[] = $key . '=' . $value;
149 | }
150 | }
151 | return implode(', ', $parts);
152 | }
153 |
154 | /**
155 | * Returns a string representation of the HTTP Cache-Control header
156 | *
157 | * @return string
158 | */
159 | public function toString()
160 | {
161 | return 'Cache-Control: ' . $this->getFieldValue();
162 | }
163 |
164 | /**
165 | * Internal function for parsing the value part of a
166 | * HTTP Cache-Control header
167 | *
168 | * @param string $value
169 | * @throws Exception\InvalidArgumentException
170 | * @return array
171 | */
172 | protected static function parseValue($value)
173 | {
174 | $value = trim($value);
175 |
176 | $directives = [];
177 |
178 | // handle empty string early so we don't need a separate start state
179 | if ($value == '') {
180 | return $directives;
181 | }
182 |
183 | $lastMatch = null;
184 |
185 | state_directive:
186 | switch (static::match(['[a-zA-Z][a-zA-Z_-]*'], $value, $lastMatch)) {
187 | case 0:
188 | $directive = $lastMatch;
189 | goto state_value;
190 | // intentional fall-through
191 |
192 | default:
193 | throw new Exception\InvalidArgumentException('expected DIRECTIVE');
194 | }
195 |
196 | state_value:
197 | switch (static::match(['="[^"]*"', '=[^",\s;]*'], $value, $lastMatch)) {
198 | case 0:
199 | $directives[$directive] = substr($lastMatch, 2, -1);
200 | goto state_separator;
201 | // intentional fall-through
202 |
203 | case 1:
204 | $directives[$directive] = rtrim(substr($lastMatch, 1));
205 | goto state_separator;
206 | // intentional fall-through
207 |
208 | default:
209 | $directives[$directive] = true;
210 | goto state_separator;
211 | }
212 |
213 | state_separator:
214 | switch (static::match(['\s*,\s*', '$'], $value, $lastMatch)) {
215 | case 0:
216 | goto state_directive;
217 | // intentional fall-through
218 |
219 | case 1:
220 | return $directives;
221 |
222 | default:
223 | throw new Exception\InvalidArgumentException('expected SEPARATOR or END');
224 | }
225 | }
226 |
227 | /**
228 | * Internal function used by parseValue to match tokens
229 | *
230 | * @param array $tokens
231 | * @param string $string
232 | * @param string $lastMatch
233 | * @return int
234 | */
235 | protected static function match($tokens, &$string, &$lastMatch)
236 | {
237 | // Ensure we have a string
238 | $value = (string) $string;
239 |
240 | foreach ($tokens as $i => $token) {
241 | if (preg_match('/^' . $token . '/', $value, $matches)) {
242 | $lastMatch = $matches[0];
243 | $string = substr($value, strlen($matches[0]));
244 | return $i;
245 | }
246 | }
247 | return -1;
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/Header/Connection.php:
--------------------------------------------------------------------------------
1 | setValue(trim($value));
44 |
45 | return $header;
46 | }
47 |
48 | /**
49 | * Set Connection header to define persistent connection
50 | *
51 | * @param bool $flag
52 | * @return $this
53 | */
54 | public function setPersistent($flag)
55 | {
56 | $this->value = (bool) $flag
57 | ? self::CONNECTION_KEEP_ALIVE
58 | : self::CONNECTION_CLOSE;
59 | return $this;
60 | }
61 |
62 | /**
63 | * Get whether this connection is persistent
64 | *
65 | * @return bool
66 | */
67 | public function isPersistent()
68 | {
69 | return ($this->value === self::CONNECTION_KEEP_ALIVE);
70 | }
71 |
72 | /**
73 | * Set arbitrary header value
74 | * RFC allows any token as value, 'close' and 'keep-alive' are commonly used
75 | *
76 | * @param string $value
77 | * @return $this
78 | */
79 | public function setValue($value)
80 | {
81 | HeaderValue::assertValid($value);
82 | $this->value = strtolower($value);
83 | return $this;
84 | }
85 |
86 | /**
87 | * Connection header name
88 | *
89 | * @return string
90 | */
91 | public function getFieldName()
92 | {
93 | return 'Connection';
94 | }
95 |
96 | /**
97 | * Connection header value
98 | *
99 | * @return string
100 | */
101 | public function getFieldValue()
102 | {
103 | return $this->value;
104 | }
105 |
106 | /**
107 | * Return header line
108 | *
109 | * @return string
110 | */
111 | public function toString()
112 | {
113 | return 'Connection: ' . $this->getFieldValue();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Header/ContentDisposition.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Content-Disposition';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Content-Disposition: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/ContentEncoding.php:
--------------------------------------------------------------------------------
1 | value = $value;
41 | }
42 | }
43 |
44 | public function getFieldName()
45 | {
46 | return 'Content-Encoding';
47 | }
48 |
49 | public function getFieldValue()
50 | {
51 | return (string) $this->value;
52 | }
53 |
54 | public function toString()
55 | {
56 | return 'Content-Encoding: ' . $this->getFieldValue();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Header/ContentLanguage.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Content-Language';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Content-Language: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/ContentLength.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Content-Length';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Content-Length: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/ContentLocation.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Content-MD5';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Content-MD5: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/ContentRange.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Content-Range';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Content-Range: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/ContentSecurityPolicy.php:
--------------------------------------------------------------------------------
1 | directives;
79 | }
80 |
81 | /**
82 | * Sets the directive to consist of the source list
83 | *
84 | * Reverses http://www.w3.org/TR/CSP/#parsing-1
85 | *
86 | * @param string $name The directive name.
87 | * @param array $sources The source list.
88 | * @return $this
89 | * @throws Exception\InvalidArgumentException If the name is not a valid directive name.
90 | */
91 | public function setDirective($name, array $sources)
92 | {
93 | if (! in_array($name, $this->validDirectiveNames, true)) {
94 | throw new Exception\InvalidArgumentException(sprintf(
95 | '%s expects a valid directive name; received "%s"',
96 | __METHOD__,
97 | (string) $name
98 | ));
99 | }
100 |
101 | if ($name === 'block-all-mixed-content'
102 | || $name === 'upgrade-insecure-requests'
103 | ) {
104 | if ($sources) {
105 | throw new Exception\InvalidArgumentException(sprintf(
106 | 'Received value for %s directive; none expected',
107 | $name
108 | ));
109 | }
110 |
111 | $this->directives[$name] = '';
112 | return $this;
113 | }
114 |
115 | if (empty($sources)) {
116 | if ('report-uri' === $name) {
117 | if (isset($this->directives[$name])) {
118 | unset($this->directives[$name]);
119 | }
120 | return $this;
121 | }
122 |
123 | $this->directives[$name] = "'none'";
124 | return $this;
125 | }
126 |
127 | array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
128 | $this->directives[$name] = implode(' ', $sources);
129 |
130 | return $this;
131 | }
132 |
133 | /**
134 | * Create Content Security Policy header from a given header line
135 | *
136 | * @param string $headerLine The header line to parse.
137 | * @return static
138 | * @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
139 | */
140 | public static function fromString($headerLine)
141 | {
142 | $header = new static();
143 | $headerName = $header->getFieldName();
144 | list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
145 | // Ensure the proper header name
146 | if (strcasecmp($name, $headerName) != 0) {
147 | throw new Exception\InvalidArgumentException(sprintf(
148 | 'Invalid header line for %s string: "%s"',
149 | $headerName,
150 | $name
151 | ));
152 | }
153 | // As per http://www.w3.org/TR/CSP/#parsing
154 | $tokens = explode(';', $value);
155 | foreach ($tokens as $token) {
156 | $token = trim($token);
157 | if ($token) {
158 | list($directiveName, $directiveValue) = array_pad(explode(' ', $token, 2), 2, null);
159 | if (! isset($header->directives[$directiveName])) {
160 | $header->setDirective(
161 | $directiveName,
162 | $directiveValue === null ? [] : [$directiveValue]
163 | );
164 | }
165 | }
166 | }
167 | return $header;
168 | }
169 |
170 | /**
171 | * Get the header name
172 | *
173 | * @return string
174 | */
175 | public function getFieldName()
176 | {
177 | return 'Content-Security-Policy';
178 | }
179 |
180 | /**
181 | * Get the header value
182 | *
183 | * @return string
184 | */
185 | public function getFieldValue()
186 | {
187 | $directives = [];
188 | foreach ($this->directives as $name => $value) {
189 | $directives[] = sprintf('%s %s;', $name, $value);
190 | }
191 | return str_replace(' ;', ';', implode(' ', $directives));
192 | }
193 |
194 | /**
195 | * Return the header as a string
196 | *
197 | * @return string
198 | */
199 | public function toString()
200 | {
201 | return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
202 | }
203 |
204 | public function toStringMultipleHeaders(array $headers)
205 | {
206 | $strings = [$this->toString()];
207 | foreach ($headers as $header) {
208 | if (! $header instanceof ContentSecurityPolicy) {
209 | throw new Exception\RuntimeException(
210 | 'The ContentSecurityPolicy multiple header implementation can only'
211 | . ' accept an array of ContentSecurityPolicy headers'
212 | );
213 | }
214 | $strings[] = $header->toString();
215 | }
216 |
217 | return implode("\r\n", $strings) . "\r\n";
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/Header/ContentTransferEncoding.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Content-Transfer-Encoding';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Content-Transfer-Encoding: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/Cookie.php:
--------------------------------------------------------------------------------
1 | getName(), $nvPairs)) {
33 | throw new Exception\InvalidArgumentException(sprintf(
34 | 'Two cookies with the same name were provided to %s',
35 | __METHOD__
36 | ));
37 | }
38 |
39 | $nvPairs[$setCookie->getName()] = $setCookie->getValue();
40 | }
41 |
42 | return new static($nvPairs);
43 | }
44 |
45 | public static function fromString($headerLine)
46 | {
47 | $header = new static();
48 |
49 | list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
50 |
51 | // check to ensure proper header type for this factory
52 | if (strtolower($name) !== 'cookie') {
53 | throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
54 | }
55 |
56 | $nvPairs = preg_split('#;\s*#', $value);
57 |
58 | $arrayInfo = [];
59 | foreach ($nvPairs as $nvPair) {
60 | $parts = explode('=', $nvPair, 2);
61 | if (count($parts) != 2) {
62 | throw new Exception\RuntimeException('Malformed Cookie header found');
63 | }
64 | list($name, $value) = $parts;
65 | $arrayInfo[$name] = urldecode($value);
66 | }
67 |
68 | $header->exchangeArray($arrayInfo);
69 |
70 | return $header;
71 | }
72 |
73 | public function __construct(array $array = [])
74 | {
75 | parent::__construct($array, ArrayObject::ARRAY_AS_PROPS);
76 | }
77 |
78 | /**
79 | * @param bool $encodeValue
80 | *
81 | * @return $this
82 | */
83 | public function setEncodeValue($encodeValue)
84 | {
85 | $this->encodeValue = (bool) $encodeValue;
86 | return $this;
87 | }
88 |
89 | /**
90 | * @return bool
91 | */
92 | public function getEncodeValue()
93 | {
94 | return $this->encodeValue;
95 | }
96 |
97 | public function getFieldName()
98 | {
99 | return 'Cookie';
100 | }
101 |
102 | public function getFieldValue()
103 | {
104 | $nvPairs = [];
105 |
106 | foreach ($this->flattenCookies($this) as $name => $value) {
107 | $nvPairs[] = $name . '=' . (($this->encodeValue) ? urlencode($value) : $value);
108 | }
109 |
110 | return implode('; ', $nvPairs);
111 | }
112 |
113 | protected function flattenCookies($data, $prefix = null)
114 | {
115 | $result = [];
116 | foreach ($data as $key => $value) {
117 | $key = $prefix ? $prefix . '[' . $key . ']' : $key;
118 | if (is_array($value)) {
119 | $result = array_merge($result, $this->flattenCookies($value, $key));
120 | } else {
121 | $result[$key] = $value;
122 | }
123 | }
124 |
125 | return $result;
126 | }
127 |
128 | public function toString()
129 | {
130 | return 'Cookie: ' . $this->getFieldValue();
131 | }
132 |
133 | /**
134 | * Get the cookie as a string, suitable for sending as a "Cookie" header in an
135 | * HTTP request
136 | *
137 | * @return string
138 | */
139 | public function __toString()
140 | {
141 | return $this->toString();
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Header/Date.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Etag';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Etag: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Exception/DomainException.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Expect';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Expect: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Expires.php:
--------------------------------------------------------------------------------
1 | directives;
83 | }
84 |
85 | /**
86 | * Sets the directive to consist of the source list
87 | *
88 | * @param string $name The directive name.
89 | * @param string[] $sources The source list.
90 | * @return $this
91 | * @throws Exception\InvalidArgumentException If the name is not a valid directive name.
92 | */
93 | public function setDirective($name, array $sources)
94 | {
95 | if (! in_array($name, $this->validDirectiveNames, true)) {
96 | throw new Exception\InvalidArgumentException(sprintf(
97 | '%s expects a valid directive name; received "%s"',
98 | __METHOD__,
99 | (string) $name
100 | ));
101 | }
102 | if (empty($sources)) {
103 | $this->directives[$name] = "'none'";
104 | return $this;
105 | }
106 |
107 | array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
108 |
109 | $this->directives[$name] = implode(' ', $sources);
110 | return $this;
111 | }
112 |
113 | /**
114 | * Create Feature Policy header from a given header line
115 | *
116 | * @param string $headerLine The header line to parse.
117 | * @return static
118 | * @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
119 | */
120 | public static function fromString($headerLine)
121 | {
122 | $header = new static();
123 | $headerName = $header->getFieldName();
124 | list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
125 | // Ensure the proper header name
126 | if (strcasecmp($name, $headerName) !== 0) {
127 | throw new Exception\InvalidArgumentException(sprintf(
128 | 'Invalid header line for %s string: "%s"',
129 | $headerName,
130 | $name
131 | ));
132 | }
133 | // As per https://w3c.github.io/webappsec-feature-policy/#algo-parse-policy-directive
134 | $tokens = explode(';', $value);
135 | foreach ($tokens as $token) {
136 | $token = trim($token);
137 | if ($token) {
138 | list($directiveName, $directiveValue) = array_pad(explode(' ', $token, 2), 2, null);
139 | if (! isset($header->directives[$directiveName])) {
140 | $header->setDirective(
141 | $directiveName,
142 | $directiveValue === null ? [] : [$directiveValue]
143 | );
144 | }
145 | }
146 | }
147 |
148 | return $header;
149 | }
150 |
151 | /**
152 | * Get the header name
153 | *
154 | * @return string
155 | */
156 | public function getFieldName()
157 | {
158 | return 'Feature-Policy';
159 | }
160 |
161 | /**
162 | * Get the header value
163 | *
164 | * @return string
165 | */
166 | public function getFieldValue()
167 | {
168 | $directives = [];
169 | foreach ($this->directives as $name => $value) {
170 | $directives[] = sprintf('%s %s;', $name, $value);
171 | }
172 | return implode(' ', $directives);
173 | }
174 |
175 | /**
176 | * Return the header as a string
177 | *
178 | * @return string
179 | */
180 | public function toString()
181 | {
182 | return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/Header/From.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'From';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'From: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/GenericHeader.php:
--------------------------------------------------------------------------------
1 | setFieldName($fieldName);
71 | }
72 |
73 | if ($fieldValue !== null) {
74 | $this->setFieldValue($fieldValue);
75 | }
76 | }
77 |
78 | /**
79 | * Set header field name
80 | *
81 | * @param string $fieldName
82 | * @return $this
83 | * @throws Exception\InvalidArgumentException If the name does not match with RFC 2616 format.
84 | */
85 | public function setFieldName($fieldName)
86 | {
87 | if (! is_string($fieldName) || empty($fieldName)) {
88 | throw new Exception\InvalidArgumentException('Header name must be a string');
89 | }
90 |
91 | /*
92 | * Following RFC 7230 section 3.2
93 | *
94 | * header-field = field-name ":" [ field-value ]
95 | * field-name = token
96 | * token = 1*tchar
97 | * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
98 | * "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
99 | */
100 | if (! preg_match('/^[!#$%&\'*+\-\.\^_`|~0-9a-zA-Z]+$/', $fieldName)) {
101 | throw new Exception\InvalidArgumentException(
102 | 'Header name must be a valid RFC 7230 (section 3.2) field-name.'
103 | );
104 | }
105 |
106 | $this->fieldName = $fieldName;
107 | return $this;
108 | }
109 |
110 | /**
111 | * Retrieve header field name
112 | *
113 | * @return string
114 | */
115 | public function getFieldName()
116 | {
117 | return $this->fieldName;
118 | }
119 |
120 | /**
121 | * Set header field value
122 | *
123 | * @param string $fieldValue
124 | * @return $this
125 | */
126 | public function setFieldValue($fieldValue)
127 | {
128 | $fieldValue = (string) $fieldValue;
129 | HeaderValue::assertValid($fieldValue);
130 |
131 | if (preg_match('/^\s+$/', $fieldValue)) {
132 | $fieldValue = '';
133 | }
134 |
135 | $this->fieldValue = $fieldValue;
136 | return $this;
137 | }
138 |
139 | /**
140 | * Retrieve header field value
141 | *
142 | * @return string
143 | */
144 | public function getFieldValue()
145 | {
146 | return $this->fieldValue;
147 | }
148 |
149 | /**
150 | * Cast to string as a well formed HTTP header line
151 | *
152 | * Returns in form of "NAME: VALUE\r\n"
153 | *
154 | * @return string
155 | */
156 | public function toString()
157 | {
158 | return $this->getFieldName() . ': ' . $this->getFieldValue();
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/Header/GenericMultiHeader.php:
--------------------------------------------------------------------------------
1 | getFieldName();
31 | $values = [$this->getFieldValue()];
32 | foreach ($headers as $header) {
33 | if (! $header instanceof static) {
34 | throw new Exception\InvalidArgumentException(
35 | 'This method toStringMultipleHeaders was expecting an array of headers of the same type'
36 | );
37 | }
38 | $values[] = $header->getFieldValue();
39 | }
40 | return $name . ': ' . implode(',', $values) . "\r\n";
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Header/HeaderInterface.php:
--------------------------------------------------------------------------------
1 | 254
48 | ) {
49 | continue;
50 | }
51 |
52 | $string .= $value[$i];
53 | }
54 |
55 | return $string;
56 | }
57 |
58 | /**
59 | * Validate a header value.
60 | *
61 | * Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
62 | * tabs are allowed in values; only one whitespace character is allowed
63 | * between visible characters.
64 | *
65 | * @see http://en.wikipedia.org/wiki/HTTP_response_splitting
66 | * @param string $value
67 | * @return bool
68 | */
69 | public static function isValid($value)
70 | {
71 | $value = (string) $value;
72 | $length = strlen($value);
73 | for ($i = 0; $i < $length; $i += 1) {
74 | $ascii = ord($value[$i]);
75 |
76 | // Non-visible, non-whitespace characters
77 | // 9 === horizontal tab
78 | // 32-126, 128-254 === visible
79 | // 127 === DEL
80 | // 255 === null byte
81 | if (($ascii < 32 && $ascii !== 9)
82 | || $ascii === 127
83 | || $ascii > 254
84 | ) {
85 | return false;
86 | }
87 | }
88 |
89 | return true;
90 | }
91 |
92 | /**
93 | * Assert a header value is valid.
94 | *
95 | * @param string $value
96 | * @throws Exception\RuntimeException for invalid values
97 | * @return void
98 | */
99 | public static function assertValid($value)
100 | {
101 | if (! self::isValid($value)) {
102 | throw new Exception\InvalidArgumentException('Invalid header value');
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Header/Host.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Host';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Host: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/IfMatch.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'If-Match';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'If-Match: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/IfModifiedSince.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'If-None-Match';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'If-None-Match: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/IfRange.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'If-Range';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'If-Range: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/IfUnmodifiedSince.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Keep-Alive';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Keep-Alive: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/LastModified.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Max-Forwards';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Max-Forwards: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/MultipleHeaderInterface.php:
--------------------------------------------------------------------------------
1 | isValid()) {
34 | throw new Exception\InvalidArgumentException('Invalid header value for Origin key: "' . $name . '"');
35 | }
36 |
37 | return new static($value);
38 | }
39 |
40 | /**
41 | * @param string|null $value
42 | */
43 | public function __construct($value = null)
44 | {
45 | if ($value !== null) {
46 | HeaderValue::assertValid($value);
47 | $this->value = $value;
48 | }
49 | }
50 |
51 | public function getFieldName()
52 | {
53 | return 'Origin';
54 | }
55 |
56 | public function getFieldValue()
57 | {
58 | return (string) $this->value;
59 | }
60 |
61 | public function toString()
62 | {
63 | return 'Origin: ' . $this->getFieldValue();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Header/Pragma.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Pragma';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Pragma: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/ProxyAuthenticate.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Proxy-Authenticate';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Proxy-Authenticate: ' . $this->getFieldValue();
58 | }
59 |
60 | public function toStringMultipleHeaders(array $headers)
61 | {
62 | $strings = [$this->toString()];
63 | foreach ($headers as $header) {
64 | if (! $header instanceof ProxyAuthenticate) {
65 | throw new Exception\RuntimeException(
66 | 'The ProxyAuthenticate multiple header implementation can only accept'
67 | . ' an array of ProxyAuthenticate headers'
68 | );
69 | }
70 | $strings[] = $header->toString();
71 | }
72 | return implode("\r\n", $strings);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Header/ProxyAuthorization.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'Proxy-Authorization';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'Proxy-Authorization: ' . $this->getFieldValue();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Header/Range.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Range';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Range: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Referer.php:
--------------------------------------------------------------------------------
1 | uri->setFragment(null);
30 |
31 | return $this;
32 | }
33 |
34 | /**
35 | * Return header name
36 | *
37 | * @return string
38 | */
39 | public function getFieldName()
40 | {
41 | return 'Referer';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Header/Refresh.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Refresh';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Refresh: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/RetryAfter.php:
--------------------------------------------------------------------------------
1 | getFieldName())) {
40 | throw new Exception\InvalidArgumentException(
41 | 'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
42 | );
43 | }
44 |
45 | if (is_numeric($date)) {
46 | $dateHeader->setDeltaSeconds($date);
47 | } else {
48 | $dateHeader->setDate($date);
49 | }
50 |
51 | return $dateHeader;
52 | }
53 |
54 | /**
55 | * Set number of seconds
56 | *
57 | * @param int $delta
58 | * @return $this
59 | */
60 | public function setDeltaSeconds($delta)
61 | {
62 | $this->deltaSeconds = (int) $delta;
63 | return $this;
64 | }
65 |
66 | /**
67 | * Get number of seconds
68 | *
69 | * @return int
70 | */
71 | public function getDeltaSeconds()
72 | {
73 | return $this->deltaSeconds;
74 | }
75 |
76 | /**
77 | * Get header name
78 | *
79 | * @return string
80 | */
81 | public function getFieldName()
82 | {
83 | return 'Retry-After';
84 | }
85 |
86 | /**
87 | * Returns date if it's set, or number of seconds
88 | *
89 | * @return int|string
90 | */
91 | public function getFieldValue()
92 | {
93 | return ($this->date === null) ? $this->deltaSeconds : $this->getDate();
94 | }
95 |
96 | /**
97 | * Return header line
98 | *
99 | * @return string
100 | */
101 | public function toString()
102 | {
103 | return 'Retry-After: ' . $this->getFieldValue();
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Header/Server.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Server';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Server: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/TE.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'TE';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'TE: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Trailer.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Trailer';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Trailer: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/TransferEncoding.php:
--------------------------------------------------------------------------------
1 | value = $value;
41 | }
42 | }
43 |
44 | public function getFieldName()
45 | {
46 | return 'Transfer-Encoding';
47 | }
48 |
49 | public function getFieldValue()
50 | {
51 | return (string) $this->value;
52 | }
53 |
54 | public function toString()
55 | {
56 | return 'Transfer-Encoding: ' . $this->getFieldValue();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Header/Upgrade.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Upgrade';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Upgrade: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/UserAgent.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'User-Agent';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'User-Agent: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Vary.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Vary';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Vary: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/Via.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Via';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Via: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Header/WWWAuthenticate.php:
--------------------------------------------------------------------------------
1 | value = $value;
42 | }
43 | }
44 |
45 | public function getFieldName()
46 | {
47 | return 'WWW-Authenticate';
48 | }
49 |
50 | public function getFieldValue()
51 | {
52 | return (string) $this->value;
53 | }
54 |
55 | public function toString()
56 | {
57 | return 'WWW-Authenticate: ' . $this->getFieldValue();
58 | }
59 |
60 | public function toStringMultipleHeaders(array $headers)
61 | {
62 | $strings = [$this->toString()];
63 | foreach ($headers as $header) {
64 | if (! $header instanceof WWWAuthenticate) {
65 | throw new Exception\RuntimeException(
66 | 'The WWWAuthenticate multiple header implementation can only'
67 | . ' accept an array of WWWAuthenticate headers'
68 | );
69 | }
70 | $strings[] = $header->toString();
71 | }
72 | return implode("\r\n", $strings);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Header/Warning.php:
--------------------------------------------------------------------------------
1 | value = $value;
39 | }
40 | }
41 |
42 | public function getFieldName()
43 | {
44 | return 'Warning';
45 | }
46 |
47 | public function getFieldValue()
48 | {
49 | return (string) $this->value;
50 | }
51 |
52 | public function toString()
53 | {
54 | return 'Warning: ' . $this->getFieldValue();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/HeaderLoader.php:
--------------------------------------------------------------------------------
1 | Header\Accept::class,
22 | 'acceptcharset' => Header\AcceptCharset::class,
23 | 'acceptencoding' => Header\AcceptEncoding::class,
24 | 'acceptlanguage' => Header\AcceptLanguage::class,
25 | 'acceptranges' => Header\AcceptRanges::class,
26 | 'age' => Header\Age::class,
27 | 'allow' => Header\Allow::class,
28 | 'authenticationinfo' => Header\AuthenticationInfo::class,
29 | 'authorization' => Header\Authorization::class,
30 | 'cachecontrol' => Header\CacheControl::class,
31 | 'connection' => Header\Connection::class,
32 | 'contentdisposition' => Header\ContentDisposition::class,
33 | 'contentencoding' => Header\ContentEncoding::class,
34 | 'contentlanguage' => Header\ContentLanguage::class,
35 | 'contentlength' => Header\ContentLength::class,
36 | 'contentlocation' => Header\ContentLocation::class,
37 | 'contentmd5' => Header\ContentMD5::class,
38 | 'contentrange' => Header\ContentRange::class,
39 | 'contentsecuritypolicy' => Header\ContentSecurityPolicy::class,
40 | 'contenttransferencoding' => Header\ContentTransferEncoding::class,
41 | 'contenttype' => Header\ContentType::class,
42 | 'cookie' => Header\Cookie::class,
43 | 'date' => Header\Date::class,
44 | 'etag' => Header\Etag::class,
45 | 'expect' => Header\Expect::class,
46 | 'expires' => Header\Expires::class,
47 | 'featurepolicy' => Header\FeaturePolicy::class,
48 | 'from' => Header\From::class,
49 | 'host' => Header\Host::class,
50 | 'ifmatch' => Header\IfMatch::class,
51 | 'ifmodifiedsince' => Header\IfModifiedSince::class,
52 | 'ifnonematch' => Header\IfNoneMatch::class,
53 | 'ifrange' => Header\IfRange::class,
54 | 'ifunmodifiedsince' => Header\IfUnmodifiedSince::class,
55 | 'keepalive' => Header\KeepAlive::class,
56 | 'lastmodified' => Header\LastModified::class,
57 | 'location' => Header\Location::class,
58 | 'maxforwards' => Header\MaxForwards::class,
59 | 'origin' => Header\Origin::class,
60 | 'pragma' => Header\Pragma::class,
61 | 'proxyauthenticate' => Header\ProxyAuthenticate::class,
62 | 'proxyauthorization' => Header\ProxyAuthorization::class,
63 | 'range' => Header\Range::class,
64 | 'referer' => Header\Referer::class,
65 | 'refresh' => Header\Refresh::class,
66 | 'retryafter' => Header\RetryAfter::class,
67 | 'server' => Header\Server::class,
68 | 'setcookie' => Header\SetCookie::class,
69 | 'te' => Header\TE::class,
70 | 'trailer' => Header\Trailer::class,
71 | 'transferencoding' => Header\TransferEncoding::class,
72 | 'upgrade' => Header\Upgrade::class,
73 | 'useragent' => Header\UserAgent::class,
74 | 'vary' => Header\Vary::class,
75 | 'via' => Header\Via::class,
76 | 'warning' => Header\Warning::class,
77 | 'wwwauthenticate' => Header\WWWAuthenticate::class,
78 | ];
79 | }
80 |
--------------------------------------------------------------------------------
/src/PhpEnvironment/RemoteAddress.php:
--------------------------------------------------------------------------------
1 | useProxy = $useProxy;
53 | return $this;
54 | }
55 |
56 | /**
57 | * Checks proxy handling setting.
58 | *
59 | * @return bool Current setting value.
60 | */
61 | public function getUseProxy()
62 | {
63 | return $this->useProxy;
64 | }
65 |
66 | /**
67 | * Set list of trusted proxy addresses
68 | *
69 | * @param array $trustedProxies
70 | * @return $this
71 | */
72 | public function setTrustedProxies(array $trustedProxies)
73 | {
74 | $this->trustedProxies = $trustedProxies;
75 | return $this;
76 | }
77 |
78 | /**
79 | * Set the header to introspect for proxy IPs
80 | *
81 | * @param string $header
82 | * @return $this
83 | */
84 | public function setProxyHeader($header = 'X-Forwarded-For')
85 | {
86 | $this->proxyHeader = $this->normalizeProxyHeader($header);
87 | return $this;
88 | }
89 |
90 | /**
91 | * Returns client IP address.
92 | *
93 | * @return string IP address.
94 | */
95 | public function getIpAddress()
96 | {
97 | $ip = $this->getIpAddressFromProxy();
98 | if ($ip) {
99 | return $ip;
100 | }
101 |
102 | // direct IP address
103 | if (isset($_SERVER['REMOTE_ADDR'])) {
104 | return $_SERVER['REMOTE_ADDR'];
105 | }
106 |
107 | return '';
108 | }
109 |
110 | /**
111 | * Attempt to get the IP address for a proxied client
112 | *
113 | * @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2
114 | * @return false|string
115 | */
116 | protected function getIpAddressFromProxy()
117 | {
118 | if (! $this->useProxy
119 | || (isset($_SERVER['REMOTE_ADDR']) && ! in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))
120 | ) {
121 | return false;
122 | }
123 |
124 | $header = $this->proxyHeader;
125 | if (! isset($_SERVER[$header]) || empty($_SERVER[$header])) {
126 | return false;
127 | }
128 |
129 | // Extract IPs
130 | $ips = explode(',', $_SERVER[$header]);
131 | // trim, so we can compare against trusted proxies properly
132 | $ips = array_map('trim', $ips);
133 | // remove trusted proxy IPs
134 | $ips = array_diff($ips, $this->trustedProxies);
135 |
136 | // Any left?
137 | if (empty($ips)) {
138 | return false;
139 | }
140 |
141 | // Since we've removed any known, trusted proxy servers, the right-most
142 | // address represents the first IP we do not know about -- i.e., we do
143 | // not know if it is a proxy server, or a client. As such, we treat it
144 | // as the originating IP.
145 | // @see http://en.wikipedia.org/wiki/X-Forwarded-For
146 | $ip = array_pop($ips);
147 | return $ip;
148 | }
149 |
150 | /**
151 | * Normalize a header string
152 | *
153 | * Normalizes a header string to a format that is compatible with
154 | * $_SERVER
155 | *
156 | * @param string $header
157 | * @return string
158 | */
159 | protected function normalizeProxyHeader($header)
160 | {
161 | $header = strtoupper($header);
162 | $header = str_replace('-', '_', $header);
163 | if (0 !== strpos($header, 'HTTP_')) {
164 | $header = 'HTTP_' . $header;
165 | }
166 | return $header;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/PhpEnvironment/Response.php:
--------------------------------------------------------------------------------
1 | version) {
40 | $this->version = $this->detectVersion();
41 | }
42 | return $this->version;
43 | }
44 |
45 | /**
46 | * Detect the current used protocol version.
47 | * If detection failed it falls back to version 1.0.
48 | *
49 | * @return string
50 | */
51 | protected function detectVersion()
52 | {
53 | if (isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.1') {
54 | return self::VERSION_11;
55 | }
56 |
57 | return self::VERSION_10;
58 | }
59 |
60 | /**
61 | * @return bool
62 | */
63 | public function headersSent()
64 | {
65 | return headers_sent();
66 | }
67 |
68 | /**
69 | * @return bool
70 | */
71 | public function contentSent()
72 | {
73 | return $this->contentSent;
74 | }
75 |
76 | /**
77 | * Send HTTP headers
78 | *
79 | * @return $this
80 | */
81 | public function sendHeaders()
82 | {
83 | if ($this->headersSent()) {
84 | return $this;
85 | }
86 |
87 | $status = $this->renderStatusLine();
88 | header($status);
89 |
90 | /** @var \Zend\Http\Header\HeaderInterface $header */
91 | foreach ($this->getHeaders() as $header) {
92 | if ($header instanceof MultipleHeaderInterface) {
93 | header($header->toString(), false);
94 | continue;
95 | }
96 | header($header->toString());
97 | }
98 |
99 | $this->headersSent = true;
100 | return $this;
101 | }
102 |
103 | /**
104 | * Send content
105 | *
106 | * @return $this
107 | */
108 | public function sendContent()
109 | {
110 | if ($this->contentSent()) {
111 | return $this;
112 | }
113 |
114 | echo $this->getContent();
115 | $this->contentSent = true;
116 | return $this;
117 | }
118 |
119 | /**
120 | * Send HTTP response
121 | *
122 | * @return $this
123 | */
124 | public function send()
125 | {
126 | $this->sendHeaders()
127 | ->sendContent();
128 | return $this;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Response/Stream.php:
--------------------------------------------------------------------------------
1 | contentLength = $contentLength;
64 | }
65 |
66 | /**
67 | * Get content length
68 | *
69 | * @return int|null
70 | */
71 | public function getContentLength()
72 | {
73 | return $this->contentLength;
74 | }
75 |
76 | /**
77 | * Get the response as stream
78 | *
79 | * @return resource
80 | */
81 | public function getStream()
82 | {
83 | return $this->stream;
84 | }
85 |
86 | /**
87 | * Set the response stream
88 | *
89 | * @param resource $stream
90 | * @return $this
91 | */
92 | public function setStream($stream)
93 | {
94 | $this->stream = $stream;
95 | return $this;
96 | }
97 |
98 | /**
99 | * Get the cleanup trigger
100 | *
101 | * @return bool
102 | */
103 | public function getCleanup()
104 | {
105 | return $this->cleanup;
106 | }
107 |
108 | /**
109 | * Set the cleanup trigger
110 | *
111 | * @param bool $cleanup
112 | */
113 | public function setCleanup($cleanup = true)
114 | {
115 | $this->cleanup = $cleanup;
116 | }
117 |
118 | /**
119 | * Get file name associated with the stream
120 | *
121 | * @return string
122 | */
123 | public function getStreamName()
124 | {
125 | return $this->streamName;
126 | }
127 |
128 | /**
129 | * Set file name associated with the stream
130 | *
131 | * @param string $streamName Name to set
132 | * @return $this
133 | */
134 | public function setStreamName($streamName)
135 | {
136 | $this->streamName = $streamName;
137 | return $this;
138 | }
139 |
140 | /**
141 | * Create a new Zend\Http\Response\Stream object from a stream
142 | *
143 | * @param string $responseString
144 | * @param resource $stream
145 | * @return $this
146 | * @throws Exception\InvalidArgumentException
147 | * @throws Exception\OutOfRangeException
148 | */
149 | public static function fromStream($responseString, $stream)
150 | {
151 | if (! is_resource($stream) || get_resource_type($stream) !== 'stream') {
152 | throw new Exception\InvalidArgumentException('A valid stream is required');
153 | }
154 |
155 | $headerComplete = false;
156 | $headersString = '';
157 | $responseArray = [];
158 |
159 | if ($responseString) {
160 | $responseArray = explode("\n", $responseString);
161 | }
162 |
163 | while (! empty($responseArray)) {
164 | $nextLine = array_shift($responseArray);
165 | $headersString .= $nextLine . "\n";
166 | $nextLineTrimmed = trim($nextLine);
167 | if ($nextLineTrimmed == '') {
168 | $headerComplete = true;
169 | break;
170 | }
171 | }
172 |
173 | if (! $headerComplete) {
174 | while (false !== ($nextLine = fgets($stream))) {
175 | $headersString .= trim($nextLine) . "\r\n";
176 | if ($nextLine == "\r\n" || $nextLine == "\n") {
177 | $headerComplete = true;
178 | break;
179 | }
180 | }
181 | }
182 |
183 | if (! $headerComplete) {
184 | throw new Exception\OutOfRangeException('End of header not found');
185 | }
186 |
187 | /** @var Stream $response */
188 | $response = static::fromString($headersString);
189 |
190 | if (is_resource($stream)) {
191 | $response->setStream($stream);
192 | }
193 |
194 | if (! empty($responseArray)) {
195 | $response->content = implode("\n", $responseArray);
196 | }
197 |
198 | $headers = $response->getHeaders();
199 | foreach ($headers as $header) {
200 | if ($header instanceof \Zend\Http\Header\ContentLength) {
201 | $response->setContentLength((int) $header->getFieldValue());
202 | $contentLength = $response->getContentLength();
203 | if (strlen($response->content) > $contentLength) {
204 | throw new Exception\OutOfRangeException(sprintf(
205 | 'Too much content was extracted from the stream (%d instead of %d bytes)',
206 | strlen($response->content),
207 | $contentLength
208 | ));
209 | }
210 | break;
211 | }
212 | }
213 |
214 | return $response;
215 | }
216 |
217 | /**
218 | * Get the response body as string
219 | *
220 | * This method returns the body of the HTTP response (the content), as it
221 | * should be in it's readable version - that is, after decoding it (if it
222 | * was decoded), deflating it (if it was gzip compressed), etc.
223 | *
224 | * If you want to get the raw body (as transferred on wire) use
225 | * $this->getRawBody() instead.
226 | *
227 | * @return string
228 | */
229 | public function getBody()
230 | {
231 | if ($this->stream !== null) {
232 | $this->readStream();
233 | }
234 | return parent::getBody();
235 | }
236 |
237 | /**
238 | * Get the raw response body (as transferred "on wire") as string
239 | *
240 | * If the body is encoded (with Transfer-Encoding, not content-encoding -
241 | * IE "chunked" body), gzip compressed, etc. it will not be decoded.
242 | *
243 | * @return string
244 | */
245 | public function getRawBody()
246 | {
247 | if ($this->stream) {
248 | $this->readStream();
249 | }
250 | return $this->content;
251 | }
252 |
253 | /**
254 | * Read stream content and return it as string
255 | *
256 | * Function reads the remainder of the body from the stream and closes the stream.
257 | *
258 | * @return string
259 | */
260 | protected function readStream()
261 | {
262 | $contentLength = $this->getContentLength();
263 | if (null !== $contentLength) {
264 | $bytes = $contentLength - $this->contentStreamed;
265 | } else {
266 | $bytes = -1; // Read the whole buffer
267 | }
268 |
269 | if (! is_resource($this->stream) || $bytes == 0) {
270 | return '';
271 | }
272 |
273 | $this->content .= stream_get_contents($this->stream, $bytes);
274 | $this->contentStreamed += strlen($this->content);
275 |
276 | if ($this->getContentLength() == $this->contentStreamed) {
277 | $this->stream = null;
278 | }
279 | }
280 |
281 | /**
282 | * Destructor
283 | */
284 | public function __destruct()
285 | {
286 | if (is_resource($this->stream)) {
287 | $this->stream = null; //Could be listened by others
288 | }
289 | if ($this->cleanup) {
290 | ErrorHandler::start(E_WARNING);
291 | unlink($this->streamName);
292 | ErrorHandler::stop();
293 | }
294 | }
295 | }
296 |
--------------------------------------------------------------------------------