Skip to content

add documentation for retry and redirect plugins, split building own, document library usage #87

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions clients/curl-client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,24 @@ To install the cURL client, run:

$ composer require php-http/curl-client

Usage
-----

The cURL client needs a :ref:`message <message-factory>` and a :ref:`stream <message-factory>`
factory in order to to work::
.. include:: includes/install-message-factory.inc

use Http\Client\Curl\Client;
.. include:: includes/install-discovery.inc
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sagikazarmark is this what you meant by mentioning the factories in the installation instructions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+The cURL client needs a :ref:message factory <message-factory> and

I meant this. If the message factory is necessary, it probably worths mentioning in the install instructions:

http://docs.php-http.org/en/latest/clients/guzzle5-adapter.html#installation

There are two include files for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which i now include. correct like this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, missed that they are included.


$client = new Client($messageFactory, $streamFactory);
Usage
-----

Using `php-http/message <https://packagist.org/packages/php-http/message>`_::
The cURL client needs a :ref:`message factory <message-factory>` and a
:ref:`stream factory <stream-factory>` in order to to work. You can either specify the factory
explicitly::

use Http\Client\Curl\Client;
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;

$client = new Client(new DiactorosMessageFactory(), new DiactorosStreamFactory());

Using `php-http/discovery <https://packagist.org/packages/php-http/discovery>`_::
Or you can use :doc:`../discovery`::

use Http\Client\Curl\Client;
use Http\Discovery\MessageFactoryDiscovery;
Expand All @@ -53,7 +52,8 @@ You can use `cURL options <http://php.net/curl_setopt>`_ to configure Client::
];
$client = new Client(MessageFactoryDiscovery::find(), StreamFactoryDiscovery::find(), $options);

These options cannot be used (will be overwritten by Client):
The following options can not be changed in the set up. Most of them are to be provided with the
request instead:

* CURLOPT_CUSTOMREQUEST
* CURLOPT_FOLLOWLOCATION
Expand Down
2 changes: 1 addition & 1 deletion clients/includes/further-reading.inc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
* Use :ref:`plugins <plugins>` to customize the way HTTP requests are sent and
* Use :doc:`plugins </plugins/introduction>` to customize the way HTTP requests are sent and
responses processed by following redirects, adding Authentication or Cookie
headers and more.
* Learn how you can decouple your code from any PSR-7 implementation by using a
Expand Down
2 changes: 0 additions & 2 deletions components/client-common.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.. _client-common:

Client Common
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i propose we stop using labels for whole doc chapters

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't really know why or why not, so fine with me :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess he means using doc reference instead. Am I right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with a label like this, you write :ref:client-common. but to specify a whole chapter, you can do:doc:`/path/to/client-common```. i would only use labels when we want to deep link to specific sections.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok, got it.

=============

Expand Down
6 changes: 5 additions & 1 deletion discovery.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ Discovery
=========

The discovery service allows to find and use installed resources.

Under the hood it uses `Puli`_ for the discovery logic. All of our packages provide Puli resources.
Discovery is simply a convenience wrapper to statically access clients and factories for when
Dependency Injection is not an option. Discovery is useful in libraries that want to offer
zero-configuration services relying on the virtual packages. If you have Dependency Injection available,
using Puli directly is more elegant (see for example the Symfony HttplugBundle).

Consumers of libraries using discovery still need to make sure they install one of the implementations.
Discovery can only find installed code, not fetch code from other sources.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its a bit redundant but maybe not that obvious for beginners...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I think it is enough to make clear that discovery discovers installed stuff. WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is many question about discovery, writing more things about (even if its redundant) is for the best IMO


Currently available discovery services:

- HTTP Client Discovery
Expand All @@ -32,7 +36,7 @@ In both cases you have to install the discovery package itself:

.. code-block:: bash

$ composer require php-http/discovery
$ composer require php-http/discovery

As mentioned above, discovery relies on Puli. In order to use discovery, you need to also set up Puli.
The easiest way is installing the composer-plugin which automatically configures all the composer packages to act as
Expand Down
7 changes: 7 additions & 0 deletions httplug/library-developers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ When you construct HTTP message objects in your library, you should not depend
on a concrete PSR-7 message implementation. Instead, use the
:ref:`PHP-HTTP message factory <message-factory>`.

Plugins
-------

If your library relies on specific plugins, the recommended way is to provide a factory method for
your users, so they can create the correct client from a base HttpClient. See
:ref:`plugin-client.libraries` for a concrete example.

User documentation
------------------

Expand Down
4 changes: 2 additions & 2 deletions httplug/migrating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The monolithic Ivory package has been separated into several smaller, more speci
Instead of ``Ivory\HttpAdapter\PsrHttpAdapter``, use ``Http\Client\HttpClient``.
The HttpClient simply has a method to send requests.

