Interface-based DI configuration in Symfony - php

In my Symfony project I'll have lot of classes that have similar dependecies (however, the classes are not directly related to each other). For example, most of them requires access to EventBus.
In other framework I was able to specify an interface for the class, for example:
interface EventBusAwareInterface
{
public setEventBus(EventBus $bus);
public getEventBus() : EventBus
}
and then configure DI container to recognize such objects that implements this interface, and call their setEventBus() method with proper argument.
I wonder if there's a method to do the same in Symfony4.

You can use _instanceof directive in your services.yaml like that:
services:
_instanceof:
App\EventBusAwareInterface:
calls:
- method: setEventBus
arguments:
- '#event.bus.service'

My original comment was not quire correct. You can use #inject but it seems to require an additional jms bundle. Could have sworn the container supported it out of the box but I guess not.
However, autowire supports a #required annotation which seems to do the trick.
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
trait RouterTrait
{
/** #var RouterInterface */
protected $router;
/**
* #param RouterInterface $router
* #required
*/
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
// Copied directly from Symfony ControllerTrait
protected function generateUrl(
string $route,
array $parameters = array(),
int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
return $this->router->generate($route, $parameters, $referenceType);
}
protected function redirect($url, $status = 302) : RedirectResponse
{
return new RedirectResponse($url, $status);
}
protected function redirectToRoute($route, array $parameters = array(), $status = 302) : RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
}
Now, any autowired service that uses the RouterTrait will automatically get the router injected.

