update v1.0.7.9 R.C.

This is a Release Candidate. We are still testing.
This commit is contained in:
Sujit Prasad
2016-08-03 20:04:36 +05:30
parent 8b6b924d09
commit ffa56a43cb
3830 changed files with 181529 additions and 495353 deletions

View File

@@ -0,0 +1,73 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 2.5.4 - 2016-02-04
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#42](https://github.com/zendframework/zend-http/pull/42) updates dependencies
to ensure it can work with PHP 5.5+ and 7.0+, as well as zend-stdlib
2.5+/3.0+.
## 2.5.3 - 2015-09-14
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#23](https://github.com/zendframework/zend-http/pull/23) fixes a BC break
introduced with fixes for [ZF2015-04](http://framework.zend.com/security/advisory/ZF2015-04),
pertaining specifically to the `SetCookie` header. The fix backs out a
check for message splitting syntax, as that particular class already encodes
the value in a manner that prevents the attack. It also adds tests to ensure
the security vulnerability remains patched.
## 2.5.2 - 2015-08-05
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#7](https://github.com/zendframework/zend-http/pull/7) fixes a call in the
proxy adapter to `Response::extractCode()`, which does not exist, to
`Response::fromString()->getStatusCode()`, which does.
- [#8](https://github.com/zendframework/zend-http/pull/8) ensures that the Curl
client adapter enables the `CURLINFO_HEADER_OUT`, which is required to ensure
we can fetch the raw request after it is sent.
- [#14](https://github.com/zendframework/zend-http/pull/14) fixes
`Zend\Http\PhpEnvironment\Request` to ensure that empty `SCRIPT_FILENAME` and
`SCRIPT_NAME` values which result in an empty `$baseUrl` will not raise an
`E_WARNING` when used to do a `strpos()` check during base URI detection.

View File

@@ -0,0 +1,229 @@
# CONTRIBUTING
## RESOURCES
If you wish to contribute to Zend Framework, please be sure to
read/subscribe to the following resources:
- [Coding Standards](https://github.com/zendframework/zf2/wiki/Coding-Standards)
- [Contributor's Guide](http://framework.zend.com/participate/contributor-guide)
- ZF Contributor's mailing list:
Archives: http://zend-framework-community.634137.n4.nabble.com/ZF-Contributor-f680267.html
Subscribe: zf-contributors-subscribe@lists.zend.com
- ZF Contributor's IRC channel:
#zftalk.dev on Freenode.net
If you are working on new features or refactoring [create a proposal](https://github.com/zendframework/zend-http/issues/new).
## Reporting Potential Security Issues
If you have encountered a potential security vulnerability, please **DO NOT** report it on the public
issue tracker: send it to us at [zf-security@zend.com](mailto:zf-security@zend.com) instead.
We will work with you to verify the vulnerability and patch it as soon as possible.
When reporting issues, please provide the following information:
- Component(s) affected
- A description indicating how to reproduce the issue
- A summary of the security vulnerability and impact
We request that you contact us via the email address above and give the project
contributors a chance to resolve the vulnerability and issue a new release prior
to any public exposure; this helps protect users and provides them with a chance
to upgrade and/or update in order to protect their applications.
For sensitive email communications, please use [our PGP key](http://framework.zend.com/zf-security-pgp-key.asc).
## RUNNING TESTS
> ### Note: testing versions prior to 2.4
>
> This component originates with Zend Framework 2. During the lifetime of ZF2,
> testing infrastructure migrated from PHPUnit 3 to PHPUnit 4. In most cases, no
> changes were necessary. However, due to the migration, tests may not run on
> versions < 2.4. As such, you may need to change the PHPUnit dependency if
> attempting a fix on such a version.
To run tests:
- Clone the repository:
```console
$ git clone git@github.com:zendframework/zend-http.git
$ cd
```
- Install dependencies via composer:
```console
$ curl -sS https://getcomposer.org/installer | php --
$ ./composer.phar install
```
If you don't have `curl` installed, you can also download `composer.phar` from https://getcomposer.org/
- Run the tests via `phpunit` and the provided PHPUnit config, like in this example:
```console
$ ./vendor/bin/phpunit
```
You can turn on conditional tests with the phpunit.xml file.
To do so:
- Copy `phpunit.xml.dist` file to `phpunit.xml`
- Edit `phpunit.xml` to enable any specific functionality you
want to test, as well as to provide test values to utilize.
## Running Coding Standards Checks
This component uses [php-cs-fixer](http://cs.sensiolabs.org/) for coding
standards checks, and provides configuration for our selected checks.
`php-cs-fixer` is installed by default via Composer.
To run checks only:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --dry-run --config-file=.php_cs
```
To have `php-cs-fixer` attempt to fix problems for you, omit the `--dry-run`
flag:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --config-file=.php_cs
```
If you allow php-cs-fixer to fix CS issues, please re-run the tests to ensure
they pass, and make sure you add and commit the changes after verification.
## Recommended Workflow for Contributions
Your first step is to establish a public repository from which we can
pull your work into the master repository. We recommend using
[GitHub](https://github.com), as that is where the component is already hosted.
1. Setup a [GitHub account](http://github.com/), if you haven't yet
2. Fork the repository (http://github.com/zendframework/zend-http)
3. Clone the canonical repository locally and enter it.
```console
$ git clone git://github.com:zendframework/zend-http.git
$ cd zend-http
```
4. Add a remote to your fork; substitute your GitHub username in the command
below.
```console
$ git remote add {username} git@github.com:{username}/zend-http.git
$ git fetch {username}
```
### Keeping Up-to-Date
Periodically, you should update your fork or personal repository to
match the canonical ZF repository. Assuming you have setup your local repository
per the instructions above, you can do the following:
```console
$ git checkout master
$ git fetch origin
$ git rebase origin/master
# OPTIONALLY, to keep your remote up-to-date -
$ git push {username} master:master
```
If you're tracking other branches -- for example, the "develop" branch, where
new feature development occurs -- you'll want to do the same operations for that
branch; simply substitute "develop" for "master".
### Working on a patch
We recommend you do each new feature or bugfix in a new branch. This simplifies
the task of code review as well as the task of merging your changes into the
canonical repository.
A typical workflow will then consist of the following:
1. Create a new local branch based off either your master or develop branch.
2. Switch to your new local branch. (This step can be combined with the
previous step with the use of `git checkout -b`.)
3. Do some work, commit, repeat as necessary.
4. Push the local branch to your remote repository.
5. Send a pull request.
The mechanics of this process are actually quite trivial. Below, we will
create a branch for fixing an issue in the tracker.
```console
$ git checkout -b hotfix/9295
Switched to a new branch 'hotfix/9295'
```
... do some work ...
```console
$ git commit
```
... write your log message ...
```console
$ git push {username} hotfix/9295:hotfix/9295
Counting objects: 38, done.
Delta compression using up to 2 threads.
Compression objects: 100% (18/18), done.
Writing objects: 100% (20/20), 8.19KiB, done.
Total 20 (delta 12), reused 0 (delta 0)
To ssh://git@github.com/{username}/zend-http.git
b5583aa..4f51698 HEAD -> master
```
To send a pull request, you have two options.
If using GitHub, you can do the pull request from there. Navigate to
your repository, select the branch you just created, and then select the
"Pull Request" button in the upper right. Select the user/organization
"zendframework" as the recipient.
If using your own repository - or even if using GitHub - you can use `git
format-patch` to create a patchset for us to apply; in fact, this is
**recommended** for security-related patches. If you use `format-patch`, please
send the patches as attachments to:
- zf-devteam@zend.com for patches without security implications
- zf-security@zend.com for security patches
#### What branch to issue the pull request against?
Which branch should you issue a pull request against?
- For fixes against the stable release, issue the pull request against the
"master" branch.
- For new features, or fixes that introduce new elements to the public API (such
as new public methods or properties), issue the pull request against the
"develop" branch.
### Branch Cleanup
As you might imagine, if you are a frequent contributor, you'll start to
get a ton of branches both locally and on your remote.
Once you know that your changes have been accepted to the master
repository, we suggest doing some cleanup of these branches.
- Local branch cleanup
```console
$ git branch -d <branchname>
```
- Remote branch removal
```console
$ git push {username} :<branchname>
```

View File

@@ -0,0 +1,28 @@
Copyright (c) 2005-2015, Zend Technologies USA, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Zend Technologies USA, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,12 @@
# zend-http
[![Build Status](https://secure.travis-ci.org/zendframework/zend-http.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-http)
[![Coverage Status](https://coveralls.io/repos/zendframework/zend-http/badge.svg?branch=master)](https://coveralls.io/r/zendframework/zend-http?branch=master)
`Zend\Http` is a primary foundational component of Zend Framework. Since much of
what PHP does is web-based, specifically HTTP, it makes sense to have a performant,
extensible, concise and consistent API to do all things HTTP.
- File issues at https://github.com/zendframework/zend-http/issues
- Documentation is at http://framework.zend.com/manual/current/en/index.html#zend-http

View File

@@ -0,0 +1,38 @@
{
"name": "zendframework/zend-http",
"description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests",
"license": "BSD-3-Clause",
"keywords": [
"zf2",
"http"
],
"homepage": "https://github.com/zendframework/zend-http",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev",
"dev-develop": "2.6-dev"
}
},
"require": {
"php": "^5.5 || ^7.0",
"zendframework/zend-loader": "^2.5",
"zendframework/zend-stdlib": "^2.5 || ^3.0",
"zendframework/zend-uri": "^2.5",
"zendframework/zend-validator": "^2.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"phpunit/PHPUnit": "^4.0",
"zendframework/zend-config": "^2.5"
},
"autoload": {
"psr-4": {
"Zend\\Http\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"ZendTest\\Http\\": "test/"
}
}
}

View File

@@ -0,0 +1,54 @@
# HTTP Client - Static Usage
## Overview
The `Zend\Http` component also provides `Zend\Http\ClientStatic`, a static HTTP client which exposes
a simplified API for quickly performing GET and POST operations:
## Quick Start
```php
use Zend\Http\ClientStatic;
// Simple GET request
$response = ClientStatic::get('http://example.org');
// More complex GET request, specifying query string 'foo=bar' and adding a
// custom header to request JSON data be returned (Accept: application/json)
$response = ClientStatic::get(
'http://example.org',
array('foo' => 'bar'),
array('Accept' => 'application/json')
);
// We can also do a POST request using the same format. Here we POST
// login credentials (username/password) to a login page:
$response = ClientStatic::post('https://example.org/login.php', array(
'username' => 'foo',
'password' => 'bar',
));
```
## Available Methods
**get**
`get(string $url, array $query = array(), array $headers = array(), mixed $body = null,
$clientOptions = null)`
Perform an HTTP `GET` request using the provided URL, query string variables, headers and request
body. The fifth parameter can be used to pass configuration options to the HTTP Client instance.
Returns Zend\\Http\\Response
<!-- -->
**post**
`post(string $url, array $params, array $headers = array(), mixed $body = null, $clientOptions =
null)`
Perform an HTTP `POST` request using the provided URL, parameters, headers and request body. The
fifth parameter can be used to pass configuration options to the HTTP Client instance.
Returns Zend\\Http\\Response

View File

@@ -0,0 +1,451 @@
# HTTP Client - Connection Adapters
## Overview
`Zend\Http\Client` is based on a connection adapter design. The connection adapter is the object in
charge of performing the actual connection to the server, as well as writing requests and reading
responses. This connection adapter can be replaced, and you can create and extend the default
connection adapters to suite your special needs, without the need to extend or replace the entire
*HTTP* client class, and with the same interface.
Currently, the `Zend\Http\Client` class provides four built-in connection adapters:
- `Zend\Http\Client\Adapter\Socket` (default)
- `Zend\Http\Client\Adapter\Proxy`
- `Zend\Http\Client\Adapter\Curl`
- `Zend\Http\Client\Adapter\Test`
The `Zend\Http\Client` object's adapter connection adapter is set using the 'adapter' configuration
option. When instantiating the client object, you can set the 'adapter' configuration option to a
string containing the adapter's name (eg. 'Zend\\Http\\Client\\Adapter\\Socket') or to a variable
holding an adapter object (eg. `new Zend\Http\Client\Adapter\Socket`). You can also set the adapter
later, using the `Zend\Http\Client->setAdapter()` method.
## The Socket Adapter
The default connection adapter is the `Zend\Http\Client\Adapter\Socket` adapter - this adapter will
be used unless you explicitly set the connection adapter. The Socket adapter is based on *PHP*'s
built-in fsockopen() function, and does not require any special extensions or compilation flags.
The Socket adapter allows several extra configuration options that can be set using
`Zend\Http\Client->setOptions()` or passed to the client constructor.
> ## Note
#### Persistent TCP Connections
Using persistent *TCP* connections can potentially speed up *HTTP* requests - but in most use cases,
will have little positive effect and might overload the *HTTP* server you are connecting to.
It is recommended to use persistent *TCP* connections only if you connect to the same server very
frequently, and are sure that the server is capable of handling a large number of concurrent
connections. In any case you are encouraged to benchmark the effect of persistent connections on
both the client speed and server load before using this option.
Additionally, when using persistent connections it is recommended to enable Keep-Alive *HTTP*
requests as described in \[the configuration section\](zend.http.client.options)- otherwise
persistent connections might have little or no effect.
#### note
#### HTTPS SSL Stream Parameters
`ssltransport`, `sslcert` and `sslpassphrase` are only relevant when connecting using *HTTPS*.
While the default *SSL* settings should work for most applications, you might need to change them if
the server you are connecting to requires special client setup. If so, you should read the sections
about *SSL* transport layers and options
[here](http://www.php.net/manual/en/transports.php#transports.inet).
**Changing the HTTPS transport layer**
```php
// Set the configuration parameters
$config = array(
'adapter' => 'Zend\Http\Client\Adapter\Socket',
'ssltransport' => 'tls'
);
// Instantiate a client object
$client = new Zend\Http\Client('https://www.example.com', $config);
// The following request will be sent over a TLS secure connection.
$response = $client->send();
```
The result of the example above will be similar to opening a *TCP* connection using the following
*PHP* command:
`fsockopen('tls://www.example.com', 443)`
### Customizing and accessing the Socket adapter stream context
`Zend\Http\Client\Adapter\Socket` provides direct access to the underlying [stream
context](http://php.net/manual/en/stream.contexts.php) used to connect to the remote server. This
allows the user to pass specific options and parameters to the *TCP* stream, and to the *SSL*
wrapper in case of *HTTPS* connections.
You can access the stream context using the following methods of `Zend\Http\Client\Adapter\Socket`:
> - **setStreamContext($context)** Sets the stream context to be used by the adapter. Can accept
either a stream context resource created using the
[stream\_context\_create()](http://php.net/manual/en/function.stream-context-create.php) *PHP*
function, or an array of stream context options, in the same format provided to this function.
Providing an array will create a new stream context using these options, and set it.
- **getStreamContext()** Get the stream context of the adapter. If no stream context was set, will
create a default stream context and return it. You can then set or get the value of different
context options using regular *PHP* stream context functions.
**Setting stream context options for the Socket adapter**
```php
// Array of options
$options = array(
'socket' => array(
// Bind local socket side to a specific interface
'bindto' => '10.1.2.3:50505'
),
'ssl' => array(
// Verify server side certificate,
// do not accept invalid or self-signed SSL certificates
'verify_peer' => true,
'allow_self_signed' => false,
// Capture the peer's certificate
'capture_peer_cert' => true
)
);
// Create an adapter object and attach it to the HTTP client
$adapter = new Zend\Http\Client\Adapter\Socket();
$client = new Zend\Http\Client();
$client->setAdapter($adapter);
// Method 1: pass the options array to setStreamContext()
$adapter->setStreamContext($options);
// Method 2: create a stream context and pass it to setStreamContext()
$context = stream_context_create($options);
$adapter->setStreamContext($context);
// Method 3: get the default stream context and set the options on it
$context = $adapter->getStreamContext();
stream_context_set_option($context, $options);
// Now, perform the request
$response = $client->send();
// If everything went well, you can now access the context again
$opts = stream_context_get_options($adapter->getStreamContext());
echo $opts['ssl']['peer_certificate'];
```
> ## Note
Note that you must set any stream context options before using the adapter to perform actual
requests. If no context is set before performing *HTTP* requests with the Socket adapter, a default
stream context will be created. This context resource could be accessed after performing any
requests using the `getStreamContext()` method.
## The Proxy Adapter
The `Zend\Http\Client\Adapter\Proxy` adapter is similar to the default Socket adapter - only the
connection is made through an *HTTP* proxy server instead of a direct connection to the target
server. This allows usage of `Zend\Http\Client` behind proxy servers - which is sometimes needed for
security or performance reasons.
Using the Proxy adapter requires several additional configuration parameters to be set, in addition
to the default 'adapter' option:
`proxy_host` should always be set - if it is not set, the client will fall back to a direct
connection using `Zend\Http\Client\Adapter\Socket`. `proxy_port` defaults to '8080' - if your proxy
listens on a different port you must set this one as well.
`proxy_user` and `proxy_pass` are only required if your proxy server requires you to authenticate.
Providing these will add a 'Proxy-Authentication' header to the request. If your proxy does not
require authentication, you can leave these two options out.
`proxy_auth` sets the proxy authentication type, if your proxy server requires authentication.
Possibly values are similar to the ones accepted by the `Zend\Http\Client::setAuth()` method.
Currently, only basic authentication (`Zend\Http\Client::AUTH_BASIC`) is supported.
**Using Zend\\Http\\Client behind a proxy server**
```php
// Set the configuration parameters
$config = array(
'adapter' => 'Zend\Http\Client\Adapter\Proxy',
'proxy_host' => 'proxy.int.zend.com',
'proxy_port' => 8000,
'proxy_user' => 'shahar.e',
'proxy_pass' => 'bananashaped'
);
// Instantiate a client object
$client = new Zend\Http\Client('http://www.example.com', $config);
// Continue working...
```
As mentioned, if `proxy_host` is not set or is set to a blank string, the connection will fall back
to a regular direct connection. This allows you to easily write your application in a way that
allows a proxy to be used optionally, according to a configuration parameter.
> ## Note
Since the proxy adapter inherits from `Zend\Http\Client\Adapter\Socket`, you can use the stream
context access method (see \[this section\](zend.http.client.adapters.socket.streamcontext)) to set
stream context options on Proxy connections as demonstrated above.
## The cURL Adapter
cURL is a standard *HTTP* client library that is distributed with many operating systems and can be
used in *PHP* via the cURL extension. It offers functionality for many special cases which can occur
for a *HTTP* client and make it a perfect choice for a *HTTP* adapter. It supports secure
connections, proxy, all sorts of authentication mechanisms and shines in applications that move
large files around between servers.
**Setting cURL options**
```php
$config = array(
'adapter' => 'Zend\Http\Client\Adapter\Curl',
'curloptions' => array(CURLOPT_FOLLOWLOCATION => true),
);
$client = new Zend\Http\Client($uri, $config);
```
By default the cURL adapter is configured to behave exactly like the Socket Adapter and it also
accepts the same configuration parameters as the Socket and Proxy adapters. You can also change the
cURL options by either specifying the 'curloptions' key in the constructor of the adapter or by
calling `setCurlOption($name, $value)`. The `$name` key corresponds to the CURL\_\* constants of the
cURL extension. You can get access to the Curl handle by calling `$adapter->getHandle();`
**Transfering Files by Handle**
You can use cURL to transfer very large files over *HTTP* by filehandle.
```php
$putFileSize = filesize("filepath");
$putFileHandle = fopen("filepath", "r");
$adapter = new Zend\Http\Client\Adapter\Curl();
$client = new Zend\Http\Client();
$client->setAdapter($adapter);
$client->setMethod('PUT');
$adapter->setOptions(array(
'curloptions' => array(
CURLOPT_INFILE => $putFileHandle,
CURLOPT_INFILESIZE => $putFileSize
)
));
$client->send();
```
## The Test Adapter
Sometimes, it is very hard to test code that relies on *HTTP* connections. For example, testing an
application that pulls an *RSS* feed from a remote server will require a network connection, which
is not always available.
For this reason, the `Zend\Http\Client\Adapter\Test` adapter is provided. You can write your
application to use `Zend\Http\Client`, and just for testing purposes, for example in your unit
testing suite, you can replace the default adapter with a Test adapter (a mock object), allowing you
to run tests without actually performing server connections.
The `Zend\Http\Client\Adapter\Test` adapter provides an additional method, `setResponse()`. This
method takes one parameter, which represents an *HTTP* response as either text or a
`Zend\Http\Response` object. Once set, your Test adapter will always return this response, without
even performing an actual *HTTP* request.
**Testing Against a Single HTTP Response Stub**
```php
// Instantiate a new adapter and client
$adapter = new Zend\Http\Client\Adapter\Test();
$client = new Zend\Http\Client('http://www.example.com', array(
'adapter' => $adapter
));
// Set the expected response
$adapter->setResponse(
"HTTP/1.1 200 OK" . "\r\n" .
"Content-type: text/xml" . "\r\n" .
"\r\n" .
'<?xml version="1.0" encoding="UTF-8"?>' .
'<rss version="2.0" ' .
' xmlns:content="http://purl.org/rss/1.0/modules/content/"' .
' xmlns:wfw="http://wellformedweb.org/CommentAPI/"' .
' xmlns:dc="http://purl.org/dc/elements/1.1/">' .
' <channel>' .
' <title>Premature Optimization</title>' .
// and so on...
'</rss>');
$response = $client->send();
// .. continue parsing $response..
```
The above example shows how you can preset your *HTTP* client to return the response you need. Then,
you can continue testing your own code, without being dependent on a network connection, the
server's response, etc. In this case, the test would continue to check how the application parses
the *XML* in the response body.
Sometimes, a single method call to an object can result in that object performing multiple *HTTP*
transactions. In this case, it's not possible to use setResponse() alone because there's no
opportunity to set the next response(s) your program might need before returning to the caller.
**Testing Against Multiple HTTP Response Stubs**
```php
// Instantiate a new adapter and client
$adapter = new Zend\Http\Client\Adapter\Test();
$client = new Zend\Http\Client('http://www.example.com', array(
'adapter' => $adapter
));
// Set the first expected response
$adapter->setResponse(
"HTTP/1.1 302 Found" . "\r\n" .
"Location: /" . "\r\n" .
"Content-Type: text/html" . "\r\n" .
"\r\n" .
'<html>' .
' <head><title>Moved</title></head>' .
' <body><p>This page has moved.</p></body>' .
'</html>');
// Set the next successive response
$adapter->addResponse(
"HTTP/1.1 200 OK" . "\r\n" .
"Content-Type: text/html" . "\r\n" .
"\r\n" .
'<html>' .
' <head><title>My Pet Store Home Page</title></head>' .
' <body><p>...</p></body>' .
'</html>');
// inject the http client object ($client) into your object
// being tested and then test your object's behavior below
```
The `setResponse()` method clears any responses in the `Zend\Http\Client\Adapter\Test`'s buffer and
sets the first response that will be returned. The `addResponse()` method will add successive
responses.
The responses will be replayed in the order that they were added. If more requests are made than the
number of responses stored, the responses will cycle again in order.
In the example above, the adapter is configured to test your object's behavior when it encounters a
302 redirect. Depending on your application, following a redirect may or may not be desired
behavior. In our example, we expect that the redirect will be followed and we configure the test
adapter to help us test this. The initial 302 response is set up with the `setResponse()` method and
the 200 response to be returned next is added with the `addResponse()` method. After configuring the
test adapter, inject the *HTTP* client containing the adapter into your object under test and test
its behavior.
If you need the adapter to fail on demand you can use `setNextRequestWillFail($flag)`. The method
will cause the next call to `connect()` to throw an
`Zend\Http\Client\Adapter\Exception\RuntimeException` exception. This can be useful when our
application caches content from an external site (in case the site goes down) and you want to test
this feature.
**Forcing the adapter to fail**
```php
// Instantiate a new adapter and client
$adapter = new Zend\Http\Client\Adapter\Test();
$client = new Zend\Http\Client('http://www.example.com', array(
'adapter' => $adapter
));
// Force the next request to fail with an exception
$adapter->setNextRequestWillFail(true);
try {
// This call will result in a Zend\Http\Client\Adapter\Exception\RuntimeException
$client->send();
} catch (Zend\Http\Client\Adapter\Exception\RuntimeException $e) {
// ...
}
// Further requests will work as expected until
// you call setNextRequestWillFail(true) again
```
## Creating your own connection adapters
`Zend\Http\Client` has been designed so that you can create and use your own connection adapters.
You could, for example, create a connection adapter that uses persistent sockets, or a connection
adapter with caching abilities, and use them as needed in your application.
In order to do so, you must create your own adapter class that implements the
`Zend\Http\Client\Adapter\AdapterInterface` interface. The following example shows the skeleton of a
user-implemented adapter class. All the public functions defined in this example must be defined in
your adapter as well:
**Creating your own connection adapter**
```php
class MyApp\Http\Client\Adapter\BananaProtocol
implements Zend\Http\Client\Adapter\AdapterInterface
{
/**
* Set Adapter Options
*
* @param array $config
*/
public function setOptions($config = array())
{
// This rarely changes - you should usually copy the
// implementation in Zend\Http\Client\Adapter\Socket.
}
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param boolean $secure
*/
public function connect($host, $port = 80, $secure = false)
{
// Set up the connection to the remote server
}
/**
* Send request to the remote server
*
* @param string $method
* @param Zend\Uri\Http $url
* @param string $http_ver
* @param array $headers
* @param string $body
* @return string Request as text
*/
public function write($method,
$url,
$http_ver = '1.1',
$headers = array(),
$body = '')
{
// Send request to the remote server.
// This function is expected to return the full request
// (headers and body) as a string
}
/**
* Read response from server
*
* @return string
*/
public function read()
{
// Read response from remote server and return it as a string
}
/**
* Close the connection to the server
*
*/
public function close()
{
// Close the connection to the remote server - called last.
}
}
// Then, you could use this adapter:
$client = new Zend\Http\Client(array(
'adapter' => 'MyApp\Http\Client\Adapter\BananaProtocol'
));
```

View File

@@ -0,0 +1,341 @@
# HTTP Client - Advanced Usage
## HTTP Redirections
`Zend\Http\Client` automatically handles *HTTP* redirections, and by default will follow up to 5
redirections. This can be changed by setting the `maxredirects` configuration parameter.
According to the *HTTP*/1.1 RFC, *HTTP* 301 and 302 responses should be treated by the client by
resending the same request to the specified location - using the same request method. However, most
clients to not implement this and always use a `GET` request when redirecting. By default,
`Zend\Http\Client` does the same - when redirecting on a 301 or 302 response, all `GET` and POST
parameters are reset, and a `GET` request is sent to the new location. This behavior can be changed
by setting the `strictredirects` configuration parameter to boolean `TRUE`:
**Forcing RFC 2616 Strict Redirections on 301 and 302 Responses**
```php
// Strict Redirections
$client->setOptions(array('strictredirects' => true));
// Non-strict Redirections
$client->setOptions(array('strictredirects' => false));
```
You can always get the number of redirections done after sending a request using the
`getRedirectionsCount()` method.
## Adding Cookies and Using Cookie Persistence
`Zend\Http\Client` provides an easy interface for adding cookies to your request, so that no direct
header modification is required. Cookies can be added using either the addCookie() or `setCookies`
method. The `addCookie` method has a number of operating modes:
**Setting Cookies Using addCookie()**
```php
// Easy and simple: by providing a cookie name and cookie value
$client->addCookie('flavor', 'chocolate chips');
// By providing a Zend\Http\Header\SetCookie object
$cookie = Zend\Http\Header\SetCookie::fromString('Set-Cookie: flavor=chocolate%20chips');
$client->addCookie($cookie);
// Multiple cookies can be set at once by providing an
// array of Zend\Http\Header\SetCookie objects
$cookies = array(
Zend\Http\Header\SetCookie::fromString('Set-Cookie: flavorOne=chocolate%20chips'),
Zend\Http\Header\SetCookie::fromString('Set-Cookie: flavorTwo=vanilla'),
);
$client->addCookie($cookies);
```
The `setCookies()` method works in a similar manner, except that it requires an array of cookie
values as its only argument and also clears the cookie container before adding the new cookies:
**Setting Cookies Using setCookies()**
```php
// setCookies accepts an array of cookie values as $name => $value
$client->setCookies(array(
'flavor' => 'chocolate chips',
'amount' => 10,
));
```
For more information about `Zend\Http\Header\SetCookie` objects, see \[this
section\](zend.http.headers).
`Zend\Http\Client` also provides a means for simplifying cookie stickiness - that is having the
client internally store all sent and received cookies, and resend them on subsequent requests:
`Zend\Http\Cookies`. This is useful, for example when you need to log in to a remote site first and
receive and authentication or session ID cookie before sending further requests.
**Enabling Cookie Stickiness**
```php
$headers = $client->getRequest()->getHeaders();
$cookies = new Zend\Http\Cookies($headers);
// First request: log in and start a session
$client->setUri('http://example.com/login.php');
$client->setParameterPost(array('user' => 'h4x0r', 'password' => 'l33t'));
$client->setMethod('POST');
$response = $client->getResponse();
$cookies->addCookiesFromResponse($response, $client->getUri());
// Now we can send our next request
$client->setUri('http://example.com/read_member_news.php');
$client->setCookies($cookies->getMatchingCookies($client->getUri()));
$client->setMethod('GET');
```
For more information about the `Zend\Http\Cookies` class, see this section
&lt;zend.http.client.cookies&gt;.
## Setting Custom Request Headers
Setting custom headers is performed by first fetching the header container from the client's
`Zend\Http\Request` object. This method is quite diverse and can be used in several ways, as the
following example shows:
**Setting A Single Custom Request Header**
```php
// Fetch the container
$headers = $client->getRequest()->getHeaders();
// Setting a single header. Will not overwrite any
// previously-added headers of the same name.
$headers->addHeaderLine('Host', 'www.example.com');
// Another way of doing the exact same thing
$headers->addHeaderLine('Host: www.example.com');
// Another way of doing the exact same thing using
// the provided Zend\Http\Header class
$headers->addHeader(Zend\Http\Header\Host::fromString('Host: www.example.com'));
// You can also add multiple headers at once by passing an
// array to addHeaders using any of the formats below:
$headers->addHeaders(array(
// Zend\Http\Header\* object
Zend\Http\Header\Host::fromString('Host: www.example.com'),
// Header name as array key, header value as array key value
'Cookie' => 'PHPSESSID=1234567890abcdef1234567890abcdef',
// Raw header string
'Cookie: language=he',
));
```
`Zend\Http\Client` also provides a convenience method for setting request headers, `setHeaders`.
This method will create a new header container, add the specified headers and then store the new
header container in it's `Zend\Http\Request` object. As a consequence, any pre-existing headers will
be erased.
**Setting Multiple Custom Request Headers**
```php
// Setting multiple headers. Will remove all existing
// headers and add new ones to the Request header container
$client->setHeaders(array(
Zend\Http\Header\Host::fromString('Host: www.example.com'),
'Accept-Encoding' => 'gzip,deflate',
'X-Powered-By: Zend Framework',
));
```
## File Uploads
You can upload files through *HTTP* using the setFileUpload method. This method takes a file name as
the first parameter, a form name as the second parameter, and data as a third optional parameter. If
the third data parameter is `NULL`, the first file name parameter is considered to be a real file on
disk, and `Zend\Http\Client` will try to read this file and upload it. If the data parameter is not
`NULL`, the first file name parameter will be sent as the file name, but no actual file needs to
exist on the disk. The second form name parameter is always required, and is equivalent to the
"name" attribute of an `<input>` tag, if the file was to be uploaded through an *HTML* form. A
fourth optional parameter provides the file's content-type. If not specified, and `Zend\Http\Client`
reads the file from the disk, the `mime_content_type` function will be used to guess the file's
content type, if it is available. In any case, the default MIME type will be
application/octet-stream.
**Using setFileUpload to Upload Files**
```php
// Uploading arbitrary data as a file
$text = 'this is some plain text';
$client->setFileUpload('some_text.txt', 'upload', $text, 'text/plain');
// Uploading an existing file
$client->setFileUpload('/tmp/Backup.tar.gz', 'bufile');
// Send the files
$client->setMethod('POST');
$client->send();
```
In the first example, the `$text` variable is uploaded and will be available as `$_FILES['upload']`
on the server side. In the second example, the existing file `/tmp/Backup.tar.gz` is uploaded to the
server and will be available as `$_FILES['bufile']`. The content type will be guessed automatically
if possible - and if not, the content type will be set to 'application/octet-stream'.
> ## Note
#### Uploading files
When uploading files, the *HTTP* request content-type is automatically set to multipart/form-data.
Keep in mind that you must send a POST or PUT request in order to upload files. Most servers will
ignore the request body on other request methods.
## Sending Raw POST Data
You can use a `Zend\Http\Client` to send raw POST data using the `setRawBody()` method. This method
takes one parameter: the data to send in the request body. When sending raw POST data, it is
advisable to also set the encoding type using `setEncType()`.
**Sending Raw POST Data**
```php
$xml = '<book>' .
' <title>Islands in the Stream</title>' .
' <author>Ernest Hemingway</author>' .
' <year>1970</year>' .
'</book>';
$client->setMethod('POST');
$client->setRawBody($xml);
$client->setEncType('text/xml');
$client->send();
```
The data should be available on the server side through *PHP*'s `$HTTP_RAW_POST_DATA` variable or
through the `php://input` stream.
> ## Note
#### Using raw POST data
Setting raw POST data for a request will override any POST parameters or file uploads. You should
not try to use both on the same request. Keep in mind that most servers will ignore the request body
unless you send a POST request.
## HTTP Authentication
Currently, `Zend\Http\Client` only supports basic *HTTP* authentication. This feature is utilized
using the `setAuth()` method, or by specifying a username and a password in the URI. The `setAuth()`
method takes 3 parameters: The user name, the password and an optional authentication type
parameter. As mentioned, currently only basic authentication is supported (digest authentication
support is planned).
**Setting HTTP Authentication User and Password**
```php
// Using basic authentication
$client->setAuth('shahar', 'myPassword!', Zend\Http\Client::AUTH_BASIC);
// Since basic auth is default, you can just do this:
$client->setAuth('shahar', 'myPassword!');
// You can also specify username and password in the URI
$client->setUri('http://christer:secret@example.com');
```
## Sending Multiple Requests With the Same Client
`Zend\Http\Client` was also designed specifically to handle several consecutive requests with the
same object. This is useful in cases where a script requires data to be fetched from several places,
or when accessing a specific *HTTP* resource requires logging in and obtaining a session cookie, for
example.
When performing several requests to the same host, it is highly recommended to enable the
'keepalive' configuration flag. This way, if the server supports keep-alive connections, the
connection to the server will only be closed once all requests are done and the Client object is
destroyed. This prevents the overhead of opening and closing *TCP* connections to the server.
When you perform several requests with the same client, but want to make sure all the
request-specific parameters are cleared, you should use the `resetParameters()` method. This ensures
that GET and POST parameters, request body and headers are reset and are not reused in the next
request.
> ## Note
#### Resetting parameters
Note that cookies are not reset by default when the `resetParameters()` method is used. To clean all
cookies as well, use `resetParameters(true)`, or call `clearCookies()` after calling
`resetParameters()`.
Another feature designed specifically for consecutive requests is the `Zend\Http\Cookies` object.
This "Cookie Jar" allow you to save cookies set by the server in a request, and send them back on
consecutive requests transparently. This allows, for example, going through an authentication
request before sending the actual data-fetching request.
If your application requires one authentication request per user, and consecutive requests might be
performed in more than one script in your application, it might be a good idea to store the Cookies
object in the user's session. This way, you will only need to authenticate the user once every
session.
**Performing consecutive requests with one client**
```php
// First, instantiate the client
$client = new Zend\Http\Client('http://www.example.com/fetchdata.php', array(
'keepalive' => true
));
// Do we have the cookies stored in our session?
if (isset($_SESSION['cookiejar']) &&
$_SESSION['cookiejar'] instanceof Zend\Http\Cookies) {
$cookieJar = $_SESSION['cookiejar'];
} else {
// If we don't, authenticate and store cookies
$client->setUri('http://www.example.com/login.php');
$client->setParameterPost(array(
'user' => 'shahar',
'pass' => 'somesecret'
));
$response = $client->setMethod('POST')->send();
$cookieJar = Zend\Http\Cookies::fromResponse($response);
// Now, clear parameters and set the URI to the original one
// (note that the cookies that were set by the server are now
// stored in the jar)
$client->resetParameters();
$client->setUri('http://www.example.com/fetchdata.php');
}
// Add the cookies to the new request
$client->setCookies($cookieJar->getMatchingCookies($client->getUri()));
$response = $client->setMethod('GET')->send();
// Store cookies in session, for next page
$_SESSION['cookiejar'] = $cookieJar;
```
## Data Streaming
By default, `Zend\Http\Client` accepts and returns data as *PHP* strings. However, in many cases
there are big files to be received, thus keeping them in memory might be unnecessary or too
expensive. For these cases, `Zend\Http\Client` supports writing data to files (streams).
In order to receive data from the server as stream, use `setStream()`. Optional argument specifies
the filename where the data will be stored. If the argument is just `TRUE` (default), temporary file
will be used and will be deleted once response object is destroyed. Setting argument to `FALSE`
disables the streaming functionality.
When using streaming, `send()` method will return object of class `Zend\Http\Response\Stream`, which
has two useful methods: `getStreamName()` will return the name of the file where the response is
stored, and `getStream()` will return stream from which the response could be read.
You can either write the response to pre-defined file, or use temporary file for storing it and send
it out or write it to another file using regular stream functions.
> ## Receiving file from HTTP server with streaming
```php
$client-setStream(); // will use temp file
$response = $client-send();
// copy file
copy($response-getStreamName(), "my/downloads/file");
// use stream
$fp = fopen("my/downloads/file2", "w");
stream_copy_to_stream($response-getStream(), $fp);
// Also can write to known file
$client-setStream("my/downloads/myfile")-send();
```

View File

@@ -0,0 +1,230 @@
# HTTP Client
## Overview
`Zend\Http\Client` provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP)
requests. `Zend\Http\Client` supports the most simple features expected from an *HTTP* client, as
well as some more complex features such as *HTTP* authentication and file uploads. Successful
requests (and most unsuccessful ones too) return a `Zend\Http\Response` object, which provides
access to the response's headers and body (see this
section &lt;zend.http.response&gt;).
## Quick Start
The class constructor optionally accepts a URL as its first parameter (can be either a string or a
`Zend\Uri\Http` object), and an array or `Zend\Config\Config` object containing configuration
options. The `send()` method is used to submit the request to the remote server, and a
`Zend\Http\Response` object is returned:
```php
use Zend\Http\Client;
$client = new Client('http://example.org', array(
'maxredirects' => 0,
'timeout' => 30
));
$response = $client->send();
```
Both constructor parameters can be left out, and set later using the setUri() and setConfig()
methods:
```php
use Zend\Http\Client;
$client = new Client();
$client->setUri('http://example.org');
$client->setOptions(array(
'maxredirects' => 0,
'timeout' => 30
));
$response = $client->send();
```
`Zend\Http\Client` can also dispatch requests using a separately configured `request` object (see
the Zend\\\\Http\\\\Request manual page&lt;zend.http.request&gt; for full details of the methods
available):
```php
use Zend\Http\Client;
use Zend\Http\Request;
$request = new Request();
$request->setUri('http://example.org');
$client = new Client();
$response = $client->send($request);
```
> ## Note
`Zend\Http\Client` uses `Zend\Uri\Http` to validate URLs. See the Zend\\\\Uri manual
page&lt;zend.uri&gt; for more information on the validation process.
## Configuration
The constructor and setOptions() method accepts an associative array of configuration parameters, or
a `Zend\Config\Config` object. Setting these parameters is optional, as they all have default
values.
The options are also passed to the adapter class upon instantiation, so the same array or
`Zend\Config\Config` object) can be used for adapter configuration. See the Zend Http Client adapter
section&lt;zend.http.client.adapters&gt; for more information on the adapter-specific options
available.
## Examples
### Performing a Simple GET Request
Performing simple *HTTP* requests is very easily done:
```php
use Zend\Http\Client;
$client = new Client('http://example.org');
$response = $client->send();
```
### Using Request Methods Other Than GET
The request method can be set using `setMethod()`. If no method is specified, the method set by the
last `setMethod()` call is used. If `setMethod()` was never called, the default request method is
`GET`.
```php
use Zend\Http\Client;
$client = new Client('http://example.org');
// Performing a POST request
$client->setMethod('POST');
$response = $client->send();
```
For convenience, `Zend\Http\Request` defines all the request methods as class constants,
`Zend\Http\Request::METHOD_GET`, `Zend\Http\Request::METHOD_POST` and so on:
```php
use Zend\Http\Client;
use Zend\Http\Request;
$client = new Client('http://example.org');
// Performing a POST request
$client->setMethod(Request::METHOD_POST);
$response = $client->send();
```
### Setting GET parameters
Adding `GET` parameters to an *HTTP* request is quite simple, and can be done either by specifying
them as part of the URL, or by using the `setParameterGet()` method. This method takes the `GET`
parameters as an associative array of name =&gt; value `GET` variables.
```php
use Zend\Http\Client;
$client = new Client();
// This is equivalent to setting a URL in the Client's constructor:
$client->setUri('http://example.com/index.php?knight=lancelot');
// Adding several parameters with one call
$client->setParameterGet(array(
'first_name' => 'Bender',
'middle_name' => 'Bending',
'last_name' => 'Rodríguez',
'made_in' => 'Mexico',
));
```
### Setting POST Parameters
While `GET` parameters can be sent with every request method, `POST` parameters are only sent in the
body of `POST` requests. Adding `POST` parameters to a request is very similar to adding `GET`
parameters, and can be done with the `setParameterPost()` method, which is identical to the
`setParameterGet()` method in structure.
```php
use Zend\Http\Client;
$client = new Client();
// Setting several POST parameters, one of them with several values
$client->setParameterPost(array(
'language' => 'es',
'country' => 'ar',
'selection' => array(45, 32, 80)
));
```
Note that when sending `POST` requests, you can set both `GET` and `POST` parameters. On the other
hand, setting POST parameters on a non-`POST` request will not trigger an error, rendering it
useless. Unless the request is a `POST` request, `POST` parameters are simply ignored.
### Connecting to SSL URLs
If you are trying to connect to an SSL (https) URL and are using the default
(`Zend\Http\Client\Adapter\Socket`) adapter, you may need to set the `sslcapath` configuration
option in order to allow PHP to validate the SSL certificate:
```php
use Zend\Http\Client;
$client = new Client('https://example.org', array(
'sslcapath' => '/etc/ssl/certs'
));
$response = $client->send();
```
The exact path to use will vary depending on your Operating System. Without this you'll get the
exception "Unable to enable crypto on TCP connection" when trying to connect.
Alternatively, you could switch to the curl adapter, which negotiates SSL connections more
transparently:
```php
use Zend\Http\Client;
$client = new Client('https://example.org', array(
'adapter' => 'Zend\Http\Client\Adapter\Curl'
));
$response = $client->send();
```
### A Complete Example
```php
use Zend\Http\Client;
$client = new Client();
$client->setUri('http://www.example.com');
$client->setMethod('POST');
$client->setParameterPost(array(
'foo' => 'bar'
));
$response = $client->send();
if ($response->isSuccess()) {
// the POST was successful
}
```
or the same thing, using a request object:
```php
use Zend\Http\Client;
use Zend\Http\Request;
$request = new Request();
$request->setUri('http://www.example.com');
$request->setMethod('POST');
$request->getPost()->set('foo', 'bar');
$client = new Client();
$response = $client->send($request);
if ($response->isSuccess()) {
// the POST was successful
}
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,451 @@
# The Request Class
## Overview
The `Zend\Http\Request` object is responsible for providing a fluent API that allows a developer to
interact with all the various parts of an HTTP request.
A typical HTTP request looks like this:
##
## | METHOD | URI | VERSION |
## | HEADERS |
## | BODY |
In simplified terms, the request consists of a method, *URI* and HTTP version number which together
make up the "Request Line." Next come the HTTP headers, of which there can be 0 or more. After that
is the request body, which is typically used when a client wishes to send data to the server in the
form of an encoded file, or include a set of POST parameters, for example. More information on the
structure and specification of a HTTP request can be found in [RFC-2616 on the W3.org
site](http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html).
## Quick Start
Request objects can either be created from the provided `fromString()` factory, or, if you wish to
have a completely empty object to start with, by simply instantiating the `Zend\Http\Request` class.
```php
use Zend\Http\Request;
$request = Request::fromString(<<<EOS
POST /foo HTTP/1.1
\r\n
HeaderField1: header-field-value1
HeaderField2: header-field-value2
\r\n\r\n
foo=bar&
EOS
);
// OR, the completely equivalent
$request = new Request();
$request->setMethod(Request::METHOD_POST);
$request->setUri('/foo');
$request->getHeaders()->addHeaders(array(
'HeaderField1' => 'header-field-value1',
'HeaderField2' => 'header-field-value2',
));
$request->getPost()->set('foo', 'bar');
```
## Configuration Options
No configuration options are available.
## Available Methods
**Request::fromString**
`Request::fromString(string $string)`
A factory that produces a Request object from a well-formed HTTP Request string.
Returns `Zend\Http\Request`
<!-- -->
**setMethod**
`setMethod(string $method)`
Set the method for this request.
Returns `Zend\Http\Request`
<!-- -->
**getMethod**
`getMethod()`
Return the method for this request.
Returns string
<!-- -->
**setUri**
`setUri(string|Zend\Uri\Http $uri)`
Set the URI/URL for this request; this can be a string or an instance of `Zend\Uri\Http`.
Returns `Zend\Http\Request`
<!-- -->
**getUri**
`getUri()`
Return the URI for this request object.
Returns `Zend\Uri\Http`
<!-- -->
**getUriString**
`getUriString()`
Return the URI for this request object as a string.
Returns string
<!-- -->
**setVersion**
`setVersion(string $version)`
Set the HTTP version for this object, one of 1.0 or 1.1 (`Request::VERSION_10`,
`Request::VERSION_11`).
Returns `Zend\Http\Request`
<!-- -->
**getVersion**
`getVersion()`
Return the HTTP version for this request.
Returns string
<!-- -->
**setQuery**
`setQuery(Zend\Stdlib\ParametersInterface $query)`
Provide an alternate Parameter Container implementation for query parameters in this object. (This
is NOT the primary API for value setting; for that, see `getQuery()`).
Returns `Zend\Http\Request`
<!-- -->
**getQuery**
`getQuery(string|null $name, mixed|null $default)`
Return the parameter container responsible for query parameters or a single query parameter.
Returns `string`, `Zend\Stdlib\ParametersInterface`, or `null` depending on value of `$name`
argument.
<!-- -->
**setPost**
`setPost(Zend\Stdlib\ParametersInterface $post)`
Provide an alternate Parameter Container implementation for POST parameters in this object. (This is
NOT the primary API for value setting; for that, see `getPost()`).
Returns `Zend\Http\Request`
<!-- -->
**getPost**
`getPost(string|null $name, mixed|null $default)`
Return the parameter container responsible for POST parameters or a single POST parameter.
Returns `string`, `Zend\Stdlib\ParametersInterface`, or `null` depending on value of `$name`
argument.
<!-- -->
**getCookie**
`getCookie()`
Return the Cookie header, this is the same as calling
*$request-&gt;getHeaders()-&gt;get('Cookie');*.
Returns `Zend\Http\Header\Cookie`
<!-- -->
**setFiles**
`setFiles(Zend\Stdlib\ParametersInterface $files)`
Provide an alternate Parameter Container implementation for file parameters in this object, (This is
NOT the primary API for value setting; for that, see `getFiles()`).
Returns `Zend\Http\Request`
<!-- -->
**getFiles**
`getFiles(string|null $name, mixed|null $default)`
Return the parameter container responsible for file parameters or a single file parameter.
Returns `string`, `Zend\Stdlib\ParametersInterface`, or `null` depending on value of `$name`
argument.
<!-- -->
**setHeaders**
`setHeaders(Zend\Http\Headers $headers)`
Provide an alternate Parameter Container implementation for headers in this object, (this is NOT the
primary API for value setting, for that see `getHeaders()`).
Returns `Zend\Http\Request`
<!-- -->
**getHeaders**
`getHeaders(string|null $name, mixed|null $default)`
Return the container responsible for storing HTTP headers. This container exposes the primary API
for manipulating headers set in the HTTP request. See the section on
Zend\\\\Http\\\\Headers&lt;zend.http.headers&gt; for more information.
Returns `Zend\Http\Headers` if `$name` is `null`. Returns `Zend\Http\Header\HeaderInterface` or
`ArrayIterator` if `$name` matches one or more stored headers, respectively.
<!-- -->
**setMetadata**
`setMetadata(string|int|array|Traversable $spec, mixed $value)`
Set message metadata.
Non-destructive setting of message metadata; always adds to the metadata, never overwrites the
entire metadata container.
Returns `Zend\Http\Request`
<!-- -->
**getMetadata**
`getMetadata(null|string|int $key, null|mixed $default)`
Retrieve all metadata or a single metadatum as specified by key.
Returns mixed
<!-- -->
**setContent**
`setContent(mixed $value)`
Set request body (content).
Returns `Zend\Http\Request`
<!-- -->
**getContent**
`getContent()`
Get request body (content).
Returns mixed
<!-- -->
**isOptions**
`isOptions()`
Is this an OPTIONS method request?
Returns bool
<!-- -->
**isGet**
`isGet()`
Is this a GET method request?
Returns bool
<!-- -->
**isHead**
`isHead()`
Is this a HEAD method request?
Returns bool
<!-- -->
**isPost**
`isPost()`
Is this a POST method request?
Returns bool
<!-- -->
**isPut**
`isPut()`
Is this a PUT method request?
Returns bool
<!-- -->
**isDelete**
`isDelete()`
Is this a DELETE method request?
Returns bool
<!-- -->
**isTrace**
`isTrace()`
Is this a TRACE method request?
Returns bool
<!-- -->
**isConnect**
`isConnect()`
Is this a CONNECT method request?
Returns bool
<!-- -->
**isPatch**
`isPatch()`
Is this a PATCH method request?
Returns bool
<!-- -->
**isXmlHttpRequest**
`isXmlHttpRequest()`
Is this a Javascript XMLHttpRequest?
Returns bool
<!-- -->
**isFlashRequest**
`isFlashRequest()`
Is this a Flash request?
Returns bool
<!-- -->
**renderRequestLine**
`renderRequestLine()`
Return the formatted request line (first line) for this HTTP request.
Returns string
<!-- -->
**toString**
`toString()`
Returns string
<!-- -->
**\_\_toString**
`__toString()`
Allow PHP casting of this object.
Returns string
## Examples
**Generating a Request object from a string**
```php
use Zend\Http\Request;
$string = "GET /foo HTTP/1.1\r\n\r\nSome Content";
$request = Request::fromString($string);
$request->getMethod(); // returns Request::METHOD_GET
$request->getUri(); // returns Zend\Uri\Http object
$request->getUriString(); // returns '/foo'
$request->getVersion(); // returns Request::VERSION_11 or '1.1'
$request->getContent(); // returns 'Some Content'
```
**Retrieving and setting headers**
```php
use Zend\Http\Request;
use Zend\Http\Header\Cookie;
$request = new Request();
$request->getHeaders()->get('Content-Type'); // return content type
$request->getHeaders()->addHeader(new Cookie(array('foo' => 'bar')));
foreach ($request->getHeaders() as $header) {
echo $header->getFieldName() . ' with value ' . $header->getFieldValue();
}
```
**Retrieving and setting GET and POST values**
```php
use Zend\Http\Request;
$request = new Request();
// getPost() and getQuery() both return, by default, a Parameters object, which extends ArrayObject
$request->getPost()->foo = 'Foo value';
$request->getQuery()->bar = 'Bar value';
$request->getPost('foo'); // returns 'Foo value'
$request->getQuery()->offsetGet('bar'); // returns 'Bar value'
```
**Generating a formatted HTTP Request from a Request object**
```php
use Zend\Http\Request;
$request = new Request();
$request->setMethod(Request::METHOD_POST);
$request->setUri('/foo');
$request->getHeaders()->addHeaders(array(
'HeaderField1' => 'header-field-value1',
'HeaderField2' => 'header-field-value2',
));
$request->getPost()->set('foo', 'bar');
$request->setContent($request->getPost()->toString());
echo $request->toString();
/** Will produce:
POST /foo HTTP/1.1
HeaderField1: header-field-value1
HeaderField2: header-field-value2
foo=bar
*/
```

View File

@@ -0,0 +1,352 @@
# The Response Class
## Overview
The `Zend\Http\Response` class is responsible for providing a fluent API that allows a developer to
interact with all the various parts of an HTTP response.
A typical HTTP Response looks like this:
##
## | VERSION | CODE | REASON |
## | HEADERS |
## | BODY |
The first line of the response consists of the HTTP version, status code, and the reason string for
the provided status code; this is called the Response Line. Next is a set of headers; there can be 0
or an unlimited number of headers. The remainder of the response is the response body, which is
typically a string of HTML that will render on the client's browser, but which can also be a place
for request/response payload data typical of an AJAX request. More information on the structure and
specification of an HTTP response can be found in [RFC-2616 on the W3.org
site](http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html).
## Quick Start
Response objects can either be created from the provided `fromString()` factory, or, if you wish to
have a completely empty object to start with, by simply instantiating the `Zend\Http\Response`
class.
```php
use Zend\Http\Response;
$response = Response::fromString(<<<EOS
HTTP/1.0 200 OK
HeaderField1: header-field-value
HeaderField2: header-field-value2
<html>
<body>
Hello World
</body>
</html>
EOS);
// OR
$response = new Response();
$response->setStatusCode(Response::STATUS_CODE_200);
$response->getHeaders()->addHeaders(array(
'HeaderField1' => 'header-field-value',
'HeaderField2' => 'header-field-value2',
));
$response->setContent(<<<EOS
<html>
<body>
Hello World
</body>
</html>
EOS
);
```
## Configuration Options
No configuration options are available.
## Available Methods
**Response::fromString**
`Response::fromString(string $string)`
Populate object from string
Returns `Zend\Http\Response`
<!-- -->
**renderStatusLine**
`renderStatusLine()`
Render the status line header
Returns string
<!-- -->
**setHeaders**
`setHeaders(Zend\Http\Headers $headers)`
Provide an alternate Parameter Container implementation for headers in this object. (This is NOT the
primary API for value setting; for that, see `getHeaders()`.)
Returns `Zend\Http\Request`
<!-- -->
**getHeaders**
`getHeaders()`
Return the container responsible for storing HTTP headers. This container exposes the primary API
for manipulating headers set in the HTTP response. See the section on
Zend\\\\Http\\\\Headers&lt;zend.http.headers&gt; for more information.
Returns `Zend\Http\Headers`
<!-- -->
**setVersion**
`setVersion(string $version)`
Set the HTTP version for this object, one of 1.0 or 1.1 (`Request::VERSION_10`,
`Request::VERSION_11`).
Returns `Zend\Http\Request`.
<!-- -->
**getVersion**
`getVersion()`
Return the HTTP version for this request
Returns string
<!-- -->
**setStatusCode**
`setStatusCode(numeric $code)`
Set HTTP status code
Returns `Zend\Http\Response`
<!-- -->
**getStatusCode**
`getStatusCode()`
Retrieve HTTP status code
Returns int
<!-- -->
**setReasonPhrase**
`setReasonPhrase(string $reasonPhrase)`
Set custom HTTP status message
Returns `Zend\Http\Response`
<!-- -->
**getReasonPhrase**
`getReasonPhrase()`
Get HTTP status message
Returns string
<!-- -->
**isClientError**
`isClientError()`
Does the status code indicate a client error?
Returns bool
<!-- -->
**isForbidden**
`isForbidden()`
Is the request forbidden due to ACLs?
Returns bool
<!-- -->
**isInformational**
`isInformational()`
Is the current status "informational"?
Returns bool
<!-- -->
**isNotFound**
`isNotFound()`
Does the status code indicate the resource is not found?
Returns bool
<!-- -->
**isOk**
`isOk()`
Do we have a normal, OK response?
Returns bool
<!-- -->
**isServerError**
`isServerError()`
Does the status code reflect a server error?
Returns bool
<!-- -->
**isRedirect**
`isRedirect()`
Do we have a redirect?
Returns bool
<!-- -->
**isSuccess**
`isSuccess()`
Was the response successful?
Returns bool
<!-- -->
**decodeChunkedBody**
`decodeChunkedBody(string $body)`
Decode a "chunked" transfer-encoded body and return the decoded text
Returns string
<!-- -->
**decodeGzip**
`decodeGzip(string $body)`
Decode a gzip encoded message (when Content-encoding = gzip)
Currently requires PHP with zlib support
Returns string
<!-- -->
**decodeDeflate**
`decodeDeflate(string $body)`
Decode a zlib deflated message (when Content-encoding = deflate)
Currently requires PHP with zlib support
Returns string
<!-- -->
**setMetadata**
`setMetadata(string|int|array|Traversable $spec, mixed $value)`
Set message metadata
Non-destructive setting of message metadata; always adds to the metadata, never overwrites the
entire metadata container.
Returns `Zend\Stdlib\Message`
<!-- -->
**getMetadata**
`getMetadata(null|string|int $key, null|mixed $default)`
Retrieve all metadata or a single metadatum as specified by key
Returns mixed
<!-- -->
**setContent**
`setContent(mixed $value)`
Set message content
Returns `Zend\Stdlib\Message`
<!-- -->
**getContent**
`getContent()`
Get raw message content
Returns mixed
<!-- -->
**getBody**
`getBody()`
Get decoded message content
Returns mixed
<!-- -->
**toString**
`toString()`
Returns string
## Examples
**Generating a Response object from a string**
```php
use Zend\Http\Response;
$request = Response::fromString(<<<EOS
HTTP/1.0 200 OK
HeaderField1: header-field-value
HeaderField2: header-field-value2
<html>
<body>
Hello World
</body>
</html>
EOS);
```
**Generating a formatted HTTP Response from a Response object**
```php
use Zend\Http\Response;
$response = new Response();
$response->setStatusCode(Response::STATUS_CODE_200);
$response->getHeaders()->addHeaders(array(
'HeaderField1' => 'header-field-value',
'HeaderField2' => 'header-field-value2',
));
$response->setContent(<<<EOS
<html>
<body>
Hello World
</body>
</html>
EOS);
```

View File

@@ -0,0 +1,13 @@
{
"title": "Zend\\Http",
"target": "html/",
"content": [
"book/zend.http.request.md",
"book/zend.http.response.md",
"book/zend.http.headers.md",
"book/zend.http.client.md",
"book/zend.http.client.adapters.md",
"book/zend.http.client.advanced.md",
"book/zend.http.client-static.md"
]
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
use Zend\Stdlib\Message;
/**
* HTTP standard message (Request/Response)
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4
*/
abstract class AbstractMessage extends Message
{
/**#@+
* @const string Version constant numbers
*/
const VERSION_10 = '1.0';
const VERSION_11 = '1.1';
/**#@-*/
/**
* @var string
*/
protected $version = self::VERSION_11;
/**
* @var Headers|null
*/
protected $headers = null;
/**
* Set the HTTP version for this object, one of 1.0 or 1.1
* (AbstractMessage::VERSION_10, AbstractMessage::VERSION_11)
*
* @param string $version (Must be 1.0 or 1.1)
* @return AbstractMessage
* @throws Exception\InvalidArgumentException
*/
public function setVersion($version)
{
if ($version != self::VERSION_10 && $version != self::VERSION_11) {
throw new Exception\InvalidArgumentException(
'Not valid or not supported HTTP version: ' . $version
);
}
$this->version = $version;
return $this;
}
/**
* Return the HTTP version for this request
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Provide an alternate Parameter Container implementation for headers in this object,
* (this is NOT the primary API for value setting, for that see getHeaders())
*
* @see getHeaders()
* @param Headers $headers
* @return AbstractMessage
*/
public function setHeaders(Headers $headers)
{
$this->headers = $headers;
return $this;
}
/**
* Return the header container responsible for headers
*
* @return Headers
*/
public function getHeaders()
{
if ($this->headers === null || is_string($this->headers)) {
// this is only here for fromString lazy loading
$this->headers = (is_string($this->headers)) ? Headers::fromString($this->headers) : new Headers();
}
return $this->headers;
}
/**
* Allow PHP casting of this object
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
/**
* An interface description for Zend\Http\Client\Adapter classes.
*
* These classes are used as connectors for Zend\Http\Client, performing the
* tasks of connecting, writing, reading and closing connection to the server.
*/
interface AdapterInterface
{
/**
* Set the configuration array for the adapter
*
* @param array $options
*/
public function setOptions($options = []);
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
*/
public function connect($host, $port = 80, $secure = false);
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $url
* @param string $httpVer
* @param array $headers
* @param string $body
* @return string Request as text
*/
public function write($method, $url, $httpVer = '1.1', $headers = [], $body = '');
/**
* Read response from server
*
* @return string
*/
public function read();
/**
* Close the connection to the server
*
*/
public function close();
}

View File

@@ -0,0 +1,527 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Client\Adapter\AdapterInterface as HttpAdapter;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Stdlib\ArrayUtils;
/**
* An adapter class for Zend\Http\Client based on the curl extension.
* Curl requires libcurl. See for full requirements the PHP manual: http://php.net/curl
*/
class Curl implements HttpAdapter, StreamInterface
{
/**
* Parameters array
*
* @var array
*/
protected $config = [];
/**
* What host/port are we connected to?
*
* @var array
*/
protected $connectedTo = [null, null];
/**
* The curl session handle
*
* @var resource|null
*/
protected $curl = null;
/**
* List of cURL options that should never be overwritten
*
* @var array
*/
protected $invalidOverwritableCurlOptions;
/**
* Response gotten from server
*
* @var string
*/
protected $response = null;
/**
* Stream for storing output
*
* @var resource
*/
protected $outputStream;
/**
* Adapter constructor
*
* Config is set using setOptions()
*
* @throws AdapterException\InitializationException
*/
public function __construct()
{
if (!extension_loaded('curl')) {
throw new AdapterException\InitializationException(
'cURL extension has to be loaded to use this Zend\Http\Client adapter'
);
}
$this->invalidOverwritableCurlOptions = [
CURLOPT_HTTPGET,
CURLOPT_POST,
CURLOPT_UPLOAD,
CURLOPT_CUSTOMREQUEST,
CURLOPT_HEADER,
CURLOPT_RETURNTRANSFER,
CURLOPT_HTTPHEADER,
CURLOPT_INFILE,
CURLOPT_INFILESIZE,
CURLOPT_PORT,
CURLOPT_MAXREDIRS,
CURLOPT_CONNECTTIMEOUT,
];
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @return Curl
* @throws AdapterException\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
throw new AdapterException\InvalidArgumentException(
'Array or Traversable object expected, got ' . gettype($options)
);
}
/** Config Key Normalization */
foreach ($options as $k => $v) {
unset($options[$k]); // unset original value
$options[str_replace(['-', '_', ' ', '.'], '', strtolower($k))] = $v; // replace w/ normalized
}
if (isset($options['proxyuser']) && isset($options['proxypass'])) {
$this->setCurlOption(CURLOPT_PROXYUSERPWD, $options['proxyuser'] . ":" . $options['proxypass']);
unset($options['proxyuser'], $options['proxypass']);
}
if (isset($options['sslverifypeer'])) {
$this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $options['sslverifypeer']);
unset($options['sslverifypeer']);
}
foreach ($options as $k => $v) {
$option = strtolower($k);
switch ($option) {
case 'proxyhost':
$this->setCurlOption(CURLOPT_PROXY, $v);
break;
case 'proxyport':
$this->setCurlOption(CURLOPT_PROXYPORT, $v);
break;
default:
if (is_array($v) && isset($this->config[$option]) && is_array($this->config[$option])) {
$v = ArrayUtils::merge($this->config[$option], $v);
}
$this->config[$option] = $v;
break;
}
}
return $this;
}
/**
* Retrieve the array of all configuration options
*
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Direct setter for cURL adapter related options.
*
* @param string|int $option
* @param mixed $value
* @return Curl
*/
public function setCurlOption($option, $value)
{
if (!isset($this->config['curloptions'])) {
$this->config['curloptions'] = [];
}
$this->config['curloptions'][$option] = $value;
return $this;
}
/**
* Initialize curl
*
* @param string $host
* @param int $port
* @param bool $secure
* @return void
* @throws AdapterException\RuntimeException if unable to connect
*/
public function connect($host, $port = 80, $secure = false)
{
// If we're already connected, disconnect first
if ($this->curl) {
$this->close();
}
// Do the actual connection
$this->curl = curl_init();
if ($port != 80) {
curl_setopt($this->curl, CURLOPT_PORT, intval($port));
}
if (isset($this->config['timeout'])) {
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT_MS, $this->config['timeout'] * 1000);
} else {
curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT, $this->config['timeout']);
}
if (defined('CURLOPT_TIMEOUT_MS')) {
curl_setopt($this->curl, CURLOPT_TIMEOUT_MS, $this->config['timeout'] * 1000);
} else {
curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->config['timeout']);
}
}
if (isset($this->config['maxredirects'])) {
// Set Max redirects
curl_setopt($this->curl, CURLOPT_MAXREDIRS, $this->config['maxredirects']);
}
if (!$this->curl) {
$this->close();
throw new AdapterException\RuntimeException('Unable to Connect to ' . $host . ':' . $port);
}
if ($secure !== false) {
// Behave the same like Zend\Http\Adapter\Socket on SSL options.
if (isset($this->config['sslcert'])) {
curl_setopt($this->curl, CURLOPT_SSLCERT, $this->config['sslcert']);
}
if (isset($this->config['sslpassphrase'])) {
curl_setopt($this->curl, CURLOPT_SSLCERTPASSWD, $this->config['sslpassphrase']);
}
}
// Update connected_to
$this->connectedTo = [$host, $port];
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param float $httpVersion
* @param array $headers
* @param string $body
* @return string $request
* @throws AdapterException\RuntimeException If connection fails, connected
* to wrong host, no PUT file defined, unsupported method, or unsupported
* cURL option.
* @throws AdapterException\InvalidArgumentException if $method is currently not supported
*/
public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body = '')
{
// Make sure we're properly connected
if (!$this->curl) {
throw new AdapterException\RuntimeException("Trying to write but we are not connected");
}
if ($this->connectedTo[0] != $uri->getHost() || $this->connectedTo[1] != $uri->getPort()) {
throw new AdapterException\RuntimeException("Trying to write but we are connected to the wrong host");
}
// set URL
curl_setopt($this->curl, CURLOPT_URL, $uri->__toString());
// ensure correct curl call
$curlValue = true;
switch ($method) {
case 'GET':
$curlMethod = CURLOPT_HTTPGET;
break;
case 'POST':
$curlMethod = CURLOPT_POST;
break;
case 'PUT':
// There are two different types of PUT request, either a Raw Data string has been set
// or CURLOPT_INFILE and CURLOPT_INFILESIZE are used.
if (is_resource($body)) {
$this->config['curloptions'][CURLOPT_INFILE] = $body;
}
if (isset($this->config['curloptions'][CURLOPT_INFILE])) {
// Now we will probably already have Content-Length set, so that we have to delete it
// from $headers at this point:
if (!isset($headers['Content-Length'])
&& !isset($this->config['curloptions'][CURLOPT_INFILESIZE])
) {
throw new AdapterException\RuntimeException(
'Cannot set a file-handle for cURL option CURLOPT_INFILE'
. ' without also setting its size in CURLOPT_INFILESIZE.'
);
}
if (isset($headers['Content-Length'])) {
$this->config['curloptions'][CURLOPT_INFILESIZE] = (int) $headers['Content-Length'];
unset($headers['Content-Length']);
}
if (is_resource($body)) {
$body = '';
}
$curlMethod = CURLOPT_UPLOAD;
} else {
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "PUT";
}
break;
case 'PATCH':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "PATCH";
break;
case 'DELETE':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "DELETE";
break;
case 'OPTIONS':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "OPTIONS";
break;
case 'TRACE':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "TRACE";
break;
case 'HEAD':
$curlMethod = CURLOPT_CUSTOMREQUEST;
$curlValue = "HEAD";
break;
default:
// For now, through an exception for unsupported request methods
throw new AdapterException\InvalidArgumentException("Method '$method' currently not supported");
}
if (is_resource($body) && $curlMethod != CURLOPT_UPLOAD) {
throw new AdapterException\RuntimeException("Streaming requests are allowed only with PUT");
}
// get http version to use
$curlHttp = ($httpVersion == 1.1) ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
// mark as HTTP request and set HTTP method
curl_setopt($this->curl, CURLOPT_HTTP_VERSION, $curlHttp);
curl_setopt($this->curl, $curlMethod, $curlValue);
// Set the CURLINFO_HEADER_OUT flag so that we can retrieve the full request string later
curl_setopt($this->curl, CURLINFO_HEADER_OUT, true);
if ($this->outputStream) {
// headers will be read into the response
curl_setopt($this->curl, CURLOPT_HEADER, false);
curl_setopt($this->curl, CURLOPT_HEADERFUNCTION, [$this, "readHeader"]);
// and data will be written into the file
curl_setopt($this->curl, CURLOPT_FILE, $this->outputStream);
} else {
// ensure headers are also returned
curl_setopt($this->curl, CURLOPT_HEADER, true);
// ensure actual response is returned
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
}
// Treating basic auth headers in a special way
if (array_key_exists('Authorization', $headers) && 'Basic' == substr($headers['Authorization'], 0, 5)) {
curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($this->curl, CURLOPT_USERPWD, base64_decode(substr($headers['Authorization'], 6)));
unset($headers['Authorization']);
}
// set additional headers
if (!isset($headers['Accept'])) {
$headers['Accept'] = '';
}
$curlHeaders = [];
foreach ($headers as $key => $value) {
$curlHeaders[] = $key . ': ' . $value;
}
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);
/**
* Make sure POSTFIELDS is set after $curlMethod is set:
* @link http://de2.php.net/manual/en/function.curl-setopt.php#81161
*/
if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true)) {
curl_setopt($this->curl, CURLOPT_POSTFIELDS, $body);
} elseif ($curlMethod == CURLOPT_UPLOAD) {
// this covers a PUT by file-handle:
// Make the setting of this options explicit (rather than setting it through the loop following a bit lower)
// to group common functionality together.
curl_setopt($this->curl, CURLOPT_INFILE, $this->config['curloptions'][CURLOPT_INFILE]);
curl_setopt($this->curl, CURLOPT_INFILESIZE, $this->config['curloptions'][CURLOPT_INFILESIZE]);
unset($this->config['curloptions'][CURLOPT_INFILE]);
unset($this->config['curloptions'][CURLOPT_INFILESIZE]);
}
// set additional curl options
if (isset($this->config['curloptions'])) {
foreach ((array) $this->config['curloptions'] as $k => $v) {
if (!in_array($k, $this->invalidOverwritableCurlOptions)) {
if (curl_setopt($this->curl, $k, $v) == false) {
throw new AdapterException\RuntimeException(sprintf(
'Unknown or erroreous cURL option "%s" set',
$k
));
}
}
}
}
// send the request
$response = curl_exec($this->curl);
// if we used streaming, headers are already there
if (!is_resource($this->outputStream)) {
$this->response = $response;
}
$request = curl_getinfo($this->curl, CURLINFO_HEADER_OUT);
$request .= $body;
if (empty($this->response)) {
throw new AdapterException\RuntimeException("Error in cURL request: " . curl_error($this->curl));
}
// separating header from body because it is dangerous to accidentially replace strings in the body
$responseHeaderSize = curl_getinfo($this->curl, CURLINFO_HEADER_SIZE);
$responseHeaders = substr($this->response, 0, $responseHeaderSize);
// cURL automatically decodes chunked-messages, this means we have to
// disallow the Zend\Http\Response to do it again.
$responseHeaders = preg_replace("/Transfer-Encoding:\s*chunked\\r\\n/", "", $responseHeaders);
// cURL can automatically handle content encoding; prevent double-decoding from occurring
if (isset($this->config['curloptions'][CURLOPT_ENCODING])
&& '' == $this->config['curloptions'][CURLOPT_ENCODING]
) {
$responseHeaders = preg_replace("/Content-Encoding:\s*gzip\\r\\n/", '', $responseHeaders);
}
// cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string:
$responseHeaders = preg_replace(
"/HTTP\/1.0\s*200\s*Connection\s*established\\r\\n\\r\\n/",
'',
$responseHeaders
);
// replace old header with new, cleaned up, header
$this->response = substr_replace($this->response, $responseHeaders, 0, $responseHeaderSize);
// Eliminate multiple HTTP responses.
do {
$parts = preg_split('|(?:\r?\n){2}|m', $this->response, 2);
$again = false;
if (isset($parts[1]) && preg_match("|^HTTP/1\.[01](.*?)\r\n|mi", $parts[1])) {
$this->response = $parts[1];
$again = true;
}
} while ($again);
return $request;
}
/**
* Return read response from server
*
* @return string
*/
public function read()
{
return $this->response;
}
/**
* Close the connection to the server
*
*/
public function close()
{
if (is_resource($this->curl)) {
curl_close($this->curl);
}
$this->curl = null;
$this->connectedTo = [null, null];
}
/**
* Get cUrl Handle
*
* @return resource
*/
public function getHandle()
{
return $this->curl;
}
/**
* Set output stream for the response
*
* @param resource $stream
* @return Curl
*/
public function setOutputStream($stream)
{
$this->outputStream = $stream;
return $this;
}
/**
* Header reader function for CURL
*
* @param resource $curl
* @param string $header
* @return int
*/
public function readHeader($curl, $header)
{
$this->response .= $header;
return strlen($header);
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception\ExceptionInterface as HttpClientException;
interface ExceptionInterface extends HttpClientException
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
/**
*/
class InitializationException extends RuntimeException
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class OutOfRangeException extends Exception\OutOfRangeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
use Zend\Http\Client\Exception;
/**
*/
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter\Exception;
/**
*/
class TimeoutException extends RuntimeException implements ExceptionInterface
{
const READ_TIMEOUT = 1000;
}

View File

@@ -0,0 +1,294 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Zend\Http\Client;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Http\Response;
use Zend\Stdlib\ErrorHandler;
/**
* HTTP Proxy-supporting Zend\Http\Client adapter class, based on the default
* socket based adapter.
*
* Should be used if proxy HTTP access is required. If no proxy is set, will
* fall back to Zend\Http\Client\Adapter\Socket behavior. Just like the
* default Socket adapter, this adapter does not require any special extensions
* installed.
*/
class Proxy extends Socket
{
/**
* Parameters array
*
* @var array
*/
protected $config = [
'ssltransport' => 'ssl',
'sslcert' => null,
'sslpassphrase' => null,
'sslverifypeer' => true,
'sslcapath' => null,
'sslallowselfsigned' => false,
'sslusecontext' => false,
'proxy_host' => '',
'proxy_port' => 8080,
'proxy_user' => '',
'proxy_pass' => '',
'proxy_auth' => Client::AUTH_BASIC,
'persistent' => false
];
/**
* Whether HTTPS CONNECT was already negotiated with the proxy or not
*
* @var bool
*/
protected $negotiated = false;
/**
* Set the configuration array for the adapter
*
* @param array $options
*/
public function setOptions($options = [])
{
//enforcing that the proxy keys are set in the form proxy_*
foreach ($options as $k => $v) {
if (preg_match("/^proxy[a-z]+/", $k)) {
$options['proxy_' . substr($k, 5, strlen($k))] = $v;
unset($options[$k]);
}
}
parent::setOptions($options);
}
/**
* Connect to the remote server
*
* Will try to connect to the proxy server. If no proxy was set, will
* fall back to the target server (behave like regular Socket adapter)
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws AdapterException\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
// If no proxy is set, fall back to Socket adapter
if (! $this->config['proxy_host']) {
parent::connect($host, $port, $secure);
return;
}
/* Url might require stream context even if proxy connection doesn't */
if ($secure) {
$this->config['sslusecontext'] = true;
}
// Connect (a non-secure connection) to the proxy server
parent::connect(
$this->config['proxy_host'],
$this->config['proxy_port'],
false
);
}
/**
* Send request to the proxy server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @throws AdapterException\RuntimeException
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// If no proxy is set, fall back to default Socket adapter
if (! $this->config['proxy_host']) {
return parent::write($method, $uri, $httpVer, $headers, $body);
}
// Make sure we're properly connected
if (! $this->socket) {
throw new AdapterException\RuntimeException("Trying to write but we are not connected");
}
$host = $this->config['proxy_host'];
$port = $this->config['proxy_port'];
if ($this->connectedTo[0] != "tcp://$host" || $this->connectedTo[1] != $port) {
throw new AdapterException\RuntimeException("Trying to write but we are connected to the wrong proxy server");
}
// Add Proxy-Authorization header
if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) {
$headers['proxy-authorization'] = Client::encodeAuthHeader(
$this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth']
);
}
// if we are proxying HTTPS, preform CONNECT handshake with the proxy
if ($uri->getScheme() == 'https' && (! $this->negotiated)) {
$this->connectHandshake($uri->getHost(), $uri->getPort(), $httpVer, $headers);
$this->negotiated = true;
}
// Save request method for later
$this->method = $method;
// Build request headers
if ($this->negotiated) {
$path = $uri->getPath();
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "$method $path HTTP/$httpVer\r\n";
} else {
$request = "$method $uri HTTP/$httpVer\r\n";
}
// Add all headers to the request string
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = "$k: $v";
}
$request .= "$v\r\n";
}
if (is_resource($body)) {
$request .= "\r\n";
} else {
// Add the request body
$request .= "\r\n" . $body;
}
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (!$test) {
throw new AdapterException\RuntimeException("Error writing request to proxy server", 0, $error);
}
if (is_resource($body)) {
if (stream_copy_to_stream($body, $this->socket) == 0) {
throw new AdapterException\RuntimeException('Error writing request to server');
}
}
return $request;
}
/**
* Preform handshaking with HTTPS proxy using CONNECT method
*
* @param string $host
* @param int $port
* @param string $httpVer
* @param array $headers
* @throws AdapterException\RuntimeException
*/
protected function connectHandshake($host, $port = 443, $httpVer = '1.1', array &$headers = [])
{
$request = "CONNECT $host:$port HTTP/$httpVer\r\n" .
"Host: " . $host . "\r\n";
// Add the user-agent header
if (isset($this->config['useragent'])) {
$request .= "User-agent: " . $this->config['useragent'] . "\r\n";
}
// If the proxy-authorization header is set, send it to proxy but remove
// it from headers sent to target host
if (isset($headers['proxy-authorization'])) {
$request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n";
unset($headers['proxy-authorization']);
}
$request .= "\r\n";
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (!$test) {
throw new AdapterException\RuntimeException("Error writing request to proxy server", 0, $error);
}
// Read response headers only
$response = '';
$gotStatus = false;
ErrorHandler::start();
while ($line = fgets($this->socket)) {
$gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
if ($gotStatus) {
$response .= $line;
if (!rtrim($line)) {
break;
}
}
}
ErrorHandler::stop();
// Check that the response from the proxy is 200
if (Response::fromString($response)->getStatusCode() != 200) {
throw new AdapterException\RuntimeException("Unable to connect to HTTPS proxy. Server response: " . $response);
}
// If all is good, switch socket to secure mode. We have to fall back
// through the different modes
$modes = [
STREAM_CRYPTO_METHOD_TLS_CLIENT,
STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
STREAM_CRYPTO_METHOD_SSLv2_CLIENT
];
$success = false;
foreach ($modes as $mode) {
$success = stream_socket_enable_crypto($this->socket, true, $mode);
if ($success) {
break;
}
}
if (! $success) {
throw new AdapterException\RuntimeException("Unable to connect to" .
" HTTPS server through proxy: could not negotiate secure connection.");
}
}
/**
* Close the connection to the server
*
*/
public function close()
{
parent::close();
$this->negotiated = false;
}
/**
* Destructor: make sure the socket is disconnected
*
*/
public function __destruct()
{
if ($this->socket) {
$this->close();
}
}
}

View File

@@ -0,0 +1,634 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Client\Adapter\AdapterInterface as HttpAdapter;
use Zend\Http\Client\Adapter\Exception as AdapterException;
use Zend\Http\Response;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\ErrorHandler;
/**
* A sockets based (stream\socket\client) adapter class for Zend\Http\Client. Can be used
* on almost every PHP environment, and does not require any special extensions.
*/
class Socket implements HttpAdapter, StreamInterface
{
/**
* Map SSL transport wrappers to stream crypto method constants
*
* @var array
*/
protected static $sslCryptoTypes = [
'ssl' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
'sslv2' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
'sslv3' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
'tls' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
];
/**
* The socket for server connection
*
* @var resource|null
*/
protected $socket = null;
/**
* What host/port are we connected to?
*
* @var array
*/
protected $connectedTo = [null, null];
/**
* Stream for storing output
*
* @var resource
*/
protected $outStream = null;
/**
* Parameters array
*
* @var array
*/
protected $config = [
'persistent' => false,
'ssltransport' => 'ssl',
'sslcert' => null,
'sslpassphrase' => null,
'sslverifypeer' => true,
'sslcafile' => null,
'sslcapath' => null,
'sslallowselfsigned' => false,
'sslusecontext' => false,
];
/**
* Request method - will be set by write() and might be used by read()
*
* @var string
*/
protected $method = null;
/**
* Stream context
*
* @var resource
*/
protected $context = null;
/**
* Adapter constructor, currently empty. Config is set using setOptions()
*
*/
public function __construct()
{
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @throws AdapterException\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (!is_array($options)) {
throw new AdapterException\InvalidArgumentException(
'Array or Zend\Config object expected, got ' . gettype($options)
);
}
foreach ($options as $k => $v) {
$this->config[strtolower($k)] = $v;
}
}
/**
* Retrieve the array of all configuration options
*
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Set the stream context for the TCP connection to the server
*
* Can accept either a pre-existing stream context resource, or an array
* of stream options, similar to the options array passed to the
* stream_context_create() PHP function. In such case a new stream context
* will be created using the passed options.
*
* @since Zend Framework 1.9
*
* @param mixed $context Stream context or array of context options
* @throws Exception\InvalidArgumentException
* @return Socket
*/
public function setStreamContext($context)
{
if (is_resource($context) && get_resource_type($context) == 'stream-context') {
$this->context = $context;
} elseif (is_array($context)) {
$this->context = stream_context_create($context);
} else {
// Invalid parameter
throw new AdapterException\InvalidArgumentException(
"Expecting either a stream context resource or array, got " . gettype($context)
);
}
return $this;
}
/**
* Get the stream context for the TCP connection to the server.
*
* If no stream context is set, will create a default one.
*
* @return resource
*/
public function getStreamContext()
{
if (! $this->context) {
$this->context = stream_context_create();
}
return $this->context;
}
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws AdapterException\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
// If we are connected to the wrong host, disconnect first
$connectedHost = (strpos($this->connectedTo[0], '://'))
? substr($this->connectedTo[0], (strpos($this->connectedTo[0], '://') + 3), strlen($this->connectedTo[0]))
: $this->connectedTo[0];
if ($connectedHost != $host || $this->connectedTo[1] != $port) {
if (is_resource($this->socket)) {
$this->close();
}
}
// Now, if we are not connected, connect
if (!is_resource($this->socket) || ! $this->config['keepalive']) {
$context = $this->getStreamContext();
if ($secure || $this->config['sslusecontext']) {
if ($this->config['sslverifypeer'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'verify_peer', $this->config['sslverifypeer'])) {
throw new AdapterException\RuntimeException('Unable to set sslverifypeer option');
}
}
if ($this->config['sslcafile']) {
if (!stream_context_set_option($context, 'ssl', 'cafile', $this->config['sslcafile'])) {
throw new AdapterException\RuntimeException('Unable to set sslcafile option');
}
}
if ($this->config['sslcapath']) {
if (!stream_context_set_option($context, 'ssl', 'capath', $this->config['sslcapath'])) {
throw new AdapterException\RuntimeException('Unable to set sslcapath option');
}
}
if ($this->config['sslallowselfsigned'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'allow_self_signed', $this->config['sslallowselfsigned'])) {
throw new AdapterException\RuntimeException('Unable to set sslallowselfsigned option');
}
}
if ($this->config['sslcert'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'local_cert', $this->config['sslcert'])) {
throw new AdapterException\RuntimeException('Unable to set sslcert option');
}
}
if ($this->config['sslpassphrase'] !== null) {
if (!stream_context_set_option($context, 'ssl', 'passphrase', $this->config['sslpassphrase'])) {
throw new AdapterException\RuntimeException('Unable to set sslpassphrase option');
}
}
}
$flags = STREAM_CLIENT_CONNECT;
if ($this->config['persistent']) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
ErrorHandler::start();
$this->socket = stream_socket_client(
$host . ':' . $port,
$errno,
$errstr,
(int) $this->config['timeout'],
$flags,
$context
);
$error = ErrorHandler::stop();
if (!$this->socket) {
$this->close();
throw new AdapterException\RuntimeException(
sprintf(
'Unable to connect to %s:%d%s',
$host,
$port,
($error ? ' . Error #' . $error->getCode() . ': ' . $error->getMessage() : '')
),
0,
$error
);
}
// Set the stream timeout
if (!stream_set_timeout($this->socket, (int) $this->config['timeout'])) {
throw new AdapterException\RuntimeException('Unable to set the connection timeout');
}
if ($secure || $this->config['sslusecontext']) {
if ($this->config['ssltransport'] && isset(static::$sslCryptoTypes[$this->config['ssltransport']])) {
$sslCryptoMethod = static::$sslCryptoTypes[$this->config['ssltransport']];
} else {
$sslCryptoMethod = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
}
ErrorHandler::start();
$test = stream_socket_enable_crypto($this->socket, true, $sslCryptoMethod);
$error = ErrorHandler::stop();
if (!$test || $error) {
// Error handling is kind of difficult when it comes to SSL
$errorString = '';
if (extension_loaded('openssl')) {
while (($sslError = openssl_error_string()) != false) {
$errorString .= "; SSL error: $sslError";
}
}
$this->close();
if ((! $errorString) && $this->config['sslverifypeer']) {
// There's good chance our error is due to sslcapath not being properly set
if (! ($this->config['sslcafile'] || $this->config['sslcapath'])) {
$errorString = 'make sure the "sslcafile" or "sslcapath" option are properly set for the environment.';
} elseif ($this->config['sslcafile'] && !is_file($this->config['sslcafile'])) {
$errorString = 'make sure the "sslcafile" option points to a valid SSL certificate file';
} elseif ($this->config['sslcapath'] && !is_dir($this->config['sslcapath'])) {
$errorString = 'make sure the "sslcapath" option points to a valid SSL certificate directory';
}
}
if ($errorString) {
$errorString = ": $errorString";
}
throw new AdapterException\RuntimeException(sprintf(
'Unable to enable crypto on TCP connection %s%s',
$host,
$errorString
), 0, $error);
}
$host = $this->config['ssltransport'] . "://" . $host;
} else {
$host = 'tcp://' . $host;
}
// Update connectedTo
$this->connectedTo = [$host, $port];
}
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @throws AdapterException\RuntimeException
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// Make sure we're properly connected
if (! $this->socket) {
throw new AdapterException\RuntimeException('Trying to write but we are not connected');
}
$host = $uri->getHost();
$host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host;
if ($this->connectedTo[0] != $host || $this->connectedTo[1] != $uri->getPort()) {
throw new AdapterException\RuntimeException('Trying to write but we are connected to the wrong host');
}
// Save request method for later
$this->method = $method;
// Build request headers
$path = $uri->getPath();
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "{$method} {$path} HTTP/{$httpVer}\r\n";
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = ucfirst($k) . ": $v";
}
$request .= "$v\r\n";
}
if (is_resource($body)) {
$request .= "\r\n";
} else {
// Add the request body
$request .= "\r\n" . $body;
}
// Send the request
ErrorHandler::start();
$test = fwrite($this->socket, $request);
$error = ErrorHandler::stop();
if (false === $test) {
throw new AdapterException\RuntimeException('Error writing request to server', 0, $error);
}
if (is_resource($body)) {
if (stream_copy_to_stream($body, $this->socket) == 0) {
throw new AdapterException\RuntimeException('Error writing request to server');
}
}
return $request;
}
/**
* Read response from server
*
* @throws AdapterException\RuntimeException
* @return string
*/
public function read()
{
// First, read headers only
$response = '';
$gotStatus = false;
while (($line = fgets($this->socket)) !== false) {
$gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
if ($gotStatus) {
$response .= $line;
if (rtrim($line) === '') {
break;
}
}
}
$this->_checkSocketReadTimeout();
$responseObj= Response::fromString($response);
$statusCode = $responseObj->getStatusCode();
// Handle 100 and 101 responses internally by restarting the read again
if ($statusCode == 100 || $statusCode == 101) {
return $this->read();
}
// Check headers to see what kind of connection / transfer encoding we have
$headers = $responseObj->getHeaders();
/**
* Responses to HEAD requests and 204 or 304 responses are not expected
* to have a body - stop reading here
*/
if ($statusCode == 304 || $statusCode == 204 ||
$this->method == \Zend\Http\Request::METHOD_HEAD) {
// Close the connection if requested to do so by the server
$connection = $headers->get('connection');
if ($connection && $connection->getFieldValue() == 'close') {
$this->close();
}
return $response;
}
// If we got a 'transfer-encoding: chunked' header
$transferEncoding = $headers->get('transfer-encoding');
$contentLength = $headers->get('content-length');
if ($transferEncoding !== false) {
if (strtolower($transferEncoding->getFieldValue()) == 'chunked') {
do {
$line = fgets($this->socket);
$this->_checkSocketReadTimeout();
$chunk = $line;
// Figure out the next chunk size
$chunksize = trim($line);
if (! ctype_xdigit($chunksize)) {
$this->close();
throw new AdapterException\RuntimeException('Invalid chunk size "' .
$chunksize . '" unable to read chunked body');
}
// Convert the hexadecimal value to plain integer
$chunksize = hexdec($chunksize);
// Read next chunk
$readTo = ftell($this->socket) + $chunksize;
do {
$currentPos = ftell($this->socket);
if ($currentPos >= $readTo) {
break;
}
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream, $readTo - $currentPos) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$line = fread($this->socket, $readTo - $currentPos);
if ($line === false || strlen($line) === 0) {
$this->_checkSocketReadTimeout();
break;
}
$chunk .= $line;
}
} while (! feof($this->socket));
ErrorHandler::start();
$chunk .= fgets($this->socket);
ErrorHandler::stop();
$this->_checkSocketReadTimeout();
if (!$this->outStream) {
$response .= $chunk;
}
} while ($chunksize > 0);
} else {
$this->close();
throw new AdapterException\RuntimeException('Cannot handle "' .
$transferEncoding->getFieldValue() . '" transfer encoding');
}
// We automatically decode chunked-messages when writing to a stream
// this means we have to disallow the Zend\Http\Response to do it again
if ($this->outStream) {
$response = str_ireplace("Transfer-Encoding: chunked\r\n", '', $response);
}
// Else, if we got the content-length header, read this number of bytes
} elseif ($contentLength !== false) {
// If we got more than one Content-Length header (see ZF-9404) use
// the last value sent
if (is_array($contentLength)) {
$contentLength = $contentLength[count($contentLength) - 1];
}
$contentLength = $contentLength->getFieldValue();
$currentPos = ftell($this->socket);
for ($readTo = $currentPos + $contentLength;
$readTo > $currentPos;
$currentPos = ftell($this->socket)) {
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream, $readTo - $currentPos) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$chunk = fread($this->socket, $readTo - $currentPos);
if ($chunk === false || strlen($chunk) === 0) {
$this->_checkSocketReadTimeout();
break;
}
$response .= $chunk;
}
// Break if the connection ended prematurely
if (feof($this->socket)) {
break;
}
}
// Fallback: just read the response until EOF
} else {
do {
if ($this->outStream) {
if (stream_copy_to_stream($this->socket, $this->outStream) == 0) {
$this->_checkSocketReadTimeout();
break;
}
} else {
$buff = fread($this->socket, 8192);
if ($buff === false || strlen($buff) === 0) {
$this->_checkSocketReadTimeout();
break;
} else {
$response .= $buff;
}
}
} while (feof($this->socket) === false);
$this->close();
}
// Close the connection if requested to do so by the server
$connection = $headers->get('connection');
if ($connection && $connection->getFieldValue() == 'close') {
$this->close();
}
return $response;
}
/**
* Close the connection to the server
*
*/
public function close()
{
if (is_resource($this->socket)) {
ErrorHandler::start();
fclose($this->socket);
ErrorHandler::stop();
}
$this->socket = null;
$this->connectedTo = [null, null];
}
/**
* Check if the socket has timed out - if so close connection and throw
* an exception
*
* @throws AdapterException\TimeoutException with READ_TIMEOUT code
*/
protected function _checkSocketReadTimeout()
{
if ($this->socket) {
$info = stream_get_meta_data($this->socket);
$timedout = $info['timed_out'];
if ($timedout) {
$this->close();
throw new AdapterException\TimeoutException(
"Read timed out after {$this->config['timeout']} seconds",
AdapterException\TimeoutException::READ_TIMEOUT
);
}
}
}
/**
* Set output stream for the response
*
* @param resource $stream
* @return \Zend\Http\Client\Adapter\Socket
*/
public function setOutputStream($stream)
{
$this->outStream = $stream;
return $this;
}
/**
* Destructor: make sure the socket is disconnected
*
* If we are in persistent TCP mode, will not close the connection
*
*/
public function __destruct()
{
if (! $this->config['persistent']) {
if ($this->socket) {
$this->close();
}
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
/**
* An interface description for Zend\Http\Client\Adapter\Stream classes.
*
* This interface describes Zend\Http\Client\Adapter which supports streaming.
*/
interface StreamInterface
{
/**
* Set output stream
*
* This function sets output stream where the result will be stored.
*
* @param resource $stream Stream to write the output to
*
*/
public function setOutputStream($stream);
}

View File

@@ -0,0 +1,216 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Adapter;
use Traversable;
use Zend\Http\Response;
use Zend\Stdlib\ArrayUtils;
/**
* A testing-purposes adapter.
*
* Should be used to test all components that rely on Zend\Http\Client,
* without actually performing an HTTP request. You should instantiate this
* object manually, and then set it as the client's adapter. Then, you can
* set the expected response using the setResponse() method.
*/
class Test implements AdapterInterface
{
/**
* Parameters array
*
* @var array
*/
protected $config = [];
/**
* Buffer of responses to be returned by the read() method. Can be
* set using setResponse() and addResponse().
*
* @var array
*/
protected $responses = ["HTTP/1.1 400 Bad Request\r\n\r\n"];
/**
* Current position in the response buffer
*
* @var int
*/
protected $responseIndex = 0;
/**
* Whether or not the next request will fail with an exception
*
* @var bool
*/
protected $nextRequestWillFail = false;
/**
* Adapter constructor, currently empty. Config is set using setOptions()
*/
public function __construct()
{
}
/**
* Set the nextRequestWillFail flag
*
* @param bool $flag
* @return \Zend\Http\Client\Adapter\Test
*/
public function setNextRequestWillFail($flag)
{
$this->nextRequestWillFail = (bool) $flag;
return $this;
}
/**
* Set the configuration array for the adapter
*
* @param array|Traversable $options
* @throws Exception\InvalidArgumentException
*/
public function setOptions($options = [])
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (! is_array($options)) {
throw new Exception\InvalidArgumentException(
'Array or Traversable object expected, got ' . gettype($options)
);
}
foreach ($options as $k => $v) {
$this->config[strtolower($k)] = $v;
}
}
/**
* Connect to the remote server
*
* @param string $host
* @param int $port
* @param bool $secure
* @throws Exception\RuntimeException
*/
public function connect($host, $port = 80, $secure = false)
{
if ($this->nextRequestWillFail) {
$this->nextRequestWillFail = false;
throw new Exception\RuntimeException('Request failed');
}
}
/**
* Send request to the remote server
*
* @param string $method
* @param \Zend\Uri\Uri $uri
* @param string $httpVer
* @param array $headers
* @param string $body
* @return string Request as string
*/
public function write($method, $uri, $httpVer = '1.1', $headers = [], $body = '')
{
// Build request headers
$path = $uri->getPath();
if (empty($path)) {
$path = '/';
}
if ($uri->getQuery()) {
$path .= '?' . $uri->getQuery();
}
$request = "{$method} {$path} HTTP/{$httpVer}\r\n";
foreach ($headers as $k => $v) {
if (is_string($k)) {
$v = ucfirst($k) . ": $v";
}
$request .= "$v\r\n";
}
// Add the request body
$request .= "\r\n" . $body;
// Do nothing - just return the request as string
return $request;
}
/**
* Return the response set in $this->setResponse()
*
* @return string
*/
public function read()
{
if ($this->responseIndex >= count($this->responses)) {
$this->responseIndex = 0;
}
return $this->responses[$this->responseIndex++];
}
/**
* Close the connection (dummy)
*
*/
public function close()
{
}
/**
* Set the HTTP response(s) to be returned by this adapter
*
* @param \Zend\Http\Response|array|string $response
*/
public function setResponse($response)
{
if ($response instanceof Response) {
$response = $response->toString();
}
$this->responses = (array) $response;
$this->responseIndex = 0;
}
/**
* Add another response to the response buffer.
*
* @param string|Response $response
*/
public function addResponse($response)
{
if ($response instanceof Response) {
$response = $response->toString();
}
$this->responses[] = $response;
}
/**
* Sets the position of the response buffer. Selects which
* response will be returned on the next call to read().
*
* @param int $index
* @throws Exception\OutOfRangeException
*/
public function setResponseIndex($index)
{
if ($index < 0 || $index >= count($this->responses)) {
throw new Exception\OutOfRangeException(
'Index out of range of response buffer size');
}
$this->responseIndex = $index;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception\ExceptionInterface as HttpException;
interface ExceptionInterface extends HttpException
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
/**
*/
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
class OutOfRangeException extends Exception\OutOfRangeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Client\Exception;
use Zend\Http\Exception;
/**
*/
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
/**
* Http static client
*/
class ClientStatic
{
/**
* @var Client
*/
protected static $client;
/**
* Get the static HTTP client
*
* @param array|Traversable $options
* @return Client
*/
protected static function getStaticClient($options = null)
{
if (!isset(static::$client) || $options !== null) {
static::$client = new Client(null, $options);
}
return static::$client;
}
/**
* HTTP GET METHOD (static)
*
* @param string $url
* @param array $query
* @param array $headers
* @param mixed $body
* @param array|Traversable $clientOptions
* @return Response|bool
*/
public static function get($url, $query = [], $headers = [], $body = null, $clientOptions = null)
{
if (empty($url)) {
return false;
}
$request= new Request();
$request->setUri($url);
$request->setMethod(Request::METHOD_GET);
if (!empty($query) && is_array($query)) {
$request->getQuery()->fromArray($query);
}
if (!empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (!empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
/**
* HTTP POST METHOD (static)
*
* @param string $url
* @param array $params
* @param array $headers
* @param mixed $body
* @param array|Traversable $clientOptions
* @throws Exception\InvalidArgumentException
* @return Response|bool
*/
public static function post($url, $params, $headers = [], $body = null, $clientOptions = null)
{
if (empty($url)) {
return false;
}
$request= new Request();
$request->setUri($url);
$request->setMethod(Request::METHOD_POST);
if (!empty($params) && is_array($params)) {
$request->getPost()->fromArray($params);
} else {
throw new Exception\InvalidArgumentException('The array of post parameters is empty');
}
if (!isset($headers['Content-Type'])) {
$headers['Content-Type'] = Client::ENC_URLENCODED;
}
if (!empty($headers) && is_array($headers)) {
$request->getHeaders()->addHeaders($headers);
}
if (!empty($body)) {
$request->setContent($body);
}
return static::getStaticClient($clientOptions)->send($request);
}
}

View File

@@ -0,0 +1,367 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http;
use ArrayIterator;
use Zend\Http\Header\SetCookie;
use Zend\Uri;
/**
* A Zend\Http\Cookies object is designed to contain and maintain HTTP cookies, and should
* be used along with Zend\Http\Client in order to manage cookies across HTTP requests and
* responses.
*
* The class contains an array of Zend\Http\Header\Cookie objects. Cookies can be added
* automatically from a request or manually. Then, the Cookies class can find and return the
* cookies needed for a specific HTTP request.
*
* A special parameter can be passed to all methods of this class that return cookies: Cookies
* can be returned either in their native form (as Zend\Http\Header\Cookie objects) or as strings -
* the later is suitable for sending as the value of the "Cookie" header in an HTTP request.
* You can also choose, when returning more than one cookie, whether to get an array of strings
* (by passing Zend\Http\Client\Cookies::COOKIE_STRING_ARRAY) or one unified string for all cookies
* (by passing Zend\Http\Client\Cookies::COOKIE_STRING_CONCAT).
*
* @link http://wp.netscape.com/newsref/std/cookie_spec.html for some specs.
*/
class Cookies extends Headers
{
/**
* Return cookie(s) as a Zend\Http\Cookie object
*
*/
const COOKIE_OBJECT = 0;
/**
* Return cookie(s) as a string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_ARRAY = 1;
/**
* Return all cookies as one long string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_CONCAT = 2;
/**
* Return all cookies as one long string (strict mode)
* - Single space after the semi-colon separating each cookie
* - Remove trailing semi-colon, if any
*/
const COOKIE_STRING_CONCAT_STRICT = 3;
/**
* @var array
*/
protected $cookies = [];
/**
* @var \Zend\Http\Headers
*/
protected $headers = null;
/**
* @var array
*/
protected $rawCookies;
/**
* @static
* @throws Exception\RuntimeException
* @param $string
* @return void
*/
public static function fromString($string)
{
throw new Exception\RuntimeException(
__CLASS__ . '::' . __FUNCTION__ . ' should not be used as a factory, use '
. __NAMESPACE__ . '\Headers::fromtString() instead.'
);
}
/**
* Add a cookie to the class. Cookie should be passed either as a Zend\Http\Header\SetCookie object
* or as a string - in which case an object is created from the string.
*
* @param SetCookie|string $cookie
* @param Uri\Uri|string $refUri Optional reference URI (for domain, path, secure)
* @throws Exception\InvalidArgumentException
*/
public function addCookie($cookie, $refUri = null)
{
if (is_string($cookie)) {
$cookie = SetCookie::fromString($cookie, $refUri);
}
if ($cookie instanceof SetCookie) {
$domain = $cookie->getDomain();
$path = $cookie->getPath();
if (!isset($this->cookies[$domain])) {
$this->cookies[$domain] = [];
}
if (!isset($this->cookies[$domain][$path])) {
$this->cookies[$domain][$path] = [];
}
$this->cookies[$domain][$path][$cookie->getName()] = $cookie;
$this->rawCookies[] = $cookie;
} else {
throw new Exception\InvalidArgumentException('Supplient argument is not a valid cookie string or object');
}
}
/**
* Parse an HTTP response, adding all the cookies set in that response
*
* @param Response $response
* @param Uri\Uri|string $refUri Requested URI
*/
public function addCookiesFromResponse(Response $response, $refUri)
{
$cookieHdrs = $response->getHeaders()->get('Set-Cookie');
if (is_array($cookieHdrs) || $cookieHdrs instanceof ArrayIterator) {
foreach ($cookieHdrs as $cookie) {
$this->addCookie($cookie, $refUri);
}
} elseif (is_string($cookieHdrs)) {
$this->addCookie($cookieHdrs, $refUri);
}
}
/**
* Get all cookies in the cookie jar as an array
*
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @return array|string
*/
public function getAllCookies($retAs = self::COOKIE_OBJECT)
{
$cookies = $this->_flattenCookiesArray($this->cookies, $retAs);
return $cookies;
}
/**
* Return an array of all cookies matching a specific request according to the request URI,
* whether session cookies should be sent or not, and the time to consider as "now" when
* checking cookie expiry time.
*
* @param string|Uri\Uri $uri URI to check against (secure, domain, path)
* @param bool $matchSessionCookies Whether to send session cookies
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings
* @param int $now Override the current time when checking for expiry time
* @throws Exception\InvalidArgumentException if invalid URI specified
* @return array|string
*/
public function getMatchingCookies(
$uri,
$matchSessionCookies = true,
$retAs = self::COOKIE_OBJECT,
$now = null
) {
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (!$uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException("Invalid URI string or object passed");
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; does not contain a host');
}
// First, reduce the array of cookies to only those matching domain and path
$cookies = $this->_matchDomain($host);
$cookies = $this->_matchPath($cookies, $uri->getPath());
$cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT);
// Next, run Cookie->match on all cookies to check secure, time and session matching
$ret = [];
foreach ($cookies as $cookie) {
if ($cookie->match($uri, $matchSessionCookies, $now)) {
$ret[] = $cookie;
}
}
// Now, use self::_flattenCookiesArray again - only to convert to the return format ;)
$ret = $this->_flattenCookiesArray($ret, $retAs);
return $ret;
}
/**
* Get a specific cookie according to a URI and name
*
* @param Uri\Uri|string $uri The uri (domain and path) to match
* @param string $cookieName The cookie's name
* @param int $retAs Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @throws Exception\InvalidArgumentException if invalid URI specified or invalid $retAs value
* @return SetCookie|string
*/
public function getCookie($uri, $cookieName, $retAs = self::COOKIE_OBJECT)
{
if (is_string($uri)) {
$uri = Uri\UriFactory::factory($uri, 'http');
} elseif (!$uri instanceof Uri\Uri) {
throw new Exception\InvalidArgumentException('Invalid URI specified');
}
$host = $uri->getHost();
if (empty($host)) {
throw new Exception\InvalidArgumentException('Invalid URI specified; host missing');
}
// Get correct cookie path
$path = $uri->getPath();
$lastSlashPos = strrpos($path, '/') ?: 0;
$path = substr($path, 0, $lastSlashPos);
if (! $path) {
$path = '/';
}
if (isset($this->cookies[$uri->getHost()][$path][$cookieName])) {
$cookie = $this->cookies[$uri->getHost()][$path][$cookieName];
switch ($retAs) {
case self::COOKIE_OBJECT:
return $cookie;
case self::COOKIE_STRING_ARRAY:
case self::COOKIE_STRING_CONCAT:
return $cookie->__toString();
default:
throw new Exception\InvalidArgumentException("Invalid value passed for \$retAs: {$retAs}");
}
}
return false;
}
/**
* Helper function to recursively flatten an array. Should be used when exporting the
* cookies array (or parts of it)
*
* @param \Zend\Http\Header\SetCookie|array $ptr
* @param int $retAs What value to return
* @return array|string
*/
protected function _flattenCookiesArray($ptr, $retAs = self::COOKIE_OBJECT)
{
if (is_array($ptr)) {
$ret = ($retAs == self::COOKIE_STRING_CONCAT ? '' : []);
foreach ($ptr as $item) {
if ($retAs == self::COOKIE_STRING_CONCAT) {
$ret .= $this->_flattenCookiesArray($item, $retAs);
} else {
$ret = array_merge($ret, $this->_flattenCookiesArray($item, $retAs));
}
}
return $ret;
} elseif ($ptr instanceof SetCookie) {
switch ($retAs) {
case self::COOKIE_STRING_ARRAY:
return [$ptr->__toString()];
case self::COOKIE_STRING_CONCAT:
return $ptr->__toString();
case self::COOKIE_OBJECT:
default:
return [$ptr];
}
}
return;
}
/**
* Return a subset of the cookies array matching a specific domain
*
* @param string $domain
* @return array
*/
protected function _matchDomain($domain)
{
$ret = [];
foreach (array_keys($this->cookies) as $cdom) {
if (SetCookie::matchCookieDomain($cdom, $domain)) {
$ret[$cdom] = $this->cookies[$cdom];
}
}
return $ret;
}
/**
* Return a subset of a domain-matching cookies that also match a specified path
*
* @param array $domains
* @param string $path
* @return array
*/
protected function _matchPath($domains, $path)
{
$ret = [];
foreach ($domains as $dom => $pathsArray) {
foreach (array_keys($pathsArray) as $cpath) {
if (SetCookie::matchCookiePath($cpath, $path)) {
if (! isset($ret[$dom])) {
$ret[$dom] = [];
}
$ret[$dom][$cpath] = $pathsArray[$cpath];
}
}
}
return $ret;
}
/**
* Create a new Cookies object and automatically load into it all the
* cookies set in a Response object. If $uri is set, it will be
* considered as the requested URI for setting default domain and path
* of the cookie.
*
* @param Response $response HTTP Response object
* @param Uri\Uri|string $refUri The requested URI
* @return Cookies
* @todo Add the $uri functionality.
*/
public static function fromResponse(Response $response, $refUri)
{
$jar = new static();
$jar->addCookiesFromResponse($response, $refUri);
return $jar;
}
/**
* Tells if the array of cookies is empty
*
* @return bool
*/
public function isEmpty()
{
return count($this) == 0;
}
/**
* Empties the cookieJar of any cookie
*
* @return Cookies
*/
public function reset()
{
$this->cookies = $this->rawCookies = [];
return $this;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class OutOfRangeException extends \OutOfRangeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Exception;
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,456 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use stdClass;
/**
* Abstract Accept Header
*
* Naming conventions:
*
* Accept: audio/mp3; q=0.2; version=0.5, audio/basic+mp3
* |------------------------------------------------------| header line
* |------| field name
* |-----------------------------------------------| field value
* |-------------------------------| field value part
* |------| type
* |--| subtype
* |--| format
* |----| subtype
* |---| format
* |-------------------| parameter set
* |-----------| parameter
* |-----| parameter key
* |--| parameter value
* |---| priority
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
* @author Dolf Schimmel - Freeaqingme
*/
abstract class AbstractAccept implements HeaderInterface
{
/**
*
* @var stdClass[]
*/
protected $fieldValueParts = [];
protected $regexAddType;
/**
* Determines if since last mutation the stack was sorted
*
* @var bool
*/
protected $sorted = false;
/**
* Parse a full header line or just the field value part.
*
* @param string $headerLine
*/
public function parseHeaderLine($headerLine)
{
if (strpos($headerLine, ':') !== false) {
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
if (strtolower($name) !== strtolower($this->getFieldName())) {
$value = $headerLine; // This is just for preserve the BC.
}
} else {
$value = $headerLine;
}
HeaderValue::assertValid($value);
foreach ($this->getFieldValuePartsFromHeaderLine($value) as $value) {
$this->addFieldValuePartToQueue($value);
}
}
/**
* Factory method: parse Accept header string
*
* @param string $headerLine
* @return Accept
*/
public static function fromString($headerLine)
{
$obj = new static();
$obj->parseHeaderLine($headerLine);
return $obj;
}
/**
* Parse the Field Value Parts represented by a header line
*
* @param string $headerLine
* @throws Exception\InvalidArgumentException If header is invalid
* @return array
*/
public function getFieldValuePartsFromHeaderLine($headerLine)
{
// process multiple accept values, they may be between quotes
if (!preg_match_all('/(?:[^,"]|"(?:[^\\\"]|\\\.)*")+/', $headerLine, $values)
|| !isset($values[0])
) {
throw new Exception\InvalidArgumentException(
'Invalid header line for ' . $this->getFieldName() . ' header string'
);
}
$out = [];
foreach ($values[0] as $value) {
$value = trim($value);
$out[] = $this->parseFieldValuePart($value);
}
return $out;
}
/**
* Parse the accept params belonging to a media range
*
* @param string $fieldValuePart
* @return stdClass
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $subtypeWhole = $type = $fieldValuePart;
if ($pos = strpos($fieldValuePart, ';')) {
$type = substr($fieldValuePart, 0, $pos);
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
}
$format = '*';
$subtype = '*';
return (object) [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw)
];
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return array
*/
protected function getParametersFromFieldValuePart($fieldValuePart)
{
$params = [];
if ((($pos = strpos($fieldValuePart, ';')) !== false)) {
preg_match_all('/(?:[^;"]|"(?:[^\\\"]|\\\.)*")+/', $fieldValuePart, $paramsStrings);
if (isset($paramsStrings[0])) {
array_shift($paramsStrings[0]);
$paramsStrings = $paramsStrings[0];
}
foreach ($paramsStrings as $param) {
$explode = explode('=', $param, 2);
$value = trim($explode[1]);
if (isset($value[0]) && $value[0] == '"' && substr($value, -1) == '"') {
$value = substr(substr($value, 1), 0, -1);
}
$params[trim($explode[0])] = stripslashes($value);
}
}
return $params;
}
/**
* Get field value
*
* @param array|null $values
* @return string
*/
public function getFieldValue($values = null)
{
if (!$values) {
return $this->getFieldValue($this->fieldValueParts);
}
$strings = [];
foreach ($values as $value) {
$params = $value->params;
array_walk($params, [$this, 'assembleAcceptParam']);
$strings[] = implode(';', [$value->typeString] + $params);
}
return implode(', ', $strings);
}
/**
* Assemble and escape the field value parameters based on RFC 2616 section 2.1
*
* @todo someone should review this thoroughly
* @param string $value
* @param string $key
* @return string
*/
protected function assembleAcceptParam(&$value, $key)
{
$separators = ['(', ')', '<', '>', '@', ',', ';', ':',
'/', '[', ']', '?', '=', '{', '}', ' ', "\t"];
$escaped = preg_replace_callback(
'/[[:cntrl:]"\\\\]/', // escape cntrl, ", \
function ($v) {
return '\\' . $v[0];
},
$value
);
if ($escaped == $value && !array_intersect(str_split($value), $separators)) {
$value = $key . '=' . $value;
} else {
$value = $key . '="' . $escaped . '"';
}
return $value;
}
/**
* Add a type, with the given priority
*
* @param string $type
* @param int|float $priority
* @param array (optional) $params
* @throws Exception\InvalidArgumentException
* @return Accept
*/
protected function addType($type, $priority = 1, array $params = [])
{
if (!preg_match($this->regexAddType, $type)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid type; received "%s"',
__METHOD__,
(string) $type
));
}
if (!is_int($priority) && !is_float($priority) && !is_numeric($priority)
|| $priority > 1 || $priority < 0
) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a numeric priority; received %s',
__METHOD__,
(string) $priority
));
}
if ($priority != 1) {
$params = ['q' => sprintf('%01.1f', $priority)] + $params;
}
$assembledString = $this->getFieldValue(
[(object) ['typeString' => $type, 'params' => $params]]
);
$value = $this->parseFieldValuePart($assembledString);
$this->addFieldValuePartToQueue($value);
return $this;
}
/**
* Does the header have the requested type?
*
* @param array|string $matchAgainst
* @return bool
*/
protected function hasType($matchAgainst)
{
return (bool) $this->match($matchAgainst);
}
/**
* Match a media string against this header
*
* @param array|string $matchAgainst
* @return Accept\FieldValuePArt\AcceptFieldValuePart|bool The matched value or false
*/
public function match($matchAgainst)
{
if (is_string($matchAgainst)) {
$matchAgainst = $this->getFieldValuePartsFromHeaderLine($matchAgainst);
}
foreach ($this->getPrioritized() as $left) {
foreach ($matchAgainst as $right) {
if ($right->type == '*' || $left->type == '*') {
if ($this->matchAcceptParams($left, $right)) {
$left->setMatchedAgainst($right);
return $left;
}
}
if ($left->type == $right->type) {
if (($left->subtype == $right->subtype || ($right->subtype == '*' || $left->subtype == '*')) &&
($left->format == $right->format || $right->format == '*' || $left->format == '*')
) {
if ($this->matchAcceptParams($left, $right)) {
$left->setMatchedAgainst($right);
return $left;
}
}
}
}
}
return false;
}
/**
* Return a match where all parameters in argument #1 match those in argument #2
*
* @param array $match1
* @param array $match2
* @return bool|array
*/
protected function matchAcceptParams($match1, $match2)
{
foreach ($match2->params as $key => $value) {
if (isset($match1->params[$key])) {
if (strpos($value, '-')) {
preg_match(
'/^(?|([^"-]*)|"([^"]*)")-(?|([^"-]*)|"([^"]*)")\z/',
$value,
$pieces
);
if (count($pieces) == 3 &&
(version_compare($pieces[1], $match1->params[$key], '<=') xor
version_compare($pieces[2], $match1->params[$key], '>=')
)
) {
return false;
}
} elseif (strpos($value, '|')) {
$options = explode('|', $value);
$good = false;
foreach ($options as $option) {
if ($option == $match1->params[$key]) {
$good = true;
break;
}
}
if (!$good) {
return false;
}
} elseif ($match1->params[$key] != $value) {
return false;
}
}
}
return $match1;
}
/**
* Add a key/value combination to the internal queue
*
* @param stdClass $value
* @return number
*/
protected function addFieldValuePartToQueue($value)
{
$this->fieldValueParts[] = $value;
$this->sorted = false;
}
/**
* Sort the internal Field Value Parts
*
* @See rfc2616 sect 14.1
* Media ranges can be overridden by more specific media ranges or
* specific media types. If more than one media range applies to a given
* type, the most specific reference has precedence. For example,
*
* Accept: text/*, text/html, text/html;level=1, * /*
*
* have the following precedence:
*
* 1) text/html;level=1
* 2) text/html
* 3) text/*
* 4) * /*
*
* @return number
*/
protected function sortFieldValueParts()
{
$sort = function ($a, $b) { // If A has higher precedence than B, return -1.
if ($a->priority > $b->priority) {
return -1;
} elseif ($a->priority < $b->priority) {
return 1;
}
// Asterisks
$values = ['type', 'subtype', 'format'];
foreach ($values as $value) {
if ($a->$value == '*' && $b->$value != '*') {
return 1;
} elseif ($b->$value == '*' && $a->$value != '*') {
return -1;
}
}
if ($a->type == 'application' && $b->type != 'application') {
return -1;
} elseif ($b->type == 'application' && $a->type != 'application') {
return 1;
}
//@todo count number of dots in case of type==application in subtype
// So far they're still the same. Longest string length may be more specific
if (strlen($a->raw) == strlen($b->raw)) {
return 0;
}
return (strlen($a->raw) > strlen($b->raw)) ? -1 : 1;
};
usort($this->fieldValueParts, $sort);
$this->sorted = true;
}
/**
* @return array with all the keys, values and parameters this header represents:
*/
public function getPrioritized()
{
if (!$this->sorted) {
$this->sortFieldValueParts();
}
return $this->fieldValueParts;
}
}

View File

@@ -0,0 +1,268 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use DateTime;
use DateTimeZone;
/**
* Abstract Date/Time Header
* Supports headers that have date/time as value
*
* @see Zend\Http\Header\Date
* @see Zend\Http\Header\Expires
* @see Zend\Http\Header\IfModifiedSince
* @see Zend\Http\Header\IfUnmodifiedSince
* @see Zend\Http\Header\LastModified
*
* Note for 'Location' header:
* While RFC 1945 requires an absolute URI, most of the browsers also support relative URI
* This class allows relative URIs, and let user retrieve URI instance if strict validation needed
*/
abstract class AbstractDate implements HeaderInterface
{
/**
* Date formats according to RFC 2616
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
*/
const DATE_RFC1123 = 0;
const DATE_RFC1036 = 1;
const DATE_ANSIC = 2;
/**
* Date instance for this header
*
* @var DateTime
*/
protected $date = null;
/**
* Date output format
*
* @var string
*/
protected static $dateFormat = 'D, d M Y H:i:s \G\M\T';
/**
* Date formats defined by RFC 2616. RFC 1123 date is required
* RFC 1036 and ANSI C formats are provided for compatibility with old servers/clients
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
*
* @var array
*/
protected static $dateFormats = [
self::DATE_RFC1123 => 'D, d M Y H:i:s \G\M\T',
self::DATE_RFC1036 => 'D, d M y H:i:s \G\M\T',
self::DATE_ANSIC => 'D M j H:i:s Y',
];
/**
* Create date-based header from string
*
* @param string $headerLine
* @return AbstractDate
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$dateHeader = new static();
list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate($date);
return $dateHeader;
}
/**
* Create date-based header from strtotime()-compatible string
*
* @param int|string $time
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromTimeString($time)
{
return static::fromTimestamp(strtotime($time));
}
/**
* Create date-based header from Unix timestamp
*
* @param int $time
*
* @return self
*
* @throws Exception\InvalidArgumentException
*/
public static function fromTimestamp($time)
{
$dateHeader = new static();
if (! $time || ! is_numeric($time)) {
throw new Exception\InvalidArgumentException(
'Invalid time for "' . $dateHeader->getFieldName() . '" header string'
);
}
$dateHeader->setDate(new DateTime('@' . $time));
return $dateHeader;
}
/**
* Set date output format
*
* @param int $format
* @throws Exception\InvalidArgumentException
*/
public static function setDateFormat($format)
{
if (!isset(static::$dateFormats[$format])) {
throw new Exception\InvalidArgumentException(
"No constant defined for provided date format: {$format}"
);
}
static::$dateFormat = static::$dateFormats[$format];
}
/**
* Return current date output format
*
* @return string
*/
public static function getDateFormat()
{
return static::$dateFormat;
}
/**
* Set the date for this header, this can be a string or an instance of \DateTime
*
* @param string|DateTime $date
* @return AbstractDate
* @throws Exception\InvalidArgumentException
*/
public function setDate($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (!($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$date->setTimezone(new DateTimeZone('GMT'));
$this->date = $date;
return $this;
}
/**
* Return date for this header
*
* @return string
*/
public function getDate()
{
return $this->date()->format(static::$dateFormat);
}
/**
* Return date for this header as an instance of \DateTime
*
* @return DateTime
*/
public function date()
{
if ($this->date === null) {
$this->date = new DateTime(null, new DateTimeZone('GMT'));
}
return $this->date;
}
/**
* Compare provided date to date for this header
* Returns < 0 if date in header is less than $date; > 0 if it's greater, and 0 if they are equal.
* @see \strcmp()
*
* @param string|DateTime $date
* @return int
* @throws Exception\InvalidArgumentException
*/
public function compareTo($date)
{
if (is_string($date)) {
try {
$date = new DateTime($date, new DateTimeZone('GMT'));
} catch (\Exception $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid Date passed as string (%s)', (string) $date),
$e->getCode(),
$e
);
}
} elseif (!($date instanceof DateTime)) {
throw new Exception\InvalidArgumentException('Date must be an instance of \DateTime or a string');
}
$dateTimestamp = $date->getTimestamp();
$thisTimestamp = $this->date()->getTimestamp();
return ($thisTimestamp === $dateTimestamp) ? 0 : (($thisTimestamp > $dateTimestamp) ? 1 : -1);
}
/**
* Get header value as formatted date
*
* @return string
*/
public function getFieldValue()
{
return $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getDate();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,145 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\Exception as UriException;
use Zend\Uri\UriFactory;
use Zend\Uri\UriInterface;
/**
* Abstract Location Header
* Supports headers that have URI as value
* @see Zend\Http\Header\Location
* @see Zend\Http\Header\ContentLocation
* @see Zend\Http\Header\Referer
*
* Note for 'Location' header:
* While RFC 1945 requires an absolute URI, most of the browsers also support relative URI
* This class allows relative URIs, and let user retrieve URI instance if strict validation needed
*/
abstract class AbstractLocation implements HeaderInterface
{
/**
* URI for this header
*
* @var UriInterface
*/
protected $uri = null;
/**
* Create location-based header from string
*
* @param string $headerLine
* @return AbstractLocation
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$locationHeader = new static();
// ZF-5520 - IIS bug, no space after colon
list($name, $uri) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($locationHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $locationHeader->getFieldName() . '" header string'
);
}
HeaderValue::assertValid($uri);
$locationHeader->setUri(trim($uri));
return $locationHeader;
}
/**
* Set the URI/URL for this header, this can be a string or an instance of Zend\Uri\Http
*
* @param string|UriInterface $uri
* @return AbstractLocation
* @throws Exception\InvalidArgumentException
*/
public function setUri($uri)
{
if (is_string($uri)) {
try {
$uri = UriFactory::factory($uri);
} catch (UriException\InvalidUriPartException $e) {
throw new Exception\InvalidArgumentException(
sprintf('Invalid URI passed as string (%s)', (string) $uri),
$e->getCode(),
$e
);
}
} elseif (!($uri instanceof UriInterface)) {
throw new Exception\InvalidArgumentException('URI must be an instance of Zend\Uri\Http or a string');
}
$this->uri = $uri;
return $this;
}
/**
* Return the URI for this header
*
* @return string
*/
public function getUri()
{
if ($this->uri instanceof UriInterface) {
return $this->uri->toString();
}
return $this->uri;
}
/**
* Return the URI for this header as an instance of Zend\Uri\Http
*
* @return UriInterface
*/
public function uri()
{
if ($this->uri === null || is_string($this->uri)) {
$this->uri = UriFactory::factory($this->uri);
}
return $this->uri;
}
/**
* Get header value as URI string
*
* @return string
*/
public function getFieldValue()
{
return $this->getUri();
}
/**
* Output header line
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getUri();
}
/**
* Allow casting to string
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,119 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class Accept extends AbstractAccept
{
/**
* @var string
*/
protected $regexAddType = '#^([a-zA-Z+-]+|\*)/(\*|[a-zA-Z0-9+-]+)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept: ' . $this->getFieldValue();
}
/**
* Add a media type, with the given priority
*
* @param string $type
* @param int|float $priority
* @param array $params
* @return Accept
*/
public function addMediaType($type, $priority = 1, array $params = [])
{
return $this->addType($type, $priority, $params);
}
/**
* Does the header have the requested media type?
*
* @param string $type
* @return bool
*/
public function hasMediaType($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return FieldValuePart\AcceptFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '/')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim($fieldValuePart);
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '/')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '/') + 1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$pos = strpos($subtype, '+');
if (false !== $pos) {
$format = trim(substr($subtype, $pos + 1));
$subtype = trim(substr($subtype, 0, $pos));
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw),
];
return new FieldValuePart\AcceptFieldValuePart((object) $aggregated);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
abstract class AbstractFieldValuePart
{
/**
* Internal object used for value retrieval
* @var object
*/
private $internalValues;
/**
* A Field Value Part this Field Value Part matched against.
* @var AbstractFieldValuePart
*/
protected $matchedAgainst;
/**
*
* @param object $internalValues
*/
public function __construct($internalValues)
{
$this->internalValues = $internalValues;
}
/**
* Set a Field Value Part this Field Value Part matched against.
*
* @param AbstractFieldValuePart $matchedAgainst
* @return AbstractFieldValuePart provides fluent interface
*/
public function setMatchedAgainst(AbstractFieldValuePart $matchedAgainst)
{
$this->matchedAgainst = $matchedAgainst;
return $this;
}
/**
* Get a Field Value Part this Field Value Part matched against.
*
* @return AbstractFieldValuePart|null
*/
public function getMatchedAgainst()
{
return $this->matchedAgainst;
}
/**
*
* @return object
*/
protected function getInternalValues()
{
return $this->internalValues;
}
/**
* @return string $typeString
*/
public function getTypeString()
{
return $this->getInternalValues()->typeString;
}
/**
* @return float $priority
*/
public function getPriority()
{
return (float) $this->getInternalValues()->priority;
}
/**
* @return \stdClass $params
*/
public function getParams()
{
return (object) $this->getInternalValues()->params;
}
/**
* @return string $raw
*/
public function getRaw()
{
return $this->getInternalValues()->raw;
}
/**
*
* @param mixed
* @return mixed
*/
public function __get($key)
{
return $this->getInternalValues()->$key;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class AcceptFieldValuePart extends AbstractFieldValuePart
{
/**
* @return string
*/
public function getSubtype()
{
return $this->getInternalValues()->subtype;
}
/**
* @return string
*/
public function getSubtypeRaw()
{
return $this->getInternalValues()->subtypeRaw;
}
/**
* @return string
*/
public function getFormat()
{
return $this->getInternalValues()->format;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class CharsetFieldValuePart extends AbstractFieldValuePart
{
/**
*
* @return string
*/
public function getCharset()
{
return $this->getInternalValues()->type;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class EncodingFieldValuePart extends AbstractFieldValuePart
{
/**
*
* @return string
*/
public function getEncoding()
{
return $this->getInternalValues()->type;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Accept\FieldValuePart;
/**
* Field Value Part
*
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
*/
class LanguageFieldValuePart extends AbstractFieldValuePart
{
public function getLanguage()
{
return $this->getInternalValues()->typeString;
}
public function getPrimaryTag()
{
return $this->getInternalValues()->type;
}
public function getSubTag()
{
return $this->getInternalValues()->subtype;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Charset Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
*/
class AcceptCharset extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Charset';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Charset: ' . $this->getFieldValue();
}
/**
* Add a charset, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addCharset($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested charset?
*
* @param string $type
* @return bool
*/
public function hasCharset($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\CharsetFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$internalValues = parent::parseFieldValuePart($fieldValuePart);
return new FieldValuePart\CharsetFieldValuePart($internalValues);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Encoding Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
*/
class AcceptEncoding extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Encoding';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Encoding: ' . $this->getFieldValue();
}
/**
* Add an encoding, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addEncoding($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested encoding?
*
* @param string $type
* @return bool
*/
public function hasEncoding($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\EncodingFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$internalValues = parent::parseFieldValuePart($fieldValuePart);
return new FieldValuePart\EncodingFieldValuePart($internalValues);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Header\Accept\FieldValuePart;
/**
* Accept Language Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
*/
class AcceptLanguage extends AbstractAccept
{
protected $regexAddType = '#^([a-zA-Z0-9+-]+|\*)$#';
/**
* Get field name
*
* @return string
*/
public function getFieldName()
{
return 'Accept-Language';
}
/**
* Cast to string
*
* @return string
*/
public function toString()
{
return 'Accept-Language: ' . $this->getFieldValue();
}
/**
* Add a language, with the given priority
*
* @param string $type
* @param int|float $priority
* @return Accept
*/
public function addLanguage($type, $priority = 1)
{
return $this->addType($type, $priority);
}
/**
* Does the header have the requested language?
*
* @param string $type
* @return bool
*/
public function hasLanguage($type)
{
return $this->hasType($type);
}
/**
* Parse the keys contained in the header line
*
* @param string $fieldValuePart
* @return \Zend\Http\Header\Accept\FieldValuePart\LanguageFieldValuePart
* @see \Zend\Http\Header\AbstractAccept::parseFieldValuePart()
*/
protected function parseFieldValuePart($fieldValuePart)
{
$raw = $fieldValuePart;
if ($pos = strpos($fieldValuePart, '-')) {
$type = trim(substr($fieldValuePart, 0, $pos));
} else {
$type = trim(substr($fieldValuePart, 0));
}
$params = $this->getParametersFromFieldValuePart($fieldValuePart);
if ($pos = strpos($fieldValuePart, ';')) {
$fieldValuePart = $type = trim(substr($fieldValuePart, 0, $pos));
}
if (strpos($fieldValuePart, '-')) {
$subtypeWhole = $format = $subtype = trim(substr($fieldValuePart, strpos($fieldValuePart, '-')+1));
} else {
$subtypeWhole = '';
$format = '*';
$subtype = '*';
}
$aggregated = [
'typeString' => trim($fieldValuePart),
'type' => $type,
'subtype' => $subtype,
'subtypeRaw' => $subtypeWhole,
'format' => $format,
'priority' => isset($params['q']) ? $params['q'] : 1,
'params' => $params,
'raw' => trim($raw)
];
return new FieldValuePart\LanguageFieldValuePart((object) $aggregated);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Accept Ranges Header
*
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.5
*/
class AcceptRanges implements HeaderInterface
{
protected $rangeUnit;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'accept-ranges') {
throw new Exception\InvalidArgumentException(
'Invalid header line for Accept-Ranges string'
);
}
$header = new static($value);
return $header;
}
public function __construct($rangeUnit = null)
{
if ($rangeUnit) {
$this->setRangeUnit($rangeUnit);
}
}
public function getFieldName()
{
return 'Accept-Ranges';
}
public function getFieldValue()
{
return $this->getRangeUnit();
}
public function setRangeUnit($rangeUnit)
{
HeaderValue::assertValid($rangeUnit);
$this->rangeUnit = $rangeUnit;
return $this;
}
public function getRangeUnit()
{
return $this->rangeUnit;
}
public function toString()
{
return 'Accept-Ranges: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Age HTTP Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.6
*/
class Age implements HeaderInterface
{
/**
* Estimate of the amount of time in seconds since the response
*
* @var int
*/
protected $deltaSeconds;
/**
* Create Age header from string
*
* @param string $headerLine
* @return Age
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'age') {
throw new Exception\InvalidArgumentException('Invalid header line for Age string: "' . $name . '"');
}
$header = new static($value);
return $header;
}
public function __construct($deltaSeconds = null)
{
if ($deltaSeconds) {
$this->setDeltaSeconds($deltaSeconds);
}
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Age';
}
/**
* Get header value (number of seconds)
*
* @return int
*/
public function getFieldValue()
{
return $this->getDeltaSeconds();
}
/**
* Set number of seconds
*
* @param int $delta
* @return RetryAfter
*/
public function setDeltaSeconds($delta)
{
if (! is_int($delta) && ! is_numeric($delta)) {
throw new Exception\InvalidArgumentException('Invalid delta provided');
}
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Return header line
* In case of overflow RFC states to set value of 2147483648 (2^31)
*
* @return string
*/
public function toString()
{
return 'Age: ' . (($this->deltaSeconds >= PHP_INT_MAX) ? '2147483648' : $this->deltaSeconds);
}
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Http\Request;
/**
* Allow Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7
*/
class Allow implements HeaderInterface
{
/**
* List of request methods
* true states that method is allowed, false - disallowed
* By default GET and POST are allowed
*
* @var array
*/
protected $methods = [
Request::METHOD_OPTIONS => false,
Request::METHOD_GET => true,
Request::METHOD_HEAD => false,
Request::METHOD_POST => true,
Request::METHOD_PUT => false,
Request::METHOD_DELETE => false,
Request::METHOD_TRACE => false,
Request::METHOD_CONNECT => false,
Request::METHOD_PATCH => false,
];
/**
* Create Allow header from header line
*
* @param string $headerLine
* @return Allow
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'allow') {
throw new Exception\InvalidArgumentException('Invalid header line for Allow string: "' . $name . '"');
}
$header = new static();
$header->disallowMethods(array_keys($header->getAllMethods()));
$header->allowMethods(explode(',', $value));
return $header;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Allow';
}
/**
* Get comma-separated list of allowed methods
*
* @return string
*/
public function getFieldValue()
{
return implode(', ', array_keys($this->methods, true, true));
}
/**
* Get list of all defined methods
*
* @return array
*/
public function getAllMethods()
{
return $this->methods;
}
/**
* Get list of allowed methods
*
* @return array
*/
public function getAllowedMethods()
{
return array_keys($this->methods, true, true);
}
/**
* Allow methods or list of methods
*
* @param array|string $allowedMethods
* @return Allow
*/
public function allowMethods($allowedMethods)
{
foreach ((array) $allowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to whitelist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = true;
}
return $this;
}
/**
* Disallow methods or list of methods
*
* @param array|string $disallowedMethods
* @return Allow
*/
public function disallowMethods($disallowedMethods)
{
foreach ((array) $disallowedMethods as $method) {
$method = trim(strtoupper($method));
if (preg_match('/\s/', $method)) {
throw new Exception\InvalidArgumentException(sprintf(
'Unable to blacklist method; "%s" is not a valid method',
$method
));
}
$this->methods[$method] = false;
}
return $this;
}
/**
* Convenience alias for @see disallowMethods()
*
* @param array|string $disallowedMethods
* @return Allow
*/
public function denyMethods($disallowedMethods)
{
return $this->disallowMethods($disallowedMethods);
}
/**
* Check whether method is allowed
*
* @param string $method
* @return bool
*/
public function isAllowedMethod($method)
{
$method = trim(strtoupper($method));
// disallow unknown method
if (! isset($this->methods[$method])) {
$this->methods[$method] = false;
}
return $this->methods[$method];
}
/**
* Return header as string
*
* @return string
*/
public function toString()
{
return 'Allow: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.ietf.org/rfc/rfc2617.txt
*/
class AuthenticationInfo implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'authentication-info') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Authentication-Info string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Authentication-Info';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Authentication-Info: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.8
*/
class Authorization implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'authorization') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Authorization string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Authorization';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Authorization: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,252 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
*/
class CacheControl implements HeaderInterface
{
/**
* @var string
*/
protected $value;
/**
* Array of Cache-Control directives
*
* @var array
*/
protected $directives = [];
/**
* Creates a CacheControl object from a headerLine
*
* @param string $headerLine
* @throws Exception\InvalidArgumentException
* @return CacheControl
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'cache-control') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Cache-Control string: ""',
$name
));
}
HeaderValue::assertValid($value);
$directives = static::parseValue($value);
// @todo implementation details
$header = new static();
foreach ($directives as $key => $value) {
$header->addDirective($key, $value);
}
return $header;
}
/**
* Required from HeaderDescription interface
*
* @return string
*/
public function getFieldName()
{
return 'Cache-Control';
}
/**
* Checks if the internal directives array is empty
*
* @return bool
*/
public function isEmpty()
{
return empty($this->directives);
}
/**
* Add a directive
* For directives like 'max-age=60', $value = '60'
* For directives like 'private', use the default $value = true
*
* @param string $key
* @param string|bool $value
* @return CacheControl - provides the fluent interface
*/
public function addDirective($key, $value = true)
{
HeaderValue::assertValid($key);
if (! is_bool($value)) {
HeaderValue::assertValid($value);
}
$this->directives[$key] = $value;
return $this;
}
/**
* Check the internal directives array for a directive
*
* @param string $key
* @return bool
*/
public function hasDirective($key)
{
return array_key_exists($key, $this->directives);
}
/**
* Fetch the value of a directive from the internal directive array
*
* @param string $key
* @return string|null
*/
public function getDirective($key)
{
return array_key_exists($key, $this->directives) ? $this->directives[$key] : null;
}
/**
* Remove a directive
*
* @param string $key
* @return CacheControl - provides the fluent interface
*/
public function removeDirective($key)
{
unset($this->directives[$key]);
return $this;
}
/**
* Assembles the directives into a comma-delimited string
*
* @return string
*/
public function getFieldValue()
{
$parts = [];
ksort($this->directives);
foreach ($this->directives as $key => $value) {
if (true === $value) {
$parts[] = $key;
} else {
if (preg_match('#[^a-zA-Z0-9._-]#', $value)) {
$value = '"' . $value.'"';
}
$parts[] = "$key=$value";
}
}
return implode(', ', $parts);
}
/**
* Returns a string representation of the HTTP Cache-Control header
*
* @return string
*/
public function toString()
{
return 'Cache-Control: ' . $this->getFieldValue();
}
/**
* Internal function for parsing the value part of a
* HTTP Cache-Control header
*
* @param string $value
* @throws Exception\InvalidArgumentException
* @return array
*/
protected static function parseValue($value)
{
$value = trim($value);
$directives = [];
// handle empty string early so we don't need a separate start state
if ($value == '') {
return $directives;
}
$lastMatch = null;
state_directive:
switch (static::match(['[a-zA-Z][a-zA-Z_-]*'], $value, $lastMatch)) {
case 0:
$directive = $lastMatch;
goto state_value;
// intentional fall-through
default:
throw new Exception\InvalidArgumentException('expected DIRECTIVE');
}
state_value:
switch (static::match(['="[^"]*"', '=[^",\s;]*'], $value, $lastMatch)) {
case 0:
$directives[$directive] = substr($lastMatch, 2, -1);
goto state_separator;
// intentional fall-through
case 1:
$directives[$directive] = rtrim(substr($lastMatch, 1));
goto state_separator;
// intentional fall-through
default:
$directives[$directive] = true;
goto state_separator;
}
state_separator:
switch (static::match(['\s*,\s*', '$'], $value, $lastMatch)) {
case 0:
goto state_directive;
// intentional fall-through
case 1:
return $directives;
default:
throw new Exception\InvalidArgumentException('expected SEPARATOR or END');
}
}
/**
* Internal function used by parseValue to match tokens
*
* @param array $tokens
* @param string $string
* @param string $lastMatch
* @return int
*/
protected static function match($tokens, &$string, &$lastMatch)
{
// Ensure we have a string
$value = (string) $string;
foreach ($tokens as $i => $token) {
if (preg_match('/^' . $token . '/', $value, $matches)) {
$lastMatch = $matches[0];
$string = substr($value, strlen($matches[0]));
return $i;
}
}
return -1;
}
}

View File

@@ -0,0 +1,117 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Connection Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10
*/
class Connection implements HeaderInterface
{
const CONNECTION_CLOSE = 'close';
const CONNECTION_KEEP_ALIVE = 'keep-alive';
/**
* Value of this header
*
* @var string
*/
protected $value = self::CONNECTION_KEEP_ALIVE;
/**
* @param $headerLine
* @return Connection
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$header = new static();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'connection') {
throw new Exception\InvalidArgumentException('Invalid header line for Connection string: "' . $name . '"');
}
$header->setValue(trim($value));
return $header;
}
/**
* Set Connection header to define persistent connection
*
* @param bool $flag
* @return Connection
*/
public function setPersistent($flag)
{
$this->value = (bool) $flag
? self::CONNECTION_KEEP_ALIVE
: self::CONNECTION_CLOSE;
return $this;
}
/**
* Get whether this connection is persistent
*
* @return bool
*/
public function isPersistent()
{
return ($this->value === self::CONNECTION_KEEP_ALIVE);
}
/**
* Set arbitrary header value
* RFC allows any token as value, 'close' and 'keep-alive' are commonly used
*
* @param string $value
* @return Connection
*/
public function setValue($value)
{
HeaderValue::assertValid($value);
$this->value = strtolower($value);
return $this;
}
/**
* Connection header name
*
* @return string
*/
public function getFieldName()
{
return 'Connection';
}
/**
* Connection header value
*
* @return string
*/
public function getFieldValue()
{
return $this->value;
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Connection: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1
*/
class ContentDisposition implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-disposition') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Disposition string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Disposition';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Disposition: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
*/
class ContentEncoding implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-encoding') {
throw new Exception\InvalidArgumentException(
'Invalid header line for Content-Encoding string: "' . $name . '"'
);
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Encoding';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Encoding: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.12
*/
class ContentLanguage implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-language') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Language string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Language';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Language: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
*/
class ContentLength implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-length') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Length string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Length';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Length: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content-Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.14
*/
class ContentLocation extends AbstractLocation
{
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Location';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.15
*/
class ContentMD5 implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-md5') {
throw new Exception\InvalidArgumentException('Invalid header line for Content-MD5 string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-MD5';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-MD5: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
*/
class ContentRange implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-range') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Range string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,153 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content Security Policy 1.0 Header
*
* @link http://www.w3.org/TR/CSP/
*/
class ContentSecurityPolicy implements HeaderInterface
{
/**
* Valid directive names
*
* @var array
*/
protected $validDirectiveNames = [
// As per http://www.w3.org/TR/CSP/#directives
'default-src',
'script-src',
'object-src',
'style-src',
'img-src',
'media-src',
'frame-src',
'font-src',
'connect-src',
'sandbox',
'report-uri',
];
/**
* The directives defined for this policy
*
* @var array
*/
protected $directives = [];
/**
* Get the list of defined directives
*
* @return array
*/
public function getDirectives()
{
return $this->directives;
}
/**
* Sets the directive to consist of the source list
*
* Reverses http://www.w3.org/TR/CSP/#parsing-1
*
* @param string $name The directive name.
* @param array $sources The source list.
* @return self
* @throws Exception\InvalidArgumentException If the name is not a valid directive name.
*/
public function setDirective($name, array $sources)
{
if (! in_array($name, $this->validDirectiveNames, true)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a valid directive name; received "%s"',
__METHOD__,
(string) $name
));
}
if (empty($sources)) {
$this->directives[$name] = "'none'";
return $this;
}
array_walk($sources, [__NAMESPACE__ . '\HeaderValue', 'assertValid']);
$this->directives[$name] = implode(' ', $sources);
return $this;
}
/**
* Create Content Security Policy header from a given header line
*
* @param string $headerLine The header line to parse.
* @return self
* @throws Exception\InvalidArgumentException If the name field in the given header line does not match.
*/
public static function fromString($headerLine)
{
$header = new static();
$headerName = $header->getFieldName();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// Ensure the proper header name
if (strcasecmp($name, $headerName) != 0) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for %s string: "%s"',
$headerName,
$name
));
}
// As per http://www.w3.org/TR/CSP/#parsing
$tokens = explode(';', $value);
foreach ($tokens as $token) {
$token = trim($token);
if ($token) {
list($directiveName, $directiveValue) = explode(' ', $token, 2);
if (!isset($header->directives[$directiveName])) {
$header->setDirective($directiveName, [$directiveValue]);
}
}
}
return $header;
}
/**
* Get the header name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Security-Policy';
}
/**
* Get the header value
*
* @return string
*/
public function getFieldValue()
{
$directives = [];
foreach ($this->directives as $name => $value) {
$directives[] = sprintf('%s %s;', $name, $value);
}
return implode(' ', $directives);
}
/**
* Return the header as a string
*
* @return string
*/
public function toString()
{
return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue());
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 @todo find section
*/
class ContentTransferEncoding implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-transfer-encoding') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Transfer-Encoding string: "%s"',
$name
));
}
// @todo implementation details
$header = new static(strtolower($value));
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Content-Transfer-Encoding';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Content-Transfer-Encoding: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,404 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use stdClass;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
*/
class ContentType implements HeaderInterface
{
/**
* @var string
*/
protected $mediaType;
/**
* @var array
*/
protected $parameters = [];
/**
* @var string
*/
protected $value;
/**
* Factory method: create an object from a string representation
*
* @param string $headerLine
* @return self
*/
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'content-type') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Content-Type string: "%s"',
$name
));
}
$parts = explode(';', $value);
$mediaType = array_shift($parts);
$header = new static($value, trim($mediaType));
if (count($parts) > 0) {
$parameters = [];
foreach ($parts as $parameter) {
$parameter = trim($parameter);
if (!preg_match('/^(?P<key>[^\s\=]+)\="?(?P<value>[^\s\"]*)"?$/', $parameter, $matches)) {
continue;
}
$parameters[$matches['key']] = $matches['value'];
}
$header->setParameters($parameters);
}
return $header;
}
public function __construct($value = null, $mediaType = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
$this->mediaType = $mediaType;
}
/**
* Determine if the mediatype value in this header matches the provided criteria
*
* @param array|string $matchAgainst
* @return string|bool Matched value or false
*/
public function match($matchAgainst)
{
if (is_string($matchAgainst)) {
$matchAgainst = $this->splitMediaTypesFromString($matchAgainst);
}
$mediaType = $this->getMediaType();
$left = $this->getMediaTypeObjectFromString($mediaType);
foreach ($matchAgainst as $matchType) {
$matchType = strtolower($matchType);
if ($mediaType == $matchType) {
return $matchType;
}
$right = $this->getMediaTypeObjectFromString($matchType);
// Is the right side a wildcard type?
if ($right->type == '*') {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
// Do the types match?
if ($right->type == $left->type) {
if ($this->validateSubtype($right, $left)) {
return $matchType;
}
}
}
return false;
}
/**
* Create a string representation of the header
*
* @return string
*/
public function toString()
{
return 'Content-Type: ' . $this->getFieldValue();
}
/**
* Get the field name
*
* @return string
*/
public function getFieldName()
{
return 'Content-Type';
}
/**
* Get the field value
*
* @return string
*/
public function getFieldValue()
{
if (null !== $this->value) {
return $this->value;
}
return $this->assembleValue();
}
/**
* Set the media type
*
* @param string $mediaType
* @return self
*/
public function setMediaType($mediaType)
{
HeaderValue::assertValid($mediaType);
$this->mediaType = strtolower($mediaType);
$this->value = null;
return $this;
}
/**
* Get the media type
*
* @return string
*/
public function getMediaType()
{
return $this->mediaType;
}
/**
* Set additional content-type parameters
*
* @param array $parameters
* @return self
*/
public function setParameters(array $parameters)
{
foreach ($parameters as $key => $value) {
HeaderValue::assertValid($key);
HeaderValue::assertValid($value);
}
$this->parameters = array_merge($this->parameters, $parameters);
$this->value = null;
return $this;
}
/**
* Get any additional content-type parameters currently set
*
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Set the content-type character set encoding
*
* @param string $charset
* @return self
*/
public function setCharset($charset)
{
HeaderValue::assertValid($charset);
$this->parameters['charset'] = $charset;
$this->value = null;
return $this;
}
/**
* Get the content-type character set encoding, if any
*
* @return null|string
*/
public function getCharset()
{
if (isset($this->parameters['charset'])) {
return $this->parameters['charset'];
}
return;
}
/**
* Assemble the value based on the media type and any available parameters
*
* @return string
*/
protected function assembleValue()
{
$mediaType = $this->getMediaType();
if (empty($this->parameters)) {
return $mediaType;
}
$parameters = [];
foreach ($this->parameters as $key => $value) {
$parameters[] = sprintf('%s=%s', $key, $value);
}
return sprintf('%s; %s', $mediaType, implode('; ', $parameters));
}
/**
* Split comma-separated media types into an array
*
* @param string $criteria
* @return array
*/
protected function splitMediaTypesFromString($criteria)
{
$mediaTypes = explode(',', $criteria);
array_walk(
$mediaTypes,
function (&$value) {
$value = trim($value);
}
);
return $mediaTypes;
}
/**
* Split a mediatype string into an object with the following parts:
*
* - type
* - subtype
* - format
*
* @param string $string
* @return stdClass
*/
protected function getMediaTypeObjectFromString($string)
{
if (!is_string($string)) {
throw new Exception\InvalidArgumentException(sprintf(
'Non-string mediatype "%s" provided',
(is_object($string) ? get_class($string) : gettype($string))
));
}
$parts = explode('/', $string, 2);
if (1 == count($parts)) {
throw new Exception\DomainException(sprintf(
'Invalid mediatype "%s" provided',
$string
));
}
$type = array_shift($parts);
$subtype = array_shift($parts);
$format = $subtype;
if (strstr($subtype, '+')) {
$parts = explode('+', $subtype, 2);
$subtype = array_shift($parts);
$format = array_shift($parts);
}
$mediaType = (object) [
'type' => $type,
'subtype' => $subtype,
'format' => $format,
];
return $mediaType;
}
/**
* Validate a subtype
*
* @param stdClass $right
* @param stdClass $left
* @return bool
*/
protected function validateSubtype($right, $left)
{
// Is the right side a wildcard subtype?
if ($right->subtype == '*') {
return $this->validateFormat($right, $left);
}
// Do the right side and left side subtypes match?
if ($right->subtype == $left->subtype) {
return $this->validateFormat($right, $left);
}
// Is the right side a partial wildcard?
if ('*' == substr($right->subtype, -1)) {
// validate partial-wildcard subtype
if (!$this->validatePartialWildcard($right->subtype, $left->subtype)) {
return false;
}
// Finally, verify format is valid
return $this->validateFormat($right, $left);
}
// Does the right side subtype match the left side format?
if ($right->subtype == $left->format) {
return true;
}
// At this point, there is no valid match
return false;
}
/**
* Validate the format
*
* Validate that the right side format matches what the left side defines.
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validateFormat($right, $left)
{
if ($right->format && $left->format) {
if ($right->format == '*') {
return true;
}
if ($right->format == $left->format) {
return true;
}
return false;
}
return true;
}
/**
* Validate a partial wildcard (i.e., string ending in '*')
*
* @param string $right
* @param string $left
* @return bool
*/
protected function validatePartialWildcard($right, $left)
{
$requiredSegment = substr($right, 0, strlen($right) - 1);
if ($requiredSegment == $left) {
return true;
}
if (strlen($requiredSegment) >= strlen($left)) {
return false;
}
if (0 === strpos($left, $requiredSegment)) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use ArrayObject;
/**
* @see http://www.ietf.org/rfc/rfc2109.txt
* @see http://www.w3.org/Protocols/rfc2109/rfc2109
*/
class Cookie extends ArrayObject implements HeaderInterface
{
protected $encodeValue = true;
public static function fromSetCookieArray(array $setCookies)
{
$nvPairs = [];
foreach ($setCookies as $setCookie) {
if (! $setCookie instanceof SetCookie) {
throw new Exception\InvalidArgumentException(sprintf(
'%s requires an array of SetCookie objects',
__METHOD__
));
}
if (array_key_exists($setCookie->getName(), $nvPairs)) {
throw new Exception\InvalidArgumentException(sprintf(
'Two cookies with the same name were provided to %s',
__METHOD__
));
}
$nvPairs[$setCookie->getName()] = $setCookie->getValue();
}
return new static($nvPairs);
}
public static function fromString($headerLine)
{
$header = new static();
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'cookie') {
throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
}
$nvPairs = preg_split('#;\s*#', $value);
$arrayInfo = [];
foreach ($nvPairs as $nvPair) {
$parts = explode('=', $nvPair, 2);
if (count($parts) != 2) {
throw new Exception\RuntimeException('Malformed Cookie header found');
}
list($name, $value) = $parts;
$arrayInfo[$name] = urldecode($value);
}
$header->exchangeArray($arrayInfo);
return $header;
}
public function __construct(array $array = [])
{
parent::__construct($array, ArrayObject::ARRAY_AS_PROPS);
}
public function setEncodeValue($encodeValue)
{
$this->encodeValue = (bool) $encodeValue;
return $this;
}
public function getEncodeValue()
{
return $this->encodeValue;
}
public function getFieldName()
{
return 'Cookie';
}
public function getFieldValue()
{
$nvPairs = [];
foreach ($this as $name => $value) {
$nvPairs[] = $name . '=' . (($this->encodeValue) ? urlencode($value) : $value);
}
return implode('; ', $nvPairs);
}
public function toString()
{
return 'Cookie: ' . $this->getFieldValue();
}
/**
* Get the cookie as a string, suitable for sending as a "Cookie" header in an
* HTTP request
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Date Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18
*/
class Date extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Date';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19
*/
class Etag implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'etag') {
throw new Exception\InvalidArgumentException('Invalid header line for Etag string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Etag';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Etag: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
class DomainException extends \DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception\ExceptionInterface as HttpException;
interface ExceptionInterface extends HttpException
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception;
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header\Exception;
use Zend\Http\Exception;
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20
*/
class Expect implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'expect') {
throw new Exception\InvalidArgumentException('Invalid header line for Expect string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Expect';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Expect: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Expires Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
*/
class Expires extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Expires';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.22
*/
class From implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'from') {
throw new Exception\InvalidArgumentException('Invalid header line for From string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'From';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'From: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,164 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Content-Location Header
*
*/
class GenericHeader implements HeaderInterface
{
/**
* @var string
*/
protected $fieldName = null;
/**
* @var string
*/
protected $fieldValue = null;
/**
* Factory to generate a header object from a string
*
* @static
* @param string $headerLine
* @return GenericHeader
*/
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
$header = new static($fieldName, $fieldValue);
return $header;
}
/**
* Splits the header line in `name` and `value` parts.
*
* @param string $headerLine
* @return string[] `name` in the first index and `value` in the second.
* @throws Exception\InvalidArgumentException If header does not match with the format ``name:value``
*/
public static function splitHeaderLine($headerLine)
{
$parts = explode(':', $headerLine, 2);
if (count($parts) !== 2) {
throw new Exception\InvalidArgumentException('Header must match with the format "name:value"');
}
if (! HeaderValue::isValid($parts[1])) {
throw new Exception\InvalidArgumentException('Invalid header value detected');
}
$parts[1] = ltrim($parts[1]);
return $parts;
}
/**
* Constructor
*
* @param null|string $fieldName
* @param null|string $fieldValue
*/
public function __construct($fieldName = null, $fieldValue = null)
{
if ($fieldName) {
$this->setFieldName($fieldName);
}
if ($fieldValue !== null) {
$this->setFieldValue($fieldValue);
}
}
/**
* Set header field name
*
* @param string $fieldName
* @return GenericHeader
* @throws Exception\InvalidArgumentException If the name does not match with RFC 2616 format.
*/
public function setFieldName($fieldName)
{
if (!is_string($fieldName) || empty($fieldName)) {
throw new Exception\InvalidArgumentException('Header name must be a string');
}
/*
* Following RFC 7230 section 3.2
*
* header-field = field-name ":" [ field-value ]
* field-name = token
* token = 1*tchar
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
* "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
*/
if (!preg_match('/^[!#$%&\'*+\-\.\^_`|~0-9a-zA-Z]+$/', $fieldName)) {
throw new Exception\InvalidArgumentException(
'Header name must be a valid RFC 7230 (section 3.2) field-name.'
);
}
$this->fieldName = $fieldName;
return $this;
}
/**
* Retrieve header field name
*
* @return string
*/
public function getFieldName()
{
return $this->fieldName;
}
/**
* Set header field value
*
* @param string $fieldValue
* @return GenericHeader
*/
public function setFieldValue($fieldValue)
{
$fieldValue = (string) $fieldValue;
HeaderValue::assertValid($fieldValue);
if (preg_match('/^\s+$/', $fieldValue)) {
$fieldValue = '';
}
$this->fieldValue = $fieldValue;
return $this;
}
/**
* Retrieve header field value
*
* @return string
*/
public function getFieldValue()
{
return $this->fieldValue;
}
/**
* Cast to string as a well formed HTTP header line
*
* Returns in form of "NAME: VALUE\r\n"
*
* @return string
*/
public function toString()
{
return $this->getFieldName() . ': ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
class GenericMultiHeader extends GenericHeader implements MultipleHeaderInterface
{
public static function fromString($headerLine)
{
list($fieldName, $fieldValue) = GenericHeader::splitHeaderLine($headerLine);
if (strpos($fieldValue, ',')) {
$headers = [];
foreach (explode(',', $fieldValue) as $multiValue) {
$headers[] = new static($fieldName, $multiValue);
}
return $headers;
} else {
$header = new static($fieldName, $fieldValue);
return $header;
}
}
public function toStringMultipleHeaders(array $headers)
{
$name = $this->getFieldName();
$values = [$this->getFieldValue()];
foreach ($headers as $header) {
if (!$header instanceof static) {
throw new Exception\InvalidArgumentException('This method toStringMultipleHeaders was expecting an array of headers of the same type');
}
$values[] = $header->getFieldValue();
}
return $name . ': ' . implode(',', $values) . "\r\n";
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Interface for HTTP Header classes.
*/
interface HeaderInterface
{
/**
* Factory to generate a header object from a string
*
* @param string $headerLine
* @return self
* @throws Exception\InvalidArgumentException If the header does not match RFC 2616 definition.
* @see http://tools.ietf.org/html/rfc2616#section-4.2
*/
public static function fromString($headerLine);
/**
* Retrieve header name
*
* @return string
*/
public function getFieldName();
/**
* Retrieve header value
*
* @return string
*/
public function getFieldValue();
/**
* Cast to string
*
* Returns in form of "NAME: VALUE"
*
* @return string
*/
public function toString();
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
final class HeaderValue
{
/**
* Private constructor; non-instantiable.
*/
private function __construct()
{
}
/**
* Filter a header value
*
* Ensures CRLF header injection vectors are filtered.
*
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
* tabs are allowed in values; only one whitespace character is allowed
* between visible characters.
*
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
* @param string $value
* @return string
*/
public static function filter($value)
{
$value = (string) $value;
$length = strlen($value);
$string = '';
for ($i = 0; $i < $length; $i += 1) {
$ascii = ord($value[$i]);
// Non-visible, non-whitespace characters
// 9 === horizontal tab
// 32-126, 128-254 === visible
// 127 === DEL
// 255 === null byte
if (($ascii < 32 && $ascii !== 9)
|| $ascii === 127
|| $ascii > 254
) {
continue;
}
$string .= $value[$i];
}
return $string;
}
/**
* Validate a header value.
*
* Per RFC 7230, only VISIBLE ASCII characters, spaces, and horizontal
* tabs are allowed in values; only one whitespace character is allowed
* between visible characters.
*
* @see http://en.wikipedia.org/wiki/HTTP_response_splitting
* @param string $value
* @return bool
*/
public static function isValid($value)
{
$value = (string) $value;
$length = strlen($value);
for ($i = 0; $i < $length; $i += 1) {
$ascii = ord($value[$i]);
// Non-visible, non-whitespace characters
// 9 === horizontal tab
// 32-126, 128-254 === visible
// 127 === DEL
// 255 === null byte
if (($ascii < 32 && $ascii !== 9)
|| $ascii === 127
|| $ascii > 254
) {
return false;
}
}
return true;
}
/**
* Assert a header value is valid.
*
* @param string $value
* @throws Exception\RuntimeException for invalid values
* @return void
*/
public static function assertValid($value)
{
if (! self::isValid($value)) {
throw new Exception\InvalidArgumentException('Invalid header value');
}
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23
*/
class Host implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'host') {
throw new Exception\InvalidArgumentException('Invalid header line for Host string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Host';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Host: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
*/
class IfMatch implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-match') {
throw new Exception\InvalidArgumentException('Invalid header line for If-Match string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-Match';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-Match: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* If-Modified-Since Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
*/
class IfModifiedSince extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'If-Modified-Since';
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
*/
class IfNoneMatch implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-none-match') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for If-None-Match string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-None-Match';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-None-Match: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27
*/
class IfRange implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'if-range') {
throw new Exception\InvalidArgumentException('Invalid header line for If-Range string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'If-Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'If-Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* If-Unmodified-Since Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28
*/
class IfUnmodifiedSince extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'If-Unmodified-Since';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @todo Search for RFC for this header
*/
class KeepAlive implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'keep-alive') {
throw new Exception\InvalidArgumentException('Invalid header line for Keep-Alive string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Keep-Alive';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Keep-Alive: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Last-Modified Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.29
*/
class LastModified extends AbstractDate
{
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Last-Modified';
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30
*/
class Location extends AbstractLocation
{
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Location';
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.31
*/
class MaxForwards implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'max-forwards') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Max-Forwards string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Max-Forwards';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Max-Forwards: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
interface MultipleHeaderInterface extends HeaderInterface
{
public function toStringMultipleHeaders(array $headers);
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\UriFactory;
/**
* @throws Exception\InvalidArgumentException
* @see http://tools.ietf.org/id/draft-abarth-origin-03.html#rfc.section.2
*/
class Origin implements HeaderInterface
{
/**
* @var string
*/
protected $value = '';
public static function fromString($headerLine)
{
list($name, $value) = explode(': ', $headerLine, 2);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'origin') {
throw new Exception\InvalidArgumentException('Invalid header line for Origin string: "' . $name . '"');
}
$uri = UriFactory::factory($value);
if (!$uri->isValid()) {
throw new Exception\InvalidArgumentException('Invalid header value for Origin key: "' . $name . '"');
}
return new static($value);
}
/**
* @param string|null $value
*/
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Origin';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Origin: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32
*/
class Pragma implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'pragma') {
throw new Exception\InvalidArgumentException('Invalid header line for Pragma string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Pragma';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Pragma: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.33
*/
class ProxyAuthenticate implements MultipleHeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'proxy-authenticate') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Proxy-Authenticate string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Proxy-Authenticate';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Proxy-Authenticate: ' . $this->getFieldValue();
}
public function toStringMultipleHeaders(array $headers)
{
$strings = [$this->toString()];
foreach ($headers as $header) {
if (!$header instanceof ProxyAuthenticate) {
throw new Exception\RuntimeException(
'The ProxyAuthenticate multiple header implementation can only accept'
. ' an array of ProxyAuthenticate headers'
);
}
$strings[] = $header->toString();
}
return implode("\r\n", $strings);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.34
*/
class ProxyAuthorization implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'proxy-authorization') {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header line for Proxy-Authorization string: "%s"',
$name
));
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Proxy-Authorization';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Proxy-Authorization: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2
*/
class Range implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'range') {
throw new Exception\InvalidArgumentException('Invalid header line for Range string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Range';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Range: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
use Zend\Uri\Http as HttpUri;
/**
* Content-Location Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36
*/
class Referer extends AbstractLocation
{
/**
* Set the URI/URL for this header
* according to RFC Referer URI should not have fragment
*
* @param string|HttpUri $uri
* @return Referer
*/
public function setUri($uri)
{
parent::setUri($uri);
$this->uri->setFragment(null);
return $this;
}
/**
* Return header name
*
* @return string
*/
public function getFieldName()
{
return 'Referer';
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @todo FIND SPEC FOR THIS
*/
class Refresh implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'refresh') {
throw new Exception\InvalidArgumentException('Invalid header line for Refresh string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Refresh';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Refresh: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,107 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* Retry-After HTTP Header
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
*/
class RetryAfter extends AbstractDate
{
/**
* Value of header in delta-seconds
* By default set to 1 hour
*
* @var int
*/
protected $deltaSeconds = 3600;
/**
* Create Retry-After header from string
*
* @param string $headerLine
* @return RetryAfter
* @throws Exception\InvalidArgumentException
*/
public static function fromString($headerLine)
{
$dateHeader = new static();
list($name, $date) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== strtolower($dateHeader->getFieldName())) {
throw new Exception\InvalidArgumentException(
'Invalid header line for "' . $dateHeader->getFieldName() . '" header string'
);
}
if (is_numeric($date)) {
$dateHeader->setDeltaSeconds($date);
} else {
$dateHeader->setDate($date);
}
return $dateHeader;
}
/**
* Set number of seconds
*
* @param int $delta
* @return RetryAfter
*/
public function setDeltaSeconds($delta)
{
$this->deltaSeconds = (int) $delta;
return $this;
}
/**
* Get number of seconds
*
* @return int
*/
public function getDeltaSeconds()
{
return $this->deltaSeconds;
}
/**
* Get header name
*
* @return string
*/
public function getFieldName()
{
return 'Retry-After';
}
/**
* Returns date if it's set, or number of seconds
*
* @return int|string
*/
public function getFieldValue()
{
return ($this->date === null) ? $this->deltaSeconds : $this->getDate();
}
/**
* Return header line
*
* @return string
*/
public function toString()
{
return 'Retry-After: ' . $this->getFieldValue();
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Http\Header;
/**
* @throws Exception\InvalidArgumentException
* @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.38
*/
class Server implements HeaderInterface
{
/**
* @var string
*/
protected $value;
public static function fromString($headerLine)
{
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
// check to ensure proper header type for this factory
if (strtolower($name) !== 'server') {
throw new Exception\InvalidArgumentException('Invalid header line for Server string: "' . $name . '"');
}
// @todo implementation details
$header = new static($value);
return $header;
}
public function __construct($value = null)
{
if ($value) {
HeaderValue::assertValid($value);
$this->value = $value;
}
}
public function getFieldName()
{
return 'Server';
}
public function getFieldValue()
{
return $this->value;
}
public function toString()
{
return 'Server: ' . $this->getFieldValue();
}
}

Some files were not shown because too many files have changed in this diff Show More