If you used the ``Ivory\HttpAdapter\HttpAdapter``, have a look at :ref:`client-common`
If you used the ``Ivory\HttpAdapter\HttpAdapter``, have a look at :doc:`../components/client-common`
and use the ``Http\Client\Utils\HttpMethodsClient`` which wraps any HttpClient
and provides the convenience methods to send requests without creating
RequestInterface instances.
Expand All @@ -26,7 +26,7 @@ Instead of the ``$options`` argument, configure the client appropriately during
If you need different settings, create different instances of the client.
You can use :doc:`/plugins/index` to further tune your client.

If you used the ``request`` method, have a look at :ref:`client-common` and
If you used the ``request`` method, have a look at :doc:`../components/client-common` and
use the ``Http\Client\Utils\HttpMethodsClient`` which wraps any HttpClient and
provides the convenience methods to send requests without creating
RequestInterface instances.
2 changes: 2 additions & 0 deletions message/message-factory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ The ``MessageFactory`` aims to provide an easy way to construct messages.
Usage
-----

.. _stream-factory:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is sort of a temporary label until we expand this documentation.


This package provides interfaces for PSR-7 factories including:

- ``MessageFactory``
Expand Down
101 changes: 101 additions & 0 deletions plugins/build-your-own.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
Building Custom Plugins
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no changes in this text, just moved out of the introduction as it was getting too long

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

=======================

When writing your own Plugin, you need to be aware that the Plugin Client is async first.
This means that every plugin must be written with Promises. More about this later.

Each plugin must implement the ``Http\Client\Plugin\Plugin`` interface.

This interface defines the ``handleRequest`` method that allows to modify behavior of the call::

/**
* Handles the request and returns the response coming from the next callable.
*
* @param RequestInterface $request Request to use.
* @param callable $next Callback to call to have the request, it muse have the request as it first argument.
* @param callable $first First element in the plugin chain, used to to restart a request from the beginning.
*
* @return Promise
*/
public function handleRequest(RequestInterface $request, callable $next, callable $first);

The ``$request`` comes from an upstream plugin or Plugin Client itself.
You can replace it and pass a new version downstream if you need.

.. note::

Be aware that the request is immutable.

The ``$next`` callable is the next plugin in the execution chain. When you need to call it, you must pass the ``$request``
as the first argument of this callable.

For example a simple plugin setting a header would look like this::

public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$newRequest = $request->withHeader('MyHeader', 'MyValue');

return $next($newRequest);
}

The ``$first`` callable is the first plugin in the chain. It allows you to completely reboot the execution chain, or send
another request if needed, while still going through all the defined plugins.
Like in case of the ``$next`` callable, you must pass the ``$request`` as the first argument::

public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
if ($someCondition) {
$newRequest = new Request();
$promise = $first($newRequest);

// Use the promise to do some jobs ...
}

return $next($request);
}

.. warning::

In this example the condition is not superfluous:
you need to have some way to not call the ``$first`` callable each time
or you will end up in an infinite execution loop.

The ``$next`` and ``$first`` callables will return a :doc:`/components/promise`.
You can manipulate the ``ResponseInterface`` or the ``Exception`` by using the
``then`` method of the promise::

public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$newRequest = $request->withHeader('MyHeader', 'MyValue');

return $next($request)->then(function (ResponseInterface $response) {
return $response->withHeader('MyResponseHeader', 'value');
}, function (Exception $exception) {
echo $exception->getMessage();

throw $exception;
});
}

.. warning::

Contract for the ``Http\Promise\Promise`` is temporary until a
PSR_ is released. Once it is out, we will use this PSR in HTTPlug and
deprecate the old contract.

To better understand the whole process check existing implementations in the
`plugin repository`_.

Contributing Your Plugins to PHP-HTTP
-------------------------------------

We are open to contributions. If the plugin is of general interest and is not too complex, the best
is to do a Pull Request to ``php-http/plugins``. Please see the :doc:`contribution guide <../development/contributing>`.
We don't promise that every plugin gets merged into the core. We need to keep the core as small as
possible with only the most widely used plugins to keep it maintainable.

The alternative is providing your plugins in your own repository. Please let us know when you do,
we would like to add a list of existing third party plugins to the list of plugins.

.. _PSR: https://groups.google.com/forum/?fromgroups#!topic/php-fig/wzQWpLvNSjs
.. _plugin repository: https://github.com/php-http/plugins
1 change: 1 addition & 0 deletions plugins/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ request or you can even start a completely new request. This gives you full cont
.. toctree::

introduction
build-your-own
authentication
cache
content-length
Expand Down
134 changes: 44 additions & 90 deletions plugins/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ Install the plugin client in your project with Composer_:

$ composer require php-http/plugins



How it works
------------

Expand Down Expand Up @@ -69,105 +67,61 @@ The recommended way to order plugins is the following:

.. note::