Yes, something even simpler is very possible. However, I would not encourage over-usage as it can very quickly introduce things like method name collisions and reduce code readability.
That said, Symfony introduced service auto-wiring concept starting with 3.3 (I think), which can be used to have dependency injection with zero-config. In PHP, interfaces cannot contain implementation, but, traits can. So, you could do something like this:
trait FooTraitHandler
{
/**
* #var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
And then:
class RealService
{
use FooTraitHandler;
public function multiply($a, $b)
{
$this->logger->log(LogLevel::ALERT, "Doing some basic math!");
return $a * $b;
}
}
And finally, for example, your controller could inject this RealService service and use multiply method as usual.
So, couple of things worth mentioning:
You do not need pair of getter/setter - trait's member is visible in class utilizing it.
You can utilize many traits, achieving just what you wanted with many interfaces
Finally, if some of utilized traits have methods with same name, you'll get a fatal error. As per official docs:
If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved. To resolve naming conflicts between Traits used in the same class, the insteadof operator needs to be used to choose exactly one of the conflicting methods.
But, in my opinion, doing so deteriorates code readability substantially.
Hope this helps...

Related

Syfmony - load service on boot

I posted another question trying to find a way to statically access a repository class outside of a controller in a custom "helper" class.
So far the only way I have figured out how to achieve this is using the code below. If anyone wants to chime into the other question about "best practice" or "design patterns" please do.
I opened this question to seek the best method on having a singleton service (?) loaded when symfony boots so other classes can access it statically without any dependency injection. I haven't had much luck on finding any official docs or common practices. I know singleton is anti practice, but is the method below the best way, or is there a more ideal solution?
services.yml
parameters:
entity.device: Asterisk\DbBundle\Entity\Device
services:
asterisk.repository.device:
class: Asterisk\DbBundle\Entity\Repositories\DeviceRepository
factory: ["#doctrine.orm.asterisk_entity_manager", getRepository]
arguments:
- %entity.device%
tags:
- {name: kernel.event_listener, event: kernel.request, method: onKernelRequest}
DeviceRepository
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public function __construct(EntityManager $entityManager, ClassMetadata $class)
{
parent::__construct($entityManager, $class);
if(static::$instance instanceof static == false)
static::$instance = $this;
}
public static function getInstance()
{
return static::$instance;
}
public function onKernelRequest($event)
{
return;
}
}
Glad to see you are not running around anymore.
Your approach is not going to work unless someone grabs the repository out of the container first so self::$instance is initialized. But you really don't want to do this anyways. Super hacky.
You want to inject the repository service into your kernel listener. Trying to make the repository act as a kernel listener is just not a good design. So just make a service for your repository and then a second one for the listener. It may seem a bit strange at first but it really does work well in practice and it's the way S2 is designed.
If for some reason you are stuck with the notion that you have to be able to access the container globally then be aware that your kernel is defined globally(take a look at app.php) and it has a getContainer method in it.
$repo = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
But again, there should be no need to do this.
==============================
Update - It looks like you are trying to use the listener functionality just to setup singletons. You should try to avoid singletons but if you really think you need them then the global access to the kernel can be used:
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public static function getInstance()
{
if (!static::$instance) {
static::$instance = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
}
return static::$instance;
}
Poor design but at least it get's rid of the listener hack and it avoids creating the repository until it's actually needed. It aslo means you can access the repository from commands (listeners are not setup when commands are called).
I do not understand what the profit will be about this method. The idea of the servicecontainer is to make just one instance of each class and give a reference (or pointer if you like) to any method who asks to use this same instance. Let me proof it:
Service definition:
// app/config.yml
services:
app.test:
class: Vendor\AppBundle\Service\Test
and a custom class:
// src/AppBundle/Service/Test.php
namespace AppBundle/Service;
class Test {
public $test = 0;
}
and a controller:
// src/AppBundle/Controller/DefaultController
namespace AppBundle/Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction()
{
$instance1 = $this->get('app.test');
$instance2 = $this->get('app.test');
$instance1->test = 1;
echo $instance2->test; // RETURNS 1 !!!
exit;
}

Laravel : Dependency injection vs Facades?

What, I had been doing previously was to inject only MY MODELS using the constructor and use Facades for the Laravel's provided classes i.e. Session, Auth, Validator etc, for example. Will it be a good idea if I inject each and every class (either mine or Laravel's) through construct and use it by $this->.. syntax or should I inject my own classes using constructor and use Facades for anything provided by Laravel?
To be more specific, here is what my controllers normally look like:
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark ) {
$this->user = $user;
$this->bookmark = $bookmark
}
public function foobar ( ) {
$user_id = Input::get('bar');
...
Session::get('someInfo');
...
return Redirect::to('/');
}
...
}
Should I structure my methods like controller like following, instead?
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark, Input $input, Session $session, Redirect $redirect ) {
$this->user = $user;
$this->bookmark = $bookmark
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Laravel now supports the same Dependency-Injection functionality for route-related methods of classes (not just constructors), such as controllers and middleware.
You could prevent unnecessary injections by only injecting to methods where the dependency is unique, perhaps leaving more common dependencies in the constructor:
class MyController extends BaseController
{
public function __construct( Input $input, Session $session, Redirect $redirect ) {
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( User $user, Bookmark $bookmark ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Conclusion
As for whether you should do it this way, that's up to you, but consider to:
First, use Dependency-Injection vs Facade (which also enables IDE auto-completion).
Then, wherever dependency is unique (and not required by most routes), use per-method injection vs constructor.
Because making all unique dependencies appear in method definition is easier to unit-test (and seems cleaner to me).
It is elegant and useful to inject certain classes, such as Request. In my opinion they should be specified in controller methods where they are needed, as they are then logically connected to the method implementation. Awesome thus far.
I find two facades to be problemmatic - App and Log. Neither are logically connected to a controller or its actions. App and Log are not inputs in any context. As App and Log are utility classes they are relevant to services and repositories as well, and it gets downright nasty if you type hint them in controllers and then pass them on as constructor or method parameters to your support classes.
An additional issue is that App facade does not implement the Illuminate\Contracts\Auth\Guard interface that it proxies, so my IDE lights up with warnings as static analysis is not possible.
For the sake of consistency and overall separation of concerns I would therefore instantiate both App and Log within a constructor or method, depending on how widespread they are used in a class. To make my IDE happy I created the below class to give me a properly typed instance wherever I need it:
<?php namespace App\Components;
use Illuminate\Contracts\Auth\Guard;
use Psr\Log\LoggerInterface;
/**
* Get the underlying object instances of facades from the container.
*/
class AppGlobal
{
/**
* Returns the global logger instance.
*
* #return LoggerInterface
*/
public static function log()
{
return app('log');
}
/**
* Returns the global auth instance, which internally proxies a guard.
*
* #return Guard
*/
public static function auth()
{
return app('auth');
}
}
If you need an object wit properties - put it in as an injection (e.g Input, Session...), otherwise, if you don't store any data in the object and pretty happy using class, than go with facades (e.g Log::..., Redirect::...).
Laravel has replaced many of it's facade with helpers for example
use Auth;
and
Auth::user()
is now just
auth()->user()
this makes thing simpler and neater (also prevents mistakes)
I would suggest using the helpers where possible and if no helper exists, use the facade because it is easier to mock than an injected instance.

Is it possible to define method with different parameters in a PHP interface?

I'm developing a service that is being injected a Logger object but I can have 2 different kind of loggers, I'm planning on having a syslog logger and a queue message system logger. Is this possible?
The idea is having an interface:
interface Loggable
{
public function log() ;
}
and 2 classes that implement that interface:
class Syslogger implements Loggable
{
public function log()
{
...
}
}
class QMSLogger implements Loggable
{
public function log($queueName)
{
...
}
}
The only way I could come with is having an array as a parameter and use it on one class and not using on the other one... but that is a little bit smelly :P
You're asking if it's possible: yes it is, but…
If you implement an interface, you must respect its contract.
interface Loggable
{
public function log();
}
This interface's contract is you can call log() without any parameter.
In order to respect that, you can make the parameter optional:
class QMSLogger implements Loggable
{
public function log($queueName = null)
{
...
}
}
This is perfectly valid PHP and it respects the Liskov Substitution Principle. Of course, you must not use that optional parameter when coding against the interface, else you are obviously breaking the interface. Such parameter can be useful only when you are using the implementation (e.g. in some part of the code which is tightly coupled to the QMSLogger).
However this is probably not the solution to your problem as $queueName seems to be a configuration value and it might be better to pass it in the class' constructor (as explained in the other answer).
As stated in the comments, that's not the same interface. If you cannot generalize the interface across all possible logger implementations, make the configuration differences part of the instance constructor:
class QMSLogger implements Loggable {
protected $queueName;
public function __construct($queueName) {
$this->queueName = $queueName;
}
public function log() {
...
}
}
I came across a similar case where I wanted to create an interface that simply ensure any classes that implemented it would have a method of the same name, but would allow for implementation with different parameters.
/**
* Interface Loggable
* #method log
*/
interface Loggable
{
}
Now the Loggable interface can be implemented with different parameters like so.
class Syslogger implements Loggable
{
public function log($key, $value)
{
...
}
}
You can also pass the parameters as an array , in this way you respect the contract from one hand and also be flexible to insert any values with any amount inside the array , check this out :
abstract class FeaturesAbstract
{
/**
* #param array $paramsArray
*
* #return mixed
*/
abstract public function addExecute($paramsArray);
}
And to actually use this method you could send the parameters like this :
$this->abstract->addExecute(array('paramA' => $paramA, 'paramB' => $paramB));
And then inside the concrete implementation you get the parameters like this :
/**
* #param array $paramsArray
*
* #return void
*/
public function addExecute($paramsArray)
{
$a = $paramsArray['paramA'];
$b = $paramsArray['paramB'];
$c = ...
}
Good luck :)

