Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Sign in
Toggle navigation
S
Silex
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
common
Silex
Commits
6445b9e9
Commit
6445b9e9
authored
Mar 10, 2012
by
DrBenton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add Routes Middlewares
parent
8d30526d
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
203 additions
and
0 deletions
+203
-0
doc/usage.rst
doc/usage.rst
+57
-0
src/Silex/Application.php
src/Silex/Application.php
+13
-0
src/Silex/Controller.php
src/Silex/Controller.php
+16
-0
tests/Silex/Tests/ApplicationTest.php
tests/Silex/Tests/ApplicationTest.php
+117
-0
No files found.
doc/usage.rst
View file @
6445b9e9
...
@@ -352,6 +352,63 @@ object that is returned by the routing methods::
...
@@ -352,6 +352,63 @@ object that is returned by the routing methods::
It only makes sense to name routes if you use providers that make use
It only makes sense to name routes if you use providers that make use
of the ``RouteCollection``.
of the ``RouteCollection``.
Routes middlewares
~~~~~~~~~~~~~~~~~~
You can define one or more Routes Middlewares, and link them to your routes.
Routes Middlewares are just "PHP callables" (i.e. a Closure or a
"ClassName::methodName" string, like others Silex callbacks), which will
be triggered when their route is matched.
Middlewares are fired just before the route callback,
but after Application ``before`` filters, which have precedence
- see next section about these ``before`` filters.
This mechanism can be used for a lot of use case - for example, a
"anonymous/logged user" simple control::
$mustBeAnonymous = function (Request $request) use ($app) {
if ($app['session']->has('userId')) {
return $app->redirect('/user/logout');
}
};
$mustBeLogged = function (Request $request) use ($app) {
if (!$app['session']->has('userId')) {
return $app->redirect('/user/login');
}
};
$app->get('/user/subscribe', function () {
...
})
->middleware($mustBeAnonymous);
$app->get('/user/login', function () {
...
})
->middleware($mustBeAnonymous);
$app->get('/user/my-profile', function () {
...
})
->middleware($mustBeLogged);
You can call the ``middleware`` function several times for a single route.
The middlewares will be triggered in the order you added them to the route.
For convenience, the routes middlewares functions are triggered with the current
Request as their only argument.
If any of the routes middlewares returns a Symfony Http Response, this response
will short-circuit the whole rendering : the next middlewares won't run, neither
the route callback.
As in route callbacks, you can redirect to another page by from a route middleware
by returning a redirect response, which you can create by calling the
Application ``redirect`` method.
A route Middleware can return a Symfony Http Response or null.
A RuntimeException will be thrown if anything else is returned.
Before and after filters
Before and after filters
------------------------
------------------------
...
...
src/Silex/Application.php
View file @
6445b9e9
...
@@ -111,6 +111,18 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
...
@@ -111,6 +111,18 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
return
new
RedirectableUrlMatcher
(
$app
[
'routes'
],
$app
[
'request_context'
]);
return
new
RedirectableUrlMatcher
(
$app
[
'routes'
],
$app
[
'request_context'
]);
});
});
$this
[
'route_middlewares_trigger'
]
=
$this
->
protect
(
function
(
KernelEvent
$event
)
use
(
$app
)
{
foreach
(
$event
->
getRequest
()
->
attributes
->
get
(
'_middlewares'
,
array
())
as
$callback
)
{
$ret
=
call_user_func
(
$callback
,
$event
->
getRequest
());
if
(
$ret
instanceof
Response
)
{
$event
->
setResponse
(
$ret
);
return
;
}
elseif
(
null
!==
$ret
)
{
throw
new
\RuntimeException
(
'Middleware for route "'
.
$event
->
getRequest
()
->
attributes
->
get
(
'_route'
)
.
'" returned an invalid response value. Must return null or an instance of Response.'
);
}
}
});
$this
[
'request.default_locale'
]
=
'en'
;
$this
[
'request.default_locale'
]
=
'en'
;
$this
[
'request'
]
=
function
()
{
$this
[
'request'
]
=
function
()
{
...
@@ -409,6 +421,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
...
@@ -409,6 +421,7 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
if
(
HttpKernelInterface
::
MASTER_REQUEST
===
$event
->
getRequestType
())
{
if
(
HttpKernelInterface
::
MASTER_REQUEST
===
$event
->
getRequestType
())
{
$this
->
beforeDispatched
=
true
;
$this
->
beforeDispatched
=
true
;
$this
[
'dispatcher'
]
->
dispatch
(
SilexEvents
::
BEFORE
,
$event
);
$this
[
'dispatcher'
]
->
dispatch
(
SilexEvents
::
BEFORE
,
$event
);
$this
[
'route_middlewares_trigger'
](
$event
);
}
}
}
}
...
...
src/Silex/Controller.php
View file @
6445b9e9
...
@@ -154,6 +154,22 @@ class Controller
...
@@ -154,6 +154,22 @@ class Controller
return
$this
;
return
$this
;
}
}
/**
* Sets a callback to handle before triggering the route callback.
* (a.k.a. "Route Middleware")
*
* @param mixed $callback A PHP callback to be triggered when the Route is matched, just before the route callback
* @return Controller $this The current Controller instance
*/
public
function
middleware
(
$callback
)
{
$middlewareCallbacks
=
$this
->
route
->
getDefault
(
'_middlewares'
);
$middlewareCallbacks
[]
=
$callback
;
$this
->
route
->
setDefault
(
'_middlewares'
,
$middlewareCallbacks
);
return
$this
;
}
/**
/**
* Freezes the controller.
* Freezes the controller.
*
*
...
...
tests/Silex/Tests/ApplicationTest.php
View file @
6445b9e9
...
@@ -15,6 +15,9 @@ use Silex\Application;
...
@@ -15,6 +15,9 @@ use Silex\Application;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\HttpKernel\Exception\HttpException
;
use
Symfony\Component\HttpKernel\Exception\HttpException
;
use
Symfony\Component\HttpFoundation\Response
;
use
Symfony\Component\HttpFoundation\RedirectResponse
;
use
Symfony\Component\HttpKernel\HttpKernelInterface
;
/**
/**
* Application test cases.
* Application test cases.
...
@@ -166,6 +169,120 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
...
@@ -166,6 +169,120 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this
->
assertEquals
(
'text/html; charset=ISO-8859-1'
,
$response
->
headers
->
get
(
'Content-Type'
));
$this
->
assertEquals
(
'text/html; charset=ISO-8859-1'
,
$response
->
headers
->
get
(
'Content-Type'
));
}
}
public
function
testRoutesMiddlewares
()
{
$app
=
new
Application
();
$test
=
$this
;
$middlewareTarget
=
array
();
$middleware1
=
function
(
Request
$request
)
use
(
&
$middlewareTarget
,
$test
)
{
$test
->
assertEquals
(
'/reached'
,
$request
->
getRequestUri
());
$middlewareTarget
[]
=
'middleware1_triggered'
;
};
$middleware2
=
function
(
Request
$request
)
use
(
&
$middlewareTarget
,
$test
)
{
$test
->
assertEquals
(
'/reached'
,
$request
->
getRequestUri
());
$middlewareTarget
[]
=
'middleware2_triggered'
;
};
$middleware3
=
function
(
Request
$request
)
use
(
&
$middlewareTarget
,
$test
)
{
throw
new
\Exception
(
'This middleware shouldn\'t run!'
);
};
$app
->
get
(
'/reached'
,
function
()
use
(
&
$middlewareTarget
)
{
$middlewareTarget
[]
=
'route_triggered'
;
return
'hello'
;
})
->
middleware
(
$middleware1
)
->
middleware
(
$middleware2
);
$app
->
get
(
'/never-reached'
,
function
()
use
(
&
$middlewareTarget
)
{
throw
new
\Exception
(
'This route shouldn\'t run!'
);
})
->
middleware
(
$middleware3
);
$result
=
$app
->
handle
(
Request
::
create
(
'/reached'
));
$this
->
assertSame
(
array
(
'middleware1_triggered'
,
'middleware2_triggered'
,
'route_triggered'
),
$middlewareTarget
);
$this
->
assertEquals
(
'hello'
,
$result
->
getContent
());
}
public
function
testRoutesMiddlewaresWithResponseObject
()
{
$app
=
new
Application
();
$app
->
get
(
'/foo'
,
function
()
{
throw
new
\Exception
(
'This route shouldn\'t run!'
);
})
->
middleware
(
function
()
{
return
new
Response
(
'foo'
);
});
$request
=
Request
::
create
(
'/foo'
);
$result
=
$app
->
handle
(
$request
);
$this
->
assertEquals
(
'foo'
,
$result
->
getContent
());
}
public
function
testRoutesMiddlewaresWithRedirectResponseObject
()
{
$app
=
new
Application
();
$app
->
get
(
'/foo'
,
function
()
{
throw
new
\Exception
(
'This route shouldn\'t run!'
);
})
->
middleware
(
function
()
use
(
$app
)
{
return
$app
->
redirect
(
'/bar'
);
});
$request
=
Request
::
create
(
'/foo'
);
$result
=
$app
->
handle
(
$request
);
$this
->
assertInstanceOf
(
'Symfony\Component\HttpFoundation\RedirectResponse'
,
$result
);
$this
->
assertEquals
(
'/bar'
,
$result
->
getTargetUrl
());
}
public
function
testRoutesMiddlewaresTriggeredAfterSilexBeforeFilters
()
{
$app
=
new
Application
();
$middlewareTarget
=
array
();
$middleware
=
function
(
Request
$request
)
use
(
&
$middlewareTarget
)
{
$middlewareTarget
[]
=
'middleware_triggered'
;
};
$app
->
get
(
'/foo'
,
function
()
use
(
&
$middlewareTarget
)
{
$middlewareTarget
[]
=
'route_triggered'
;
})
->
middleware
(
$middleware
);
$app
->
before
(
function
()
use
(
&
$middlewareTarget
)
{
$middlewareTarget
[]
=
'before_triggered'
;
});
$app
->
handle
(
Request
::
create
(
'/foo'
));
$this
->
assertSame
(
array
(
'before_triggered'
,
'middleware_triggered'
,
'route_triggered'
),
$middlewareTarget
);
}
/**
* @expectedException RuntimeException
*/
public
function
testNonResponseAndNonNullReturnFromRouteMiddlewareShouldThrowRuntimeException
()
{
$app
=
new
Application
();
$middleware
=
function
(
Request
$request
)
{
return
'string return'
;
};
$app
->
get
(
'/'
,
function
()
{
return
'hello'
;
})
->
middleware
(
$middleware
);
$app
->
handle
(
Request
::
create
(
'/'
),
HttpKernelInterface
::
MASTER_REQUEST
,
false
);
}
/**
/**
* @expectedException RuntimeException
* @expectedException RuntimeException
*/
*/
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment