diff --git a/.coveralls.yml b/.coveralls.yml index 53bda829c8..bc71b62f87 100644 --- a/.coveralls.yml +++ b/.coveralls.yml @@ -1,3 +1,2 @@ coverage_clover: clover.xml json_path: coveralls-upload.json -src_dir: src diff --git a/.gitignore b/.gitignore index a7fc91d633..f146c86138 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,9 @@ .*.sw* .*.un~ nbproject +doc/html/ tmp/ +zf-mkdoc-theme/ clover.xml composer.lock diff --git a/.travis.yml b/.travis.yml index 7c39d1bac9..7995517e78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,24 @@ language: php branches: except: - - /^release-.*$/ + - /^release-\d+\.\d+\.\d+.*$/ - /^ghgfk-.*$/ cache: directories: - $HOME/.composer/cache + - vendor + - $HOME/.local + - zf-mkdoc-theme env: global: - TESTS_ZEND_HTTP_CLIENT_ONLINE=true + - SITE_URL: https://zendframework.github.io/zend-http + - GH_USER_NAME: "Matthew Weier O'Phinney" + - GH_USER_EMAIL: matthew@weierophinney.net + - GH_REF: github.com/zendframework/zend-http.git + - secure: "dla3bBhLgsNCSTFaQJF41P56GoNTDOlZA4OGdu14xsTdtcbncPtZ33oojsxXH5XtdB7bYTpMTx7IC+2FEIrWvfbtaFSdOBoriZz+DuvZKCaD/M56H3q3gAumuXvLBH/R+14uFLNRViztP/6uUUVvwRGSnYub2byaX5xTWRelzQjU15p6l8DA7N7RB9hWeZq+41RhpH3DBZOFHpsy+ezy/GWu005CVdSkBG4p5wHW9hs7LBFqn+kN0Qa1zjX3G+p/Gn28+ro1FF5T8TK/hbI/0hDnVJUjSOUXi5VcsOFcnJ50XZBa42PYI6/P6zSI7uuqQzlsefGXslsnYcsmagmACACrnH0nHJB29jlIGzRLOSGpFzOLDjDjlSCOHwZjeLF6tGeEtBQMp+VNnM3GQb5YEaXEmvvt+m+LEvm8mD2w4A47yydYP6AoLBlGpEEORfqUAhSLOJLoXuGAb90wEe4mIRIJegqQQihWppIMWIVXS2kdGSwzA/WnAuyAKywUiXgCcxsW6Aa8DZl1vTgeBA2MkwsBB3IhAweRVaJHnv4ijVWTN0BrAd0naPB+8XzcXgbtpsoRl+Z5RFAFCJQ+nJucdFMkKyr1DotfspyhnbRw4gOZufjCzTQaox8/DbPS0dmb1Zbzb1t80uqPQmMwdtek4Ovm7IVX2iRW3pluS9PWg5Q=" matrix: fast_finish: true @@ -24,6 +32,8 @@ matrix: - php: 5.6 env: - EXECUTE_TEST_COVERALLS=true + - DEPLOY_DOCS="$(if [[ $TRAVIS_BRANCH == 'master' && $TRAVIS_PULL_REQUEST == 'false' ]]; then echo -n 'true' ; else echo -n 'false' ; fi)" + - PATH="$HOME/.local/bin:$PATH" - php: 7 - php: hhvm allow_failures: @@ -45,6 +55,10 @@ script: - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; fi - if [[ $EXECUTE_TEST_COVERALLS != 'true' ]]; then ./vendor/bin/phpunit ; fi - if [[ $EXECUTE_CS_CHECK == 'true' ]]; then ./vendor/bin/php-cs-fixer fix -v --diff --dry-run ; fi + - if [[ $DEPLOY_DOCS == "true" && "$TRAVIS_TEST_RESULT" == "0" ]]; then wget -O theme-installer.sh "https://raw.githubusercontent.com/zendframework/zf-mkdoc-theme/master/theme-installer.sh" ; chmod 755 theme-installer.sh ; ./theme-installer.sh ; fi + +after_success: + - if [[ $DEPLOY_DOCS == "true" ]]; then echo "Preparing to build and deploy documentation" ; ./zf-mkdoc-theme/deploy.sh ; echo "Completed deploying documentation" ; fi after_script: - if [[ $EXECUTE_TEST_COVERALLS == 'true' ]]; then ./vendor/bin/coveralls ; fi diff --git a/CHANGELOG.md b/CHANGELOG.md index d903395349..9953edd429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,13 @@ All notable changes to this project will be documented in this file, in reverse ### Added -- Nothing. +- [#44](https://github.com/zendframework/zend-http/pull/44), + [#45](https://github.com/zendframework/zend-http/pull/45), + [#46](https://github.com/zendframework/zend-http/pull/46), + [#47](https://github.com/zendframework/zend-http/pull/47), + [#48](https://github.com/zendframework/zend-http/pull/48), and + [#49](https://github.com/zendframework/zend-http/pull/49) prepare the + documentation for publication at https://zendframework.github.io/zend-http/ ### Deprecated diff --git a/README.md b/README.md index d7cdd3a63c..50fa784165 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,13 @@ [![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. +zend-http provides the HTTP message abstraction used by +[zend-mvc](https://zendframework.github.io/zend-mvc/), and also provides an +extensible, adapter-driven HTTP client library. +This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as +it predates that specification. For PSR-7 support, please see our +[Diactoros component](https://zendframework.github.io/zend-diactoros/). - File issues at https://github.com/zendframework/zend-http/issues -- Documentation is at http://framework.zend.com/manual/current/en/index.html#zend-http +- Documentation is at https://zendframework.github.io/zend-http/ diff --git a/doc/book/client/adapters.md b/doc/book/client/adapters.md new file mode 100644 index 0000000000..5a676ef732 --- /dev/null +++ b/doc/book/client/adapters.md @@ -0,0 +1,529 @@ +# HTTP Client Connection Adapters + +`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 suit your special needs, without the need to extend or replace the entire +HTTP client class. + +Currently, zend-http provides four built-in client connection adapters: + +- `Zend\Http\Client\Adapter\Socket` (default) +- `Zend\Http\Client\Adapter\Proxy` +- `Zend\Http\Client\Adapter\Curl` +- `Zend\Http\Client\Adapter\Test` + +The client can accept an adapter via either its `setAdapter()` method, or during +instantiation via its `adapter` configuration option. When using configuration, +the value of the `adapter` option may be one of: + +- an adapter instance +- the fully qualified class name of an adapter + +## The Socket adapter + +The default connection adapter used when none is specified is the +`Zend\Http\Client\Adapter\Socket` 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 +via either the client's constructor or `setOptions()` method: + +Parameter | Description | Expected Type | Default Value +---------------------|--------------------------------------------------------------------------------------|---------------|-------------- +`persistent` | Whether to use persistent TCP connections | boolean | `FALSE` +`ssltransport` | SSL transport layer (eg. `sslv2`, `tls`) | string | `ssl` +`sslcert` | Path to a PEM encoded SSL certificate | string | `NULL` +`sslpassphrase` | Passphrase for the SSL certificate file | string | `NULL` +`sslverifypeer` | Whether to verify the SSL peer | string | `TRUE` +`sslcapath` | Path to SSL certificate directory | string | `NULL` +`sslallowselfsigned` | Whether to allow self-signed certificates | string | `FALSE` +`sslusecontext` | Enables proxied connections to use SSL even if the proxy connection itself does not. | boolean | `FALSE` + +> ### 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, we recommend enabling +> Keep-Alive HTTP requests as described in [the client configuration section](intro.md#configuration); +> otherwise persistent connections might have little or no effect. + +> ### HTTPS SSL stream parameters +> +> `ssltransport`, `sslcert` and `sslpassphrase` are only relevant when +> connecting using HTTPS. While the default SSL/TLS 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, please read the +> [PHP manual chapter on SSL and TLS transport options](http://php.net/transports.inet). + +### Changing the HTTPS transport layer + +```php +use Zend\Http\Client; + +// Set the configuration parameters +$config = [ + 'adapter' => Client\Adapter\Socket::class, + 'ssltransport' => 'tls', +]; + +// Instantiate a client object +$client = new 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: + +```php +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/stream.contexts) 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/stream_context_create) 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 current stream context of the adapter. If no + stream context was set, this method 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 +use Zend\Http\Client; + +// Array of options +$options = [ + 'socket' => [ + // Bind local socket side to a specific interface + 'bindto' => '10.1.2.3:50505', + ], + 'ssl' => [ + // 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 Client\Adapter\Socket(); +$client = new 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']; +``` + +> #### Set stream context options prior to requests +> +> 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 + +`Zend\Http\Client\Adapter\Proxy` is similar to the default `Socket` adapter; the +primary difference is that 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 required for +security or performance reasons. + +Using the `Proxy` adapter requires several additional client configuration +parameters to be set, in addition to the default `adapter` option: + +Parameter | Description | Expected Type | Example Value +-------------|--------------------------------|---------------|-------------- +`proxy_host` | Proxy server address | string | 'proxy.myhost.com'’ or '10.1.2.3' +`proxy_port` | Proxy server TCP port | integer | 8080 (default) or 81 +`proxy_user` | Proxy user name, if required | string | 'shahar' or '' for none (default) +`proxy_pass` | Proxy password, if required | string | 'secret' or '' for none (default) +`proxy_auth` | Proxy HTTP authentication type | string | `Zend\Http\Client::AUTH_BASIC` (default) + +`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. Possible 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 +use Zend\Http\Client; + +// Set the configuration parameters +$config = [ + 'adapter' => Client\Adapter\Proxy::class, + 'proxy_host' => 'proxy.int.zend.com', + 'proxy_port' => 8000, + 'proxy_user' => 'shahar.e', + 'proxy_pass' => 'bananashaped', +]; + +// Instantiate a client object +$client = new 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 +write your application in a way that allows a proxy to be used optionally, +according to a configuration parameter. + +> ### Access to stream context +> +> Since the proxy adapter inherits from `Zend\Http\Client\Adapter\Socket`, you +> can use the stream context access method (see +> [above](#setting-stream-context-options-for-the-socket-adapter)) to set stream +> context options on `Proxy` connections. + +## 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, proxies, and multiple +authentication mechanisms. In particular, it is very performant with regards to +transfering large files. + +### Setting cURL options + +```php +use Zend\Http\Client; + +$config = [ + 'adapter' => Client\Adapter\Curl::class, + 'curloptions' => [CURLOPT_FOLLOWLOCATION => true], +]; +$client = new 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)`; option names correspond to the `CURL_*` +constants of the cURL extension. You can get access to the underling cURL handle +by calling `$adapter->getHandle();` + +### Transfering files by handle + +You can use cURL to transfer very large files over HTTP by filehandle. + +```php +use Zend\Http\Client; + +$putFileSize = filesize('filepath'); +$putFileHandle = fopen('filepath', 'r'); + +$adapter = new Client\Adapter\Curl(); +$client = new Client(); +$client->setAdapter($adapter); +$client->setMethod('PUT'); +$adapter->setOptions([ + 'curloptions' => [ + CURLOPT_INFILE => $putFileHandle, + CURLOPT_INFILESIZE => $putFileSize, + ], +]); +$client->send(); +``` + +## The Test adapter + +Testing code that relies on HTTP connections poses difficulties. For example, +testing an application that pulls an RSS feed from a remote server will require +a network connection, which is not always available. + +`Zend\Http\Client\Adapter\Test` provides a solution for these situations and +acts as a mock object for unit tests. You can write your application to use +`Zend\Http\Client`, and, when testing (either in your unit test suite, or in +non-production environments), replace the default adapter with the test adapter, +allowing you to run tests without actually performing server connections. + +`Zend\Http\Client\Adapter\Test` provides two additional methods, `setResponse()` +and `addResponse()`. Each takes one parameter, which represents an HTTP +response as either text or a `Zend\Http\Response` object. `setResponse()` sets +an individual response to always return from any request; `addResponse()` allows +aggregating a sequence of responses. In both cases, responses are returned +without performing actual HTTP requests. + +### Testing against a single HTTP response stub + +```php +use Zend\Http\Client; + +// Instantiate a new adapter and client +$adapter = new Client\Adapter\Test(); +$client = new Client( + 'http://www.example.com', + ['adapter' => $adapter] +); + +// Set the expected response +$adapter->setResponse( + "HTTP/1.1 200 OK\r\n" + . "Content-type: text/xml\r\n" + . "\r\n" + . '' + . '' + . ' ' + . ' Premature Optimization' + // and so on... + . '' +); + +$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 +use Zend\Http\Client; + +// Instantiate a new adapter and client +$adapter = new Client\Adapter\Test(); +$client = new Client( + 'http://www.example.com', + ['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" + . '' + . ' Moved' + . '