Constructor-Injection when extending a class

This question is not strictly related to Symfony 2, but as I use Symfony 2 components and will later likely use a Symfony\Component\DependencyInjection\Container as DI-Container, it might be relevant.
I am currently building a small library using components from Symfony 2, e.g. HttpFoundation, Validator, Yaml. My Domain Services are all extending a basic AbstractService providing nothing but Doctrine\ORM\EntityManager and Symfony\Component\Validator\Validator via Constructor-Injection like this:
abstract class AbstractService
{
protected $em;
protected $validator;
/**
* #param Doctrine\ORM\EntityManager $em
* #param Symfony\Component\Validator\Validator $validator
*/
public function __construct(EntityManager $em, Validator $validator)
{
$this->em = $em;
$this->validator = $validator;
}
}
A Service-class extending this AbstractService may now need to inject additonal components, like Symfony\Component\HttpFoundation\Session. As of I do it like this:
class MyService extends AbstractService
{
/**
* #var Symfony\Component\HttpFoundation\Session
*/
protected $session;
/**
* #param Symfony\Component\HttpFoundation\Session $session
* #param Doctrine\ORM\EntityManager $em
* #param Symfony\Component\Validator\Validator $validator
*/
public function __construct(Session $session, EntityManager $em, Validator $validator)
{
parent::__construct($em, $validator);
$this->session = $session;
}
}
Is there a more elegant way to solve this without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
As I see it, when I use Setter-Injection for Session, I have to add checks before accessing it in my methods, whether it is already injected, which I want to avoid. On the other hand I don't want to "repeat" injecting the basic components shared by all services.
An alternative would be not to extend your Services from the abstract type. Change the abstract type to a real class and then inject it to your Services, e.g.
class MyService
…
public function __construct(ServiceHelper $serviceHelper)
{
$this->serviceHelper = $serviceHelper;
}
}
Yet another option would be to not pass the dependencies until they are needed, e.g.
class MyService
…
public function somethingRequiringEntityManager($entityManager)
{
// do something with EntityManager and members of MyService
}
}
Is there a more elegant way to solve this without having to reiterate the parent's constructor arguments, e.g. by using Setter-Injection for Session instead?
Well, by using setter-injection like you've mentioned. Apart from that I see two possible solutions to your immediate problem:
Make the parent constructor accept a Session object as well, but make it null by default. This to me is ugly, since the parent doesn't actually need the session object, and if you have other implementations, this will result in a long parameter list for the parent constructor, so this is actually not really an option.
Pass in a more abstract object. Be it a ParameterObject for that specific type of object, or just a simple Registry, this should work. Again; not preferable and as you're using dependency injection, you probably already know why.
I do have to ask though; where's the harm in using your current way? I don't actually see the downside. I'd just go ahead and carry on. If you discover you're using even more parameters in the constructor, think about what the service's intentions are and why it needs it, chances are that particular pieces of behaviour can be moved to the objects you're requesting instead.
Well you could use singleton for the Session class and access it's instance anywhere, but that might not be an option if you want to restrict it's usage.
But then again Session::getInstance()->getStuff seem's pretty much the same as $this->session->getStuff so to answer your question - I don't think there's much of a choice here and you approach seems fine.
Oh and like Gordon said: you shouldn't change the order of the arguments.
I went through the same problem while developing my Abstract Controller Bundle — when controllers are defined as services — and ended up using the setter injection in the base class.

