Skip to content

Commit

Permalink
Merge pull request #11 from krakphp/8-pimple-aware-middleware
Browse files Browse the repository at this point in the history
Pimple Aware Middleware #8 

Closes #8
  • Loading branch information
ragboyjr committed Dec 30, 2016
2 parents 24e3ddd + 4af333f commit d19ac9e
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 28 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@
"guzzlehttp/psr7": "^1.3",
"league/plates": "^3.1",
"peridot-php/peridot": "^1.18",
"symfony/var-dumper": "^3.1"
"symfony/var-dumper": "^3.2"
}
}
201 changes: 198 additions & 3 deletions doc/app.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Application
===========
===
App
===

The app is the central point for an http application. It manages core services to build
your application: Pimple Container, Evenement event dispatcher, stacks of middleware,
Expand Down Expand Up @@ -43,7 +44,7 @@ The App at its core manages a stack of http middleware. When the ``serve()`` fun
The App also servers as a callable middleware that you can use in any place you would use a normal middleware. This is very useful for things like Mounting.

Mounting
--------
========

Http Apps allow the mounting of middleware onto the app itself. You mount the middleware on a prefix. This is similar to adding middleware onto route groups, but it is different because these middleware are executed before the routing phase. This fact is crucial because it allows you to perform authentication before routing and mount full featured apps without having to go through the routing process of the base app.

Expand Down Expand Up @@ -90,3 +91,197 @@ example:
$app->serve();
In this example, all calls to the ``/api*`` will be handled via the ``$api`` application instead of the base ``$app``. Every call to ``/api/users*`` will now have to go through Basic authentication before the routing starts.

Pimple Aware Middleware
=======================

API
===

class App implements \\ArrayAccess, Evenement\\EventEmitterInterface
--------------------------------------------------------------------

__construct(Pimple\\Container $container = null)
Entry point into creating the app. You can optionally pass in a container if you'd like. Else one will be created.
createStack($name, array $entries = [])
Creates a Pimple aware ``Krak\Mw\MwStack`` with the app's Pimple Container.

.. code-block:: php
<?php
$app = new Krak\Mw\Http\App();
$stack = $app->createStack('Stack');
$stack->push('pimple_service_name');
If the ``pimple_service_name`` is defined in the container, then it will use that for the middleware.

defineStack($key, $name, array $entries = [])
Defines a Pimple aware stack as a parameter into the pimple container. This is just a convenience method for defining stacks on the container because each stack needs to be protected via ``$container->protect($stack)``.

.. code-block:: php
<?php
$app = new Krak\Mw\Http\App();
$app->defineStack('my_stack', 'Stack');
$app['my_stack']->push(function() {});
withRoutePrefix($prefix)


The App class also implements every method in the ``Pimple\Container`` class and forwards it to the appropriate container instance in the application.

/** forwards to the RouteGroup */
public function match(...$args) {
return $this['routes']->match(...$args);
}
/** forwards to the RouteGroup */
public function group(...$args) {
return $this['routes']->group(...$args);
}
/** utility for creating a new app on a new route prefix */
public function withRoutePrefix($prefix) {
$app = clone $this;
$app['routes'] = RouteGroup::createWithGroup($prefix, $app['routes']);
return $app;
}
/** mounts a middleware onto the stack at uri prefix. The mounted middleware is
pushed on the main http stack and will be executed before the standard routing
middleware. This is useful for mounting other applications or authentication
middleware */
public function mount($prefix, $mw) {
$mw = function(...$all_params) use ($prefix, $mw) {
list($req, $next, $invoke) = $all_params;
if (!$mw instanceof self) {
return $invoke($mw, ...$all_params);
}
$prefix = Util\joinUri($this['routes']->getPrefix(), $prefix);
$mw = $mw->withRoutePrefix($prefix);
return $invoke($mw, ...$all_params);
};

$mw = mw\filter($mw, function($req) use ($prefix, $mw) {
$prefix = Util\joinUri($this['routes']->getPrefix(), $prefix);
return strpos($req->getUri()->getPath(), $prefix) === 0;
});

return $this->push($mw);
}

