Dependency Injection
 Dependency Inversion Principle
Highly Coupled Code is Bad
Some coupling is unavoidable
Decoupled Code is Good
Easier to test
Easier to debug
Easier to reason about
Common Dependencies
Infrastructure - eg Database, third party module
Utility objects - eg Logger
Environment - eg FileSystem, system clock
Static Method calls - eg DataAccess::saveUser($u)
“Current” Dependencies
Request/Response
Environment variables
User details
Session details
Time
Spot the dependencies
class OrderProcessor {

function __construct() {

$this->orderRepository = new MysqlOrderRepository();

}
function completeOrder($order) {

  global $logger;

$order->completedAt = new DateTimeImmutable;

$logger->log("Order {$order->id} is complete");

$this->orderRepository->save($order);

Mailer::sendOrderCompleteEmail($order);

}

}
Spot the dependencies
class OrderProcessor {

function __construct() {

$this->orderRepository = new MysqlOrderRepository();

}
function completeOrder($order) {

  global $logger;

$order->completedAt = new DateTimeImmutable;

$logger->log("Order {$order->id} is complete");

$this->orderRepository->save($order);

Mailer::sendOrderCompleteEmail($order);

}

}
Hard Questions
Q: What does OrderProcessor depend on?

A: Read the entire class to find out!
Q: Where is Mailer used in my application?

A: Grep everything in your project for “Mailer::".
Q: How can I test completeOrder() without sending emails?

A: ¯_(ツ)_/¯ *
* Yes, there are workarounds, but this isn't a testing talk.
What is Dependency Injection?
Dependency injection is the practice of pushing (injecting) dependencies
into an object, rather than having objects find their dependencies on their
own.
This isn't the same as the Dependency Inversion Principle in SOLID.
We'll get to that later.
DI Techniques
Constructor Injection
Setter or Property Injection
Parameter Injection
Constructor Injection
class ClassThatNeedsDatabase

{

private $db;
public function __construct(Database $db) {

$this->db = $db;

}

}
Pros
Makes dependencies explicit
Can’t modify dependencies after instantiation
Discourages violation of Single Responsibility Principle

CONS
May have a lot of constructor parameters
May never use most dependencies
Could be the wrong point in the object life cycle
Many Constructor Parameters
public function __construct(

Twig $view,

AuthorizationServer $auth,

LogRepository $errorRepo,

AuthRequestRepository $authRequestRepo,

  ClientRepository $clientRepo,

UserRepository $userRepo,

ResourceServer $resAuth,

AccessTokenRepository $accessTokenRepo,

UserValidatorInterface $validator) 

{/* a whole bunch of property assigns */}
Antipattern #1: __construct($options)
"Just throw everything in an $options array!"
Setter Injection
class ClassThatUsesLoggingButDoesNotRequireIt

{

private $logger = null;
public function setLogger(Logger $logger) {
$this->logger = $logger;
}

}
Property Injection
class ClassThatMightWantAThingCalledALogger

{

public $logger = null;

}
class SlightlySaferClassThatWantsALogger

