Custom annotation in symfony 3 controller - php

So the question is pretty straightforward. I have a code in my controller that has became redundant, and i've decided to make the annotation for it.
if (!$request->getContentType() === 'json' ) {
return new JsonResponse(array('success' => false));
}
$content = $request->getContent();
if(empty($content)){
throw new BadRequestHttpException("Content is empty");
}
$data = json_decode($content, true);
if(empty($data) || !array_key_exists('type', $data)) {
return new JsonResponse(array('success' => false));
}
How do i make custom annotation #CheckRequest in which I can use the $request object as a parameter?

You need to make a custom annotation and then a listener that injects the annotation reader and handles the kernel.controller event:
Annotation
/**
* #Annotation
*/
class CheckRequest
{
}
Service Definition
services:
controller_check_request:
class: AppBundle\EventListener\ControllerCheckRequestListener
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController}
arguments:
- "#annotation_reader"
Listener:
namespace AppBundle\EventListener;
use AppBundle\Annotation\CheckRequest;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class ControllerCheckRequestListener
{
/** #var Reader */
private $reader;
/**
* #param Reader $reader
*/
public function __construct(Reader $reader)
{
$this->reader = $reader;
}
/**
* {#inheritdoc}
*/
public function onKernelController(FilterControllerEvent $event)
{
if (!is_array($controllers = $event->getController())) {
return;
}
$request = $event->getRequest();
$content = $request->getContent();
list($controller, $methodName) = $controllers;
$reflectionClass = new \ReflectionClass($controller);
$classAnnotation = $this->reader
->getClassAnnotation($reflectionClass, CheckRequest::class);
$reflectionObject = new \ReflectionObject($controller);
$reflectionMethod = $reflectionObject->getMethod($methodName);
$methodAnnotation = $this->reader
->getMethodAnnotation($reflectionMethod, CheckRequest::class);
if (!($classAnnotation || $methodAnnotation)) {
return;
}
if ($request->getContentType() !== 'json' ) {
return $event->setController(
function() {
return new JsonResponse(['success' => false]);
}
);
}
if (empty($content)) {
throw new BadRequestHttpException('Content is empty');
}
$data = json_decode($content, true);
if ($request->getContentType() !== 'json' ) {
return $event->setController(
function() {
return new JsonResponse(['success' => false]);
}
);
}
}
}
Notice that instead of returning the response, you set the entire controller with $event->setController();, and you also must return when making that call.
Then in your controller you can set it on the entire class:
use AppBundle\Annotation\CheckRequest;
/**
* #CheckRequest
*/
class YourController extends Controller
{
}
or individual methods/actions:
use AppBundle\Annotation\CheckRequest;
class TestController extends Controller
{
/**
* #Route("/", name="index")
* #CheckRequest
*/
public function indexAction(Request $request)
{
// ...
}
}

For Symfony 3.4.*
public function onKernelController(FilterControllerEvent $event){
if (!is_array($controllers = $event->getController())) {
return;
}
list($controller, $methodName) = $controllers;
$reflectionClass = new \ReflectionClass($controller);
// Controller
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$classAnnotation = $reader->getClassAnnotation($reflectionClass, AnnotationClass::class);
// Method
$reflectionMethod = $reflectionClass->getMethod($methodName);
$methodAnnotation = $reader->getMethodAnnotation($reflectionMethod, AnnotationClass::class);
if(!($classAnnotation || $methodAnnotation)){
return;
}
/** TODO CODE HERE **/
}

Related

How do you set up Session Namespaces?