/** forward to the Event Emitter */
public function on($event, callable $listener) {
return $this['event_emitter']->on($event, $listener);
}
public function once($event, callable $listener) {
return $this['event_emitter']->once($event, $listener);
}
public function removeListener($event, callable $listener) {
return $this['event_emitter']->removeListener($event, $listener);
}
public function removeAllListeners($event = null) {
return $this['event_emitter']->removeAllListeners($event);
}
public function listeners($event) {
return $this['event_emitter']->listeners($event);
}
public function emit($event, array $arguments = []) {
return $this['event_emitter']->emit($event, $arguments);
}
/** allows modifications to App in a unified way. This */
public function with(Package $pkg) {
$pkg->with($this);
return $this;
}
/** Forward to main http stack */
public function push($mw, $sort = 0, $name = null) {
return $this['stacks.http']->push($mw, $sort, $name);
}
/** Forward to main http stack */
public function pop($sort = 0) {
return $this['stacks.http']->push($sort);
}
/** Forward to main http stack */
public function unshift($mw, $sort = 0, $name = null) {
return $this['stacks.http']->unshift($mw, $sort, $name);
}
/** Forward to main http stack */
public function shift($sort = 0) {
return $this['stacks.http']->shift($sort);
}
/** forward to Pimple */
public function offsetExists($offset) {
return $this->container->offsetExists($offset);
}
/** forward to Pimple */
public function offsetGet($offset) {
return $this->container->offsetGet($offset);
}
/** forward to Pimple */
public function offsetSet($offset, $value) {
return $this->container->offsetSet($offset, $value);
}
/** forward to Pimple */
public function offsetUnset($offset) {
return $this->container->offsetUnset($offset);
}
public function factory($callable) {
return $this->container->factory($callable);
}
public function protect($callable) {
return $this->container->protect($callable);
}
public function raw($id) {
return $this->container->raw($id);
}
public function extend($id, $callable) {
return $this->container->extend($id, $callable);
}
public function keys() {
return $this->container->keys();
}
/** forward to Pimple */
public function register(Pimple\ServiceProviderInterface $provider, array $values = []) {
return $this->container->register($provider, $values);
}
/** return the pimple container */
public function getContainer() {
return $this->container;
}
/** middleware interface */
public function __invoke(...$params) {
$this->freeze();
$http = $this['stacks.http'];
return $http(...$params);
}
/** serves an app with a default server if non is provided */
public function serve() {
$serve = $this['server'];
$this->freeze();
$mws = $this['stacks.http'];
$this->emit(Events::INIT, [$this]);
$serve($mws->compose());
$this->emit(Events::FINISH, [$this]);
}

/** Composes all of the middleware together in the main mws stack */
public function freeze() {
if ($this->frozen) {
return;
}
$freezer = $this->container['freezer'];
$freezer->freezeApp($this);
$this->frozen = true;

$this->emit(Events::FROZEN, [$this]);
}
26 changes: 21 additions & 5 deletions src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ class App implements \ArrayAccess, EventEmitterInterface
public function __construct(Pimple\Container $container = null) {
$this->container = $container ?: new Pimple\Container();
$this->frozen = false;
$this->defineStack('stacks.http', 'Http');
}

/** creates a pimple aware middleware stack defined in the pimple container */
public function createStack($name, array $entries = []) {
return mw\stack(
$name,
$entries,
Mw\pimpleAwareInvoke($this->container)
);
}

/** defines a pimple aware stack in the container */
public function defineStack($key, $name, array $entries = []) {
$this[$key] = $this->protect($this->createStack($name, $entries));
}