{

public Logger $logger = null;

} // PHP 7.4+
Pros
Lazier loading
Works well with optional dependencies
Flexible across class lifecycle
Don’t need every dependency for every method call
Can change dependencies without re-instantiating the class
Cons
Harder to quickly see which dependencies a class has
Existence of dependencies in fully instantiated class not guaranteed
Null checks inside the code
Conditional injection outside the code
Checking Property
public function fileTPSReport(Report $rpt) {

/* do some stuff */

if ($this->logger) {

$this->logger->log('Did a thing');

}
/* do more stuff */

if ($this->logger) {

$this->logger->log('Did things');

}

}
Null Property
public function fileTPSReport(Report $rpt) {

/* do some stuff */

$this->logger->log('Did a thing’);
/* do more stuff */

$this->logger->log('Did things');

}
class NullLogger implements Logger {

public function log($message) {

/** noop **/

}

}
DON'T IMPLEMENT THIS NULL
LOGGER...
...BECAUSE THERE'S ONE BUILT INTO PSR/LOG
TRAITS + SETTER INJECTION
Add setter to trait
Import trait into classes
Implement interface on classes
Configure DI container to setter-inject based on interface
e.g. PsrLog{LoggerAwareInterface, LoggerAwareTrait}
Sorry, you can't have an interface implement a trait directly.
Parameter Injection
public function __invoke(

SlimHttpRequest $request,

SlimHttpResponse $response,

array $args = [])

{
/** do something, return a Response **/
}
Parameter Injection
public function orderAction(

IlluminateHttpRequest $request,

AppServicesOrderService $orderService)

{
/** do something, return a Response **/
}
Pros
Does not clutter your object with refs to collaborators which are not needed

CONS
Almost everything else about it
Moves the problem of dependency management to the caller
Mix & Match as Needed
Dependency Inversion Principle
High level modules should not depend on low level modules; both should
depend on abstractions.
Abstractions should not depend on details.  Details should depend upon
abstractions.
Tl;dr: use, and expose, interfaces with just enough functionality to get the job
done.
Abstrations Should Not Be Leaky
class Camry implements HasGasPedal {
public function pressAccelerator();
} // namespace ToyotaVehicles
class Model3 implements HasGasPedal {
public function pressAccelerator();
} // namespace TeslaVehicles
Abstrations Should Not Be Leaky
class MysqlUserRepo implements UserRepository {

public function getById(int $id): ?User {}

}
class ElasticUserRepo implements UserRepository {

public function getById(int $id): ?User {}

}
class gRPCUserAPI implements UserRepository {

public function getById(int $id): ?User {}

}
interface User { /** various signatures **/ }
Let’s Do Some Refactoring
class OrderProcessor {

function __construct() {

$this->orderRepository = new MysqlOrderRepository();

}
function completeOrder($order) {

  global $logger;

$order->completedAt = new DateTimeImmutable;

$logger->log("Order {$order->id} is complete");

$this->orderRepository->save($order);

Mailer::sendOrderCompleteEmail($order);

}

}
Let’s Do Some Refactoring
class OrderProcessor {

function __construct(OrderRepository $orderRepo, Logger $logger) {

$this->orderRepository = $orderRepo;

$this->logger = $logger;

}
function completeOrder($order) {

$order->completedAt = new DateTimeImmutable;

$this->logger->log(“Order {$order->id} is complete");

$this->orderRepository->save($order);

Mailer::sendOrderCompleteEmail($order);

}

}
Let’s Do Some Refactoring
class OrderProcessor {

function __construct(OrderRepository $orderRepo, Logger $logger,

DateTimeImmutable $now, Mailer $mailer) {

$this->orderRepository = $orderRepo;

$this->logger = $logger;

$this->now = $now;

$this->mailer = $mailer;

}
function completeOrder($order) {

$order->completedAt = $this->now;

$this->logger->log(“Order {$order->id} is complete");

$this->orderRepository->save($order);

$this->mailer->sendOrderCompleteEmail($order);

}

}
Let’s Do Some Refactoring
class OrderProcessor {

function __construct(OrderRepository $orderRepo, Logger $logger,

Mailer $mailer) {

$this->orderRepository = $orderRepo;

$this->logger = $logger;

$this->now = $now;

}
function completeOrder($order, DateTimeImmutable $now) {

$order->completedAt = $now;

$this->logger->log(“Order {$order->id} is complete");

$this->orderRepository->save($order);

$this->mailer->sendOrderCompleteEmail($order);

}

}
What is a DI Container?
A dependency injection container is an object used to manage the
instantiation of other objects.
If you have one, it will be the place where the "new" keyword gets used
more than anywhere else in your app, either via configuration code you
write or under the hood.
What a DI Container is NOT
Not the only place you can (or should) use the "new" keyword in your
application.
Factories
Value objects
Not required for dependency injection.
Not to be used as a Service Locator
PSR-11(FKA Container-Interop)
namespace PsrContainer;
interface ContainerInterface {

public function get($id);

public function has($id);

}
Twittee: A DI Container in a 140 Tweet
class Container {

protected $s=array();

function __set($k, $c) { $this->s[$k]=$c; }

function __get($k) { return $this->s[$k]($this); }

}
Using Twitter
class NeedsALogger {

  private $logger;

public function __construct(Logger $logger) {

  $this->logger = $logger;

  }

}

class Logger {}

$c = new Container;

$c->logger = function() { return new Logger; };

$c->myService = function($c) {

return new NeedsALogger($c->logger);

};

var_dump($c->myService); // includes the Logger
Twittee++: A PSR-11 Container in a
280 Tweet
class Container implements PsrContainerContainerInterface {

protected $s=array();

function __set($k, $c) { $this->s[$k]=$c; }

function __get($k) { return $this->s[$k]($this); }

function get($k) { return $this->s[$k]($this); }

function has($k) { return isset($this->s[$k]); }

}
Full DI Containers
Every major framework has one
Symfony (DependencyInjection component)
Zend (ServiceManager)
Laravel (IlluminateContainer)
Standalone ones for use elsewhere
Pimple (used in Slim)
LeagueContainer
Aura.Di
Disco
Beware Service Location
Antipattern #2” Service Location
class OrderProcessor {

function __construct(Container Interface $c) {

$this->orderRepository = $c->get(‘OrderRepository’);

$this->logger = $c->get(‘Logger’);

$this->mailer = $c->get(‘Mailer’);

}
function completeOrder($order, DateTimeImmutable $now) {

$order->completedAt = $now;

$this->logger->log(“Order {$order->id} is complete");

$this->orderRepository->save($order);

$this->mailer->sendOrderCompleteEmail($order);

}

}
Antipattern #2” Service Location
class OrderProcessor {

protected $c;

function __construct(Container Interface $c) {

$this->orderRepository = $c->get(‘OrderRepository’);

$this->c = $c;

}
function completeOrder($order, DateTimeImmutable $now) {

$order->completedAt = $now;

$this->c->get(‘Logger’)—>log(“Order {$order->id} is complete");

$this->orderRepository->save($order);

$this->c->get(‘Mailer’)—>sendOrderCompleteEmail($order);

}

}
Using a Container
In a typical application you will use the container from
within your “controllers” and use them to inject
dependencies into your “models”.
Pimple - PSR-11 Compliant
$c = new PimpleContainer;
$c[NeedsALogger::class] = function($c) {

return new NeedsALogger($c['logger']);

};
$c['logger'] = function() {

return new Logger;

};
var_dump($c[NeedsALogger::class]); // NeedsALogger
Pimple
Use $c->factory(callable) if you don't want the same instance every time you
ask for a dependency.
Use $c->protect(callable) if you want to add a closure to your container.
Use $c->raw(dep) if you want to get a closure you set without using protect()
Not much magic
Default container of Slim 3
Refactor a Slim Route
$app = new SlimApp();
$app->get('/', function(Request $req, Response $res) {

$userRepository = new UserRepository(new PDO(/* */));

$users = $userRepository->listAll();

return $res->withJson($users);

});
Refactor a Slim Route
$app = new SlimApp();

$c = $app->getContainer();

$c['db'] = function($c) { return new PDO(/* */); };
$app->get('/', function(Request $req, Response $res) use ($c) {

$userRepository = new UserRepository($c[‘db’]);

$users = $userRepository->listAll();

return $res->withJson($users);

});
Refactor a Slim Route
$app = new SlimApp();

$c = $app->getContainer();

$c['db'] = function($c) { return new PDO(/* */); };

$c['userRepository'] = function($c) {

return new UserRepository($c['db']);

};
$app->get('/', function(Request $req, Response $res) {

$userRepository = $this->get(‘userRepository');

$users = $userRepository->listAll();

return $res->withJson($users);

});
Refactor a Slim Route
class GetUsersAction implements SlimControllerInterface {

protected $userRepository;
public function __construct(UserRepository $repo) {

$this->userRepository = $repo;

}
public function __invoke(Request $req, Response $res) {

$users = $this->userRepository->listAll();

return $res->withJson($users);

}

};

};
Refactor a Slim Route
$app = new SlimApp();

$c = $app->getContainer();

$c['db'] = function($c) { return new PDO(/* */); };

$c['userRepository'] = function($c) {

return new UserRepository($c['db']);

};

$c['getUsers'] = function($c) {

return new GetUsersAction(UserRepository $c['userRepository']);

};
$app->get('/', 'getUsers');
Recap
Dependencies in code are unavoidable, but that
doesn’t mean they need to be unmanageable
Inverting dependencies is a way to create more
flexible software
DI containers are a helpful tool for maintaining single
responsibility within objects
Resources
@iansltx
@jcarouth
Alena Holligan
• Wife, and Mother of 3 young children
• PHP Teacher at Treehouse
• Portland PHP User Group Leader
• Cascadia PHP Conference (cascadiaphp.com)
@alenaholligan alena@holligan.us https://joind.in/talk/b024a

Dependency Injection

  • 1.
  • 2.
    Highly Coupled Codeis Bad Some coupling is unavoidable
  • 3.
    Decoupled Code isGood Easier to test Easier to debug Easier to reason about
  • 4.
    Common Dependencies Infrastructure -eg Database, third party module Utility objects - eg Logger Environment - eg FileSystem, system clock Static Method calls - eg DataAccess::saveUser($u)
  • 5.
  • 6.
    Spot the dependencies classOrderProcessor {
 function __construct() {
 $this->orderRepository = new MysqlOrderRepository();
 } function completeOrder($order) {
   global $logger;
 $order->completedAt = new DateTimeImmutable;
 $logger->log("Order {$order->id} is complete");
 $this->orderRepository->save($order);
 Mailer::sendOrderCompleteEmail($order);
 }
 }
  • 7.
    Spot the dependencies classOrderProcessor {
 function __construct() {
 $this->orderRepository = new MysqlOrderRepository();
 } function completeOrder($order) {
   global $logger;
 $order->completedAt = new DateTimeImmutable;
 $logger->log("Order {$order->id} is complete");
 $this->orderRepository->save($order);
 Mailer::sendOrderCompleteEmail($order);
 }
 }
  • 8.
    Hard Questions Q: Whatdoes OrderProcessor depend on?
 A: Read the entire class to find out! Q: Where is Mailer used in my application?
 A: Grep everything in your project for “Mailer::". Q: How can I test completeOrder() without sending emails?
 A: ¯_(ツ)_/¯ * * Yes, there are workarounds, but this isn't a testing talk.
  • 9.
    What is DependencyInjection? Dependency injection is the practice of pushing (injecting) dependencies into an object, rather than having objects find their dependencies on their own. This isn't the same as the Dependency Inversion Principle in SOLID. We'll get to that later.
  • 10.
    DI Techniques Constructor Injection Setteror Property Injection Parameter Injection
  • 11.
    Constructor Injection class ClassThatNeedsDatabase
 {
 private$db; public function __construct(Database $db) {
 $this->db = $db;
 }
 }
  • 12.
    Pros Makes dependencies explicit Can’tmodify dependencies after instantiation Discourages violation of Single Responsibility Principle
 CONS May have a lot of constructor parameters May never use most dependencies Could be the wrong point in the object life cycle
  • 13.
    Many Constructor Parameters publicfunction __construct(
 Twig $view,
 AuthorizationServer $auth,
 LogRepository $errorRepo,
 AuthRequestRepository $authRequestRepo,
   ClientRepository $clientRepo,
 UserRepository $userRepo,
 ResourceServer $resAuth,
 AccessTokenRepository $accessTokenRepo,
 UserValidatorInterface $validator) 
 {/* a whole bunch of property assigns */}
  • 14.
    Antipattern #1: __construct($options) "Justthrow everything in an $options array!"
  • 15.
    Setter Injection class ClassThatUsesLoggingButDoesNotRequireIt
 {
 private$logger = null; public function setLogger(Logger $logger) { $this->logger = $logger; }
 }
  • 16.
    Property Injection class ClassThatMightWantAThingCalledALogger
 {
 public$logger = null;
 } class SlightlySaferClassThatWantsALogger
 {
 public Logger $logger = null;
 } // PHP 7.4+
  • 17.
    Pros Lazier loading Works wellwith optional dependencies Flexible across class lifecycle Don’t need every dependency for every method call Can change dependencies without re-instantiating the class
  • 18.
    Cons Harder to quicklysee which dependencies a class has Existence of dependencies in fully instantiated class not guaranteed Null checks inside the code Conditional injection outside the code
  • 19.
    Checking Property public functionfileTPSReport(Report $rpt) {
 /* do some stuff */
 if ($this->logger) {
 $this->logger->log('Did a thing');
 } /* do more stuff */
 if ($this->logger) {
 $this->logger->log('Did things');
 }
 }
  • 20.
    Null Property public functionfileTPSReport(Report $rpt) {
 /* do some stuff */
 $this->logger->log('Did a thing’); /* do more stuff */
 $this->logger->log('Did things');
 } class NullLogger implements Logger {
 public function log($message) {
 /** noop **/
 }
 }
  • 21.
    DON'T IMPLEMENT THISNULL LOGGER... ...BECAUSE THERE'S ONE BUILT INTO PSR/LOG
  • 22.
    TRAITS + SETTERINJECTION Add setter to trait Import trait into classes Implement interface on classes Configure DI container to setter-inject based on interface e.g. PsrLog{LoggerAwareInterface, LoggerAwareTrait} Sorry, you can't have an interface implement a trait directly.
  • 23.
    Parameter Injection public function__invoke(
 SlimHttpRequest $request,
 SlimHttpResponse $response,
 array $args = [])
 { /** do something, return a Response **/ }
  • 24.
    Parameter Injection public functionorderAction(
 IlluminateHttpRequest $request,
 AppServicesOrderService $orderService)
 { /** do something, return a Response **/ }
  • 25.
    Pros Does not clutteryour object with refs to collaborators which are not needed
 CONS Almost everything else about it Moves the problem of dependency management to the caller
  • 26.
    Mix & Matchas Needed
  • 27.
    Dependency Inversion Principle Highlevel modules should not depend on low level modules; both should depend on abstractions. Abstractions should not depend on details.  Details should depend upon abstractions. Tl;dr: use, and expose, interfaces with just enough functionality to get the job done.
  • 28.
    Abstrations Should NotBe Leaky class Camry implements HasGasPedal { public function pressAccelerator(); } // namespace ToyotaVehicles class Model3 implements HasGasPedal { public function pressAccelerator(); } // namespace TeslaVehicles
  • 29.
    Abstrations Should NotBe Leaky class MysqlUserRepo implements UserRepository {
 public function getById(int $id): ?User {}
 } class ElasticUserRepo implements UserRepository {
 public function getById(int $id): ?User {}
 } class gRPCUserAPI implements UserRepository {
 public function getById(int $id): ?User {}
 } interface User { /** various signatures **/ }
  • 30.
    Let’s Do SomeRefactoring class OrderProcessor {
 function __construct() {
 $this->orderRepository = new MysqlOrderRepository();
 } function completeOrder($order) {
   global $logger;
 $order->completedAt = new DateTimeImmutable;
 $logger->log("Order {$order->id} is complete");
 $this->orderRepository->save($order);
 Mailer::sendOrderCompleteEmail($order);
 }
 }
  • 31.
    Let’s Do SomeRefactoring class OrderProcessor {
 function __construct(OrderRepository $orderRepo, Logger $logger) {
 $this->orderRepository = $orderRepo;
 $this->logger = $logger;
 } function completeOrder($order) {
 $order->completedAt = new DateTimeImmutable;
 $this->logger->log(“Order {$order->id} is complete");
 $this->orderRepository->save($order);
 Mailer::sendOrderCompleteEmail($order);
 }
 }
  • 32.
    Let’s Do SomeRefactoring class OrderProcessor {
 function __construct(OrderRepository $orderRepo, Logger $logger,
 DateTimeImmutable $now, Mailer $mailer) {
 $this->orderRepository = $orderRepo;
 $this->logger = $logger;
 $this->now = $now;
 $this->mailer = $mailer;
 } function completeOrder($order) {
 $order->completedAt = $this->now;
 $this->logger->log(“Order {$order->id} is complete");
 $this->orderRepository->save($order);
 $this->mailer->sendOrderCompleteEmail($order);
 }
 }
  • 33.
    Let’s Do SomeRefactoring class OrderProcessor {
 function __construct(OrderRepository $orderRepo, Logger $logger,
 Mailer $mailer) {
 $this->orderRepository = $orderRepo;
 $this->logger = $logger;
 $this->now = $now;
 } function completeOrder($order, DateTimeImmutable $now) {
 $order->completedAt = $now;
 $this->logger->log(“Order {$order->id} is complete");
 $this->orderRepository->save($order);
 $this->mailer->sendOrderCompleteEmail($order);
 }
 }
  • 34.
    What is aDI Container? A dependency injection container is an object used to manage the instantiation of other objects. If you have one, it will be the place where the "new" keyword gets used more than anywhere else in your app, either via configuration code you write or under the hood.
  • 35.
    What a DIContainer is NOT Not the only place you can (or should) use the "new" keyword in your application. Factories Value objects Not required for dependency injection. Not to be used as a Service Locator
  • 36.
    PSR-11(FKA Container-Interop) namespace PsrContainer; interfaceContainerInterface {
 public function get($id);
 public function has($id);
 }
  • 37.
    Twittee: A DIContainer in a 140 Tweet class Container {
 protected $s=array();
 function __set($k, $c) { $this->s[$k]=$c; }
 function __get($k) { return $this->s[$k]($this); }
 }
  • 38.
    Using Twitter class NeedsALogger{
   private $logger;
 public function __construct(Logger $logger) {
   $this->logger = $logger;
   }
 }
 class Logger {}
 $c = new Container;
 $c->logger = function() { return new Logger; };
 $c->myService = function($c) {
 return new NeedsALogger($c->logger);
 };
 var_dump($c->myService); // includes the Logger
  • 39.
    Twittee++: A PSR-11Container in a 280 Tweet class Container implements PsrContainerContainerInterface {
 protected $s=array();
 function __set($k, $c) { $this->s[$k]=$c; }
 function __get($k) { return $this->s[$k]($this); }
 function get($k) { return $this->s[$k]($this); }
 function has($k) { return isset($this->s[$k]); }
 }
  • 40.
    Full DI Containers Everymajor framework has one Symfony (DependencyInjection component) Zend (ServiceManager) Laravel (IlluminateContainer) Standalone ones for use elsewhere Pimple (used in Slim) LeagueContainer Aura.Di Disco
  • 41.
  • 42.
    Antipattern #2” ServiceLocation class OrderProcessor {
 function __construct(Container Interface $c) {
 $this->orderRepository = $c->get(‘OrderRepository’);
 $this->logger = $c->get(‘Logger’);
 $this->mailer = $c->get(‘Mailer’);
 } function completeOrder($order, DateTimeImmutable $now) {
 $order->completedAt = $now;
 $this->logger->log(“Order {$order->id} is complete");
 $this->orderRepository->save($order);
 $this->mailer->sendOrderCompleteEmail($order);
 }
 }
  • 43.
    Antipattern #2” ServiceLocation class OrderProcessor {
 protected $c;
 function __construct(Container Interface $c) {
 $this->orderRepository = $c->get(‘OrderRepository’);
 $this->c = $c;
 } function completeOrder($order, DateTimeImmutable $now) {
 $order->completedAt = $now;
 $this->c->get(‘Logger’)—>log(“Order {$order->id} is complete");
 $this->orderRepository->save($order);
 $this->c->get(‘Mailer’)—>sendOrderCompleteEmail($order);
 }
 }
  • 44.
    Using a Container Ina typical application you will use the container from within your “controllers” and use them to inject dependencies into your “models”.
  • 45.
    Pimple - PSR-11Compliant $c = new PimpleContainer; $c[NeedsALogger::class] = function($c) {
 return new NeedsALogger($c['logger']);
 }; $c['logger'] = function() {
 return new Logger;
 }; var_dump($c[NeedsALogger::class]); // NeedsALogger
  • 46.
    Pimple Use $c->factory(callable) ifyou don't want the same instance every time you ask for a dependency. Use $c->protect(callable) if you want to add a closure to your container. Use $c->raw(dep) if you want to get a closure you set without using protect() Not much magic Default container of Slim 3
  • 47.
    Refactor a SlimRoute $app = new SlimApp(); $app->get('/', function(Request $req, Response $res) {
 $userRepository = new UserRepository(new PDO(/* */));
 $users = $userRepository->listAll();
 return $res->withJson($users);
 });
  • 48.
    Refactor a SlimRoute $app = new SlimApp();
 $c = $app->getContainer();
 $c['db'] = function($c) { return new PDO(/* */); }; $app->get('/', function(Request $req, Response $res) use ($c) {
 $userRepository = new UserRepository($c[‘db’]);
 $users = $userRepository->listAll();
 return $res->withJson($users);
 });
  • 49.
    Refactor a SlimRoute $app = new SlimApp();
 $c = $app->getContainer();
 $c['db'] = function($c) { return new PDO(/* */); };
 $c['userRepository'] = function($c) {
 return new UserRepository($c['db']);
 }; $app->get('/', function(Request $req, Response $res) {
 $userRepository = $this->get(‘userRepository');
 $users = $userRepository->listAll();
 return $res->withJson($users);
 });
  • 50.
    Refactor a SlimRoute class GetUsersAction implements SlimControllerInterface {
 protected $userRepository; public function __construct(UserRepository $repo) {
 $this->userRepository = $repo;
 } public function __invoke(Request $req, Response $res) {
 $users = $this->userRepository->listAll();
 return $res->withJson($users);
 }
 };
 };
  • 51.
    Refactor a SlimRoute $app = new SlimApp();
 $c = $app->getContainer();
 $c['db'] = function($c) { return new PDO(/* */); };
 $c['userRepository'] = function($c) {
 return new UserRepository($c['db']);
 };
 $c['getUsers'] = function($c) {
 return new GetUsersAction(UserRepository $c['userRepository']);
 }; $app->get('/', 'getUsers');
  • 52.
    Recap Dependencies in codeare unavoidable, but that doesn’t mean they need to be unmanageable Inverting dependencies is a way to create more flexible software DI containers are a helpful tool for maintaining single responsibility within objects
  • 53.
  • 54.
    Alena Holligan • Wife,and Mother of 3 young children • PHP Teacher at Treehouse • Portland PHP User Group Leader • Cascadia PHP Conference (cascadiaphp.com) @alenaholligan alena@holligan.us https://joind.in/talk/b024a