I'm fairly new to Zend and I am trying to set a session namespace variable. I am receiving an error message stating "Configuration is missing a "session_config" key, or the value of that key is not an array".
I've followed the documentation provided by Zend on the subject but I'm coming up a bit short on how it all ties together.
I've configured the session manager in the global config file as follows:
'session_manager' => [
'config' => [
'class' => Session\Config\SessionConfig::class,
'options' => [
'name' => 'user',
],
],
'storage' => Session\Storage\SessionArrayStorage::class,
'validators' => [
Session\Validator\RemoteAddr::class,
Session\Validator\HttpUserAgent::class,
],
],
I created the session manager in the Module class as follows
use Zend\Session\SessionManager;
use Zend\Session\Config\SessionConfig;
use Zend\Session\Container;
use Zend\Session\Validator;
use Zend\Mvc\ModuleRouteListener;
class Module implements ConfigProviderInterface
{
public function onBootstrap($e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$this->bootstrapSession($e);
}
public function bootstrapSession($e)
{
$session = new SessionManager();
$session->start();
$container = new Container('initialized');
if (isset($container->init)) {
return;
}
$serviceManager = $e->getApplication()->getServiceManager();
$request = $serviceManager->get('Request');
$session->regenerateId(true);
$container->init = 1;
$container->remoteAddr = $request->getServer()->get('REMOTE_ADDR');
$container->httpUserAgent = $request->getServer()->get('HTTP_USER_AGENT');
$config = $serviceManager->get('Config');
if (! isset($config['session'])) {
return;
}
$sessionConfig = $config['session'];
if (! isset($sessionConfig['validators'])) {
return;
}
$chain = $session->getValidatorChain();
foreach ($sessionConfig['validators'] as $validator) {
switch ($validator) {
case Validator\HttpUserAgent::class:
$validator = new $validator($container->httpUserAgent);
break;
case Validator\RemoteAddr::class:
$validator = new $validator($container->remoteAddr);
break;
default:
$validator = new $validator();
}
$chain->attach('session.validate', array($validator, 'isValid'));
}
}
public function getServiceConfig()
{
return [
'factories' => [
SessionManager::class => function ($container) {
$config = $container->get('config');
if (! isset($config['session'])) {
$sessionManager = new SessionManager();
Container::setDefaultManager($sessionManager);
return $sessionManager;
}
$session = $config['session'];
$sessionConfig = null;
if (isset($session['config'])) {
$class = isset($session['config']['class'])
? $session['config']['class']
: SessionConfig::class;
$options = isset($session['config']['options'])
? $session['config']['options']
: [];
$sessionConfig = new $class();
$sessionConfig->setOptions($options);
}
$sessionStorage = null;
if (isset($session['storage'])) {
$class = $session['storage'];
$sessionStorage = new $class();
}
$sessionSaveHandler = null;
if (isset($session['save_handler'])) {
// class should be fetched from service manager
// since it will require constructor arguments
$sessionSaveHandler = $container->get($session['save_handler']);
}
$sessionManager = new SessionManager(
$sessionConfig,
$sessionStorage,
$sessionSaveHandler
);
Container::setDefaultManager($sessionManager);
return $sessionManager;
},
],
];
}
I defined the session container namespace in the module.config.php file as follows:
'session_containers' => [
'UserNamespace'
],
I try to access the namespace in the factory of the controller as follows:
namespace User\Factory;
use Interop\Container\ContainerInterface;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Zend\Session\Container;
use User\Controller\AuthController;
use User\Model\UserRepositoryInterface;
use User\Form\LoginForm;
class AuthControllerFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param null|array $options
* #return AuthController
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$formManager = $container->get('FormElementManager');
$sessionContainer = $container->get('UserNamespace');
return new AuthController(
$container->get(UserRepositoryInterface::class),
$formManager->get(LoginForm::class),
$sessionContainer
);
}
}
And finally I try to call the session namespace variable in the controller as follows:
namespace User\Controller;
use User\Form\LoginForm;
use User\Model\User;
use User\Model\UserRepositoryInterface;
use Zend\Session\Container;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class AuthController extends AbstractActionController
{
/**
* #var UserRepositoryInterface
*/
private $userRepository;
/**
* #var LoginForm
*/
private $loginForm;
private $sessionContainer;
/**
* #param UserRepositoryInterface $userRepository
* #param LoginForm $loginForm
*/
public function __construct(
UserRepositoryInterface $userRepository,
LoginForm $loginForm,
$sessionContainer
) {
$this->userRepository = $userRepository;
$this->loginForm = $loginForm;
$sessionContainer;
}
public function loginAction()
{
$request = $this->getRequest();
$viewModel = new ViewModel(['form' => $this->loginForm]);
if (! $request->isPost()) {
return $viewModel;
}
$this->loginForm->setData($request->getPost());
if (! $this->loginForm->isValid()) {
return $viewModel;
}
$loginAttempt = $this->loginForm->getData();
// var_dump($loginAttempt); die;
try {
$user = $this->userRepository->retrieveUserByUsername($loginAttempt['username']);
} catch (\Exception $ex) {
throw $ex;
}
// var_dump($user); die;
try {
$isLogin = $this->userRepository->validatePassword($user, $loginAttempt['password']);
} catch (\Exception $ex) {
throw $ex;
}
// var_dump($isLogin); die;
if($isLogin == true) {
$sessionContainer->test = "This is a test";
}
var_dump($sessionContainer->test); die;
}
I'm sure I'm a bit mixed up on my execution to make this work so any assistance or advice you can provide will be greatly appreciated.

Symfony onFlush Doctrine Listener

Hi I have an onFlush listener:
<?php
namespace FM\AppBundle\EventListener;
use FM\AdminBundle\Entity\Address\DeliveryAddress;
use Doctrine\ORM\Event\OnFlushEventArgs;
class DeliveryAddressListener
{
/**
* #param OnFlushEventArgs $args
*/
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof DeliveryAddress) {
$this->addPostalToUser($entity, $args);
}
}
}
/**
* #param DeliveryAddress $deliveryAddress
* #param OnFlushEventArgs $args
*/
public function addPostalToUser(DeliveryAddress $deliveryAddress, OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$user = $deliveryAddress->getOwner();
$user->setPostalCode($deliveryAddress->getZipCode());
}
}
service.yml:
delivery_address.listener:
class: FM\AppBundle\EventListener\DeliveryAddressListener
tags:
- { name: doctrine.event_listener, event: onFlush }
I'm trying to set the new zipCode to the User. But it does not seem to work.
Even when I'm adding a $em->persist($user).
I'm looking throught this doc: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#onflush
But I don't understand how I can make it works with this explanation:
If you create and persist a new entity in onFlush, then calling EntityManager#persist() is not enough. You have to execute an additional call to $unitOfWork->computeChangeSet($classMetadata, $entity).
When manipulating fields, they should be done in the preUpdaet/prePersist.
AppBundle/EventSubscriber/EntitySubscriber.php
namespace AppBundle\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;
class EntitySubscriber implements EventSubscriber
{
private $now;
public function __construct()
{
$this->now = \DateTime::createFromFormat('Y-m-d h:i:s', date('Y-m-d h:i:s'));
}
public function getSubscribedEvents()
{
return [
'prePersist',
'preUpdate'
];
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
if (method_exists($entity, 'setCreatedAt')) {
$entity->setUpdatedAt($this->now);
}
if (method_exists($entity, 'setUpdatedAt')) {
$entity->setUpdatedAt($this->now);
}
}
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
if (method_exists($entity, 'setUpdatedAt')) {
$entity->setUpdatedAt($this->now);
}
}
}
services.yml
app.entity_subscriber:
class: AppBundle\EventSubscriber\EntitySubscriber
tags:
- { name: doctrine.event_subscriber, connection: default }
If you need to create an object, persist it and flush it in your listener, then tlorens' answer won't work, as Doctrine docs mention that this must be done with an onFlush Event.
Initial question was how to make it work following docs advice:
If you create and persist a new entity in onFlush, then calling EntityManager#persist() is not enough. You have to execute an additional call to $unitOfWork->computeChangeSet($classMetadata, $entity).
And this is a way to achieve this:
/**
* #param OnFlushEventArgs $eventArgs
*/
public function onFlush(OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof User) {
$uow->computeChangeSets();
$changeSet = $uow->getEntityChangeSet($entity);
// In this exemple, User has a boolean property 'enabled' and a log will be created if it is passed to 'false'
if ($changeSet && isset($changeSet['enabled']) && $changeSet['enabled'][1] === false) {
$log = new Log();
$em->persist($log);
$uow->computeChangeSet($em->getClassMetadata(get_class($log)), $log);
}
}
}
Well it works when I use that:
// Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
$eventManager->removeEventListener('onFlush', $this);
My Listener
namespace FM\AppBundle\EventListener;
use FM\AdminBundle\Entity\Address\DeliveryAddress;
use Doctrine\ORM\Event\OnFlushEventArgs;
class DeliveryAddressListener
{
/**
* #param OnFlushEventArgs $args
*/
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$eventManager = $em->getEventManager();
// Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
$eventManager->removeEventListener('onFlush', $this);
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof DeliveryAddress) {
$this->addPostalToUser($entity, $args);
}
}
}
/**
* #param DeliveryAddress $deliveryAddress
* #param OnFlushEventArgs $args
*/
public function addPostalToUser(DeliveryAddress $deliveryAddress, OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$user = $deliveryAddress->getOwner();
$user->setPostalCode($deliveryAddress->getZipCode());
$em->flush();
}
}

