Skip to content

Commit

Permalink
Merge pull request #1 from krakphp/0.4
Browse files Browse the repository at this point in the history
Version 0.4
  • Loading branch information
ragboyjr committed Jan 4, 2017
2 parents 946c692 + 46d6eee commit b42321c
Show file tree
Hide file tree
Showing 23 changed files with 486 additions and 76 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [0.4.0] - 2017-01-04
### Changed

- Made `splitArgs` a public function
- Update documentation to reflect new API and changes.
- Updated `mw\compose` algorithm to use the Link as a linked list instead of the
nested closures.
- Re-implemented custom invocation via the new Context system.
- Re-designed the meta middleware system with the Link system and added documentation

### Added

- Added new Link and Context entities to provide further customization and features
to middleware
- Added Context\\PimpleContext to provide better pimple integration

## [0.3.3] - 2017-01-03
### Changed

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Mw (Middleware)

[![Author](http://img.shields.io/badge/author-@ragboyjr-blue.svg?style=flat-square)](https://twitter.com/ragboyjr)
[![Source Code](http://img.shields.io/badge/source-krak/mw-blue.svg?style=flat-square)](https://github.com/krakphp/mw)
[![Latest Version](https://img.shields.io/github/release/krakphp/mw.svg?style=flat-square)](https://github.com/krakphp/mw/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
[![Documentation Status](https://readthedocs.org/projects/mw/badge/?version=latest)](http://mw.readthedocs.io/en/latest/?badge=latest)
[![Total Downloads](https://img.shields.io/packagist/dt/krak/mw.svg?style=flat-square)](https://packagist.org/packages/krak/mw)

The Mw library is a very flexible framework for converting middleware into handlers. Middleware offer a clean syntax for implementing the [Decorator Pattern](https://en.wikipedia.org/wiki/Decorator_pattern)

Expand Down Expand Up @@ -38,7 +43,6 @@ Or build them:
make doc
```


## Tests and Examples

Run tests via:
Expand Down
73 changes: 48 additions & 25 deletions doc/advanced-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,85 @@
Avanced Usage
=============

**WARNING: These features have been removed on the v0.3 branch due to backwards compatability issues.**
.. _advanced-usage-context:

.. _advanced-usage-custom-invoke:
Context
=======

Each middleware is invoked with a ``Mw\Context`` instance. This is responsible for holding additional data to be used internally within the mw system *and* to provide additional features/usage for users. The context is available via the ``Mw\Link`` object of the middleware.

.. code-block:: php
<?php
use Krak\Mw;
$handle = mw\compose([
function($v, Mw\Link $next) {
$ctx = $next->getContext();
return 1;
}
], new Krak\Mw\Context\StdContext());
You can configure or pass in any context as long as it implements the ``Mw\Context`` interface. Currently, the context provides an invoker via the ``getInvoke`` method. This allows custom invocation of the middleware as shown in the :doc:`cookbook`.

Custom Invocation
=================

The final argument to ``mw\compose`` is a callable ``$invoke``. It defaults to ``call_user_func`` but can be any callable with the same function signature. This allows you to customize how the middleware is invoked. One specific example of this would be container aware invocation. A middleware instead of being a callable can be reference to a service in the container.
You can provide custom invocation of the middleware via the context. An invoker is any function that shares the signature of ``call_user_func``. Its sole purpose is to invoke functions with their parameters. With custom invocation, you can do cool things like have middleware as pimple identifiers.

Link
====

Here's an example of how to use the ``pimpleAwareInvoke``
The final argument to each middleware is an instance of ``Mw\Link``. The link is represents the link/chain between middlewares. Technically speaking, it's a singly-linked list of middleware that once executed will invoke the entire chain of middleware.

The link is responsible for building a set of middleware via the ``chain``.

.. code-block:: php
<?php
$c = new Pimple\Container();
$c['service'] = function() {
return function() {
return 1;
};
};
use Krak\Mw;
$handler = mw\compose([
'service',
function($next) {
return $next() + 1;
}
], null, mw\pimpleAwareInvoke($c));
$link = new Mw\Link(function($i) {
return $i * 2;
}, new Mw\Context\StdContext());
$link = $link->chain(function($i, $next) {
return $next($i) + 1;
});
assert($link(2) == 5);
assert(2 == $handler());
``chain`` takes a middleware and produces a new link that is appened to the head of the linked list of mw links. As you can see, the middleware on the second link is executed first.

Meta Middleware
~~~~~~~~~~~~~~~
===============

Custom invocation is creation is a very useful feature; however, it requires special consideration if you are creating your own Meta Middleware. Meta middleware are middleware that accept other middleware and perform some action with the middleware. ::
Custom Context and invocation is a very useful feature; however, it requires special consideration if you are creating your own Meta Middleware. Meta middleware are middleware that accept other middleware and inject middleware into the chain of middleware. ::

mw\group
mw\lazy
mw\filter

These are all meta middleware. To allow custom invocation work work for *all* middleware whever they are added, these meta middleware need to make use of the ``$invoke`` parameter that is passed to all middleware (see :ref:`mw\\compose <advanced-usage-custom-invoke>`).
These are all meta middleware. To allow *all* middleware to be properly linked and have access to the context, these meta middleware need to learn how to properly inject middleware into the mw link.

Here's an example:

.. code-block:: php
<?php
use Krak\Mw;
// maybe middleware will only invoke the middleware if the parameter is < 10
function maybe($mw) {
return function($i, $next, $invoke) use ($mw) {
if ($i >= 10) {
return $next($i); // forward to next middleware
return function($i, $next) use ($mw) {
if ($i < 10) {
/** NOTE - this is the crucial part where we prepend the `$mw` onto the link. Now, when we execute `$next`,
the `$mw` func will be first to be executed */
$next = $next->chain($mw);
}
return $invoke($mw, $i, $next, $invoke);
return $next($i);
};
}
Expand All @@ -73,7 +96,7 @@ Here's an example:
maybe(function($i, $next) {
return $next($i) + 100;
})
], null, loggingInvoke());
], new Mw\Context\StdContext(loggingInvoke()));
echo $handler(1) . PHP_EOL;
echo $handler(10) . PHP_EOL;
Expand Down
77 changes: 70 additions & 7 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The api documentation is broken up into 2 parts: Middleware documentation and Mi
Middleware Functions
~~~~~~~~~~~~~~~~~~~~

Closure compose(array $mws, callable $last = null)
Closure compose(array $mws, Context $ctx = null, callable $last = null, $link_class = Link::class)
Composes a set of middleware into a handler.

.. code-block:: php
Expand All @@ -31,6 +31,12 @@ Closure compose(array $mws, callable $last = null)

After composing the stack of middleware, the resulting handler will share the same signature as the middleware except that it **won't** have the ``$next``.

``$ctx`` will default to ``Context\StdContext`` if none is supplied, and it will be the context that is passed to the start link (see: :doc:`advanced-usage` for more details).

``$last`` represents the last middleware to be executed if no other middleware handle the parameters. This typically will throw an exception in that case, but it might be advantageous to set this to something else for your needs.

``$link_class`` is the class that will be constructed for the middleware link. It must be or extend ``Krak\Mw\Link`` (see: :doc:`advanced-usage` for more details).

Closure group(array $mws)
Creates a new *middleware* composed as one from a middleware stack.

Expand Down Expand Up @@ -105,8 +111,6 @@ Closure filter(callable $mw, callable $predicate)
Invoke Functions
~~~~~~~~~~~~~~~~

**WARNING: These features have been removed on the v0.3 branch due to backwards compatability issues.**

Closure pimpleAwareInvoke(Pimple\\Container $c, $invoke = 'call_user_func')
invokes middleware while checking if the mw is a service defined in the pimple container

Expand All @@ -116,8 +120,8 @@ Closure methodInvoke(string $method_name, $allow_callable = true, $invoke = 'cal
Stack Functions
~~~~~~~~~~~~~~~

MwStack stack($name, array $entries = [])
Creates a MwStack instance. Every stack must have a name which is just a personal identifier for the stack. It's primary use is for errors/exceptions that help the user track down which stack has an issue.
MwStack stack($name, array $entries = [], Context $ctx = null, $link_class = Link::class)
Creates a MwStack instance. Every stack must have a name which is just a personal identifier for the stack. It's primary use is for errors/exceptions that help the user track down which stack has an issue. ``$ctx`` and ``$link_class`` are forwarded to the MwStack constructor.

.. code-block:: php
Expand Down Expand Up @@ -175,13 +179,33 @@ MwStack stackMerge(...$stacks)
->push($mw2)
->push($mw4, 0, 'mw')
Utility Functions
~~~~~~~~~~~~~~~~~

array splitArgs(array $args)
Splits arguments between the parameters and middleware.

.. code-block:: php
<?php
use Krak\Mw
function middleware() {
return function(...$args) {
list($args, $next) = Mw\splitArgs($args);
return $next(...$args);
};
}
class MwStack implements Countable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The stack presents a mutable interface into a stack of middleware. Middleware can be added with a name and priority. Only one middleware with a given name may exist. Middleware that are last in the stack will be executed first once the stack is composed.

__construct($name)
Creates the mw stack with a name.
__construct($name, Context $ctx = null, $link_class = Link::class)
Creates the mw stack with a name. The ``$ctx`` and ``$link_class`` are forwarded to ``mw\compose`` once the stack is composed.
string getName()
returns the name of the middleware
MwStack push($mw, $sort = 0, $name = null)
Expand Down Expand Up @@ -210,3 +234,42 @@ Generator getEntries()
Yields the raw stack entries in the order they were added.
MwStack static createFromEntries($name, $entries)
Creates a stack with a set of entries. ``mw\stack`` internally calls this.

class Link
~~~~~~~~~~

Represents a link in the middleware chain. A link instance is passed to every middleware as the last parameter which allows the next middleware to be called. See :doc:`advanced-usage` for more details.

__construct($mw, Context $ctx, Link $next = null)
Creates a link. If ``$next`` is provided, then the created link will be the new head of that linked list.
__invoke(...$params)
Invokes the middleware. It forwards the params to the middleware and additionaly adds the next link to the end of argument list for the middleware.
chain($mw)
Creates a new link to be the head of the current list of links. The context is copied from the current link.
getContext()
returns the context instance apart of the link.

interface Context
~~~~~~~~~~~~~~~~~

Represents the middleware context utilized by the internal system.

getInvoke()
Returns the invoker configured for this context.

class Context\\StdContext implements Context
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The default context for the mw system. It simply holds the a value to the invoker for custom invocation.

__construct($invoke = 'call_user_func')

class Context\\PimpleContext implements Context
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Provides nice pimple integeration by allowing the context to act like a pimple container and it provides pimple invocation by default.

View the :doc:`cookbook/pimple-middleware` for example on this.

__construct(Container $container, $invoke = null)
The pimple contianer and an optional invoker if you don't want to use the ``pimpleAwareInvoke``
4 changes: 2 additions & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@
# built documents.
#
# The short X.Y version.
version = u'0.3'
version = u'0.4'
# The full version, including alpha/beta/rc tags.
release = u'0.3.3'
release = u'0.4.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
4 changes: 4 additions & 0 deletions doc/cookbook.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
Cookbook
========

The cookbook provides documentation on how to extend or utilize the mw system in advanced ways.

- :doc:`cookbook/custom-link-class`
- :doc:`cookbook/custom-method-middleware`
- :doc:`cookbook/pimple-middleware`
30 changes: 30 additions & 0 deletions doc/cookbook/custom-link-class.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=================
Custom Link Class
=================

You can optionally provide your own ``Link`` to allow easier access to the Context or other methods. It's best to extend the ``Mw\Link`` and just add methods and *not* data because the Link class logic is very critical to the design of the mw system.

.. code-block:: php
<?php
use Krak\Mw;
class LoggingLink extends Mw\Link
{
public function echoLog($info) {
echo $info . PHP_EOL;
}
public function log() {
return $this->getContext()->logger;
}
}
$handler = mw\compose([
function($i, $next) {
$next->echoLog('hi');
return 1;
}
], null, null, LoggingLink::class);
assert($handler(0) == 1);
4 changes: 1 addition & 3 deletions doc/cookbook/custom-method-middleware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
Custom Method Middleware
========================

**WARNING: This feature has been removed on the v0.3 branch due to backwards compatability issues.**

If you want to use middleware that are class based and use a method other than ``__invoke``, you need to use the ``methodInvoke`` invoker.

Here's an example using classes with a method of ``handle``
Expand Down Expand Up @@ -35,6 +33,6 @@ Here's an example using classes with a method of ``handle``
$handler = mw\compose([
new IdMw(),
new AppendMw('b')
], null, mw\methodInvoke('handle'));
], new Mw\Context\StdContext(mw\methodInvoke('handle')));
assert($handler('a') == 'ab');
30 changes: 30 additions & 0 deletions doc/cookbook/pimple-middleware.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=================
Pimple Middleware
=================

You can easily integrate your middleware stacks with pimple using the ``Mw\Context\PimpleContext`` which will allow any middleware to be a pimple identifier and give you access to your pimple container via the mw context.

.. code-block:: php
<?php
use Krak\Mw;
$container = new Pimple\Container();
$container['i'] = 5;
$container['inc_mw'] = function() {
return function($i, $next) {
return $next($i + 1);
};
};
$handler = mw\compose([
function($i) { return $i; },
function($i, $next) {
$ctx = $next->getContext();
return $next($i + $ctx['i']);
},
'inc_mw'
], new Mw\Context\PimpleContext($container));
assert($handler(4) == 10);
4 changes: 2 additions & 2 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Middleware provide a great system for extendable features and the Krak\\Mw libra
:maxdepth: 2

usage
api
advanced-usage
troubleshooting
cookbook
api
troubleshooting

0 comments on commit b42321c

Please sign in to comment.