Dependency Injection for Wordpress
-
Upload
mtoppa -
Category
Technology
-
view
5.089 -
download
4
description
Transcript of Dependency Injection for Wordpress
Dependency Injection forWordPress Plugin Development
Mike ToppaWordCamp Nashville
April 21, 2012#wcn12
@mtoppawww.toppa.com
@mtoppawww.toppa.com
Mike Toppa
● Director of Development, WebDevStudios● 17 years of experience in web development,
project management, and team management● Universities: Georgetown, Stanford, Penn● Dot coms: E*Trade, Ask Jeeves● Start-ups: Finexa, Kai's Candy Co● WordPress development for non-profits
Code is Poetry
@mtoppawww.toppa.com
@mtoppawww.toppa.com
We want code to be...
● Easy to understand● Easy to change● Reusable (DRY)● Bug free
“O.P.C” http://abstrusegoose.com/432
So why is it usually like this?
It happens because we are always in a hurry,and requirements are always changing.
@mtoppawww.toppa.com
But coding fast and dirtyonly makes us slower in the long run.
@mtoppawww.toppa.com
“We like to think we spend our time power typing,but we actually spend most of our time
staring into the abyss.”
- Douglas Crockfordprincipal discoverer of JSON,
Creator of JSLint
@mtoppawww.toppa.com
The ratio of time spent reading code versus writing is well over 10 to 1.
Therefore, making code easy to readmakes it easier to write.
- Bob MartinParaphrased from his book Clean Code
@mtoppawww.toppa.com
@mtoppawww.toppa.com
Clean code saves time, saves $$
@mtoppawww.toppa.com
Clean code techniques
● Using meaningful names● Don't repeat yourself (DRY)● Object oriented principles and patterns● Unit testing, test driven development (TDD)● The “boy scout rule”● Vertical slicing● ...and many more
@mtoppawww.toppa.com
Clean code techniques
● Using meaningful names● Don't repeat yourself (DRY)● Object oriented principles and patterns● Unit testing, test driven development (TDD)● The “boy scout rule”● Vertical slicing● ...and many more
@mtoppawww.toppa.com
The SOLID Principles
● Single Responsibility (SRP)● Open-Closed (OCP)● Liskov Substitution (LSP)● Interface Segregation (ISP)● Dependency Inversion (DIP)
@mtoppawww.toppa.com
The SOLID Principles
● Single Responsibility (SRP)● Open-Closed (OCP)● Liskov Substitution (LSP)● Interface Segregation (ISP)● Dependency Inversion (DIP)
@mtoppawww.toppa.com
Shashin
My plugin for displaying albums, photos, and videos from Picasa, Twitpic, and YouTube
(and others coming soon)
@mtoppawww.toppa.com
I used the new version as a test case for applyingclean code principles to WordPress plugins
From LosTechies.com
@mtoppawww.toppa.com
My Shashin plugin consists of 44 classes, each of which has a meaningful name, follows the SRP,
and “does one thing”
PicasaPhotoDisplayerSettingsMenu
YouTubeSynchronizerUninstall
etc.
@mtoppawww.toppa.com
class Admin_ShashinInstall { // ... public function run() { $this->createAlbumTable(); $this->verifyAlbumTable(); $this->createPhotoTable(); $this->verifyPhotoTable(); $this->updateSettings(); return true; }
// ...}
Shashin example
@mtoppawww.toppa.com
Class Autoloading
● The problem:● PHP lacks an equivalent statement to “import” or
“use” for classes● Having to hardcode a bunch of file paths is extra
work, and makes code harder to change
● The solution:● PHP 5.1.2 and spl_autoload_register()● PHP Standards Working Group: PSR-0
@mtoppawww.toppa.com
PSR-0: Official Description
● Each "_" character in the CLASS NAME is converted to a DIRECTORY_SEPARATOR
● The fully-qualified namespace and class is suffixed with ".php" when loading from the file system
● Alphabetic characters... may be of any combination of lower case and upper case
● ...And other rules about namespaces● Source: https://github.com/php-fig/fig-
standards/blob/master/accepted/PSR-0.md
@mtoppawww.toppa.com
Admin_ShashinInstall
is translated to
Admin/ShashinInstall.php
PSR-0 Example
@mtoppawww.toppa.com
// this is at the top of the main file for my Shashin plugin
require_once dirname(__FILE__) . '/../toppa-plugin-libraries-for-wordpress/ToppaAutoLoaderWp.php'; $shashinAutoLoader = new ToppaAutoLoaderWp('/shashin');
// that's it! I can now call “new” on any class under the shashin folder// and its class file will be automatically loaded
Toppa Plugin Libraries Autoloader
@mtoppawww.toppa.com
Shashin's capabilities are shaped by how its objects are wired together, through
implementation of theDependency Inversion Principle
From LosTechies.com
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 “Agile Software Development”
Dependency inversion applied
Button
+ poll()
<<interface>>SwitchableDevice
+ turnOn()+ turnOff()
Lamp
This is the Abstract Server pattern
class Lamp implements SwitchableDevice {public function turnOn() {
// code}
public function turnOff() {// code
}}
class Button {private $switchableDevice;
public function __construct(SwitchableDevice $switchableDevice) {$this->switchableDevice = $switchableDevice;
}
public function poll() {if (/* some condition */) {
$this->switchableDevice->turnOn();}
}}
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."
@mtoppawww.toppa.com
Composition(“Has a...”)
vs.
Inheritance(“Is a...”)
@mtoppawww.toppa.com
The SRP is about objects that do one thing
The DIP is about how to wire them togetherto create working, flexible software
@mtoppawww.toppa.com
class Button {private $lamp;
public function __construct() {$this->lamp = new Lamp();
}
//...}
Never, ever do this
@mtoppawww.toppa.com
Instead, you want toinject the object dependency
Dependency injection!
@mtoppawww.toppa.com
Dependency injection is one of manydesign patterns
for implementing theDependency inversion principle
@mtoppawww.toppa.com
It comes in two flavors
@mtoppawww.toppa.com
public function __construct(SwitchableDevice $switchableDevice) {$this->switchableDevice = $switchableDevice;
}
Constructor injection
@mtoppawww.toppa.com
public function setSwitchableDevice(SwitchableDevice $switchableDevice) {$this->switchableDevice = $switchableDevice;
}
Setter injection
@mtoppawww.toppa.com
Which to use?
It depends
@mtoppawww.toppa.com
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
@mtoppawww.toppa.com
To do this without going insane,you need an injection container
@mtoppawww.toppa.com
class Lib_ShashinContainer { // … public function getPhotoRefData() { if (!isset($this->photoRefData)) { $this->photoRefData = new Lib_ShashinPhotoRefData(); }
return $this->photoRefData; }
public function getClonablePhoto() { if (!isset($this->clonablePhoto)) { $this->getPhotoRefData(); $this->clonablePhoto = new Lib_ShashinPhoto($this->photoRefData); }
return $this->clonablePhoto; }
Example from Shashin
@mtoppawww.toppa.com
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
@mtoppawww.toppa.com
Beyond the textbook examples
@mtoppawww.toppa.com
What to do when you needa new object inside a loop
One solution is cloning
@mtoppawww.toppa.com
class Admin_ShashinSynchronizerPicasa extends Admin_ShashinSynchronizer { // …
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
@mtoppawww.toppa.com
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
@mtoppawww.toppa.com
class Public_ShashinLayoutManager { // ... 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
@mtoppawww.toppa.com
Dependency injection: key benefits
● Makes your object dependencies adaptable to emerging needs
● With an injection container, simplifies managing dependency chains
● Supports “preferring polymorphism to conditionals”, making it easy to add new class subtypes
@mtoppawww.toppa.com
Will this proliferation of objectseat up all my memory?
No
@mtoppawww.toppa.com
Use dependency injection to instantiateonly the objects you need,
when you need them
@mtoppawww.toppa.com
Make immutable objects intoproperties of the container,
so you only need to instantiate them once
@mtoppawww.toppa.com
“In PHP 5, the infrastructure of the object model was rewritten to work with object handles. Unless you explicitly clone an object by using the clone keyword you will never create behind the scene duplicates of your objects. In PHP 5, there is neither a need to pass objects by reference nor assigning them by reference.”
From http://devzone.zend.com/article/1714
PHP 5 has improved memory management
But don't overdo it:avoid needless complexity