Symfony2 get to the access_control parameters located in the security.yml

I'm trying to get the access_control parameters which are located in my security.yml as an array in my custom service.
Just like with getting the role_hierarchy parameters I thought it would work with the following code:
$accessParameters = $this->container->getParameter('security.access_control');
Unfortunately this was not the case. Can someone tell how to get the parameters?
There's no way to get the access_control parameter from the container.
This is because this parameter is only used to create request matchers which will be registered as AccessMap later given in the AccessListener, and then are left over without registering it into the container.
You can try something hacky to get these matchers back by getting them like
$context = $this->get("security.firewall.map.context.main")->getContext();
$listener = $context[0][5];
// Do reflection on "map" private member
But this is kind of an ugly solution.
Another way I can see on how to get them is to parse again the security file
use Symfony\Component\Yaml\Yaml;
$file = sprintf("%s/config/security.yml", $this->container->getParameter('kernel.root_dir'));
$parsed = Yaml::parse(file_get_contents($file));
$access = $parsed['security']['access_control'];
If you want to register this configuration into a service, you can do something like
services.yml
services:
acme.config_provider:
class: Acme\FooBundle\ConfigProvider
arguments:
- "%kernel.root_dir%"
acme.my_service:
class: Acme\FooBundle\MyService
arguments:
- "#acme.config_provider"
Acme\FooBundle\ConfigProvider
use Symfony\Component\Yaml\Yaml;
class ConfigProvider
{
protected $rootDir;
public function __construct($rootDir)
{
$this->rootDir = $rootDir;
}
public function getConfiguration()
{
$file = sprintf(
"%s/config/security.yml",
$this->rootDir
);
$parsed = Yaml::parse(file_get_contents($file));
return $parsed['security']['access_control'];
}
}
Acme\FooBundle\MyService
class MyService
{
protected $provider;
public function __construct(ConfigProvider $provider)
{
$this->provider = $provider;
}
public function doAction()
{
$access = $this->provider->getConfiguration();
foreach ($access as $line) {
// ...
}
}
}
Necro, but still relevant. This is an improvement on Touki's answer above, where we don't reparse the access_control definitions, but rather use the already configured security token, firewall and access map to work out the answer.
.../services.yml
...
My\Application\AuthenticationBundle\Security\AccessControlHelper:
class: My\Application\AuthenticationBundle\Security\AccessControlHelper
arguments:
$securityContext: "#security.context"
$firewall: '#security.firewall.map'
$accessDecisionManager: '#security.access.decision_manager'
$accessMap: '#security.access_map'
...
src/My/Application/AuthenticationBundle/Security/AccessControlHelper.php
declare(strict_types=1);
namespace My\Application\AuthenticationBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\AccessMapInterface;
use Symfony\Component\Security\Http\Firewall\AccessListener;
use Symfony\Component\Security\Http\FirewallMapInterface;
class AccessControlHelper
{
/**
* #var SecurityContextInterface
*/
protected $securityContext;
/**
* #var FirewallMapInterface
*/
protected $firewallMap;
/**
* #var AccessDecisionManagerInterface
*/
protected $accessDecisionManager;
/**
* #var AccessMapInterface
*/
protected $accessMap;
public function __construct(
SecurityContextInterface $securityContext,
FirewallMapInterface $firewallMap,
AccessDecisionManagerInterface $accessDecisionManager,
AccessMapInterface $accessMap
)
{
$this->securityContext = $securityContext;
$this->firewallMap = $firewallMap;
$this->accessDecisionManager = $accessDecisionManager;
$this->accessMap = $accessMap;
}
public function isRequestAccessible(Request $request): bool
{
$token = $this->securityContext->getToken();
if (!$token || false == $token->isAuthenticated()) {
return false;
}
list($listeners) = $this->firewallMap->getListeners($request);
if ($listeners) {
foreach ($listeners as $listener) {
if ($listener instanceof AccessListener) {
/**
* Logic here is much inspired by the AccessListener->handle(...) method.
*/
list($attributes) = $this->accessMap->getPatterns($request);
if (null === $attributes) {
continue;
}
return boolval($this->accessDecisionManager->decide($token, $attributes, $request));
}
}
}
return true;
}
public function isUriAccessible(string $uri)
{
return $this->isRequestAccessible(Request::create($uri));
}
}
Sample usage:
use My\Application\AuthenticationBundle\Security\AccessControlHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
$container = ...; // #var ContainerInterface
$accessControlHelper = $container->get(AccessControlHelper::class);
$accessControlHelper->isRequestAccessible(new Request("/foo"));
$accessControlHelper->isUriAccessible("/foo");