Testing objects with dependencies in PHPUnit

For objects which compose another object as part of their implementation, what's the best way to write the unit test so only the principle object gets tested? Trivial example:
class myObj {
public function doSomethingWhichIsLogged()
{
// ...
$logger = new logger('/tmp/log.txt');
$logger->info('some message');
// ...
}
}
I know that the object could be designed so that the logger object dependency could be injected and hence mocked in a unit test, but that's not always the case - in more complicated scenarios, you do need to compose other objects or make calls to static methods.
As we don't want to test the logger object, only the myObj, how do we proceed? Do we create a stubbed "double" with the test script? Something like:
class logger
{
public function __construct($filepath) {}
public function info($message) {}
}
class TestMyObj extends PHPUnit_Framework_TestCase
{
// ...
}
This seems feasible for small objects but would be a pain for more complicated APIs where the SUT depended on the return values. Also, what if you want to test the calls to the dependency object in the same was you can with mock objects? Is there a way of mocking objects which are instantiated by the SUT rather than being passed in?
I've read the man page on mocks but it doesn't seem to cover this situation where the dependency is composed rather than aggregated. How do you do it?
Following troelskn advise here's a basic example of what you should do.
<?php
class MyObj
{
/**
* #var LoggerInterface
*/
protected $_logger;
public function doSomethingWhichIsLogged()
{
// ...
$this->getLogger()->info('some message');
// ...
}
public function setLogger(LoggerInterface $logger)
{
$this->_logger = $logger;
}
public function getLogger()
{
return $this->_logger;
}
}
class MyObjText extends PHPUnit_Framework_TestCase
{
/**
* #var MyObj
*/
protected $_myObj;
public function setUp()
{
$this->_myObj = new MyObj;
}
public function testDoSomethingWhichIsLogged()
{
$mockedMethods = array('info');
$mock = $this->getMock('LoggerInterface', $mockedMethods);
$mock->expects($this->any())
->method('info')
->will($this->returnValue(null));
$this->_myObj->setLogger($mock);
// do your testing
}
}
More information about mock objects can be found in the manual.
As you seem to be aware already, Concrete Class Dependencies makes testing hard (or outright impossible). You need to decouple that dependency. A simple change, that doesn't break the existing API, is to default to the current behaviour, but provide a hook to override it. There are a number of ways that this could be implemented.
Some languages have tools that can inject mock classes into code, but I don't know of anything like this for PHP. In most cases, you would probably be better off refactoring your code anyway.
Looks like I misunderstood the question, let me try again:
You should use the singleton pattern or a factory for the logger, if it's not too late already:
class LoggerStub extends Logger {
public function info() {}
}
Logger::setInstance(new LoggerStub());
...
$logger = Logger::getInstance();
If you can't change the code, you could use a catch-all class that is overloading __call()
class GenericStub {
public function __call($functionName, $arguments) {}
}
There is actually a reasonably new extension for PHP class overloading released by the same guys that build PHPUnit. It lets you override the new operator in cases where you can't refactor the code, unfortunately it isn't that simple to install on Windows.
The URL is http://github.com/johannes/php-test-helpers/blob/master/

Categories