Upload
chris-tankersley
View
1.089
Download
0
Embed Size (px)
Citation preview
Into the ZF2 Service Manager Chris Tankersley ZendCon 2015
ZendCon 2015 1
Who Am I
• PHP Programmer for over 10 years • Been using ZF since ~0.5 • Daily ZF2 user • hBps://github.com/dragonmantank
ZendCon 2015 2
A running joke…
• “Symfony2 is a PHP script that turns YAML into applicaNons” • “Yeah, but Zend Framework 2 is a PHP script that turns arrays into applicaNons”
ZendCon 2015 3
ZendCon 2015 4
It kind of is…
ZendCon 2015 5
return [ 'service_manager' => [ 'initializers' => [ 'Job\Service\JobServiceAwareInitializer' ], 'invokables' => [ 'Job\ProcessorListener' => 'Job\Processor\ProcessorListener', 'Job\ServiceAccountListener' => 'Job\Service\JobServiceListener’ ], 'factories' => [ 'Job\Service' => 'Job\Service\JobServiceFactory', 'Job\Processor' => 'Job\Processor\ProcessorFactory', ] ] ]
Theater Magic
• When something awesome happens, but it’s really something mundane controlling it in the background • When it looks good from 25’
ZendCon 2015 6
The Service Manager
ZendCon 2015 7
Service Locator
• Container for storing objects or condiNons for building an object • Mostly it’s used to create objects • It does not do automaNc dependency injecNon • It is not global (unless you make it global)
ZendCon 2015 8
What can we store in there?
• Invokables • Factories • Abstract Factories • Delegators • IniNalizers • Aliases
ZendCon 2015 9
Standalone
$serviceManager = new Zend\ServiceManager\ServiceManager(); $serviceManager-‐>setService(‘MyService’, $myService); $serviceManager-‐>has(‘MyService’); $service = $serviceManager-‐>get(‘MyService’);
ZendCon 2015 10
Built into ZF2 full-stack applications
• Anything that is ServiceLocatorAwareInterface can have it injected • Controllers are the most common
ZendCon 2015 11
namespace Application\Controller; use Zend\Mvc\Controller\AbstractActionController; class IndexController extends AbstractActionController { public function indexAction() { $model = $this-‐>getServiceLocator()-‐>get(‘My\Model’); } }
Stuff we can get from the Service Manager
ZendCon 2015 12
Invokables
• Any sort of object that can be declared with ‘new’ and has no constructor parameters
ZendCon 2015 13
class MyClass { public function foo() { ... } public function bar() { ... } } class SimpleConstructor { protected $name; public function __construct() { $this-‐>name = 'baz'; } }
In ZF2 return [
'service_manager' => [ 'invokables' => [ 'Job\Processor\ProcessorListener' => 'Job\Processor\ProcessorListener', 'Job\ServiceAccountListener' => 'Job\Service\JobServiceListener' ],
]
]
ZendCon 2015 14
Quick Dependency Injection Tutorial
ZendCon 2015 15
What is Dependency Injection?
• InjecNng dependencies into classes, instead of having the class create it • Allows for much easier tesNng • Allows for a much easier Nme swapping out code • Reduces the coupling that happens between classes
php[tek] 2015 16
Method Injection class MapService { public function getLatLong(GoogleMaps $map, $street, $city, $state) {
return $map-‐>getLatLong($street . ' ' . $city . ' ' . $state); }
public function getAddress(GoogleMaps $map, $lat, $long) { return $map-‐>getAddress($lat, $long);
} }
php[tek] 2015 17
Constructor Injection class MapService { protected $map; public function __construct(GoogleMaps $map) { $this-‐>map = $map; } public function getLatLong($street, $city, $state) { return $this -‐>map -‐>getLatLong($street . ' ' . $city . ' ' . $state); } }
php[tek] 2015 18
Setter Injection class MapService { protected $map; public function setMap(GoogleMaps $map) { $this-‐>map = $map; } public function getMap() { return $this-‐>map; } public function getLatLong($street, $city, $state) { return $this-‐>getMap()-‐>getLatLong($street . ' ' . $city . ' ' . $state); } }
php[tek] 2015 19
Back to the show
ZendCon 2015 20
Factories
• A Factory is an object/method is that is used to create other objects • ZF2 Service Manager will call the factory when an object is pulled out of the Service Manager
ZendCon 2015 21
Why do we need factories?
Dependencies
ZendCon 2015 22
In ZF2
ZendCon 2015 23
return [ 'service_manager' => [ ’factories' => [ 'Google\AdWords\AdWordsUserBuilder' => 'Google\AdWords\GoogleAdWordsUserBuilderFactory', ], ] ]
ZendCon 2015 24
namespace Google\AdWords; class GoogleAdWordsUserBuilder { public function __construct(array $config) { $this-‐>config = $config; } }
ZendCon 2015 25
namespace Google\AdWords; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class GoogleAdWordsUserBuilderFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $service) { $config = $service-‐>get('Config'); return new GoogleAdWordsUserBuilder($config[‘google’]); } }
Abstract Factories
• They are factories, but they allow the creaNon of a broad range of objects instead of a single object • The factory will take addiNonal configuraNon to properly create the needed object
ZendCon 2015 26
ZendCon 2015 27
<?php namespace MyProject; use Zend\Db\TableGateway\TableGateway; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class ProjectsTableFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { $adapter = new $serviceLocator-‐>get('Zend\Db\Adapter\Adapter'); return new TableGateway('projects', $adapter); } } class CategoriesTableFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { $adapter = new $serviceLocator-‐>get('Zend\Db\Adapter\Adapter'); return new TableGateway('categories', $adapter); } }
In ZF2
ZendCon 2015 28
return [ ’abstract_factories' => [ ’MyProject\TableAbstractFactory’ ] ]
ZendCon 2015 29
<?php namespace MyProject; use Zend\Db\TableGateway\TableGateway; use Zend\ServiceManager\AbstractFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class TableAbstractFactory implements AbstractFactoryInterface { public function canCreateServiceWithName(ServiceLocatorInterface $sl, $name, $requestedName) { return preg_match("/Table$/", $requestedName); } public function createServiceWithName(ServiceLocatorInterface $sl, $name, $requestedName) { $adapter = $sl-‐>get('Zend\Db\Adapter\Adapter'); $tableName = str_replace('Table', '', $requestedName); $tableName = strtolower($tableName); return new TableGateway($tableName, $adapter); } }
Initializers
• Code that needs to run ajer an object is created • Really helpful for when lots of objects need addiNonal objects (like loggers) all across the applicaNon
ZendCon 2015 30
In ZF2
ZendCon 2015 31
return [ ’initalizers' => [ ’Job\Service\JobServiceAwareInitializer’ ] ]
ZendCon 2015 32
<?php namespace Job\Service; use Zend\ServiceManager\InitializerInterface; use Zend\ServiceManager\ServiceLocatorAwareInterface; use Zend\ServiceManager\ServiceLocatorInterface; class JobServiceAwareInitializer implements InitializerInterface { public function initialize($instance, ServiceLocatorInterface $serviceLocator) { if (!$instance instanceof JobServiceAwareInterface) { return null; } /** @var \Job\Service\JobService $jobService */ $jobService = $serviceLocator-‐>get('Job\Service'); $instance-‐>setJobService($jobService); } }
Delegators
• They are actually decorators for objects that don’t exist • They are like iniNalizers that only run on a specific key • Allow your applicaNon to tweak or modify 3rd party objects without having to extend them
ZendCon 2015 33
In ZF2
ZendCon 2015 34
return [ ’delgators' => [ ’OtherVendor\Account\AccountService’ => [ ‘MyProject\Account\AccountServiceDelegator’, ] ] ]
ZendCon 2015 35
<?php namespace MyProject; use Zend\ServiceManager\DelegatorFactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; class AccountServiceDelegator implements DelegatorFactoryInterface { public function createDelegatorWithName(ServiceLocatorInterface $sl, $name, $requestedName, $callback) { $originalService = $callback(); $accountService = new MyProject\Account\AccountService($originalService); return $accountService; } }
Aliases
• Just another name for some other key in the service manager
ZendCon 2015 36
In ZF2
ZendCon 2015 37
return [ ’aliases' => [ ’MyProject\Account\OldAccountService’ => ‘MyProject\Account\NewAccountService’, ] ]
Bad Practices
ZendCon 2015 38
Lots of Initializers
• IniNalizers run ajer every object is created • 30 iniNalizers * 50 objects created at runNme = 1500 invocaNons
• Look at using Factories instead to inject things into your objects • Look at using Delegators
ZendCon 2015 39
Functions as Factories
• The Factory system will actually take any invokable thing as a factory • That means you can use closures and anonymous classes
ZendCon 2015 40
ZendCon 2015 41
return [ 'factories' => array( 'CategoryService' => function($sm) { $categoryService = new CategoryService(); $categoryService-‐>setCategoryTable($sm-‐>get('CategoryTable')); return $categoryService; }, 'CategoryTable' => function($sm) { $tableGateway = $sm-‐>get('CategoryTableGateway'); $table = new CategoryTable($tableGateway); return $table; }, 'CategoryTableGateway' => function($sm) { $dbAdapter = $sm-‐>get('Zend\Db\Adapter\Adapter'); $resultSetPrototype = new ResultSet(); $resultSetPrototype-‐>setArrayObjectPrototype(new Category()); return new TableGateway('category', $dbAdapter, null, $resultSetPrototype); }, ]
Functions as Factories
• This makes the config uncachable, so there are performance issues
ZendCon 2015 42
Questions?
ZendCon 2015 43
Thank You!
hBp://ctankersley.com [email protected]
@dragonmantank
hBp://joind.in/talk/view/15534
ZendCon 2015 44