ZF2 getServiceLocator in ControllerPlugin class

I am trying to get service locator/entity manager in plugin class, How can I get that.
In my controller I am getting it like this.
public function getEntityManager()
{
if(null === $this->em){
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->em;
}
public function setEntityManager(EntityManager $em)
{
$this->em = $em;
}
but in plugin class I am getting error on $this->getServiceLocator() line. because this is not available in plugin class.
How can I do the same so that I can fetch some records and insert few in database in plugin.
I do have MvcEvent $e object in my plugin class, I can make use of this to get entity manager?
I have used this plugin to create my plugin
Any guide will be appriciated.
update:
namespace Auth\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\EventManager\EventInterface as Event;
use Zend\Authentication\AuthenticationService;
use Doctrine\ORM\EntityManager;
use Auth\Entity\User;
use Zend\Mvc\MvcEvent;
class AclPlugin extends AbstractPlugin
{
/*
* #var Doctrine\ORM\EntityManager
*/
protected $em;
public function checkAcl($e)
{
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
$storage = $auth->getStorage()->read();
if (!empty($storage->role))
$role = strtolower ( $storage->role );
else
$role = "guest";
} else {
$role = "guest";
}
$app = $e->getParam('application');
$acl = new \Auth\Acl\AclRules();
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
$action = $matches->getParam('action', 'index');
$resource = strtolower( $controller );
$permission = strtolower( $action );
if (!$acl->hasResource($resource)) {
throw new \Exception('Resource ' . $resource . ' not defined');
}
if ($acl->isAllowed($role, $resource, $permission)) {
$query = $this->getEntityManager($e)->createQuery('SELECT u FROM Auth\Entity\User u');
$resultIdentities = $query->execute();
var_dump($resultIdentities);
exit();
return;
} else {
$matches->setParam('controller', 'Auth\Controller\User'); // redirect
$matches->setParam('action', 'accessdenied');
return;
}
}
public function getEntityManager($e) {
var_dump($this->getController()); // returns null
exit();
if (null === $this->em) {
$this->em = $this->getController()->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->em;
}
public function setEntityManager(EntityManager $em) {
$this->em = $em;
}
}
I am calling above class in module.php
public function onBootstrap(Event $e)
{
$application = $e->getApplication();
$services = $application->getServiceManager();
$eventManager = $e->getApplication()->getEventManager();
$eventManager->attach('dispatch', array($this, 'loadConfiguration'),101);
}
public function loadConfiguration(MvcEvent $e)
{
$e->getApplication()->getServiceManager()
->get('ControllerPluginManager')->get('AclPlugin')
->checkAcl($e); //pass to the plugin...
}
I am registering this plugin in module.config.php
return array(
'controllers' => array(
'invokables' => array(
'Auth\Controller\User' => 'Auth\Controller\UserController',
),
),
'controller_plugins' => array(
'invokables' => array(
'AclPlugin' => 'Auth\Controller\Plugin\AclPlugin',
),
),
);
What do you mean with "Plugin Class"? In case you're talking abount controller plugins, you can access it using (from the controller plugin's scope): $this->getController()->getServiceLocator()->get('doctrine.entitymanager.orm_default');.
For other classes, I usually create a factory that injects the ServiceManager instance automatically. For example, in the Module class:
public function getServiceConfig()
{
return array(
'factories' => array(
'myServiceClass' => function(ServiceManager $sm) {
$instance = new Class();
$instance->setServiceManager($sm);
// Do some other configuration
return $instance;
},
),
);
}
// access it using the ServiceManager where you need it
$myService = $sm->get('myService');
changed the above AclPlugin class as below
namespace Auth\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\EventManager\EventInterface as Event;
use Zend\Authentication\AuthenticationService;
use Doctrine\ORM\EntityManager;
use Auth\Entity\User;
use Zend\Mvc\MvcEvent;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager;
class AclPlugin extends AbstractPlugin implements ServiceManagerAwareInterface
{
/*
* #var Doctrine\ORM\EntityManager
*/
protected $em;
protected $sm;
public function checkAcl($e)
{
$this->setServiceManager( $e->getApplication()->getServiceManager() );
$auth = new AuthenticationService();
if ($auth->hasIdentity()) {
$storage = $auth->getStorage()->read();
if (!empty($storage->role))
$role = strtolower ( $storage->role );
else
$role = "guest";
} else {
$role = "guest";
}
$app = $e->getParam('application');
$acl = new \Auth\Acl\AclRules();
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
$action = $matches->getParam('action', 'index');
$resource = strtolower( $controller );
$permission = strtolower( $action );
if (!$acl->hasResource($resource)) {
throw new \Exception('Resource ' . $resource . ' not defined');
}
if ($acl->isAllowed($role, $resource, $permission)) {
$query = $this->getEntityManager($e)->createQuery('SELECT u FROM Auth\Entity\User u');
$resultIdentities = $query->execute();
var_dump($resultIdentities);
foreach ($resultIdentities as $r)
echo $r->username;
exit();
return;
} else {
$matches->setParam('controller', 'Auth\Controller\User'); // redirect
$matches->setParam('action', 'accessdenied');
return;
}
}
public function getEntityManager() {
if (null === $this->em) {
$this->em = $this->sm->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->em;
}
public function setEntityManager(EntityManager $em) {
$this->em = $em;
}
/**
* Retrieve service manager instance
*
* #return ServiceManager
*/
public function getServiceManager()
{
return $this->sm->getServiceLocator();
}
/**
* Set service manager instance
*
* #param ServiceManager $locator
* #return void
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->sm = $serviceManager;
}
}
Actually getting ServiceManager in controller plugin is quite easy!
Just use: $this->getController()->getServiceLocator()
Example:
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class Translate extends AbstractPlugin
{
public function __invoke($message, $textDomain = 'default', $locale = null)
{
/** #var \Zend\I18n\Translator\Translator $translator */
$translator = $this->getController()->getServiceLocator()->get('translator');
return $translator->translate($message, $textDomain, $locale);
}
}

