I was working on a suitable example of the CoR(Chain-of-responsibility) pattern. I learned that it is often used as middleware layer in MVC.
I am trying to write a middleware layer suitable for the pattern.
I have set my scenario regarding the implementation of the pattern as follows: The classes defined in the controller method as Annotations are linked to each other with chains, and each class performs a task and moves on to the next task. CoR(Chain-of-responsibility) pattern implementation known so far.
When I look at the examples, none of the classes used as middleware have been injected with Depency injection.
My scenario is as follows: An "#Middleware()" annonation is defined to the Controller method. Sample
/**
* #Route("/", name="test")
* #Middleware({"App\Middleware\MiddlewareOne","App\Middleware\MiddlewareTwo"})
*/
public function test()
Then I get annotations in the "onKernelController" method with ControllerListener and create the classes defined here with a foreach loop and connect them to each other.
Below you will see the sample code blocks, respectively.
The problem I'm having here is How can I give the ContainerInterface or a Service class that I created in the constructor of the MiddlewareOne class to the constructor of this object?(I do not know which services are coming, a fixed service is not coming)
class ControllerListener
{
/**
* #param \Symfony\Component\HttpKernel\Event\ControllerEvent $controllerEvent
*/
public function onKernelController(ControllerEvent $controllerEvent)
{
MiddlewareManager::handle($controllerEvent);
}
}
class MiddlewareManager
{
public static function handle(ControllerEvent $controllerEvent)
{
$containerBuilder = new ContainerBuilder();
if (!is_array($controllers = $controllerEvent->getController())) {
return;
}
$reader = new AnnotationReader();
$request = $controllerEvent->getRequest();
$content = $request->getContent();
[ $controller, $methodName ] = $controllers;
$reflectionClass = new \ReflectionClass($controller);
$classAnnotation = $reader
->getClassAnnotation($reflectionClass, Middleware::class);
$reflectionObject = new \ReflectionObject($controller);
$reflectionMethod = $reflectionObject->getMethod($methodName);
$methodAnnotation = $reader
->getMethodAnnotation($reflectionMethod, Middleware::class);
if (!($classAnnotation || $methodAnnotation)) {
return;
}
$middlewareList = $classAnnotation->getMiddleware()['value'];
$middleware = null;
$temp = null;
foreach ($middlewareList as $class){
if ($middleware === null){
$middleware = new $class();
$temp = $middleware;
continue;
}
$class = new $class();
$temp->next($class);
$temp = $class;
}
return $middleware;
}
}
class DumpOneMiddleware implements Middleware
{
/** #var \App\Middleware\Middleware */
private $next;
/** #var \App\Service\ExampleService */
private $exampleService;
public function __construct(ExampleService $exampleService)
{
$this->exampleService = $exampleService;
}
/**
* #param \App\Middleware\Middleware $next
* #return \App\Middleware\Middleware
*/
public function next(Middleware $next)
{
$this->next = $next;
return $next;
}
/**
* #param \Symfony\Component\HttpFoundation\Request $request
*/
public function handle(Request $request)
{
if (!$this->next) {
return true;
}
return $this->next->handle($request);
}
}
Here I can export Container object via ControllerListener (to all middleware classes). But if I want to add another service, how can I do it?
When I examined these and similar topics, I came to the conclusion that I should look at the strategy pattern and Compailer Pass issues. But these topics didn't help me much or I couldn't understand.
I would be very grateful if you could indicate the deficiencies or mistakes in the scenario I applied and show with an example how I can inject Dependecy into the objects I created dynamically.
Related
I have a problem getting a unit test to run for my IndexController class.
The unit test just does the following (inspired from the unit-test tutorial of zf3):
IndexControllerTest.php:
public function testIndexActionCanBeAccessed()
{
$this->dispatch('/', 'GET');
$this->assertResponseStatusCode(200);
$this->assertModuleName('main');
$this->assertControllerName(IndexController::class); // as specified in router's controller name alias
$this->assertControllerClass('IndexController');
$this->assertMatchedRouteName('main');
}
In the Module.php I've some functionality to check if there is a user logged in, else he will be redirected to a login route.
Module.php:
public function onBootstrap(MvcEvent $mvcEvent)
{
/** #var AuthService $authService */
$authService = $mvcEvent->getApplication()->getServiceManager()->get(AuthService::class);
$this->auth = $authService->getAuth(); // returns the Zend AuthenticationService object
// store user and role in global viewmodel
if ($this->auth->hasIdentity()) {
$curUser = $this->auth->getIdentity();
$mvcEvent->getViewModel()->setVariable('curUser', $curUser['system_name']);
$mvcEvent->getViewModel()->setVariable('role', $curUser['role']);
$mvcEvent->getApplication()->getEventManager()->attach(MvcEvent::EVENT_ROUTE, [$this, 'checkPermission']);
} else {
$mvcEvent->getApplication()->getEventManager()->attach(MvcEvent::EVENT_DISPATCH, [$this, 'authRedirect'], 1000);
}
}
The checkPermission method just checks if the user role and the matched route are in the acl storage.
If this fails I will redirect a status code of 404.
Problem: The unit test fails: "Failed asserting response code "200", actual status code is "302"
Therefore the unit test jumps into the else case from my onBootstrap method in the Module.php where the redirect happen.
I did the following setUp in the TestCase but it doesn't work:
public function setUp()
{
// override default configuration values
$configOverrides = [];
$this->setApplicationConfig(ArrayUtils::merge(
include __DIR__ . '/../../../../config/application.config.php',
$configOverrides
));
$user = new Employee();
$user->id = 1;
$user->system_name = 'admin';
$user->role = 'Admin';
$this->authService = $this->prophesize(AuthService::class);
$auth = $this->prophesize(AuthenticationService::class);
$auth->hasIdentity()->willReturn(true);
$auth->getIdentity()->willReturn($user);
$this->authService->getAuth()->willReturn($auth->reveal());
$this->getApplicationServiceLocator()->setAllowOverride(true);
$this->getApplicationServiceLocator()->setService(AuthService::class, $this->authService->reveal());
$this->getApplicationServiceLocator()->setAllowOverride(false);
parent::setUp();
}
Hints are very appreciated
The code might differ a bit from Zend Framework 2 but If you have a simple working example in zf2 maybe I can transform it into zf3 style.
I don't use ZfcUser - just the zend-acl / zend-authentication stuff
After several days of headache I've got a working solution.
First I moved all the code within the onBootstrap to a Listener, because the phpunit mocks are generated after the zf bootstrapping and therefore are non existent in my unit tests.
The key is, that the services are generated in my callable listener method, which is called after zf finished bootstrapping.
Then PHPUnit can override the service with the provided mock.
AuthenticationListener
class AuthenticationListener implements ListenerAggregateInterface
{
use ListenerAggregateTrait;
/**
* #var AuthenticationService
*/
private $auth;
/**
* #var Acl
*/
private $acl;
/**
* Attach one or more listeners
*
* Implementors may add an optional $priority argument; the EventManager
* implementation will pass this to the aggregate.
*
* #param EventManagerInterface $events
* #param int $priority
*
* #return void
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
$this->listeners[] = $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'checkAuthentication']);
}
/**
* #param MvcEvent $event
*/
public function checkAuthentication($event)
{
$this->auth = $event->getApplication()->getServiceManager()->get(AuthenticationService::class);
$aclService = $event->getApplication()->getServiceManager()->get(AclService::class);
$this->acl = $aclService->init();
$event->getViewModel()->setVariable('acl', $this->acl);
if ($this->auth->hasIdentity()) {
$this->checkPermission($event);
} else {
$this->authRedirect($event);
}
}
// checkPermission & authRedirect method
}
Now my onBootstrap got really small, just like ZF wants it. documentation reference
Module.php
public function onBootstrap(MvcEvent $event)
{
$authListener = new AuthenticationListener();
$authListener->attach($event->getApplication()->getEventManager());
}
Finally my mocking in the unit test looks like this:
IndexControllerTest
private function authMock()
{
$mockAuth = $this->getMockBuilder(AuthenticationService::class)->disableOriginalConstructor()->getMock();
$mockAuth->expects($this->any())->method('hasIdentity')->willReturn(true);
$mockAuth->expects($this->any())->method('getIdentity')->willReturn(['id' => 1, 'systemName' => 'admin', 'role' => 'Admin']);
$this->getApplicationServiceLocator()->setAllowOverride(true);
$this->getApplicationServiceLocator()->setService(AuthenticationService::class, $mockAuth);
$this->getApplicationServiceLocator()->setAllowOverride(false);
}
I am using Laravel 4.2 and am trying to get into using phpunit to test my code rather than manually test everything. I have read Jeffrey Way's book 'Laravel Testing Decoded' but I still finding my very first test tricky. The class I am trying to test is below. What I am struggling with is - what should I test?
I don't think I should test the database or the Model $advert as these should have their own tests. In which case I think I need to either mock $advert or create a factory for it but I don't know which.
Any pointers would be greatly appreciated.
EloquentListing.php
<?php
namespace PlaneSaleing\Repo\Listing;
use Illuminate\Database\Eloquent\Model;
class EloquentListing implements ListingInterface {
protected $advert;
public function __construct(Model $advert)
{
$this->advert = $advert;
}
/**
* Get paginated listings
*
* #param int Current page
* #param int Number of listings per page
* #return StdClass object with $items and $totalItems for pagination
*/
public function byPage($page=1, $limit=10)
{
$result = new \StdClass;
$result->page = $page;
$result->limit = $limit;
$result->totalItems = 0;
$result->items = array();
$listings = $this->advert
->orderBy('created_at')
->skip( $limit * ($page-1) )
->take($limit)
->get();
// Create object to return data useful for pagination
$result->items = $listings->all();
$result->totalItems = $this->totalArticles;
return data;
}
/**
* Get total listing count
*
*
*/
protected function totalArticles()
{
return $this->advert->count();
}
}
You have to test every method you have in your class. You have constructor, which should be also tested, to see, if it sets model to your attributes, as well your protected method.
You should mock your model with mockery. It can be installed with
$ composer require mockery/mockery
Then in your test file:
<?php
use Mockery;
use ReflectionClass;
use PlaneSaleing\Repo\Listing\EloquentListing;
class EloquentListingTest extends \TestCase
{
/**
* Testing if __constructor is setting up property
*/
public function testModelSetsUp()
{
$mock = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$listing = new EloquentListing($mock);
$reflection = new ReflectionClass($listing);
// Making your attribute accessible
$property = $reflection->getProperty('advert');
$property->setAccessible(true);
$this->assertInstanceOf(Illuminate\Database\Eloquent\Model::class, $property);
}
/**
* Here you will check if your model is recieving calls
*/
public function testByPage()
{
$mock = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$mock->shouldReceive('orderBy')
->with('created_at')
->once()
->andReturn(Mockery::self())
->shouldReceive('skip')
->with(10)
->once()
->andReturn(Mockery::self())
->shouldReceive('take')
->with(10)
->andReturn(Mockery::self())
->shouldReceive('get')
->once()
->andReturn(Mockery::self())
->shouldReceive('all')
->once()
->andReturn(Mockery::self());
$listing = new EloquentListing($mock);
}
/**
* Here you will see, if your model is receiving call '->count()'
*/
public function testTotalArticles()
{
$mock = Mockery::mock(Illuminate\Database\Eloquent\Model::class);
$mock->shouldReceive('count')
->once()
->andReturn(Mockery::self());
$listing = new EloquentListing($mock);
// We will have to set method accesible
$reflection = new ReflectionClass($listing);
$method = $reflection->getMethod('totalArticles');
$method->setAccessible(true);
$listing->totalArticles();
}
}
I need to do a custom isGranted method (not using Rbac or acl module from community). So I have a service which provides the functionality. But this code:
if (!$this->userService->isGrantedCustom($this->session->offsetGet('cod_lvl'), 'ZF_INV_HOM')) {
throw new \Exception("you_are_not_allowed", 1);
}
...is duplicated in each controller and each action I have. Parameters are changing of course depends on the permission ('ZF_INV_HOM', 'ZF_TODO_DELETE' ...).
I think it's not a bad idea to do this code before the controller is called, but I can't figure what is the best solution (best architecture), and how to pass those parameters to it (I thought about annotation on controllers but how to handle this ?).
The point is, if I have to modify this code I can't imagine to do that hundreds of times, for each controllers, each action I have I need to have this code in one place.
If you don't want to pollute your Module with all this code you can also make a listener class and attach only the listener in your bootstrap method:
<?php
namespace Application\Listener;
use Application\Service\UserService;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\MvcEvent;
use Zend\EventManager\SharedEventManagerInterface;
use Zend\EventManager\SharedListenerAggregateInterface;
use Zend\Authentication\AuthenticationServiceInterface;
class IsAllowedListener implements SharedListenerAggregateInterface
{
/**
* #var AuthenticationServiceInterface
*/
protected $authService;
/**
* #var UserService
*/
protected $userService;
/**
* #var \Zend\Stdlib\CallbackHandler[]
*/
protected $sharedListeners = array();
/**
* #param SharedEventManagerInterface $events
*/
public function attachShared(SharedEventManagerInterface $events)
{
$this->sharedListeners[] = $events->attach(AbstractActionController::class, MvcEvent::EVENT_DISPATCH, array($this, 'isAllowed'), 1000);
}
public function __construct(AuthenticationServiceInterface $authService, UserService $userService ){
$this->authService = $authService;
$this->userService = $userService;
}
/**
* #param MvcEvent $event
*/
protected function isAllowed(MvcEvent $event)
{
$authService = $this->getAuthService();
$identity = $authService->getIdentity();
$userService = $this->getUserService();
if($userService->isGrantedCustom()){
// User is granted we can return
return;
}
// Return not allowed response
}
/**
* #return AuthenticationServiceInterface
*/
public function getAuthService()
{
return $this->authService;
}
/**
* #param AuthenticationServiceInterface $authService
*/
public function setAuthService(AuthenticationServiceInterface $authService)
{
$this->authService = $authService;
}
/**
* #return UserService
*/
public function getUserService()
{
return $this->userService;
}
/**
* #param UserService $userService
*/
public function setUserService(AuthenticationServiceInterface $userService)
{
$this->userService = $userService;
}
}
You need to setup a factory to inject your dependencies:
<?php
namespace Application\Listener;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
* Factory for creating the IsAllowedListener
*/
class IsAllowedListenerFactory implements FactoryInterface
{
/**
* Create the IsAllowedListener
*
* #param ServiceLocatorInterface $serviceLocator
* #return RenderLinksListener
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
$userService = $serviceLocator->get('Application\Service\UserService');
return new IsAllowedListener($authService, $userService );
}
}
And register all this in config:
'service_manager' => array(
'factories' => array(
'Application\Listener\IsAllowedListener' => 'Application\Listener\IsAllowedListenerFactory'
)
)
And then in bootstrap:
public function onBootstrap(EventInterface $event)
{
$application = $event->getTarget();
$serviceManager = $application->getServiceManager();
$eventManager = $application->getEventManager();
$sharedEventManager = $eventManager->getSharedManager();
$isAllowedListener = $serviceManager->get('Application\Listener\IsAllowedListener')
$sharedEventManager->attachAggregate($isAllowedListener);
}
Instead of using AbstractActionController::class, you could also make a specific class, so you will only listen to instances of that class.
So for example AbstractIsAllowedActionController::class or something like that.
By attaching an event listener to the SharedEventManager you can target all controllers and have the authorization check in just one place.
In this case the target is Zend\Mvc\Controller\AbstractActionController which means any controller extending it will execute the listener. The high priority of this listener will mean that it is executed prior to the target controller action, giving you the chance to handle any requests that have not been authorized.
public function onBootstrap(MvcEvent $event)
{
$application = $event->getApplication();
$eventManager = $application->getEventManager()->getSharedManager();
$eventManager->attach(
\Zend\Mvc\Controller\AbstractActionController::class, // Identity of the target controller
MvcEvent::EVENT_DISPATCH,
[$this, 'isAllowed'],
1000 // high priority
);
}
In each controller there would need to be some way that you can determine which 'resource' is being accessed.
As an example it could implement this interface
interface ResourceInterface
{
// Return a unique key representing the resource
public function getResourceId();
}
The listener could then look like this.
public function isAllowed(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
// We need the 'current' user identity
$authService = $serviceManager->get('Zend\Authentication\AuthenticationService');
$identity = $authService->getIdentity();
// The service that performs the authorization
$userService = $serviceManager->get('MyModule\Service\UserService');
// The target controller is itself a resource (the thing we want to access)
// in this example it returns an resource id so we know what we want to access
// but you could also get this 'id' from the request or config etc
$controller = $event->getTarget();
if ($controller instanceof ResourceInterface) {
$resourceName = $controller->getResourceId();
// Test the authorization, is UserX allowed resource ID Y
if (empty($resourceName) || $userService->isGrantedCustom($identity, $resourceName)) {
// early exit for success
return;
} else {
// Denied; perhaps trigger a new custom event or return a response
}
}
}
I have a class that controls an external API that I use in several projects (simplified example):
class PaymentModule
{
public function doPayment($customer_name, $currency, $language)
{
curl_setopt(POSTFIELDS, array($customer_name, $currence, $language));
curl_exec();
}
}
Now in a specific project I would like to "wrap" it and provide sensible defaults for a lot of parameters that I don't use here.
So I thought, I will just extend this class, have my IDE override all methods and then remove the parameters that I don't use, like this:
class MyPaymentModule extends PaymentModule
{
public function doPayment($customer_name)
{
$language = get_current_language();
parent::doPayment($customer_name, 'EUR', $language);
}
}
As I now learned (thanks to PHP strict standards), this violates the Liskov substitution principle, i.e. in OOP in general MyPaymentModule is expected to have the same interface as PaymentModule which in turn means MyPaymentModule::doPayment() is expected to have the same parameters as PaymentModule::doPayment().
I think it's not too uncommon that you want to create a class that provides sensible defaults to another one, so is there any common pattern to use here?
Of course I could go for two completely independent classes, but I would prefer a solution that still hints at the relationship between the two classes... after all they will always have the same methods, just one with less parameters.
Thats not possible with your current class design.
I would suggest to create a new class PaymentOptions which handles currency and language.
Passing the options to doPayment either with a new method setPaymentOptions or as an optional parameter for doPayment.
Something like this should work
class PaymentModule {
/**
* #var PaymentOptions
*/
private $paymentOptions = null;
function doPayment($customer_name, PaymentOptions $options = null) {
if ($options == null) {
$options = $this->paymentOptions;
}
if ($options == null) {
throw new Exception('...');
}
//...
}
/**
* #return PaymentOptions
*/
public function getPaymentOptions()
{
return $this->paymentOptions;
}
/**
* #param PaymentOptions $paymentOptions
*/
public function setPaymentOptions($paymentOptions)
{
$this->paymentOptions = $paymentOptions;
}
}
class MyPaymentModule extends PaymentModule {
function doPayment($customer_name, PaymentOptions $options = null)
{
//...
}
}
class PaymentOptions {
private $currency;
private $language;
/**
* #return mixed
*/
public function getCurrency()
{
return $this->currency;
}
/**
* #param mixed $currency
*/
public function setCurrency($currency)
{
$this->currency = $currency;
}
/**
* #return mixed
*/
public function getLanguage()
{
return $this->language;
}
/**
* #param mixed $language
*/
public function setLanguage($language)
{
$this->language = $language;
}
}
show me please example of the polymorphic sigleton in php.
google found very few mentions about this and all in C which is not clear to me
Here you go:
/**
* Base class for Polymorphic Singleton.
*/
class Polymorphic_Singleton {
/**
* #var Polymorphic_Singleton
*/
private static $__instance = null;
private static $__instance_class_name = "";
/**
* Gets an instance of Polymorphic_Singleton/
*
* Uses a Polymorphic Singleton design pattern. Maybe an anti-pattern. Discuss amongst yourselves.
*
* Can be called by a subclass, and will deliver an instance of that subclass, which most Singletons don't do.
* You gotta love PHP.
*
* #return Polymorphic_Singleton
*/
final public static function get_instance(){
if ( ! isset( self::$__instance ) ){
self::$__instance_class_name = get_called_class();
self::$__instance = new self::$__instance_class_name(); // call the constructor of the subclass
}
return self::$__instance;
}
/**
* Don't ever call this directly. Always use Polymorphic_Singleton ::get_instance()
*
* #throws Exception if called directly
*/
public function __construct(){
if ( ! self::$__instance ){
// do constructor stuff here...
} else {
throw new Exception( "Use 'Polymorphic_Singleton ::get_instance()' to get a reference to this class.", 100 );
}
}
}
I don't clearly understand what you meant by "polymorphic singleton". The singleton pattern itself has been explained in many places out there, for instance:
http://en.wikipedia.org/wiki/Singleton_pattern
In PHP, you can achieve singleton pattern using this simple code:
<?php
class Something
{
private static $instance;
private function __construct() {}
private function __clone() {}
public static function getInstance()
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
}
// how to use
$instance1 = Something::getInstance();
$instance2 = Something::getInstance();
var_dump($instance1 === $instance2); // returns true proving that getInstance returns identical instance