There can be exceptions to these rules. For example,
for security reasons you might not want to log the authentication information (like `Authorization` header)
and choose to put the Authentication Plugin after the Logger Plugin.

Implement your own
------------------
There can be exceptions to these rules. For example, for security reasons you might not want
to log the authentication information (like ``Authorization`` header) and choose to put the
:doc:`Authentication Plugin <authentication>` after the doc:`Logger Plugin <logger>`.

When writing your own Plugin, you need to be aware that the Plugin Client is async first.
This means that every plugin must be written with Promises. More about this later.

Each plugin must implement the ``Http\Client\Plugin\Plugin`` interface.
Configuration Options
---------------------

This interface defines the ``handleRequest`` method that allows to modify behavior of the call::
The PluginClient accepts an array of configuration options that can tweak its behavior.

/**
* Handles the request and returns the response coming from the next callable.
*
* @param RequestInterface $request Request to use.
* @param callable $next Callback to call to have the request, it muse have the request as it first argument.
* @param callable $first First element in the plugin chain, used to to restart a request from the beginning.
*
* @return Promise
*/
public function handleRequest(RequestInterface $request, callable $next, callable $first);
.. _plugin-client.max-restarts:

The ``$request`` comes from an upstream plugin or Plugin Client itself.
You can replace it and pass a new version downstream if you need.
``max_restarts``: int (default 10)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. note::

Be aware that the request is immutable.
To prevent issues with faulty plugins or endless redirects, the ``PluginClient`` injects a security
check to the start of the plugin chain. If the same request is restarted more than specified by
that value, execution is aborted and an error is raised.

The ``$next`` callable is the next plugin in the execution chain. When you need to call it, you must pass the ``$request``
as the first argument of this callable.
.. _plugin-client.libraries:

For example a simple plugin setting a header would look like this::
Libraries that Require Plugins
------------------------------

public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$newRequest = $request->withHeader('MyHeader', 'MyValue');
When :doc:`writing a library based on HTTPlug <../httplug/library-developers>`, you might require
specific plugins to be active. The recommended way for doing this is to provide a factory method
for the ``PluginClient`` that your library users should use. This allows them to inject their own
plugins or configure a different client. For example::

return $next($newRequest);
}
$myApiClient = new My\Api\Client('https://api.example.org', My\Api\HttpClientFactory::create('john', 's3cr3t'));

The ``$first`` callable is the first plugin in the chain. It allows you to completely reboot the execution chain, or send
another request if needed, while still going through all the defined plugins.
Like in case of the ``$next`` callable, you must pass the ``$request`` as the first argument::
use Http\Client\HttpClient;
use Http\Discovery\HttpClientDiscovery;

public function handleRequest(RequestInterface $request, callable $next, callable $first)
class HttpClientFactory
{
if ($someCondition) {
$newRequest = new Request();
$promise = $first($newRequest);

// Use the promise to do some jobs ...
/**
* Build the HTTP client to talk with the API.
*
* @param string $user Username
* @param string $pass Password
* @param HttpClient $client Base HTTP client
*
* @return HttpClient
*/
public static function create($user, $pass, HttpClient $client = null)
{
if (!$client) {
$client = HttpClientDiscovery::find();
}
return new PluginClient($client, [
new Http\Client\Plugin\ErrorPlugin(),
new AuthenticationPlugin(
// This API has it own authentication algorithm
new ApiAuthentication(Client::AUTH_OAUTH_TOKEN, $user, $pass)
),
]);
}

return $next($request);
}

.. warning::

In this example the condition is not superfluous:
you need to have some way to not call the ``$first`` callable each time
or you will end up in an infinite execution loop.

The ``$next`` and ``$first`` callables will return a :doc:`/components/promise`.
You can manipulate the ``ResponseInterface`` or the ``Exception`` by using the
``then`` method of the promise::

public function handleRequest(RequestInterface $request, callable $next, callable $first)
{
$newRequest = $request->withHeader('MyHeader', 'MyValue');

return $next($request)->then(function (ResponseInterface $response) {
return $response->withHeader('MyResponseHeader', 'value');
}, function (Exception $exception) {
echo $exception->getMessage();

throw $exception;
});
}

.. warning::

Contract for the ``Http\Promise\Promise`` is temporary until a
PSR_ is released. Once it is out, we will use this PSR in HTTPlug and
deprecate the old contract.

To better understand the whole process check existing implementations in the
`plugin repository`_.

Contribution
------------

We are always open to contributions. Either in form of Pull Requests to the core package or self-made plugin packages.
We encourage everyone to prefer sending Pull Requests, however we don't promise that every plugin gets
merged into the core. If this is the case, it is not because we think your work is not good enough. We try to keep
the core as small as possible with the most widely used plugin implementations.

.. _PSR: https://groups.google.com/forum/?fromgroups#!topic/php-fig/wzQWpLvNSjs
.. _plugin repository: https://github.com/php-http/plugins
Loading