Fatal error: Declaration of registerContainerConfiguration must be compatible with that of Kernel::registerContainerConfiguration

Do anyone know why this occurs?
as far I can get, the child class method is declared in the same way as parent's.
Thanks!
here is my kernel code:
<?php
require_once __DIR__.'/../src/autoload.php';
use Symfony\Framework\Kernel;
use Symfony\Components\DependencyInjection\Loader\YamlFileLoader as ContainerLoader;
use Symfony\Components\Routing\Loader\YamlFileLoader as RoutingLoader;
use Symfony\Framework\KernelBundle;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\ZendBundle\ZendBundle;
use Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle;
use Symfony\Bundle\DoctrineBundle\DoctrineBundle;
use Symfony\Bundle\DoctrineMigrationsBundle\DoctrineMigrationsBundle;
use Symfony\Bundle\DoctrineMongoDBBundle\DoctrineMongoDBBundle;
use Symfony\Bundle\PropelBundle\PropelBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Application\UfaraBundle\UfaraBundle;
class UfaraKernel extends Kernel {
public function registerRootDir() {
return __DIR__;
}
public function registerBundles() {
$bundles = array(
new KernelBundle(),
new FrameworkBundle(),
new ZendBundle(),
new SwiftmailerBundle(),
new DoctrineBundle(),
//new DoctrineMigrationsBundle(),
//new DoctrineMongoDBBundle(),
//new PropelBundle(),
//new TwigBundle(),
new UfaraBundle(),
);
if ($this->isDebug()) {
}
return $bundles;
}
public function registerBundleDirs() {
$bundles = array(
'Application' => __DIR__.'/../src/Application',
'Bundle' => __DIR__.'/../src/Bundle',
'Symfony\\Framework' => __DIR__.'/../src/vendor/symfony/src/Symfony/Framework',
'Symfony\\Bundle' => __DIR__.'/../src/vendor/symfony/src/Symfony/Bundle',
);
return $bundles;
}
public function registerContainerConfiguration(LoaderInterface $loader) {
return $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
}
public function registerRoutes() {
$loader = new RoutingLoader($this->getBundleDirs());
return $loader->load(__DIR__.'/config/routing.yml');
}
}
here is the parent class code:
<?php
namespace Symfony\Framework;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Resource\FileResource;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Loader\DelegatingLoader;
use Symfony\Component\DependencyInjection\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Framework\ClassCollectionLoader;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier#symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* The Kernel is the heart of the Symfony system. It manages an environment
* that can host bundles.
*
* #author Fabien Potencier <fabien.potencier#symfony-project.org>
*/
abstract class Kernel implements HttpKernelInterface, \Serializable
{
protected $bundles;
protected $bundleDirs;
protected $container;
protected $rootDir;
protected $environment;
protected $debug;
protected $booted;
protected $name;
protected $startTime;
protected $request;
const VERSION = '2.0.0-DEV';
/**
* Constructor.
*
* #param string $environment The environment
* #param Boolean $debug Whether to enable debugging or not
*/
public function __construct($environment, $debug)
{
$this->environment = $environment;
$this->debug = (Boolean) $debug;
$this->booted = false;
$this->rootDir = realpath($this->registerRootDir());
$this->name = basename($this->rootDir);
if ($this->debug) {
ini_set('display_errors', 1);
error_reporting(-1);
$this->startTime = microtime(true);
} else {
ini_set('display_errors', 0);
}
}
public function __clone()
{
if ($this->debug) {
$this->startTime = microtime(true);
}
$this->booted = false;
$this->container = null;
$this->request = null;
}
abstract public function registerRootDir();
abstract public function registerBundles();
abstract public function registerBundleDirs();
abstract public function registerContainerConfiguration(LoaderInterface $loader);
/**
* Checks whether the current kernel has been booted or not.
*
* #return boolean $booted
*/
public function isBooted()
{
return $this->booted;
}
/**
* Boots the current kernel.
*
* This method boots the bundles, which MUST set
* the DI container.
*
* #throws \LogicException When the Kernel is already booted
*/
public function boot()
{
if (true === $this->booted) {
throw new \LogicException('The kernel is already booted.');
}
if (!$this->isDebug()) {
require_once __DIR__.'/bootstrap.php';
}
$this->bundles = $this->registerBundles();
$this->bundleDirs = $this->registerBundleDirs();
$this->container = $this->initializeContainer();
// load core classes
ClassCollectionLoader::load(
$this->container->getParameter('kernel.compiled_classes'),
$this->container->getParameter('kernel.cache_dir'),
'classes',
$this->container->getParameter('kernel.debug'),
true
);
foreach ($this->bundles as $bundle) {
$bundle->setContainer($this->container);
$bundle->boot();
}
$this->booted = true;
}
/**
* Shutdowns the kernel.
*
* This method is mainly useful when doing functional testing.
*/
public function shutdown()
{
$this->booted = false;
foreach ($this->bundles as $bundle) {
$bundle->shutdown();
$bundle->setContainer(null);
}
$this->container = null;
}
/**
* Reboots the kernel.
*
* This method is mainly useful when doing functional testing.
*
* It is a shortcut for the call to shutdown() and boot().
*/
public function reboot()
{
$this->shutdown();
$this->boot();
}
/**
* Gets the Request instance associated with the master request.
*
* #return Request A Request instance
*/
public function getRequest()
{
return $this->request;
}
/**
* Handles a request to convert it to a response by calling the HttpKernel service.
*
* #param Request $request A Request instance
* #param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
* #param Boolean $raw Whether to catch exceptions or not
*
* #return Response $response A Response instance
*/
public function handle(Request $request = null, $type = HttpKernelInterface::MASTER_REQUEST, $raw = false)
{
if (false === $this->booted) {
$this->boot();
}
if (null === $request) {
$request = $this->container->get('request');
} else {
$this->container->set('request', $request);
}
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$this->request = $request;
}
$response = $this->container->getHttpKernelService()->handle($request, $type, $raw);
$this->container->set('request', $this->request);
return $response;
}
/**
* Gets the directories where bundles can be stored.
*
* #return array An array of directories where bundles can be stored
*/
public function getBundleDirs()
{
return $this->bundleDirs;
}
/**
* Gets the registered bundle names.
*
* #return array An array of registered bundle names
*/
public function getBundles()
{
return $this->bundles;
}
/**
* Checks if a given class name belongs to an active bundle.
*
* #param string $class A class name
*
* #return Boolean true if the class belongs to an active bundle, false otherwise
*/
public function isClassInActiveBundle($class)
{
foreach ($this->bundles as $bundle) {
$bundleClass = get_class($bundle);
if (0 === strpos($class, substr($bundleClass, 0, strrpos($bundleClass, '\\')))) {
return true;
}
}
return false;
}
/**
* Returns the Bundle name for a given class.
*
* #param string $class A class name
*
* #return string The Bundle name or null if the class does not belongs to a bundle
*/
public function getBundleForClass($class)
{
$namespace = substr($class, 0, strrpos($class, '\\'));
foreach (array_keys($this->getBundleDirs()) as $prefix) {
if (0 === $pos = strpos($namespace, $prefix)) {
return substr($namespace, strlen($prefix) + 1, strpos($class, 'Bundle\\') + 7);
}
}
}
public function getName()
{
return $this->name;
}
public function getSafeName()
{
return preg_replace('/[^a-zA-Z0-9_]+/', '', $this->name);
}
public function getEnvironment()
{
return $this->environment;
}
public function isDebug()
{
return $this->debug;
}
public function getRootDir()
{
return $this->rootDir;
}
public function getContainer()
{
return $this->container;
}
public function getStartTime()
{
return $this->debug ? $this->startTime : -INF;
}
public function getCacheDir()
{
return $this->rootDir.'/cache/'.$this->environment;
}
public function getLogDir()
{
return $this->rootDir.'/logs';
}
protected function initializeContainer()
{
$class = $this->getSafeName().ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
$location = $this->getCacheDir().'/'.$class;
$reload = $this->debug ? $this->needsReload($class, $location) : false;
if ($reload || !file_exists($location.'.php')) {
$this->buildContainer($class, $location.'.php');
}
require_once $location.'.php';
$container = new $class();
$container->set('kernel', $this);
return $container;
}
public function getKernelParameters()
{
$bundles = array();
foreach ($this->bundles as $bundle) {
$bundles[] = get_class($bundle);
}
return array_merge(
array(
'kernel.root_dir' => $this->rootDir,
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => $this->getCacheDir(),
'kernel.logs_dir' => $this->getLogDir(),
'kernel.bundle_dirs' => $this->bundleDirs,
'kernel.bundles' => $bundles,
'kernel.charset' => 'UTF-8',
'kernel.compiled_classes' => array(),
),
$this->getEnvParameters()
);
}
protected function getEnvParameters()
{
$parameters = array();
foreach ($_SERVER as $key => $value) {
if ('SYMFONY__' === substr($key, 0, 9)) {
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
}
}
return $parameters;
}
protected function needsReload($class, $location)
{
if (!file_exists($location.'.meta') || !file_exists($location.'.php')) {
return true;
}
$meta = unserialize(file_get_contents($location.'.meta'));
$time = filemtime($location.'.php');
foreach ($meta as $resource) {
if (!$resource->isUptodate($time)) {
return true;
}
}
return false;
}
protected function buildContainer($class, $file)
{
$parameterBag = new ParameterBag($this->getKernelParameters());
$container = new ContainerBuilder($parameterBag);
foreach ($this->bundles as $bundle) {
$bundle->registerExtensions($container);
if ($this->debug) {
$container->addObjectResource($bundle);
}
}
if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
$container->merge($cont);
}
$container->freeze();
foreach (array('cache', 'logs') as $name) {
$dir = $container->getParameter(sprintf('kernel.%s_dir', $name));
if (!is_dir($dir)) {
if (false === #mkdir($dir, 0777, true)) {
die(sprintf('Unable to create the %s directory (%s)', $name, dirname($dir)));
}
} elseif (!is_writable($dir)) {
die(sprintf('Unable to write in the %s directory (%s)', $name, $dir));
}
}
// cache the container
$dumper = new PhpDumper($container);
$content = $dumper->dump(array('class' => $class));
if (!$this->debug) {
$content = self::stripComments($content);
}
$this->writeCacheFile($file, $content);
if ($this->debug) {
$container->addObjectResource($this);
// save the resources
$this->writeCacheFile($this->getCacheDir().'/'.$class.'.meta', serialize($container->getResources()));
}
}
protected function getContainerLoader(ContainerInterface $container)
{
$resolver = new LoaderResolver(array(
new XmlFileLoader($container, $this->getBundleDirs()),
new YamlFileLoader($container, $this->getBundleDirs()),
new IniFileLoader($container, $this->getBundleDirs()),
new PhpFileLoader($container, $this->getBundleDirs()),
new ClosureLoader($container),
));
return new DelegatingLoader($resolver);
}
/**
* Removes comments from a PHP source string.
*
* We don't use the PHP php_strip_whitespace() function
* as we want the content to be readable and well-formatted.
*
* #param string $source A PHP string
*
* #return string The PHP string with the comments removed
*/
static public function stripComments($source)
{
if (!function_exists('token_get_all')) {
return $source;
}
$output = '';
foreach (token_get_all($source) as $token) {
if (is_string($token)) {
$output .= $token;
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$output .= $token[1];
}
}
// replace multiple new lines with a single newline
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
// reformat {} "a la python"
$output = preg_replace(array('/\n\s*\{/', '/\n\s*\}/'), array(' {', ' }'), $output);
return $output;
}
protected function writeCacheFile($file, $content)
{
$tmpFile = tempnam(dirname($file), basename($file));
if (false !== #file_put_contents($tmpFile, $content) && #rename($tmpFile, $file)) {
chmod($file, 0644);
return;
}
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
public function serialize()
{
return serialize(array($this->environment, $this->debug));
}
public function unserialize($data)
{
list($environment, $debug) = unserialize($data);
$this->__construct($environment, $debug);
}
}
Your answer lies in the imported namespaces. In the Kernel's file, there's this use clause:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;
So that ties LoaderInterface to the fully namespaced class Symfony\Component\DependencyInjection\Loader\LoaderInterface.
Basically making the signature:
public function registerContainerConfiguration(Symfony\Component\DependencyInjection\Loader\LoaderInterface $loader);
In your class, you don't import that namespace. So PHP by default assumes the class is in your namespace (since none of the imported namespaces have that interface name).
So your signature is (since you don't declare a namespace):
public function registerContainerConfiguration(\LoaderInterface $loader);
So to get them to match, simply add the use line to the top of your file:
use Symfony\Component\DependencyInjection\Loader\LoaderInterface;

Categories