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
0cdef8e5
Commit
0cdef8e5
authored
Oct 07, 2013
by
Romain Neutron
Committed by
Fabien Potencier
Oct 16, 2013
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Converters as service
parent
86fb50c1
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
190 additions
and
62 deletions
+190
-62
doc/changelog.rst
doc/changelog.rst
+1
-0
doc/usage.rst
doc/usage.rst
+36
-0
src/Silex/Application.php
src/Silex/Application.php
+5
-1
src/Silex/CallbackResolver.php
src/Silex/CallbackResolver.php
+56
-0
src/Silex/EventListener/ConverterListener.php
src/Silex/EventListener/ConverterListener.php
+5
-1
src/Silex/Provider/ServiceControllerServiceProvider.php
src/Silex/Provider/ServiceControllerServiceProvider.php
+1
-1
src/Silex/ServiceControllerResolver.php
src/Silex/ServiceControllerResolver.php
+11
-20
tests/Silex/Tests/CallbackResolverTest.php
tests/Silex/Tests/CallbackResolverTest.php
+49
-0
tests/Silex/Tests/ServiceControllerResolverTest.php
tests/Silex/Tests/ServiceControllerResolverTest.php
+26
-39
No files found.
doc/changelog.rst
View file @
0cdef8e5
...
...
@@ -5,6 +5,7 @@ Changelog
------------------
* Only convert attributes on the request that actually exist.
* Add support for using a service method as a converter.
1.1.0 (2013-07-04)
------------------
...
...
doc/usage.rst
View file @
0cdef8e5
...
...
@@ -349,6 +349,42 @@ The converter callback also receives the ``Request`` as its second argument::
// ...
})->convert('post', $callback);
A converter can also be defined as a service. For example, here is a user
converter based on Doctrine ObjectManager::
use Doctrine\Common\Persistence\ObjectManager
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class UserConverter
{
private $om;
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function convert($id)
{
if (null === $user = $this->om->find('User', (int) $id)) {
throw new NotFoundHttpException(sprintf('User %d does not exist', $id));
}
return $user;
}
}
The service will now be registered in the application, and the
convert method will be used as converter::
$app['converter.user'] = $app->share(function () {
return new UserConverter();
});
$app->get('/user/{user}', function (User $user) {
// ...
})->convert('user', 'converter.user:convert');
Requirements
~~~~~~~~~~~~
...
...
src/Silex/Application.php
View file @
0cdef8e5
...
...
@@ -100,12 +100,16 @@ class Application extends \Pimple implements HttpKernelInterface, TerminableInte
}
$dispatcher
->
addSubscriber
(
new
ResponseListener
(
$app
[
'charset'
]));
$dispatcher
->
addSubscriber
(
new
MiddlewareListener
(
$app
));
$dispatcher
->
addSubscriber
(
new
ConverterListener
(
$app
[
'routes'
]));
$dispatcher
->
addSubscriber
(
new
ConverterListener
(
$app
[
'routes'
]
,
$app
[
'callback_resolver'
]
));
$dispatcher
->
addSubscriber
(
new
StringToResponseListener
());
return
$dispatcher
;
});
$this
[
'callback_resolver'
]
=
$this
->
share
(
function
()
use
(
$app
)
{
return
new
CallbackResolver
(
$app
);
});
$this
[
'resolver'
]
=
$this
->
share
(
function
()
use
(
$app
)
{
return
new
ControllerResolver
(
$app
,
$app
[
'logger'
]);
});
...
...
src/Silex/CallbackResolver.php
0 → 100644
View file @
0cdef8e5
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace
Silex
;
class
CallbackResolver
{
const
SERVICE_PATTERN
=
"/[A-Za-z0-9\._\-]+:[a-zA-Z_
\x7f
-
\xff
][a-zA-Z0-9_
\x7f
-
\xff
]*/"
;
private
$app
;
public
function
__construct
(
\Pimple
$app
)
{
$this
->
app
=
$app
;
}
/**
* Returns true if the string is a valid service method representation.
*
* @param string $name
*
* @return Boolean
*/
public
function
isValid
(
$name
)
{
return
is_string
(
$name
)
&&
preg_match
(
static
::
SERVICE_PATTERN
,
$name
);
}
/**
* Returns a callable given its string representation.
*
* @param string $name
*
* @return array A callable array
*
* @throws \InvalidArgumentException In case the method does not exist.
*/
public
function
getCallback
(
$name
)
{
list
(
$service
,
$method
)
=
explode
(
':'
,
$name
,
2
);
if
(
!
isset
(
$this
->
app
[
$service
]))
{
throw
new
\InvalidArgumentException
(
sprintf
(
'Service "%s" does not exist.'
,
$service
));
}
return
array
(
$this
->
app
[
$service
],
$method
);
}
}
src/Silex/EventListener/ConverterListener.php
View file @
0cdef8e5
...
...
@@ -11,6 +11,7 @@
namespace
Silex\EventListener
;
use
Silex\CallbackResolver
;
use
Symfony\Component\HttpKernel\KernelEvents
;
use
Symfony\Component\HttpKernel\Event\FilterControllerEvent
;
use
Symfony\Component\EventDispatcher\EventSubscriberInterface
;
...
...
@@ -24,15 +25,17 @@ use Symfony\Component\Routing\RouteCollection;
class
ConverterListener
implements
EventSubscriberInterface
{
protected
$routes
;
protected
$callbackResolver
;
/**
* Constructor.
*
* @param RouteCollection $routes A RouteCollection instance
*/
public
function
__construct
(
RouteCollection
$routes
)
public
function
__construct
(
RouteCollection
$routes
,
CallbackResolver
$callbackResolver
)
{
$this
->
routes
=
$routes
;
$this
->
callbackResolver
=
$callbackResolver
;
}
/**
...
...
@@ -47,6 +50,7 @@ class ConverterListener implements EventSubscriberInterface
if
(
$route
&&
$converters
=
$route
->
getOption
(
'_converters'
))
{
foreach
(
$converters
as
$name
=>
$callback
)
{
if
(
$request
->
attributes
->
has
(
$name
))
{
$callback
=
$this
->
callbackResolver
->
isValid
(
$callback
)
?
$this
->
callbackResolver
->
getCallback
(
$callback
)
:
$callback
;
$request
->
attributes
->
set
(
$name
,
call_user_func
(
$callback
,
$request
->
attributes
->
get
(
$name
),
$request
));
}
}
...
...
src/Silex/Provider/ServiceControllerServiceProvider.php
View file @
0cdef8e5
...
...
@@ -20,7 +20,7 @@ class ServiceControllerServiceProvider implements ServiceProviderInterface
public
function
register
(
Application
$app
)
{
$app
[
'resolver'
]
=
$app
->
share
(
$app
->
extend
(
'resolver'
,
function
(
$resolver
,
$app
)
{
return
new
ServiceControllerResolver
(
$resolver
,
$app
);
return
new
ServiceControllerResolver
(
$resolver
,
$app
[
'callback_resolver'
]
);
}));
}
...
...
src/Silex/ServiceControllerResolver.php
View file @
0cdef8e5
...
...
@@ -11,7 +11,6 @@
namespace
Silex
;
use
Silex\Application
;
use
Symfony\Component\HttpFoundation\Request
;
use
Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
;
...
...
@@ -22,21 +21,19 @@ use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
*/
class
ServiceControllerResolver
implements
ControllerResolverInterface
{
const
SERVICE_PATTERN
=
"/[A-Za-z0-9\._\-]+:[a-zA-Z_
\x7f
-
\xff
][a-zA-Z0-9_
\x7f
-
\xff
]*/"
;
protected
$resolver
;
protected
$app
;
protected
$controllerResolver
;
protected
$callbackResolver
;
/**
* Constructor.
*
* @param ControllerResolverInterface $
r
esolver A ControllerResolverInterface instance to delegate to
* @param
Application $app An Application
instance
* @param ControllerResolverInterface $
controllerR
esolver A ControllerResolverInterface instance to delegate to
* @param
CallbackResolver $callbackResolver A service resolver
instance
*/
public
function
__construct
(
ControllerResolverInterface
$
resolver
,
Application
$app
)
public
function
__construct
(
ControllerResolverInterface
$
controllerResolver
,
CallbackResolver
$callbackResolver
)
{
$this
->
resolver
=
$r
esolver
;
$this
->
app
=
$app
;
$this
->
controllerResolver
=
$controllerR
esolver
;
$this
->
callbackResolver
=
$callbackResolver
;
}
/**
...
...
@@ -46,17 +43,11 @@ class ServiceControllerResolver implements ControllerResolverInterface
{
$controller
=
$request
->
attributes
->
get
(
'_controller'
,
null
);
if
(
!
is_string
(
$controller
)
||
!
preg_match
(
static
::
SERVICE_PATTERN
,
$controller
))
{
return
$this
->
resolver
->
getController
(
$request
);
}
list
(
$service
,
$method
)
=
explode
(
':'
,
$controller
,
2
);
if
(
!
isset
(
$this
->
app
[
$service
]))
{
throw
new
\InvalidArgumentException
(
sprintf
(
'Service "%s" does not exist.'
,
$service
));
if
(
!
$this
->
callbackResolver
->
isValid
(
$controller
))
{
return
$this
->
controllerResolver
->
getController
(
$request
);
}
return
array
(
$this
->
app
[
$service
],
$method
);
return
$this
->
callbackResolver
->
getCallback
(
$controller
);
}
/**
...
...
@@ -64,6 +55,6 @@ class ServiceControllerResolver implements ControllerResolverInterface
*/
public
function
getArguments
(
Request
$request
,
$controller
)
{
return
$this
->
r
esolver
->
getArguments
(
$request
,
$controller
);
return
$this
->
controllerR
esolver
->
getArguments
(
$request
,
$controller
);
}
}
tests/Silex/Tests/CallbackResolverTest.php
0 → 100644
View file @
0cdef8e5
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace
Silex\Tests
;
use
Silex\CallbackResolver
;
class
CallbackResolverTest
extends
\PHPUnit_Framework_Testcase
{
public
function
setup
()
{
$this
->
app
=
new
\Pimple
();
$this
->
resolver
=
new
CallbackResolver
(
$this
->
app
);
}
public
function
testShouldResolveCallback
()
{
$this
->
app
[
'some_service'
]
=
function
()
{
return
new
\stdClass
();
};
$this
->
assertTrue
(
$this
->
resolver
->
isValid
(
'some_service:methodName'
));
$this
->
assertEquals
(
array
(
$this
->
app
[
'some_service'
],
'methodName'
),
$this
->
resolver
->
getCallback
(
'some_service:methodName'
)
);
}
public
function
testNonStringsAreNotValid
()
{
$this
->
assertFalse
(
$this
->
resolver
->
isValid
(
null
));
$this
->
assertFalse
(
$this
->
resolver
->
isValid
(
'some_service::methodName'
));
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Service "some_service" does not exist.
*/
public
function
testShouldThrowAnExceptionIfServiceIsMissing
()
{
$this
->
resolver
->
getCallback
(
'some_service:methodName'
);
}
}
tests/Silex/Tests/ServiceControllerResolverTest.php
View file @
0cdef8e5
...
...
@@ -26,71 +26,58 @@ class ServiceControllerResolverTest extends \PHPUnit_Framework_Testcase
$this
->
mockResolver
=
$this
->
getMockBuilder
(
'Symfony\Component\HttpKernel\Controller\ControllerResolverInterface'
)
->
disableOriginalConstructor
()
->
getMock
();
$this
->
mockCallbackResolver
=
$this
->
getMockBuilder
(
'Silex\CallbackResolver'
)
->
disableOriginalConstructor
()
->
getMock
();
$this
->
app
=
new
Application
();
$this
->
resolver
=
new
ServiceControllerResolver
(
$this
->
mockResolver
,
$this
->
app
);
$this
->
resolver
=
new
ServiceControllerResolver
(
$this
->
mockResolver
,
$this
->
mockCallbackResolver
);
}
public
function
testShouldResolveServiceController
()
{
$this
->
app
[
'some_service'
]
=
function
()
{
return
new
\stdClass
();
};
$this
->
mockCallbackResolver
->
expects
(
$this
->
once
())
->
method
(
'isValid'
)
->
will
(
$this
->
returnValue
(
true
));
$req
=
Request
::
create
(
'/'
);
$req
->
attributes
->
set
(
'_controller'
,
'some_service:methodName'
);
$this
->
mockCallbackResolver
->
expects
(
$this
->
once
())
->
method
(
'getCallback'
)
->
with
(
'some_service:methodName'
)
->
will
(
$this
->
returnValue
(
array
(
'callback'
)));
$this
->
assertEquals
(
array
(
$this
->
app
[
'some_service'
],
'methodName'
),
$this
->
resolver
->
getController
(
$req
)
);
}
$this
->
app
[
'some_service'
]
=
function
()
{
return
new
\stdClass
();
};
public
function
testShouldDelegateNonStrings
()
{
$req
=
Request
::
create
(
'/'
);
$req
->
attributes
->
set
(
'_controller'
,
function
()
{});
$this
->
mockResolver
->
expects
(
$this
->
once
())
->
method
(
'getController'
)
->
with
(
$req
)
->
will
(
$this
->
returnValue
(
123
));
$req
->
attributes
->
set
(
'_controller'
,
'some_service:methodName'
);
$this
->
assertEquals
(
123
,
$this
->
resolver
->
getController
(
$req
));
$this
->
assertEquals
(
array
(
'callback'
)
,
$this
->
resolver
->
getController
(
$req
));
}
/**
* Note: This doesn't test the regex extensively, just a common use case
*/
public
function
testShouldDelegateNonMatchingSyntax
()
public
function
testShouldUnresolvedControllerNames
()
{
$req
=
Request
::
create
(
'/'
);
$req
->
attributes
->
set
(
'_controller'
,
'some_class::methodName'
);
$this
->
mockCallbackResolver
->
expects
(
$this
->
once
())
->
method
(
'isValid'
)
->
with
(
'some_class::methodName'
)
->
will
(
$this
->
returnValue
(
false
));
$this
->
mockResolver
->
expects
(
$this
->
once
())
->
method
(
'getController'
)
->
with
(
$req
)
->
will
(
$this
->
returnValue
(
123
));
->
method
(
'getController'
)
->
with
(
$req
)
->
will
(
$this
->
returnValue
(
123
));
$this
->
assertEquals
(
123
,
$this
->
resolver
->
getController
(
$req
));
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Service "some_service" does not exist.
*/
public
function
testShouldThrowIfServiceIsMissing
()
{
$req
=
Request
::
create
(
'/'
);
$req
->
attributes
->
set
(
'_controller'
,
'some_service:methodName'
);
$this
->
resolver
->
getController
(
$req
);
}
public
function
testShouldDelegateGetArguments
()
{
$req
=
Request
::
create
(
'/'
);
$this
->
mockResolver
->
expects
(
$this
->
once
())
->
method
(
'getArguments'
)
->
with
(
$req
)
->
will
(
$this
->
returnValue
(
123
));
->
method
(
'getArguments'
)
->
with
(
$req
)
->
will
(
$this
->
returnValue
(
123
));
$this
->
assertEquals
(
123
,
$this
->
resolver
->
getArguments
(
$req
,
function
()
{}));
}
...
...
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