This page has moved.

' + . '' +); + +// Set the next successive response +$adapter->addResponse( + "HTTP/1.1 200 OK\r\n" + . "Content-Type: text/html\r\n" + . "\r\n" + . '' + . ' My Pet Store Home Page' + . '

...

' + . '' +); + +// 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 adapter'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. + +### Forcing the adapter to fail + +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`. +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. + +```php +use Zend\Http\Client; + +// Instantiate a new adapter and client +$adapter = new Client\Adapter\Test(); +$client = new Client( + 'http://www.example.com', + ['adapter' => $adapter] +); + +// Force the next request to fail with an exception +$adapter->setNextRequestWillFail(true); + +try { + // This call will result in an exception. + $client->send(); +} catch (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 +`Zend\Http\Client\Adapter\AdapterInterface`. 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. + +```php +namespace MyApp\Http\Client\Adapter; + +use Zend\Http\Client\Adapter\AdapterInterface; + +class BananaProtocol implements AdapterInterface +{ + /** + * Set Adapter Options + * + * @param array $config + */ + public function setOptions($config = []) + { + // 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 = [], + $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. + } +} +``` + +Use the adapter as you would any other: + +```php +use MyApp\Http\Client\Adapter\BananaProtocol; +use Zend\Http\Client; + +$client = new Client([ + 'adapter' => BananaProtocol::class, +]); +``` diff --git a/doc/book/client/advanced.md b/doc/book/client/advanced.md new file mode 100644 index 0000000000..849b1f0925 --- /dev/null +++ b/doc/book/client/advanced.md @@ -0,0 +1,357 @@ +# 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 query and body 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`: + +```php +// Strict Redirections +$client->setOptions(['strictredirects' => true]); + +// Non-strict Redirections +$client->setOptions(['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 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. `addCookie()` can accept +either a name and value, a `SetCookie` header instance, or an array of +`SetCookie` header instances. + +```php +use Zend\Http\Header\SetCookie; + +// Basic usage: provide a cookie name and cookie value: +$client->addCookie('flavor', 'chocolate chips'); + +// Or provide a SetCookie instance: +$cookie = SetCookie::fromString('Set-Cookie: flavor=chocolate%20chips'); +$client->addCookie($cookie); + +// Multiple cookies can be set at once by providing an +// array of SetCookie instances: +$cookies = [ + SetCookie::fromString('Set-Cookie: flavorOne=chocolate%20chips'), + 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 name/value pairs as its only argument, and also clears the +cookie container before adding the new cookies: + +```php +// setCookies accepts an array of cookie values as $name => $value +$client->setCookies([ + 'flavor' => 'chocolate chips', + 'amount' => 10, +]); +``` + +See the [Headers documentation](../headers.md#setcookie) for more detail on the +`SetCookie` class. + +### Enabling Cookie Stickiness + +`Zend\Http\Client` also provides a means for simplifying cookie "stickiness" +— i.e., having the client internally store all sent and received cookies, +and resending them on subsequent requests. — via the `Zend\Http\Cookies` +class. This is useful when you need to log in to a remote site first and +receive an authentication or session ID cookie before sending further requests. + +```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(['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'); +``` + +See the chapter on [cookies](cookies.md) for more detail. + +## Setting custom request headers + +Setting custom headers is performed by first fetching the header container from +the client's `Zend\Http\Request` instance. This `Headers` container offers a +number of methods for setting headers: + +```php +use Zend\Http\Header; + +// Fetch the container +$headers = $client->getRequest()->getHeaders(); + +// Setting a single header using a name and value. Will not overwrite any // +previously-added headers of the same name. +$headers->addHeaderLine('Host', 'www.example.com'); + +// You can also use a full header line: +$headers->addHeaderLine('Host: www.example.com'); + +// Sometimes you may want to use a HeaderInterface instance: +$headers->addHeader(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([ + // Zend\Http\Header\* instance: + Header\Host::fromString('Host: www.example.com'), + + // Header name/value pair: + '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 its +`Zend\Http\Request` instance. As a consequence, any pre-existing headers will +be erased: + +```php +use Zend\Http\Header; + +// Setting multiple headers via the client; removes all existing headers, +// replacing the request header container with the following: +$client->setHeaders([ + 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 +`` 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`. + +```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, defaulting to `application/octet-stream`. + +> ### 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 send raw POST data via `Zend\Http\Client` 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()`. + +```php +$xml = '' + . ' Islands in the Stream' + . ' Ernest Hemingway' + . ' 1970' + . ''; +$client->setMethod('POST'); +$client->setRawBody($xml); +$client->setEncType('text/xml'); +$client->send(); +``` + +The data should be available on the server side through PHP's `php://input` +stream. + +> ### Raw POST data overrides other content +> +> 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. + +```php +use Zend\Http\Client; + +// Using basic authentication +$client->setAuth('shahar', 'myPassword!', 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 request headers are reset and are not reused in the next request. + +> ### 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 +use Zend\Http\Client; +use Zend\Http\Cookies; + +// First, instantiate the client +$client = new Client( + 'http://www.example.com/fetchdata.php', + ['keepalive' => true] +); + +// Do we have the cookies stored in our session? +if (isset($_SESSION['cookiejar']) + && $_SESSION['cookiejar'] instanceof Cookies +) { + $cookieJar = $_SESSION['cookiejar']; +} else { + // If we don't, authenticate and store cookies + $client->setUri('http://www.example.com/login.php'); + $client->setParameterPost([ + 'user' => 'shahar', + 'pass' => 'somesecret', + ]); + $response = $client->setMethod('POST')->send(); + $cookieJar = 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()`. The +single, optional argument specifies the filename where the data will be stored. +If the argument is just `TRUE` (default), a temporary file will be used and +will be deleted once the response object is destroyed. Setting the argument to +`FALSE` disables the streaming functionality. + +When using streaming, the `send()` method will return an 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. + +```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); + +// write to an existing file: +$client-setStream('my/downloads/myfile')-send(); +``` diff --git a/doc/book/client/cookies.md b/doc/book/client/cookies.md new file mode 100644 index 0000000000..00a773adaf --- /dev/null +++ b/doc/book/client/cookies.md @@ -0,0 +1,128 @@ +# Client Cookies + +`Zend\Http\Cookies` can be used with `Zend\Http\Client` to manage sending +cookies in the request and setting cookies from the response; it is populated +from the `Set-Cookie` headers obtained from a client response, and then used to +populate the `Cookie` headers for a client request. This is highly useful in +cases where you need to maintain a user session over consecutive HTTP requests, +automatically sending the session ID cookies when required. Additionally, the +`Zend\Http\Cookies` object can be serialized and stored in `$_SESSION` when +needed. + +`Zend\Http\Client` already provides methods for managing cookies for requests; +`Zend\Http\Cookies` manages the parsing of `Set-Cookie` headers returned in the +response, and allows persisting them. Additionally, `Cookies` can return a +subset of cookies that match the current request, ensuring you are only sending +relevant cookies. + +## Usage + +`Cookies` is an extension of `Zend\Http\Headers`, and inherits its methods. It +can be instantiated without any arguments. + +```php +use Zend\Http\Cookies; + +$cookies = new Cookies(); +``` + +On your first client request, you likely won't have any cookies, so the +instance does nothing. + +Once you've made your first request, you can start using it. Populate it from +the response: + +```php +$response = $client->send(); + +$cookies->addCookiesFromResponse($response, $client->getUri()); +``` + +Alternately, you can create your initial `Cookies` instance using the static `fromResponse()` method: + +```php +$cookies = Cookies::fromResponse($response, $client->getUri()); +``` + +On subsequent requests, we'll notify the client of our cookies. To do this, we +should use the same URI we'll use for the request. + +```php +$client->setUri($uri); +$client->setCookies($cookies->getMatchingCookies($uri)); +``` + +After the request, don't forget to add any cookies returned! + +Essentially, `Cookies` aggregates all cookies for our client interactions, and +allows us to send only those relevant to a given request. + +## Serializing and caching cookies + +To cache cookies — e.g., to store in `$_SESSION`, or between job +invocations — you will need to serialize them. `Zend\Http\Cookies` +provides this functionality via the `getAllCookies()` method. + +If your cache storage allows array structures, use the `COOKIE_STRING_ARRAY` constant: + +```php +$cookiesToCache = $cookies->getAllCookies($cookies::COOKIE_STRING_ARRAY); +``` + +If your cache storage only allows string values, use `COOKIE_STRING_CONCAT`: + +```php +$cookiesToCache = $cookies->getAllCookies($cookies::COOKIE_STRING_CONCAT); +``` + +When you retrieve the value later, you can test its type to determine how to +deserialize the values: + +```php +use Zend\Http\Cookies; +use Zend\Http\Headers; + +$cookies = new Cookies(); + +if (is_array($cachedCookies)) { + foreach ($cachedCookies as $cookie) { + $cookies->addCookie($cookie); + } +} elseif (is_string($cachedCookies)) { + foreach (Headers::fromString($cachedCookies) as $cookie) { + $cookies->addCookie($cookie); + } +} +``` + +## Public methods + +Besides the methods demonstrated in the examples, `Zend\Http\Cookies` defines the following: + +Method signature | Description +------------------------------------------------------------------- | ----------- +`static fromResponse(Response $response, string $refUri) : Cookies` | Create a `Cookies` instance from a response and the request URI. Parses all `Set-Cookie` headers, maps them to the URI, and aggregates them. +`addCookie(string|SetCookie $cookie, string $refUri = null) : void` | Add a cookie, mapping it to the given URI. If no URI is provided, it will be inferred from the cookie value's domain and path. +`addCookiesFromResponse(Response $response, string $refUri) : void` | Add all `Set-Cookie` values from the provided response, mapping to the given URI. +`getAllCookies(int $retAs = self::COOKIE_OBJECT) : array|string` | Retrieve all cookies. Returned array will have either `SetCookie` instances (the default), strings for each `Set-Cookie` declaration, or a single string containing all declarations, based on the `COOKIE_*` constant used. +`getCookie(/* ... */) : string|SetCookie` | Retrieve a single cookie by name for the given URI. See below for argument details. +`getMatchingCookies(/* ... */) : array` | See below for details. +`isEmpty() : bool` | Whether or not the instance aggregates any cookies currently. +`reset() : void` | Clear all aggregated cookies from the instance. + +`getCookie()` accepts the following arguments, in the following order: + +Argument | Description +---------------------------------- | ----------- +`string $uri` | URI to match when retrieving the cookie. Will use its protocol, domain, and path. +`string $cookieName` | The specific cookie name to retrieve. +`int $retAs = self::COOKIE_OBJECT` | How to return matched cookies; defaults to `SetCookie` objects. Can be any of the `Cookies::COOKIE_*` constant values. + +`getMatchingCookies()` accepts the following arguments, in the following order: + +Argument | Description +---------------------------------- | ----------- +`string $uri` | URI to match when retrieving cookies. Will use its protocol, domain, and path. +`bool $matchSessionCookies = true` | Whether or not to also return related session cookies. +`int $retAs = self::COOKIE_OBJECT` | How to return matched cookies; defaults to `SetCookie` objects. Can be any of the `Cookies::COOKIE_*` constant values. +`int $now = null` | Timestamp against which to match; defaults to `time()`. Any expired cookies will be ignored. diff --git a/doc/book/client/intro.md b/doc/book/client/intro.md new file mode 100644 index 0000000000..51766c1e18 --- /dev/null +++ b/doc/book/client/intro.md @@ -0,0 +1,258 @@ +# HTTP Client + +`Zend\Http\Client` provides an interface for performing Hyper-Text Transfer +Protocol (HTTP) requests. `Zend\Http\Client` supports all basic 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 the chapter on [Responses](../response.md) for +more details). + +## Quick Start + +The class constructor optionally accepts a URL as its first parameter (which can +be either a string or a `Zend\Uri\Http` object), and an array or `Traversable` +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', + [ + 'maxredirects' => 0, + 'timeout' => 30, + ] +); +$response = $client->send(); +``` + +Both constructor parameters can be left out, and set later using the `setUri()` +and `setOptions()` methods: + +```php +use Zend\Http\Client; + +$client = new Client(); +$client->setUri('http://example.org'); +$client->setOptions([ + 'maxredirects' => 0, + 'timeout' => 30, +]); +$response = $client->send(); +``` + +`Zend\Http\Client` can also dispatch requests using a separately configured +`request` object (see the [Request](../request.md) manual 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); +``` + +> ### URL validation +> +> `Zend\Http\Client` uses `Zend\Uri\Http` to validate URLs. See the +> [zend-uri](http://framework.zend.com/manual/current/en/index.html#zend-uri) +> documentation for more information. + +## Configuration + +The constructor and `setOptions()` method accept an associative array or +`Traversable` instance containing configuration parameters. Setting these +parameters is optional, as they all have default values. + +Parameter | Description | Expected Values | Default Value +------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------- +`maxredirects` | Maximum number of redirections to follow (0 = none) | integer | 5 +`strictredirects` | Whether to strictly follow the RFC when redirecting (see this section) | boolean | FALSE +`useragent` | User agent identifier string (sent in request headers) | string | `Zend\Http\Client` +`timeout` | Connection timeout (seconds) | integer | 10 +`httpversion` | HTTP protocol version (usually '1.1' or '1.0') | string | 1.1 +`adapter` | Connection adapter class to use (see this section) | mixed | `Zend\Http\Client\Adapter\Socket` +`keepalive` | Whether to enable keep-alive connections with the server. Useful and might improve performance if several consecutive requests to the same server are performed. | boolean | FALSE +`storeresponse` | Whether to store last response for later retrieval with getLastResponse(). If set to FALSE, getLastResponse() will return NULL. | boolean | TRUE +`encodecookies` | Whether to pass the cookie value through urlencode/urldecode. Enabling this breaks support with some web servers. Disabling this limits the range of values the cookies can contain. | boolean | TRUE +`outputstream` | Destination for streaming of received data (options: string (filename), true for temp file, false/null to disable streaming) | boolean | FALSE +`rfc3986strict` | Whether to strictly adhere to RFC 3986 (in practice, this means replacing '+' with '%20') | boolean | FALSE + +The options are also passed to the adapter class upon instantiation, so the same +configuration can be used for adapter configuration. See the +[adapters](adapters.md) section for more information on the adapter-specific +options available. + +## Examples + +### Performing a GET request + +GET is the default method used, and requires no special configuration. + +```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 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 query parameters + +Adding query parameters to an HTTP request can be done either by specifying them +as part of the URL, or by using the `setParameterGet()` method. This method +takes the query parameters as an associative array of name/value pairs. + +```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([ + 'first_name' => 'Bender', + 'middle_name' => 'Bending', + 'last_name' => 'Rodríguez', + 'made_in' => 'Mexico', +]); +``` + +### Setting form-encoded body parameters + +While query parameters can be sent with every request method, other methods can +accept parameters via the request body. In many cases, these are +`application/x-www-form-urlencoded` parameters; zend-http allows you to specify +such parameters usingthe `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([ + 'language' => 'es', + 'country' => 'ar', + 'selection' => [45, 32, 80], +]); +``` + +Note that when sending `POST` requests (or an request allowing a request body), +you can set both query and `POST` parameters. On the other hand, setting POST +parameters on a `GET` request will not trigger an error, rendering it useless. + +### Connecting to SSL URLs + +If you are trying to connect to an SSL or TLS (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', + [ + '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', + [ + 'adapter' => 'Zend\Http\Client\Adapter\Curl', + ] +); +$response = $client->send(); +``` + +## Complete Example + +```php +use Zend\Http\Client; + +$client = new Client(); +$client->setUri('http://www.example.com'); +$client->setMethod('POST'); +$client->setParameterPost([ + '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 +} +``` diff --git a/doc/book/client/static.md b/doc/book/client/static.md new file mode 100644 index 0000000000..e4240250e3 --- /dev/null +++ b/doc/book/client/static.md @@ -0,0 +1,66 @@ +# HTTP Client - Static Usage + +zend-http provides another client implementation, `Zend\Http\ClientStatic`, a +static HTTP client which exposes a simplified API for quickly performing one-off `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', + ['foo' => 'bar'], + ['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', + [ + 'username' => 'foo', + 'password' => 'bar', + ] +); +``` + +## Available Methods + +### get() + +```php +get( + string $url, + array $query = [], + array $headers = [], + mixed $body = null, + $clientOptions = null +) : Response +``` + +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. + +### post() + +```php +post( + string $url, + array $params, + array $headers = [], + mixed $body = null, + $clientOptions = null +) : Response +``` + +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. diff --git a/doc/book/headers.md b/doc/book/headers.md new file mode 100644 index 0000000000..4d03eaf595 --- /dev/null +++ b/doc/book/headers.md @@ -0,0 +1,594 @@ +# Headers + +`Zend\Http\Headers` is a container for HTTP headers. It is typically accessed as +part of a `Zend\Http\Request` or `Zend\Http\Response` instance, via a +`getHeaders()` call. The `Headers` container will lazily load actual +`Zend\Http\Header\HeaderInterface` instances as to reduce the overhead of header +specific parsing. + +The class under the `Zend\Http\Header` namespace are the domain specific +implementations for the various types of headers that one might encounter during +the typical HTTP request. If a header of unknown type is encountered, it will be +implemented as a `Zend\Http\Header\GenericHeader` instance. See the below table +for a list of the various HTTP headers and the API that is specific to each +header type. + +## Quick Start + +The quickest way to get started interacting with header objects is by retrieving +an already populated `Headers` container from a request or response instance. + +```php +// $client is an instance of Zend\Http\Client + +// You can retrieve the request headers by first retrieving +// the Request object and then calling getHeaders on it +$requestHeaders = $client->getRequest()->getHeaders(); + +// The same method also works for retrieving Response headers +$responseHeaders = $client->getResponse()->getHeaders(); +``` + +`Zend\Http\Headers` can also extract headers from a string: + +```php +use Zend\Http\Headers; + +$headerString = << 0` if it's greater, and `= 0` if they are equal. See [strcmp](http://www.php.net/manual/en/function.strcmp.php). +`date() | DateTime` | Return date for this header as an instance of `DateTime`. + +## AbstractLocation Methods + +`Zend\Http\Header\AbstractLocation` defines the following methods in addition to +those it inherits from the [HeaderInterface](#headerinterface-methods). The +`ContentLocation`, `Location`, and `Referer` header types inherit from it. + +For brevity, we map the following references to the following classes or +namespaces: + +- `Uri`: `Zend\Uri\UriInterface` +- `InvalidArgumentException`: `Zend\Http\Header\Exception\InvalidArgumentException` + +Method signature | Description +-------------------------------- | ----------- +`setUri(string|Uri $uri) : self` | Set the URI for this header; throws `InvalidArgumentException` for invalid `$uri` arguments. +`getUri() : string` | Return the URI for this header. +`uri() : Uri` | Return the `Uri` instance for this header. + +## List of HTTP Header Types + +Some header classes expose methods for manipulating their value. The following +list contains all of the classes available in the `Zend\Http\Header\*` +namespace, as well as any specific methods they contain. Each class implements +`Zend\Http\Header\HeaderInterface`. + +### Accept + +Extends [AbstractAccept](#abstractaccept-methods). + +Method signature | Description +------------------------------------------------------------ | ----------- +`addMediaType(string $type, int|float $priority = 1) : self` | Add a media type, with the given priority. +`hasMediaType(string $type): bool` | Does the header have the requested media type? + +### AcceptCharset + +Extends [AbstractAccept](#abstractaccept-methods). + +Method signature | Description +---------------------------------------------------------- | ----------- +`addCharset(string $type, int|float $priority = 1) : self` | Add a charset, with the given priority. +`hasCharset(string $type) : bool` | Does the header have the requested charset? + +### AcceptEncoding + +Extends [AbstractAccept](#abstractaccept-methods). + +Method signature | Description +----------------------------------------------------------- | ----------- +`addEncoding(string $type, int|float $priority = 1) : self` | Add an encoding, with the given priority. +`hasEncoding(string $type) : bool` | Does the header have the requested encoding? + +### AcceptLanguage + +Extends [AbstractAccept](#abstractaccept-methods). + +Method signature | Description +---------------------------------------------------------- | ----------- +`addLanguage(string $type, int|float $priority = 1): self` | Add a language, with the given priority. +`hasLanguage(string $type) : bool` | Does the header have the requested language? + +### AcceptRanges + +Method signature | Description +--------------------------------------- | ----------- +`getRangeUnit() : mixed` | (unknown) +`setRangeUnit(mixed $rangeUnit) : self` | (unkown) + +### Age + +Method signature | Description +-------------------------------------- | ----------- +`getDeltaSeconds() : int` | Get number of seconds. +`setDeltaSeconds(int $seconds) : self` | Set number of seconds + +### Allow + +Method signature | Description +---------------------------------------------------- | ----------- +`getAllMethods() : string[]` | Get list of all defined methods. +`getAllowedMethods() : string[]` | Get list of allowed methods. +`allowMethods(array|string $allowedMethods) : self` | Allow methods or list of methods. +`disallowMethods(array|string $allowedMethods) self` | Disallow methods or list of methods. +`denyMethods(array|string $allowedMethods) : self` | Convenience alias for `disallowMethods()`. +`isAllowedMethod(string $method) : bool` | Check whether method is allowed. + +### AuthenticationInfo + +No additional methods. + +### Authorization + +No additional methods. + +### CacheControl + +Method signature | Description +------------------------------------------------------------- | ----------- +`isEmpty(): bool` | Checks if the internal directives array is empty. +`addDirective(string $key, string|bool $value = true) : self` | Add a directive. For directives like `max-age=60`, call as `addDirective('max-age', 60)`. For directives like `private`, use the default `$value` (`true`). +`hasDirective(string $key) : bool` | Check the internal directives array for a directive. +`getDirective(string $key) : null|string` | Fetch the value of a directive from the internal directive array. +`removeDirective(string $key) : self` | Remove a directive. + +### Connection + +Method signature | Description +---------------------------------- | ----------- +`setValue($value) : self` | Set arbitrary header value. RFC allows any token as value; 'close' and 'keep-alive' are commonly used. +`isPersistent() : bool` | Whether or not the connection is persistent. +`setPersistent(bool $flag) : self` | Set Connection header to define persistent connection. + +### ContentDisposition + +No additional methods. + +### ContentEncoding + +No additional methods. + +### ContentLanguage + +No additional methods. + +### ContentLength + +No additional methods. + +### ContentLocation + +See [AbstractLocation](#abstractlocation-methods). + +### ContentMD5 + +No additional methods. + +### ContentRange + +No additional methods. + +### ContentSecurityPolicy + +Method signature | Description +--------------------------------------------------- | ----------- +`getDirectives(): array` | Retrieve the defined directives for the policy. +`setDirective(string $name, array $sources) : self` | Set the directive with the given name to include the sources. See below for an example. + +As an example: an auction site wishes to load images from any URI, plugin +content from a list of trusted media providers (including a content distribution +network), and scripts only from a server under its control hosting sanitized +Javacript: + +```php +// http://www.w3.org/TR/2012/CR-CSP-20121115/#sample-policy-definitions +$csp = new ContentSecurityPolicy(); +$csp->setDirective('default-src', []); // No sources +$csp->setDirective('img-src', ['*']); +$csp->setDirective('object-src', ['media1.example.com', 'media2.example.com', '*.cdn.example.com']); +$csp->setDirective('script-src', ['trustedscripts.example.com']); +``` + +### ContentTransferEncoding + +No additional methods. + +### ContentType + +Method signature | Description +------------------------------------------------- | ----------- +`match(array|string $matchAgainst) : bool|string` | Determine if the mediatype value in this header matches the provided criteria. +`getMediaType() : string` | Get the media type. +`setMediaType(string $mediaType) : self` | Set the media type. +`getParameters() : array` | Get any additional content-type parameters currently set. +`setParameters(array $parameters) : self` | Set additional content-type parameters. +`getCharset() : null|string` | Get the content-type character set encoding, if any. +`setCharset(string $charset) : self` | Set the content-type character set encoding. + +### Cookie + +Extends `ArrayObject`. + +Method signature | Description +------------------------------------------------------- | ----------- +`static fromSetCookieArray(array $setCookies) : Cookie` | Create an instance from the `$_COOKIE` array, or one structured like it. +`setEncodeValue(bool $encode) : self` | Set flag indicating whether or not to `urlencode()` the cookie values. +`getEncodeValue() : bool` | Get flag indicating whether or not to `urlencode()` the cookie values. + +### Date + +See [AbstractDate](#abstractdate-methods). + +### Etag + +No additional methods. + +### Expect + +No additional methods. + +### Expires + +See [AbstractDate](#abstractdate-methods). + +### From + +No additional methods. + +### Host + +No additional methods. + +### IfMatch + +No additional methods. + +### IfModifiedSince + +See [AbstractDate](#abstractdate-methods). + +### IfNoneMatch + +No additional methods. + +### IfRange + +No additional methods. + +### IfUnmodifiedSince + +See [AbstractDate](#abstractdate-methods). + +### KeepAlive + +No additional methods. + +### LastModified + +See [AbstractDate](#abstractdate-methods). + +### Location + +See [AbstractLocation](#abstractlocation-methods). + +### MaxForwards + +No additional methods. + +### Origin + +No additional methods. + +### Pragma + +No additional methods. + +### ProxyAuthenticate + +Method signature | Description +-------------------------------------------------- | ----------- +`toStringMultipleHeaders(array $headers) : string` | Creates a string representation when multiple values are present. + +### ProxyAuthorization + +No additional methods. + +### Range + +No additional methods. + +### Referer + +See [AbstractLocation](#abstractlocation-methods). + +### Refresh + +No additional methods. + +### RetryAfter + +See [AbstractDate](#abstractdate-methods). + +Method signature | Description +------------------------------------ | ----------- +`setDeltaSeconds(int $delta) : self` | Set number of seconds. +`getDeltaSeconds() : int` | Get number of seconds. + +### Server + +No additional methods. + +### SetCookie + +Method signature | Description +--------------------------------------------------------------------- | ----------- +`static matchCookieDomain(string $cookieDomain, string $host) : bool` | Check if a cookie's domain matches a host name. +`static matchCookiePath(string $cookiePath, string $path) : bool` | Check if a cookie's path matches a URL path. +`getName() : string` | Retrieve the cookie name. +`setName(string $name) : self` | Set the cookie name. +`getValue() : string` | Retrieve the cookie value. +`setValue(string $value) : self` | Set the cookie value. +`getExpires() : int` | Retrieve the expiration date for the cookie. +`setExpires(int|string $expires) : self` | Set the cookie expiration timestamp; null indicates a session cookie. +`getPath() : string` | Retrieve the URI path the cookie is bound to. +`setPath(string $path) : self` | Set the URI path the cookie is bound to. +`getDomain() : string` | Retrieve the domain the cookie is bound to. +`setDomain(string $domain) : self` | Set the domain the cookie is bound to. +`getMaxAge() : int` | Retrieve the maximum age for the cookie. +`setMaxAge(int|string $maxAge) : self` | Set the maximum age for the cookie. +`getVersion() : int` | Retrieve the cookie version. +`setVersion(int|string $version) : self` | Set the cookie version. +`isSecure(): bool` | Whether the cookies contains the Secure flag. +`setSecure(bool $secure) : self` | Set whether the cookies contain the Secure flag. +`isHttponly() : bool` | Whether the cookies can be accessed via the HTTP protocol only. +`setHttponly(bool $httponly) : self` | Set whether the cookies can be accessed only via HTTP protocol. +`isExpired() : bool` | Whether the cookie is expired. +`isSessionCookie() : bool` | Whether the cookie is a session cookie. +`setQuoteFieldValue(bool $quotedValue) : self` | Set whether the value for this cookie should be quoted. +`hasQuoteFieldValue() : bool` | Check whether the value for this cookie should be quoted. +`isValidForRequest() : bool` | Whether the cookie is valid for a given request domain, path and isSecure. +`match(string $uri, bool $matchSessionCookies, int $now) : bool` | Checks whether the cookie should be sent or not in a specific scenario. +`toStringMultipleHeaders(array $headers) : string` | Returns string representation when multiple values are present. + +### TE + +No additional methods. + +### Trailer + +No additional methods. + +### TransferEncoding + +No additional methods. + +### Upgrade + +No additional methods. + +### UserAgent + +No additional methods. + +### Vary + +No additional methods. + +### Via + +No additional methods. + +### Warning + +No additional methods. + +### WWWAuthenticate + +Defines a `toStringMultipleHeaders(array $headers)` method for serializing to +string when multiple values are present. + +## Examples + +### Retrieving headers from a Headers object + +```php +// $client is an instance of Zend\Http\Client +$response = $client->send(); +$headers = $response->getHeaders(); + +// We can check if the Request contains a specific header by +// using the ``has`` method. Returns boolean ``TRUE`` if at least +// one matching header found, and ``FALSE`` otherwise +$headers->has('Content-Type'); + +// We can retrieve all instances of a specific header by using +// the ``get`` method: +$contentTypeHeaders = $headers->get('Content-Type'); +``` + +There are three possibilities for the return value of the above call to the `get` method: + +- If no `Content-Type` header was set in the `Request`, `get` will return `false`. +- If only one `Content-Type` header was set in the `Request`, `get` will return + an instance of `Zend\Http\Header\ContentType`. +- If more than one `Content-Type` header was set in the `Request`, `get` will + return an `ArrayIterator` containing one `Zend\Http\Header\ContentType` + instance per header. + +### Adding headers to a Headers object + +```php +use Zend\Http\Header; +use Zend\Http\Headers; + +$headers = new Headers(); + +// We can directly add any object that implements Zend\Http\Header\HeaderInterface +$typeHeader = Header\ContentType::fromString('Content-Type: text/html'); +$headers->addHeader($typeHeader); + +// We can add headers using the raw string representation, either +// passing the header name and value as separate arguments... +$headers->addHeaderLine('Content-Type', 'text/html'); + +// .. or we can pass the entire header as the only argument +$headers->addHeaderLine('Content-Type: text/html'); + +// We can also add headers in bulk using addHeaders, which accepts +// an array of individual header definitions that can be in any of +// the accepted formats outlined below: +$headers->addHeaders([ + // An object implementing Header\HeaderInterface + Header\ContentType::fromString('Content-Type: text/html'), + + // A raw header string + 'Content-Type: text/html', + + // We can also pass the header name as the array key and the + // header content as that array key's value + 'Content-Type' => 'text/html', +]); +``` + +### Removing headers from a Headers object + +We can remove all headers of a specific type using the `removeHeader` method, +which accepts a single object implementing `Zend\Http\Header\HeaderInterface` + +```php +use ArrayIterator; +use Zend\Http\Header\HeaderInterface; + +// $headers is a pre-configured instance of Zend\Http\Headers + +// We can also delete individual headers or groups of headers +$matches = $headers->get('Content-Type'); + +if ($matches instanceof ArrayIterator) { + // If more than one header was found, iterate over the collection + // and remove each one individually + foreach ($headers as $header) { + $headers->removeHeader($header); + } +} elseif ($matches instanceof HeaderInterface) { + // If only a single header was found, remove it directly + $headers->removeHeader($header); +} + +// In addition to this, we can clear all the headers currently stored in +// the container by calling the clearHeaders() method +$matches->clearHeaders(); +``` diff --git a/doc/book/index.html b/doc/book/index.html new file mode 100644 index 0000000000..10b3ea6d70 --- /dev/null +++ b/doc/book/index.html @@ -0,0 +1,10 @@ +
+
+

