Download - Dependency Injection for PHP
![Page 1: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/1.jpg)
Dependency Injection for PHP
Mike Toppa Boston PHP Meetup September 9, 2015
@mtoppapokayoke.design
![Page 2: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/2.jpg)
About me
@mtoppapokayoke.design
* I’ve been developing for the web since the days of HTML 1.0, when web pages were first painted on cave walls. * I’ve been developing with PHP since 2004. * Over the years I’ve worked at Ask Jeeves, E-Trade, Stanford, Georgetown, and I was the Director of the Web Applications Team at the U Penn School of
Medicine. * Most of my work has been as a developer, but I’ve also done a good deal of project management and team management, and I’m a big believer in
Agile and Lean practices.
![Page 3: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/3.jpg)
My new house near Boston…
@mtoppapokayoke.design
* I just moved to Boston from Philadelphia this summer* and the is what my house looks like
![Page 4: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/4.jpg)
@mtoppapokayoke.design
* My wife has a new job here in Boston, and I have a new job now too. I’m co-founder and CTO of Poka Yoke Design. Our primary development platforms are WordPress and Ruby on Rails.
* I was an active member of the PHP, WordPress, and Ruby communities in Philly, and I look forward to becoming part of the communities here in Boston.* We are not hiring currently, but we are looking for projects!
![Page 5: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/5.jpg)
Overview
1. Crash course in classes and objects in PHP 2. The SOLID principles
• Single Responsibility Principle • Dependency Inversion Principle
3. Class autoloading in PHP 4. Dependency injection
• DI containers • Advanced dependency injection techniques
@mtoppapokayoke.design
![Page 6: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/6.jpg)
1. Crash course in classes and objects in PHP
@mtoppapokayoke.design
![Page 7: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/7.jpg)
@mtoppapokayoke.design
![Page 8: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/8.jpg)
Simple exampleclass Lamp { // property declaration private $maxSafeWatts = 100;
// method declaration public function getMaxSafeWatts() { return $this->maxSafeWatts; } } ---------------------------------------------------------------
// instantiate and assign $myLamp = new Lamp(); echo $myLamp->getMaxSafeWatts();
A class consists of properties, and methods which perform actions, by manipulating those properties
Encapsulation of related methods and properties. This is what a class really is.
Properties and methods have a visibility (public, private, or protected)
An object is created by instantiating a class (calling new). Then you typically assign it to a variable, and call its methods as needed
![Page 9: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/9.jpg)
Rule of thumb:avoid public properties
class Lamp { public $maxSafeWatts = 100; }
---------------------------------------------
$myLamp = new Lamp();
// dangerous to allow this! $myLamp->maxSafeWatts = 'a string to wreck your math';
Public properties can be used and abused by external code at any time.
![Page 10: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/10.jpg)
Prefer private propertiesclass Lamp { private $maxSafeWatts = 100;
public setMaxSafeWatts($watts) { if (!is_numeric($watts) || $watts > 125 || $watts < 1) { throw New Exception('invalid value for watts'); }
$this->maxSafeWatts = $watts; return $this->maxSafeWatts; } }
-------------------------------------------------------------------
$myLamp = new Lamp(); $myLamp->setMaxSafeWatts(75);
By requiring them to be set through a method call, you can control what types of values are valid, and what ranges are valid.
![Page 11: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/11.jpg)
Constructors
class Lamp { private $bulb;
public function __construct($bulb) { $this->bulb = $bulb; } }
---------------------------------------------
$myLamp = new Lamp('3 way');
The constructor is a special method, used for initializing a class.
It's optional – is called when you call “new”
A constructor does not return anything
It should be used for getting a class into a valid initial state
A common design mistake is to put a lot of complex logic in the constructor, or call it to execute the object's functionality.
![Page 12: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/12.jpg)
Type Hinting
class Lamp { private $bulb;
public function __construct(Bulb $bulb) { $this->bulb = $bulb; } }
---------------------------------------------
$myBulb = new Bulb(); $myLamp = new Lamp($bulb);
![Page 13: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/13.jpg)
Organizing your classes
One class per file, and the file name should match the class name Give the class a meaningful name and its methods meaningful names Class names and property names should be nouns or noun phrases Method names should be verbs or verb phrases Use an IDE that autocompletes variable names and method names
![Page 14: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/14.jpg)
Abstract classes and inheritance
Lamp
DeskLampFloorLamp HangingLamp
![Page 15: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/15.jpg)
Abstract classes and methodsabstract class Lamp { protected $color; protected $maxSafeWatts;
public function setColor($color) { $this->color = $color; return $this->color; }
abstract public function setMaxSafeWatts($watts); }
An abstract class cannot be implemented directly
It can also have abstract methods, which must be implemented by the child class
Protected methods and properties are essentially private, but can be used by child classes
![Page 16: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/16.jpg)
Implementing abstract classesclass FloorLamp extends Lamp { public function setMaxSafeWatts($watts) { /* if numeric and less than 150... */ $this->maxSafeWatts = $watts; return $this->maxSafeWatts; } } ------------------------------------------------------------------ class DeskLamp extends Lamp { public function setMaxSafeWatts($watts) { /* if numeric and less than 100... */ $this->maxSafeWatts = $watts; return $this->maxSafeWatts; } }
![Page 17: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/17.jpg)
Interfaces
An electrical outlet is a great example of an interface. It can power anything designed to plug into it. It doesn't need to know or care about exactly what it's connected to.
Interfaces define a set of methods a class must implement. It's similar to abstract classes in this way, but there is no inheritance.
I’ll provide an example and say more about interfaces when we get to dependency inversion
![Page 18: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/18.jpg)
2. The SOLID Principles
● Single Responsibility (SRP) ● Open-Closed (OCP) ● Liskov Substitution (LSP) ● Interface Segregation (ISP) ● Dependency Inversion (DIP)
@mtoppapokayoke.design
Introduced by Bob Martin in his book “Agile Software Development”
![Page 19: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/19.jpg)
From LosTechies.com
* Applied to classes and methods * Do one thing, do it well, do it only * For methods, this typically means changing the value of only one variable * If you are passing more than 2 or 3 arguments into a method, you are probably doing more than one thing * For classes, it means having a single conceptual responsibility
![Page 20: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/20.jpg)
The purpose is to reduce the complexity and fragility
of a class
@mtoppapokayoke.design
We want code that is flexible and easy to understand, not brittle and mind-numbing to read
When a method is manipulating multiple properties and invoking lots of other methods, it can be very hard to test, debug, and change. This is a common reason why developers feel fear when making a change – they don't know what might break
When a class has many methods and multiple responsibilities, it can be hard to understand and difficult to refactor
![Page 21: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/21.jpg)
But what does it mean to do “one thing”?
@mtoppapokayoke.design
![Page 22: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/22.jpg)
Only one reason to change
@mtoppapokayoke.design
If you have a class that compiles and prints a report, it has 2 responsibilities, and 2 reasons to change. The content of the report could change, or the format of the report could change. This class should be split into two classes.
![Page 23: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/23.jpg)
Cohesion
@mtoppapokayoke.design
Uncle Bob says:* “Classes should have a small number of instance variables. Each of the methods of a class should manipulate one or more of those variables. In general the more
variables a method manipulates the more cohesive that method is to its class.”* “When cohesion is high, it means that the methods and variables of the class are co-dependent and hang together as a logical whole.”
![Page 24: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/24.jpg)
SRP violation example
@mtoppapokayoke.design
![Page 25: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/25.jpg)
![Page 26: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/26.jpg)
![Page 27: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/27.jpg)
![Page 28: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/28.jpg)
![Page 29: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/29.jpg)
![Page 30: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/30.jpg)
Shashin
@mtoppapokayoke.design
My WordPress plugin for displaying albums, photos, and videos from Picasa, Twitpic, and
YouTube
I'm going to show SRP and DIP examples of code from Shashin
Clean code books and articles all use other languages as examples – I wanted to try applying them to PHP and WordPress
![Page 31: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/31.jpg)
![Page 32: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/32.jpg)
![Page 33: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/33.jpg)
Shashin supports multiple viewers
![Page 34: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/34.jpg)
There's a lot going on here. A lot of different data sources and display possibilities to support. How do you keep the code from becoming a tangled mess?
![Page 35: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/35.jpg)
@mtoppapokayoke.design
Shashin consists of 44 classes, each of which has a meaningful name, follows the SRP, and
“does one thing”
PicasaPhotoDisplayer SettingsMenu
YouTubeSynchronizer Uninstall
etc.
![Page 36: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/36.jpg)
Displayer.php
AlbumDisplayer.php AlbumDisplayerPicasa.php AlbumDisplayerTwitpic.php AlbumDisplayerFlickr.php
PhotoDisplayer.php PhotoDisplayerPicasa.php PhotoDisplayerTwitpic.php PhotoDisplayerFlickr.php
Shashin Classes Example
You can start to see the power of this approach here
I can add support for a new photos service by creating a new subclass, instead of having to touch code all over the place, adding a bunch of “if” statements
![Page 37: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/37.jpg)
class Installer { public function run() { $this->createAlbumTable(); $this->verifyAlbumTable(); $this->createPhotoTable(); $this->verifyPhotoTable(); $this->updateSettings(); return true; }
// ... }
Methods Example
* The whole class is about 150 lines *The run method calls the other methods (this is a simple use of the command pattern) *It reads like a table of contents *Breaking the functionality down into methods that do one thing makes the code easier to read and to unit test *Installation is especially useful to break down into small methods that can have automated tests, as plugin activation is especially difficult to debug
![Page 38: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/38.jpg)
From LosTechies.com
The next question is, how do we get all these objects working together?
It’s common to see code that hard-wires together all the parts, when those connections could be made more flexible and extensible
![Page 39: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/39.jpg)
Some definitions of the DIP
● Abstractions should not depend on details ● Code should depend on things that are at the
same or higher level of abstraction ● High level policy should not depend on low level
details ● Capture low-level dependencies in domain-
relevant abstractions ● Uh, ok…?
Definitions from DIP in the wild
![Page 40: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/40.jpg)
Naïve model of a button and lamp
Button
+ poll()
Lamp
+ turnOn() + turnOff()
class Button { private $lamp;
public function __construct(Lamp $lamp) { $this->lamp = $lamp; }
public function poll() { if (/* some condition */) { $this->lamp->turnOn(); } } } Example from Bob Martin's book
“Agile Software Development”
This solution violates the DIP
● Button depends directly on Lamp ● Changes to Lamp may require changes to Button
● Button is not reusable ● It can't control, for example, a Motor
● The high level abstraction is missing ● “the truths that do not vary when the details are changed” ● “To detect an on/off gesture from a user and relay that gesture to a target object”
![Page 41: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/41.jpg)
Dependency inversion applied
Button
+ poll()
<<interface>> SwitchableDevice
+ turnOn() + turnOff()
Lamp
This is the Abstract Server pattern
What it means: *Neither Button nor Lamp “own” the interface *Buttons can now control any device that implements SwitchableDevice *Lamps and other SwitchableDevices can now be controlled by any object that accepts a SwitchableDevice
![Page 42: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/42.jpg)
interface SwitchableDevice { public function turnOn(); public function turnOff(); }
class Lamp implements SwitchableDevice { public function turnOn() { // code }
public function turnOff() { // code } }
![Page 43: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/43.jpg)
class Button { private $switchableDevice;
public function __construct(SwitchableDevice $switchableDevice) { $this->switchableDevice = $switchableDevice; }
public function poll() { if (/* some condition */) { $this->switchableDevice->turnOn(); } } }
We’ve made only a small change to Button, but it’s a powerful one
![Page 44: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/44.jpg)
A web of collaborating objects
● The SRP and DIP together drive a “composition” approach to OO design
● From Growing Object Oriented Software, Guided by Tests: "An object oriented system is a web of collaborating objects... The behavior of the system is an emergent property of the composition of the objects - the choice of objects and how they are connected... Thinking of a system in terms of its dynamic communication structure is a significant mental shift from the static classification that most of us learn when being introduced to objects."
Author: Steve Freeman and Nat Pryce
![Page 45: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/45.jpg)
@mtoppapokayoke.design
Composition (“Has a...”)
vs.
Inheritance (“Is a...”)
![Page 46: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/46.jpg)
@mtoppapokayoke.design
The SRP is about objects that do one thing
The DIP is about how to wire them together to create working, flexible software
![Page 47: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/47.jpg)
@mtoppapokayoke.design
3. Class autoloading: the problem
● How do we include the class files we’ll need in our project?
● PHP lacks an equivalent statement to “import” or “use” for classes
● Having to hardcode a bunch of file paths for loading classes is extra work, and makes code harder to change
![Page 48: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/48.jpg)
@mtoppapokayoke.design
Class autoloading: the solution
● PHP 5.1.2 and spl_autoload_register() ● PHP 5.3 and support for namespaces ● PHP Standards Working Group: PSR-4
● http://www.php-fig.org/psr/psr-4/
* spl_autoloader_register allows you to register a function that will be called when PHP encounters a “new” call, for finding and including the file containing the class. *you can map namespaces to folders *PSR-4 provides an agreed upon convention for how to do it
![Page 49: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/49.jpg)
@mtoppapokayoke.design
when you type this:
new \Shashin\Admin\Installer;
the Shashin autoloader will load this:
/path/to/shashin/Admin/Installer.php
PSR-4 Example
![Page 50: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/50.jpg)
@mtoppapokayoke.design
// simplified version of code in Shashin’s initialization script
require_once dirname(__FILE__) . '/lib/ShashinAutoLoader.php' new ShashinAutoLoader();
// that's it! I can now call “new” on any Shashin class // and its class file will be automatically loaded
Shashin Autoloader
![Page 51: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/51.jpg)
@mtoppapokayoke.design
Dependency injection… Dependency inversion…
What’s the difference?
4. Dependency Injection
![Page 52: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/52.jpg)
Naïve button & lamp model revisited
Button
+ poll()
Lamp
+ turnOn() + turnOff()
class Button { private $lamp;
public function __construct(Lamp $lamp) { $this->lamp = $lamp; }
public function poll() { if (/* some condition */) { $this->lamp->turnOn(); } } }
● This solution violates the DIP, but it’s still doing dependency injection ● You can apply the DIP using dependency injection ● There are also other techniques for applying the DIP: Factory pattern, adapter pattern, service locator pattern
![Page 53: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/53.jpg)
class Button { private $lamp;
public function __construct() { $this->lamp = new Lamp(); }
//... }
Never, ever do this
* There was one thing that was ok in the naïve example – we were at least passing in the lamp object *Instantiating it directly in the class makes your code harder to unit test. When unit testing we want to test the method’s logic in isolation, and not get involved with the actual behavior of
other classes it uses. *When we bury these references in our code, it becomes hard to see what our collaborators are.
![Page 54: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/54.jpg)
public function __construct(SwitchableDevice $switchableDevice) { $this->switchableDevice = $switchableDevice; }
Constructor injection
![Page 55: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/55.jpg)
public function setSwitchableDevice(SwitchableDevice $switchableDevice) { $this->switchableDevice = $switchableDevice; }
Setter injection
![Page 56: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/56.jpg)
@mtoppapokayoke.design
Which to use?
It depends
● Constructor injection gives you a valid object, with all its dependencies, upon construction ● But constructor injection becomes hard to read and use when there are more than a few objects to inject ● More about this in an upcoming slide...
![Page 57: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/57.jpg)
@mtoppapokayoke.design
If class A depends on class B, and class B depends on class C,
class A should be blissfully unaware of class C
Dependency chains
We want loose coupling, not a rigid chain of dependencies that is hard to change
![Page 58: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/58.jpg)
@mtoppapokayoke.design
To do this without going insane, you need an injection container
![Page 59: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/59.jpg)
class Container { // … public function getPhotoRefData() { if (!isset($this->photoRefData)) { $this->photoRefData = new \Shashin\Lib\PhotoRefData(); }
return $this->photoRefData; }
public function getClonablePhoto() { if (!isset($this->clonablePhoto)) { $this->getPhotoRefData(); $this->clonablePhoto = new \Shashin\Lib\Photo($this->photoRefData); }
return $this->clonablePhoto; }
Example from Shashin
I am making the objects properties of the container, because they happen to be immutable objects, so they are reusable
You can see I'm using constructor injection
I'll explain in a minute why I called it a “clonable” photo
![Page 60: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/60.jpg)
public function getClonablePhotoCollection() { if (!isset($this->photoCollection)) { $this->getClonablePhoto(); $this->getSettings(); $this->clonablePhotoCollection = new Lib_ShashinPhotoCollection(); $this->clonablePhotoCollection->setClonableDataObject($this->clonablePhoto); $this->clonablePhotoCollection->setSettings($this->settings); }
return $this->clonablePhotoCollection; }
Example continued
Start with constructor injection As your design evolves, switch to setter injection once there are more than 2 objects to inject If you rely on an injection container, you don't have to worry about forgetting to call a required setter
![Page 61: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/61.jpg)
@mtoppapokayoke.design
Beyond the textbook examples
![Page 62: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/62.jpg)
@mtoppapokayoke.design
What to do when you need a new object inside a loop
One solution is cloning
![Page 63: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/63.jpg)
class SynchronizerPicasa extends Synchronizer { // setClonablePhoto method…
public function syncAlbumPhotos(array $decodedAlbumData) { // …
foreach ($decodedAlbumData['feed']['entry'] as $entry) { $photoData = $this->extractFieldsFromDecodedData( $entry, $photoRefData, ‘picasa' ); // ... $photo = clone $this->clonablePhoto; $photo->set($photoData); $photo->flush(); }
Shashin Example
![Page 64: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/64.jpg)
@mtoppapokayoke.design
What if you need a new object inside a loop, but can't know the subtype you'll need
ahead of time?
Let the injection container figure it out
This was an interesting problem for me with Shashin. It supports multiple media sources (Picasa, etc), multiple viewers (Fancybox, etc), and we could be displaying a photo or an album cover, each of which has different behaviors
![Page 65: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/65.jpg)
class LayoutManager { // setContainer method…
public function setTableBody() { // …
for ($i = 0; $i < count($this->collection); $i++) { // ...
$dataObjectDisplayer = $this->container->getDataObjectDisplayer( $this->shortcode, $this->collection[$i] );
$this->tableBody .= $dataObjectDisplayer->run();
// ... }
Shashin Example
Regardless of the type, the layout rules for the thumbnails is always the same
So here I've injected the container itself
getDataObjectDisplayer() uses the passed in arguments to determine which subtype of DataObjectDisplayer to return
![Page 66: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/66.jpg)
class Container { // …
public function getDataObjectDisplayer( //… ) { // …
$dataObjectClassName = get_class($dataObject); // ShashinPhoto $albumType = ucfirst($dataObject->albumType); // Picasa $classToCall = $dataObjectClassName . 'Displayer' . $albumType; $dataObjectDisplayer = new $classToCall();
// … return $dataObjectDisplayer; // ShashinPhotoDisplayerPicasa }
Shashin Example
![Page 67: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/67.jpg)
Off-the-shelf injection containers
• PHP-DI • Symfony\DependencyInjection • Zend\Di • Orno\Di • Dice • Aura.Di • Pimple • More info and performance analyses
* If you want to try one, start with Dice. It’s lightweight and easy to understand. It does “auto-wiring” by using reflection. It relies on type hinting in your method signatures.
* Others have you use configuration files, annotations, or PHP code to describe dependencies.
![Page 68: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/68.jpg)
@mtoppapokayoke.design
Off-the-shelf or roll your own?
* Using reflection is great for simple cases* Using configurations, you can end up with as much code as simply writing your own* I prefer writing my own. It’s not hard, and I can customize the behavior however I like without having to fight against the conventions of a pre-built container (like the
getDataObjectDisplayer method)
![Page 69: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/69.jpg)
@mtoppapokayoke.design
Dependency injection: key benefits
● Makes your objects flexible ● Can work with anything that supports the
expected interface ● Decreases coupling, which promotes
reusability, testability and maintainability ● With an injection container, simplifies managing
dependency chains
Button and lamp….
![Page 70: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/70.jpg)
But don't overdo it:avoid needless complexity
The complexity of having 44 classes in Shashin is justified by its need for flexibility
You can support a new viewer or a new photo service simply by creating a new subclass. This is much more maintainable and extensible than a single huge file with deeply nested conditionals and unclear dependencies
But if I knew I would never need that kind of flexibility, there would be no justification for creating these layers of abstraction – they would just be needless complexity
![Page 71: Dependency Injection for PHP](https://reader034.vdocument.in/reader034/viewer/2022052405/58a32a291a28ab71398b62eb/html5/thumbnails/71.jpg)
Questions?