Into the ZF2 Service Manager

Post on 22-Jan-2018

1.089 views 0 download

Transcript of Into the ZF2 Service Manager

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  chris@ctankersley.com  

@dragonmantank    

hBp://joind.in/talk/view/15534  

ZendCon  2015   44