zend-http

+ +

HTTP message and header abstractions, and HTTP client implementation. (Not a PSR-7 implementation; see Diactoros for PSR-7 support.

+ +
$ composer require zendframework/zend-http
+
+
+ diff --git a/doc/book/index.md b/doc/book/index.md new file mode 120000 index 0000000000..fe84005413 --- /dev/null +++ b/doc/book/index.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/doc/book/intro.md b/doc/book/intro.md new file mode 100644 index 0000000000..68b260a9ae --- /dev/null +++ b/doc/book/intro.md @@ -0,0 +1,35 @@ +# Introduction + +zend-http provides the HTTP message abstraction used by +[zend-mvc](https://zendframework.github.io/zend-mvc/), and also provides an +extensible, adapter-driven HTTP client library. It provides the following +abstractions: + +- Context-less `Request` and `Response` classes that expose a fluent API for + introspecting several aspects of HTTP messages: + - Request line information and response status information + - Parameters, such as those found in POST and GET + - Message Body + - Headers +- A client implementation with various adapters that allow for sending requests + and introspecting responses. + +> ### Not PSR-7! +> +> This library **does not** support [PSR-7](http://www.php-fig.org/psr/psr-7), as +> it predates that specification. For PSR-7 support, please see our +> [Diactoros component](https://zendframework.github.io/zend-diactoros/). + +## Zend\Http Request, Response and Headers + +The request, response and headers implementations of the zend-http component +provides a fluent, object-oriented interface for introspecting information from +all the various parts of an HTTP request or HTTP response. The primary classes +are `Zend\Http\Request` and `Zend\Http\Response`. Both are “context-less”, +meaning that they model a request or response in the same way whether it is +presented by a client (to **send** a request and **receive** a response) or by a +server (to **receive** a request and **send** a response). In other words, +regardless of the context, the API remains the same for introspecting their +various respective parts. Each attempts to fully model a request or response so +that a developer can create these objects from a factory, or create and populate +them manually. diff --git a/doc/book/request.md b/doc/book/request.md new file mode 100644 index 0000000000..d26677471b --- /dev/null +++ b/doc/book/request.md @@ -0,0 +1,177 @@ +# The Request Class + +`Zend\Http\Request` 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: + +```text +| METHOD | URI | VERSION | +| HEADERS | +| BODY | +``` + +In simplified terms, the request consists of a method, URI, and HTTP version +number; together, they make up the "Request Line." This line is followed by zero +or more HTTP headers, which is followed by an empty line and then the request +body; the body is typically used when a client wishes to send data — which +could be urlencoded parameters, a JSON document, an XML document, or even one or +more files — to the server. 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 +manually instantiating the `Zend\Http\Request` class with no parameters. + +```php +use Zend\Http\Request; + +$request = Request::fromString(<<setMethod(Request::METHOD_POST); +$request->setUri('/foo'); +$request->getHeaders()->addHeaders([ + 'HeaderField1' => 'header-field-value1', + 'HeaderField2' => 'header-field-value2', +]); +$request->getPost()->set('foo', 'bar'); +``` + +## Configuration Options + +No configuration options are available. + +## Available Methods + +The following table details available methods, their signatures, and a brief +description. Note that the following references refer to the following +fully qualified class names and/or namespaces: + +- `HeaderInterface`: `Zend\Http\Header\HeaderInterface` +- `Headers`: `Zend\Http\Headers` +- `Header`: `Zend\Http\Header` +- `Parameters`: `Zend\Stdlib\ParametersInterface` +- `Request`: `Zend\Http\Request` +- `Uri`: `Zend\Uri\Http` + +Method signature | Description +--------------------------------------------------------------------------- | ----------- +`static fromString(string $string) : Request` | A factory that produces a `Request` object from a well-formed HTTP request message string. +`setMethod(string $method) : self` | Set the method for this request. +`getMethod() : string` | Return the method for this request. +`setUri(string|Uri $uri) : self` | Set the URI/URL for this request; this can be a string or an instance of `Zend\Uri\Http`. +`getUri() : Uri` | Return the URI for this request object. +`getUriString() : string` | Return the URI for this request object as a string. +`setVersion(string $version) : self` | Set the HTTP version for this object, one of 1.0 or 1.1 (`Request::VERSION_10`, `Request::VERSION_11`). +`getVersion() : string` | Return the HTTP version for this request. +`setQuery(Parameters $query) : self` | 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()`). +`getQuery(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for query parameters or a single query parameter based on `$name`. +`setPost(Parameters $post) : self` | 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()`). +`getPost(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for POST parameters or a single POST parameter, based on `$name`. +`getCookie() : Header\Cookie` | Return the Cookie header, this is the same as calling `$request->getHeaders()->get('Cookie');`. +`setFiles(Parameters $files) : self` | 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()`). +`getFiles(string|null $name, mixed|null $default) : null|string|Parameters` | Return the parameter container responsible for file parameters or a single file parameter, based on `$name`. +`setHeaders(Headers $headers) : self` | Provide an alternate Parameter Container implementation for headers in this object, (this is NOT the primary API for value setting, for that see `getHeaders()`). +`getHeaders(string|null $name, mixed|null $default) : mixed` | 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 [Headers](headers.md) for more information. Return value is based on `$name`; `null` returns `Headers`, while a matched header returns a `Header\HeaderInterface` implementation for single-value headers or an `ArrayIterator` for multi-value headers. +`setMetadata(string|int|array|Traversable $spec, mixed $value) : self` | Set message metadata. Non-destructive setting of message metadata; always adds to the metadata, never overwrites the entire metadata container. +`getMetadata(null|string|int $key, null|mixed $default) : mixed` | Retrieve all metadata or a single metadatum as specified by key. +`setContent(mixed $value) : self` | Set request body (content). +`getContent() : mixed` | Get request body (content). +`isOptions() : bool` | Is this an OPTIONS method request? +`isGet() : bool` | Is this a GET method request? +`isHead() : bool` | Is this a HEAD method request? +`isPost() : bool` | Is this a POST method request? +`isPut() : bool` | Is this a PUT method request? +`isDelete() : bool` | Is this a DELETE method request? +`isTrace() : bool` | Is this a TRACE method request? +`isConnect() : bool` | Is this a CONNECT method request? +`isPatch() : bool` | Is this a PATCH method request? +`isXmlHttpRequest() : bool` | Is this a Javascript XMLHttpRequest? +`isFlashRequest() : bool` | Is this a Flash request? +`renderRequestLine() : string` | Return the formatted request line (first line) for this HTTP request. +`toString() : string` | Returns string +`__toString() : string` | Allow PHP casting of this object. + +## 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(['foo' => 'bar'])); +foreach ($request->getHeaders() as $header) { + printf("%s with value %s\n", $header->getFieldName(), $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([ + '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 +*/ +``` diff --git a/doc/book/response.md b/doc/book/response.md new file mode 100644 index 0000000000..acce073da2 --- /dev/null +++ b/doc/book/response.md @@ -0,0 +1,140 @@ +# The Response Class + +`Zend\Http\Response` 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: + +```text +| 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 zero or more 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 +instantiating the `Zend\Http\Response` class with no arguments. + +```php +use Zend\Http\Response; +$response = Response::fromString(<< + + Hello World + + +EOS); + +// OR + +$response = new Response(); +$response->setStatusCode(Response::STATUS_CODE_200); +$response->getHeaders()->addHeaders([ + 'HeaderField1' => 'header-field-value', + 'HeaderField2' => 'header-field-value2', +]); +$response->setContent(<< + + Hello World + + +EOS +); +``` + +## Configuration Options + +No configuration options are available. + +## Available Methods + +The following table details available methods, their signatures, and a brief +description. Note that the following references refer to the following +fully qualified class names and/or namespaces: + +- `Headers`: `Zend\Http\Headers` +- `Response`: `Zend\Http\Response` + +Method signature | Description +---------------------------------------------------------------------- | ----------- +`stati fromString(string $string) : Response` | Populate object from string. +`renderStatusLine() : string` | Render the status line header +`setHeaders(Headers $headers) : self` | Provide an alternate Parameter Container implementation for headers in this object. (This is NOT the primary API for value setting; for that, see `getHeaders()`.) +`getHeaders() : Headers` | 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 [Headers](headers.md) for more information. +`setVersion(string $version) : self` | Set the HTTP version for this object, one of 1.0 or 1.1 (`Request::VERSION_10`, `Request::VERSION_11`). +`getVersion() : string` | Return the HTTP version for this request. +`setStatusCode(int $code) : self` | Set HTTP status code. +`getStatusCode() : int` | Retrieve HTTP status code. +`setReasonPhrase(string $reasonPhrase) : self` | Set custom HTTP status message. +`getReasonPhrase() : string` | Get HTTP status message. +`isClientError() : bool` | Does the status code indicate a client error? +`isForbidden() : bool` | Is the request forbidden due to ACLs? +`isInformational() : bool` | Is the current status "informational"? +`isNotFound() : bool` | Does the status code indicate the resource is not found? +`isOk() : bool` | Do we have a normal, OK response? +`isServerError() : bool` | Does the status code reflect a server error? +`isRedirect() : bool` | Do we have a redirect? +`isSuccess() : bool` | Was the response successful? +`decodeChunkedBody(string $body) : string` | Decode a "chunked" transfer-encoded body and return the decoded text. +`decodeGzip(string $body) : string` | Decode a gzip encoded message (when `Content-Encoding` indicates gzip). Currently requires PHP with zlib support. +`decodeDeflate(string $body) : string` | Decode a zlib deflated message (when `Content-Encoding` indicates deflate). Currently requires PHP with zlib support. +`setMetadata(string|int|array|Traversable $spec, mixed $value) : self` | Non-destructive setting of message metadata; always adds to the metadata, never overwrites the entire metadata container. +`getMetadata(null|string|int $key, null|mixed $default) : mixed` | Retrieve all metadata or a single metadatum as specified by key. +`setContent(mixed $value) : self` | Set message content. +`getContent() : mixed` | Get raw message content. +`getBody() : mixed` | Get decoded message content. +`toString() : string` | Returns string representation of response. + +## Examples + +### Generating a Response object from a string + +```php +use Zend\Http\Response; +$request = Response::fromString(<< + + Hello World + + +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([ + 'HeaderField1' => 'header-field-value', + 'HeaderField2' => 'header-field-value2', +]); +$response->setContent(<< + + Hello World + + +EOS); +``` diff --git a/doc/book/zend.http.client-static.md b/doc/book/zend.http.client-static.md deleted file mode 100644 index d5921aa1cd..0000000000 --- a/doc/book/zend.http.client-static.md +++ /dev/null @@ -1,54 +0,0 @@ -# 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 - - diff --git a/doc/book/zend.http.client.adapters.md b/doc/book/zend.http.client.adapters.md deleted file mode 100644 index a346326c8d..0000000000 --- a/doc/book/zend.http.client.adapters.md +++ /dev/null @@ -1,451 +0,0 @@ -# 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" . - '' . - '' . - ' ' . - ' Premature Optimization' . - // and so on... - ''); - -$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" . - '' . - ' Moved' . - '

This page has moved.

' . - ''); - -// Set the next successive response -$adapter->addResponse( - "HTTP/1.1 200 OK" . "\r\n" . - "Content-Type: text/html" . "\r\n" . - "\r\n" . - '' . - ' My Pet Store Home Page' . - '

...

' . - ''); - -// 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' -)); -``` diff --git a/doc/book/zend.http.client.advanced.md b/doc/book/zend.http.client.advanced.md deleted file mode 100644 index 307d9dd163..0000000000 --- a/doc/book/zend.http.client.advanced.md +++ /dev/null @@ -1,341 +0,0 @@ -# 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 -<zend.http.client.cookies>. - -## 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 `` 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 = '' . - ' Islands in the Stream' . - ' Ernest Hemingway' . - ' 1970' . - ''; -$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(); -``` diff --git a/doc/book/zend.http.client.md b/doc/book/zend.http.client.md deleted file mode 100644 index e54c5b3fd1..0000000000 --- a/doc/book/zend.http.client.md +++ /dev/null @@ -1,230 +0,0 @@ -# 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 <zend.http.response>). - -## 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<zend.http.request> 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<zend.uri> 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<zend.http.client.adapters> 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 => 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 -} -``` diff --git a/doc/book/zend.http.headers.md b/doc/book/zend.http.headers.md deleted file mode 100644 index 67b89d69ee..0000000000 --- a/doc/book/zend.http.headers.md +++ /dev/null @@ -1,1052 +0,0 @@ -# The Headers Class - -## Overview - -The `Zend\Http\Headers` class is a container for HTTP headers. It is typically accessed as part of a -`Zend\Http\Request` or `Zend\Http\Response` `getHeaders()` call. The Headers container will lazily -load actual Header objects as to reduce the overhead of header specific parsing. - -The `Zend\Http\Header\*` classes are the domain specific implementations for the various types of -Headers that one might encounter during the typical HTTP request. If a header of unknown type is -encountered, it will be implemented as a `Zend\Http\Header\GenericHeader` instance. See the below -table for a list of the various HTTP headers and the API that is specific to each header type. - -## Quick Start - -The quickest way to get started interacting with header objects is by getting an already populated -Headers container from a request or response object. - -```php -// $client is an instance of Zend\Http\Client - -// You can retrieve the request headers by first retrieving -// the Request object and then calling getHeaders on it -$requestHeaders = $client->getRequest()->getHeaders(); - -// The same method also works for retrieving Response headers -$responseHeaders = $client->getResponse()->getHeaders(); -``` - -`Zend\Http\Headers` can also extract headers from a string: - -```php -$headerString = << - -**setPluginClassLoader** -`setPluginClassLoader(Zend\Loader\PluginClassLocator $pluginClassLoader)` - -Set an alternate implementation for the plugin class loader - -Returns `Zend\Http\Headers` - - - -**getPluginClassLoader** -`getPluginClassLoader()` - -Return an instance of a `Zend\Loader\PluginClassLocator`, lazyload and inject map if necessary. - -Returns `Zend\Loader\PluginClassLocator` - - - -**addHeaders** -`addHeaders(array|Traversable $headers)` - -Add many headers at once - -Expects an array (or `Traversable` object) of type/value pairs. - -Returns `Zend\Http\Headers` - - - -**addHeaderLine** -`addHeaderLine(string $headerFieldNameOrLine, string $fieldValue)` - -Add a raw header line, either in name => value, or as a single string 'name: value' - -This method allows for lazy-loading in that the parsing and instantiation of Header object will be -delayed until they are retrieved by either `get()` or `current()`. - -Returns `Zend\Http\Headers` - - - -**addHeader** -`addHeader(Zend\Http\Header\HeaderInterface $header)` - -Add a Header to this container, for raw values see `addHeaderLine()` and `addHeaders()`. - -Returns `Zend\Http\Headers` - - - -**removeHeader** -`removeHeader(Zend\Http\Header\HeaderInterface $header)` - -Remove a Header from the container - -Returns bool - - - -**clearHeaders** -`clearHeaders()` - -Clear all headers - -Removes all headers from queue - -Returns `Zend\Http\Headers` - - - -**get** -`get(string $name)` - -Get all headers of a certain name/type - -Returns false| `Zend\Http\Header\HeaderInterface`| `ArrayIterator` - - - -**has** -`has(string $name)` - -Test for existence of a type of header - -Returns bool - - - -**next** -`next()` - -Advance the pointer for this object as an iterator - -Returns void - - - -**key** -`key()` - -Return the current key for this object as an iterator - -Returns mixed - - - -**valid** -`valid()` - -Is this iterator still valid? - -Returns bool - - - -**rewind** -`rewind()` - -Reset the internal pointer for this object as an iterator - -Returns void - - - -**current** -`current()` - -Return the current value for this iterator, lazy loading it if need be - -Returns `Zend\Http\Header\HeaderInterface` - - - -**count** -`count()` - -Return the number of headers in this container. If all headers have not been parsed, actual count -could increase if MultipleHeader objects exist in the Request/Response. If you need an exact count, -iterate. - -Returns int - - - -**toString** -`toString()` - -Render all headers at once - -This method handles the normal iteration of headers; it is up to the concrete classes to prepend -with the appropriate status/request line. - -Returns string - - - -**toArray** -`toArray()` - -Return the headers container as an array - -Returns array - - - -**forceLoading** -`forceLoading()` - -By calling this, it will force parsing and loading of all headers, after this `count()` will be -accurate - -Returns bool - -## Zend\\Http\\Header\\HeaderInterface Methods - -**fromString** -`fromString(string $headerLine)` - -Factory to generate a header object from a string - -Returns `Zend\Http\Header\GenericHeader` - - - -**getFieldName** -`getFieldName()` - -Retrieve header field name - -Returns string - - - -**getFieldValue** -`getFieldValue()` - -Retrieve header field value - -Returns string - - - -**toString** -`toString()` - -Cast to string as a well formed HTTP header line - -Returns in form of "NAME: VALUE" - -Returns string - -## Zend\\Http\\Header\\AbstractAccept Methods - -**parseHeaderLine** -`parseHeaderLine(string $headerLine)` - -Parse the given header line and add the values - -Returns void - -**getFieldValuePartsFromHeaderLine** -`getFieldValuePartsFromHeaderLine(string $headerLine)` - -Parse the Field Value Parts represented by a header line - -Throws `Zend\Http\Header\Exception\InvalidArgumentException` if the header is invalid - -Returns array - -**getFieldValue** -`getFieldValue(array|null $values = null)` - -Get field value - -Returns string - -**match** -`match(array|string $matchAgainst)` - -Match a media string against this header. Returns the matched value or false - -Returns `Accept\FieldValuePart\AcceptFieldValuePart` or bool - -**getPrioritized** -`getPrioritized()` - -Returns all the keys, values and parameters this header represents - -Returns array - -## Zend\\Http\\Header\\AbstractDate Methods - -**setDateFormat** -static `setDateFormat(int $format)` - -Set date output format. - -Returns void - -**getDateFormat** -static `getDateFormat()` - -Return current date output format - -Returns string - -**setDate** -`setDate(string|DateTime $date)` - -Set the date for this header, this can be a string or an instance of DateTime - -Throws `Zend\Http\Header\Exception\InvalidArgumentException` if the date is neither a valid string -nor an instance of `\DateTime`. - -Returns self - -**getDate** -`getDate()` - -Return date for this header - -Returns self - -**compareTo** -`compareTo(string|DateTime $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](http://www.php.net/manual/en/function.strcmp.php). - -Returns int - -**date** -`date()` - -Return date for this header as an instance of `\DateTime` - -Returns `\DateTime` - -**fromTimestamp** -`fromTimestamp(int $time)` - -Create date-based header from Unix timestamp - -Returns self - -**fromTimeString** -`fromTimeString(string $time)` - -Create date-based header from strtotime()-compatible string - -Returns self - -## Zend\\Http\\Header\\AbstractLocation Methods - -**setUri** -`setUri(string|Zend\Uri\UriInterface $uri)` - -Set the URI/URL for this header, this can be a string or an instance of `Zend\Uri\Http` - -Throws `Zend\Http\Header\Exception\InvalidArgumentException` if `$uri` is neither a valid URL nor an -instance of `Zend\Uri\UriInterface`. - -Returns self - -**getUri** -`getUri()` - -Return the URI for this header - -Returns string - -**uri** -`uri()` - -Return the URI for this header as an instance of `Zend\Uri\Http` - -Returns `Zend\Uri\UriInterface` - -## List of HTTP Header Types - -Some header classes expose methods for manipulating their value. The following list contains all of -the classes available in the `Zend\Http\Header\*` namespace, as well as any specific methods they -contain. All these classes implement `Zend\Http\Header\HeaderInterface` and its -methods<zend.http.headers.header-description>. - -**Accept** -See `Zend\Http\Header\AbstractAccept` methods<zend.http.header.abstractaccept.methods>. - -`addMediaType(string $type, int|float $priority = 1)` -Add a media type, with the given priority - -Returns self - -`hasMediaType(string $type)` -Does the header have the requested media type? - -Returns bool - - - -**AcceptCharset** -See `Zend\Http\Header\AbstractAccept` methods<zend.http.header.abstractaccept.methods>. - -`addCharset(string $type, int|float $priority = 1)` -Add a charset, with the given priority - -Returns self - -`hasCharset(string $type)` -Does the header have the requested charset? - -Returns bool - - - -**AcceptEncoding** -See `Zend\Http\Header\AbstractAccept` methods<zend.http.header.abstractaccept.methods>. - -`addEncoding(string $type, int|float $priority = 1)` -Add an encoding, with the given priority - -Returns self - -`hasEncoding(string $type)` -Does the header have the requested encoding? - -Returns bool - - - -**AcceptLanguage** -See `Zend\Http\Header\AbstractAccept` methods<zend.http.header.abstractaccept.methods>. - -`addLanguage(string $type, int|float $priority = 1)` -Add a language, with the given priority - -Returns self - -`hasLanguage(string $type)` -Does the header have the requested language? - -Returns bool - - - -**AcceptRanges** -`getRangeUnit()` - -`setRangeUnit($rangeUnit)` - - - -**Age** -`getDeltaSeconds()` -Get number of seconds - -Returns int - -`setDeltaSeconds()` -Set number of seconds - -Returns self - - - -**Allow** -`getAllMethods()` -Get list of all defined methods - -Returns array - -`getAllowedMethods()` -Get list of allowed methods - -Returns array - -`allowMethods(array|string $allowedMethods)` -Allow methods or list of methods - -Returns self - -`disallowMethods(array|string $allowedMethods)` -Disallow methods or list of methods - -Returns self - -`denyMethods(array|string $allowedMethods)` -Convenience alias for `disallowMethods()` - -Returns self - -`isAllowedMethod(string $method)` -Check whether method is allowed - -Returns bool - - - -**AuthenticationInfo** -No additional methods - - - -**Authorization** -No additional methods - - - -**CacheControl** -`isEmpty()` -Checks if the internal directives array is empty - -Returns bool - -`addDirective(string $key, string|bool $value)` -Add a directive - -For directives like 'max-age=60', $value = '60' - -For directives like 'private', use the default $value = true - -Returns self - -`hasDirective(string $key)` -Check the internal directives array for a directive - -Returns bool - -`getDirective(string $key)` -Fetch the value of a directive from the internal directive array - -Returns string|null - -`removeDirective(string $key)` -Remove a directive - -Returns self - - - -**Connection** -`setValue($value)` -Set arbitrary header value - -RFC allows any token as value, 'close' and 'keep-alive' are commonly used - -Returns self - -`isPersistent()` -Whether the connection is persistent - -Returns bool - -`setPersistent(bool $flag)` -Set Connection header to define persistent connection - -Returns self - - - -**ContentDisposition** -No additional methods - - - -**ContentEncoding** -No additional methods - - - -**ContentLanguage** -No additional methods - - - -**ContentLength** -No additional methods - - - -**ContentLocation** -See `Zend\Http\Header\AbstractLocation` methods<zend.http.header.abstractlocation.methods>. - - - -**ContentMD5** -No additional methods - - - -**ContentRange** -No additional methods - - - -**ContentSecurityPolicy** -`getDirectives()` -Retrieve the defined directives for the policy - -Returns an array - -`setDirective(string $name, array $sources)` -Set the directive with the given name to include the sources - -As an example: an auction site wishes to load images from any URI, plugin content from a list of -trusted media providers (including a content distribution network), and scripts only from a server -under its control hosting sanitized ECMAScript: - -```php -// http://www.w3.org/TR/2012/CR-CSP-20121115/#sample-policy-definitions -// Example #2 -$csp = new ContentSecurityPolicy(); -$csp->setDirective('default-src', array()) // No sources - ->setDirective('img-src', array('*')) - ->setDirective('object-src' array('media1.example.com', 'media2.example.com', -'*.cdn.example.com')) - ->setDirective('script-src', array('trustedscripts.example.com')); -``` - -Returns self - - - -**ContentTransferEncoding** -No additional methods - - - -**ContentType** -`match(array|string $matchAgainst)` -Determine if the mediatype value in this header matches the provided criteria - -Returns bool|string - -`getMediaType()` -Get the media type - -Returns string - -`setMediaType(string $mediaType)` -Set the media type - -Returns self - -`getParameters()` -Get any additional content-type parameters currently set - -Returns array - -`setParameters(array $parameters)` -Set additional content-type parameters - -Returns self - -`getCharset()` -Get the content-type character set encoding, if any - -Returns string|null - -`setCharset(string $charset)` -Set the content-type character set encoding - -Returns self - - - -**Cookie** -Extends `ArrayObject` - -static `fromSetCookieArray(array $setCookies)` - -`setEncodeValue()` - -`getEncodeValue()` - - - -**Date** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - - - -**Etag** -No additional methods - - - -**Expect** -No additional methods - - - -**Expires** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - - - -**From** -No additional methods - - - -**Host** -No additional methods - - - -**IfMatch** -No additional methods - - - -**IfModifiedSince** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - - - -**IfNoneMatch** -No additional methods - - - -**IfRange** -No additional methods - - - -**IfUnmodifiedSince** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - - - -**KeepAlive** -No additional methods - - - -**LastModified** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - - - -**Location** -See `Zend\Http\Header\AbstractLocation` methods<zend.http.header.abstractlocation.methods>. - - - -**MaxForwards** -No additional methods - - - -**Origin** -No additional methods - - - -**Pragma** -No additional methods - - - -**ProxyAuthenticate** -`toStringMultipleHeaders(array $headers)` - - - -**ProxyAuthorization** -No additional methods - - - -**Range** -No additional methods - - - -**Referer** -See `Zend\Http\Header\AbstractLocation` methods<zend.http.header.abstractlocation.methods>. - - - -**Refresh** -No additional methods - - - -**RetryAfter** -See `Zend\Http\Header\AbstractDate` methods<zend.http.header.abstractdate.methods>. - -`setDeltaSeconds(int $delta)` -Set number of seconds - -Returns self - -`getDeltaSeconds()` -Get number of seconds - -Returns int - - - -**Server** -No additional methods - - - -**SetCookie** -`getName()` / `setName(string $name)` -The cookie name - -`getValue()` / `setValue(string $value)` -The cookie value - -`getExpires()` / `setExpires(int|string $expires)` -The time frame the cookie is valid for, null is a session cookie - -`getPath()` / `setPath(string $path)` -The URI path the cookie is bound to - -`getDomain()` / `setDomain(string $domain)` -The domain the cookie applies to - -`getMaxAge()` / `setMaxAge(int $maxAge)` -The maximum age of the cookie - -`getVersion()` / `setVersion(int $version)` -The cookie version - -`isSecure()` -Whether the cookies contains the Secure flag - -Returns bool - -`setSecure(bool $secure)` -Set whether the cookies contains the Secure flag - -Returns self - -`isHttponly()` -Whether the cookies can be accessed via HTTP only - -Returns bool - -`setHttponly(bool $httponly)` -Set whether the cookies can be accessed via HTTP only - -Returns self - -`isExpired()` -Whether the cookie is expired - -Returns bool - -`isSessionCookie()` -Whether the cookie is a session cookie - -Returns bool - -`setQuoteFieldValue(bool $quotedValue)` -Set whether the value for this cookie should be quoted - -Returns self - -`hasQuoteFieldValue()` -Check whether the value for this cookie should be quoted - -Returns bool - -`isValidForRequest()` -Whether the cookie is valid for a given request domain, path and isSecure - -Returns bool - -`match(string $uri, bool $matchSessionCookies, int $now)` -Checks whether the cookie should be sent or not in a specific scenario - -Returns bool - -static `matchCookieDomain(string $cookieDomain, string $host)` -Check if a cookie's domain matches a host name. - -Returns bool - -static `matchCookiePath(string $cookiePath, string $path)` -Check if a cookie's path matches a URL path - -Returns bool - -`toStringMultipleHeaders(array $headers)` -Returns string - - - -**TE** -No additional methods - - - -**Trailer** -No additional methods - - - -**TransferEncoding** -No additional methods - - - -**Upgrade** -No additional methods - - - -**UserAgent** -No additional methods - - - -**Vary** -No additional methods - - - -**Via** -No additional methods - - - -**Warning** -No additional methods - - - -**WWWAuthenticate** -`toStringMultipleHeaders(array $headers)` - -## Examples - -**Retrieving headers from a Zend\\Http\\Headers object** - -```php -// $client is an instance of Zend\Http\Client -$response = $client->send(); -$headers = $response->getHeaders(); - -// We can check if the Request contains a specific header by -// using the ``has`` method. Returns boolean ``TRUE`` if at least -// one matching header found, and ``FALSE`` otherwise -$headers->has('Content-Type'); - -// We can retrieve all instances of a specific header by using -// the ``get`` method: -$contentTypeHeaders = $headers->get('Content-Type'); -``` - -There are three possibilities for the return value of the above call to the `get` method: - -> - If no Content-Type header was set in the Request, `get` will return false. -- If only one Content-Type header was set in the Request, `get` will return an instance of -`Zend\Http\Header\ContentType`. -- If more than one Content-Type header was set in the Request, `get` will return an ArrayIterator -containing one `Zend\Http\Header\ContentType` instance per header. - -**Adding headers to a Zend\\Http\\Headers object** - -```php -$headers = new Zend\Http\Headers(); - -// We can directly add any object that implements Zend\Http\Header\HeaderInterface -$typeHeader = Zend\Http\Header\ContentType::fromString('Content-Type: text/html'); -$headers->addHeader($typeHeader); - -// We can add headers using the raw string representation, either -// passing the header name and value as separate arguments... -$headers->addHeaderLine('Content-Type', 'text/html'); - -// .. or we can pass the entire header as the only argument -$headers->addHeaderLine('Content-Type: text/html'); - -// We can also add headers in bulk using addHeaders, which accepts -// an array of individual header definitions that can be in any of -// the accepted formats outlined below: -$headers->addHeaders(array( - - // An object implementing Zend\Http\Header\HeaderInterface - Zend\Http\Header\ContentType::fromString('Content-Type: text/html'), - - // A raw header string - 'Content-Type: text/html', - - // We can also pass the header name as the array key and the - // header content as that array key's value - 'Content-Type' => 'text/html'); - -)); -``` - -**Removing headers from a Zend\\Http\\Headers object** - -We can remove all headers of a specific type using the `removeHeader` method, which accepts a single -object implementing `Zend\Http\Header\HeaderInterface` - -```php -// $headers is a pre-configured instance of Zend\Http\Headers - -// We can also delete individual headers or groups of headers -$matches = $headers->get('Content-Type'); - -// If more than one header was found, iterate over the collection -// and remove each one individually -if ($matches instanceof ArrayIterator) { - foreach ($headers as $header) { - $headers->removeHeader($header); - } -// If only a single header was found, remove it directly -} elseif ($matches instanceof Zend\Http\Header\HeaderInterface) { - $headers->removeHeader($header); -} - -// In addition to this, we can clear all the headers currently stored in -// the container by calling the clearHeaders() method -$matches->clearHeaders(); -``` diff --git a/doc/book/zend.http.request.md b/doc/book/zend.http.request.md deleted file mode 100644 index fb3297f88a..0000000000 --- a/doc/book/zend.http.request.md +++ /dev/null @@ -1,451 +0,0 @@ -# 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(<<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->getHeaders()->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<zend.http.headers> 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 -*/ -``` diff --git a/doc/book/zend.http.response.md b/doc/book/zend.http.response.md deleted file mode 100644 index 4cd9f44a36..0000000000 --- a/doc/book/zend.http.response.md +++ /dev/null @@ -1,352 +0,0 @@ -# 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(<< - - Hello World - - -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(<< - - Hello World - - -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<zend.http.headers> 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(<< - - Hello World - - -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(<< - - Hello World - - -EOS); -``` diff --git a/doc/bookdown.json b/doc/bookdown.json deleted file mode 100644 index a6e395b202..0000000000 --- a/doc/bookdown.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "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" - ] -} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..52f8ce3ec1 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,19 @@ +docs_dir: doc/book +site_dir: doc/html +pages: + - index.md + - Intro: intro.md + - Reference: + - Request: request.md + - Response: response.md + - Headers: headers.md + - Client: + - Intro: client/intro.md + - Adapters: client/adapters.md + - "Static Client": client/static.md + - "Cookie Persistence": client/cookies.md + - Advanced: client/advanced.md +site_name: zend-http +site_description: zend-http +repo_url: 'https://github.com/zendframework/zend-http' +copyright: 'Copyright (c) 2016 Zend Technologies USA Inc.' diff --git a/src/Client/Adapter/Curl.php b/src/Client/Adapter/Curl.php index 267f0c86f2..552b781890 100644 --- a/src/Client/Adapter/Curl.php +++ b/src/Client/Adapter/Curl.php @@ -434,13 +434,13 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = [], $body = // 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); + $responseHeaders = preg_replace("/Transfer-Encoding:\s*chunked\\r\\n/i", "", $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); + $responseHeaders = preg_replace("/Content-Encoding:\s*gzip\\r\\n/i", '', $responseHeaders); } // cURL automatically handles Proxy rewrites, remove the "HTTP/1.0 200 Connection established" string: diff --git a/src/Header/AbstractAccept.php b/src/Header/AbstractAccept.php index 7a8c379ee4..c2eac3d35c 100644 --- a/src/Header/AbstractAccept.php +++ b/src/Header/AbstractAccept.php @@ -170,7 +170,12 @@ protected function getParametersFromFieldValuePart($fieldValuePart) foreach ($paramsStrings as $param) { $explode = explode('=', $param, 2); - $value = trim($explode[1]); + if (count($explode) === 2) { + $value = trim($explode[1]); + } else { + $value = null; + } + if (isset($value[0]) && $value[0] == '"' && substr($value, -1) == '"') { $value = substr(substr($value, 1), 0, -1); } @@ -190,7 +195,7 @@ protected function getParametersFromFieldValuePart($fieldValuePart) */ public function getFieldValue($values = null) { - if (!$values) { + if ($values === null) { return $this->getFieldValue($this->fieldValueParts); } @@ -226,9 +231,9 @@ function ($v) { ); if ($escaped == $value && !array_intersect(str_split($value), $separators)) { - $value = $key . '=' . $value; + $value = $key . ($value ? '=' . $value : ''); } else { - $value = $key . '="' . $escaped . '"'; + $value = $key . ($value ? '="' . $escaped . '"' : ''); } return $value; diff --git a/src/PhpEnvironment/Request.php b/src/PhpEnvironment/Request.php index a9b51fc3f9..01e0bde59d 100644 --- a/src/PhpEnvironment/Request.php +++ b/src/PhpEnvironment/Request.php @@ -568,7 +568,6 @@ protected function detectBaseUrl() */ protected function detectBasePath() { - $filename = basename($this->getServer()->get('SCRIPT_FILENAME', '')); $baseUrl = $this->getBaseUrl(); // Empty base url detected @@ -576,6 +575,8 @@ protected function detectBasePath() return ''; } + $filename = basename($this->getServer()->get('SCRIPT_FILENAME', '')); + // basename() matches the script filename; return the directory if (basename($baseUrl) === $filename) { return str_replace('\\', '/', dirname($baseUrl)); diff --git a/test/Client/CurlTest.php b/test/Client/CurlTest.php index 4e8403fe59..014dc47d07 100644 --- a/test/Client/CurlTest.php +++ b/test/Client/CurlTest.php @@ -397,4 +397,31 @@ public function testSetCurlOptPostFields() $this->client->send(); $this->assertEquals('foo=bar', $this->client->getResponse()->getBody()); } + + + /** + * @group ZF-7683 + * @see https://github.com/zendframework/zend-http/pull/53 + * + * Note: The headers stored in ZF7683-chunked.php are case insensitive + */ + public function testNoCaseSensitiveHeaderName() + { + $this->client->setUri($this->baseuri . 'ZF7683-chunked.php'); + + $adapter = new Adapter\Curl(); + $adapter->setOptions([ + 'curloptions' => [ + CURLOPT_ENCODING => '', + ], + ]); + $this->client->setAdapter($adapter); + $this->client->setMethod('GET'); + $this->client->send(); + + $headers = $this->client->getResponse()->getHeaders(); + + $this->assertFalse($headers->has('Transfer-Encoding')); + $this->assertFalse($headers->has('Content-Encoding')); + } } diff --git a/test/Client/_files/ZF7683-chunked.php b/test/Client/_files/ZF7683-chunked.php new file mode 100644 index 0000000000..561fe12bfe --- /dev/null +++ b/test/Client/_files/ZF7683-chunked.php @@ -0,0 +1,4 @@ +assertEquals('xxx', $acceptCharsetHeader->getFieldValue()); } + public function testAcceptCharsetGetFieldValueReturnsProperValueWithTrailingSemicolon() + { + $acceptCharsetHeader = AcceptCharset::fromString('Accept-Charset: xxx;'); + $this->assertEquals('xxx', $acceptCharsetHeader->getFieldValue()); + } + + public function testAcceptCharsetGetFieldValueReturnsProperValueWithSemicolonWithoutEqualSign() + { + $acceptCharsetHeader = AcceptCharset::fromString('Accept-Charset: xxx;yyy'); + $this->assertEquals('xxx;yyy', $acceptCharsetHeader->getFieldValue()); + } + public function testAcceptCharsetToStringReturnsHeaderFormattedString() { $acceptCharsetHeader = new AcceptCharset(); diff --git a/test/Header/AcceptEncodingTest.php b/test/Header/AcceptEncodingTest.php index e85fc0e593..9707dbc8d4 100644 --- a/test/Header/AcceptEncodingTest.php +++ b/test/Header/AcceptEncodingTest.php @@ -32,6 +32,18 @@ public function testAcceptEncodingGetFieldValueReturnsProperValue() $this->assertEquals('xxx', $acceptEncodingHeader->getFieldValue()); } + public function testAcceptEncodingGetFieldValueReturnsProperValueWithTrailingSemicolon() + { + $acceptEncodingHeader = AcceptEncoding::fromString('Accept-Encoding: xxx;'); + $this->assertEquals('xxx', $acceptEncodingHeader->getFieldValue()); + } + + public function testAcceptEncodingGetFieldValueReturnsProperValueWithSemicolonWithoutEqualSign() + { + $acceptEncodingHeader = AcceptEncoding::fromString('Accept-Encoding: xxx;yyy'); + $this->assertEquals('xxx;yyy', $acceptEncodingHeader->getFieldValue()); + } + public function testAcceptEncodingToStringReturnsHeaderFormattedString() { $acceptEncodingHeader = new AcceptEncoding(); diff --git a/test/Header/AcceptLanguageTest.php b/test/Header/AcceptLanguageTest.php index bc7d4eb6b2..1942ffa546 100644 --- a/test/Header/AcceptLanguageTest.php +++ b/test/Header/AcceptLanguageTest.php @@ -32,6 +32,18 @@ public function testAcceptLanguageGetFieldValueReturnsProperValue() $this->assertEquals('xxx', $acceptLanguageHeader->getFieldValue()); } + public function testAcceptLanguageGetFieldValueReturnsProperValueWithTrailingSemicolon() + { + $acceptLanguageHeader = AcceptLanguage::fromString('Accept-Language: xxx;'); + $this->assertEquals('xxx', $acceptLanguageHeader->getFieldValue()); + } + + public function testAcceptLanguageGetFieldValueReturnsProperValueWithSemicolonWithoutEqualSign() + { + $acceptLanguageHeader = AcceptLanguage::fromString('Accept-Language: xxx;yyy'); + $this->assertEquals('xxx;yyy', $acceptLanguageHeader->getFieldValue()); + } + public function testAcceptLanguageToStringReturnsHeaderFormattedString() { $acceptLanguageHeader = new AcceptLanguage(); diff --git a/test/Header/AcceptTest.php b/test/Header/AcceptTest.php index c706f61fa8..88e7299bd1 100644 --- a/test/Header/AcceptTest.php +++ b/test/Header/AcceptTest.php @@ -465,4 +465,10 @@ public function testPreventsCRLFAttackViaFromString() $this->setExpectedException('Zend\Http\Header\Exception\InvalidArgumentException'); $header = Accept::fromString("Accept: application/text\r\n\r\nevilContent"); } + + public function testGetEmptyFieldValue() + { + $accept = new Accept(); + $accept->getFieldValue(); + } }