/** forwards to the RouteGroup */
Expand All @@ -41,14 +56,15 @@ public function withRoutePrefix($prefix) {
middleware. This is useful for mounting other applications or authentication
middleware */
public function mount($prefix, $mw) {
$mw = function($req, $next) use ($prefix, $mw) {
$mw = function(...$all_params) use ($prefix, $mw) {
list($req, $next, $invoke) = $all_params;
if (!$mw instanceof self) {
return $mw($req, $next);
return $invoke($mw, ...$all_params);
}

$prefix = Util\joinUri($this['routes']->getPrefix(), $prefix);
$mw = $mw->withRoutePrefix($prefix);
return $mw($req, $next);
return $invoke($mw, ...$all_params);
};

$mw = mw\filter($mw, function($req) use ($prefix, $mw) {
Expand Down Expand Up @@ -86,15 +102,15 @@ public function with(Package $pkg) {
}

/** Forward to main http stack */
public function push(callable $mw, $sort = 0, $name = null) {
public function push($mw, $sort = 0, $name = null) {
return $this['stacks.http']->push($mw, $sort, $name);
}
/** Forward to main http stack */
public function pop($sort = 0) {
return $this['stacks.http']->push($sort);
}
/** Forward to main http stack */
public function unshift(callable $mw, $sort = 0, $name = null) {
public function unshift($mw, $sort = 0, $name = null) {
return $this['stacks.http']->unshift($mw, $sort, $name);
}
/** Forward to main http stack */
Expand Down
1 change: 1 addition & 0 deletions src/Package/AutoArgs/AutoArgsPackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AutoArgsPackage implements Http\Package
{
public function with(Http\App $app) {
$app->register(new AutoArgsServiceProvider());
$app->defineStack('stacks.resolve_argument', 'Resolve Argument');

$app['stacks.resolve_argument']
->push(defaultValueResolveArgument(), -1)
Expand Down
3 changes: 0 additions & 3 deletions src/Package/AutoArgs/AutoArgsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
class AutoArgsServiceProvider implements Pimple\ServiceProviderInterface
{
public function register(Pimple\Container $app) {
$app['stacks.resolve_argument'] = function() {
return mw\stack('Resolve Argument');
};
$app->extend('freezer', function($freezer) {
return new AutoArgsFreezer($freezer);
});
Expand Down
5 changes: 5 additions & 0 deletions src/Package/Std/StdPackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public function __construct(array $config = []) {
public function with(Http\App $app) {
$app->register(new StdServiceProvider());

$app->defineStack('stacks.exception_handler', 'Exception Handler');
$app->defineStack('stacks.invoke_action', 'Invoke Action');
$app->defineStack('stacks.not_found_handler', 'Not Found Handler');
$app->defineStack('stacks.marshal_response', 'Marshal Response');

$app['stacks.exception_handler']
->push(stdExceptionHandler($app['response_factory']));
$app['stacks.not_found_handler']
Expand Down
6 changes: 0 additions & 6 deletions src/Package/Std/StdServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ public function register(Pimple\Container $app) {
$app['routes'] = new Http\RouteGroup();
$app['response_factory'] = $app->protect(Http\responseFactory());

$app['stacks.exception_handler'] = $app->protect(Mw\stack('Exception Handler'));
$app['stacks.invoke_action'] = $app->protect(Mw\stack('Invoke Action'));
$app['stacks.not_found_handler'] = $app->protect(Mw\stack('Not Found Handler'));
$app['stacks.marshal_response'] = $app->protect(Mw\stack('Marshal Response'));
$app['stacks.http'] = $app->protect(Mw\stack('Http'));

$app['dispatcher_factory'] = function() {
return Http\dispatcherFactory();
};
Expand Down
5 changes: 3 additions & 2 deletions src/http.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ function catchException($handler) {
};
}
function injectRouteMiddleware($prefix = '_') {
return function($req, $next) use ($prefix) {
return function(...$params) use ($prefix) {
list($req, $next, $invoke) = $params;
$attributes = $req->getAttribute('route.attributes');

if (!count($attributes->mws)) {
return $next($req);
}

$mws = $attributes->mws;
return $mws($req, $next);
return $invoke($mws, ...$params);
};
}

Expand Down

0 comments on commit d19ac9e

Please sign in to comment.