An introduction to object oriented programming in PHP. This is applicable to WordPress and other CMS/frameworks as well. Note the last half of the slideshow contains the notes for the slides shown in the first half.
3. 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
www.toppa.com @mtoppa
4. Overview
â—Ź Nuts and bolts of classes and objects in PHP
â—Ź The single responsibility principle
â—Ź The dependency inversion principle
â—Ź When to prefer OO to procedural programming
www.toppa.com @mtoppa
6. Simple example
class Lamp {
// property declaration
private $maxSafeWatts = 100;
// method declaration
public function getMaxSafeWatts() {
return $this->maxSafeWatts;
}
}
---------------------------------------------------------------
// instantiate and assign
$myLamp = new Lamp();
echo $myLamp->getMaxSafeWatts();
7. 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';
8. Prefer private properties
class 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);
9. Constructors
class Lamp {
private $bulb;
public function __construct($bulb) {
$this->bulb = $bulb;
}
}
---------------------------------------------
$myLamp = new Lamp('3 way');
10. Type Hinting
class Lamp {
private $bulb;
public function __construct(Bulb $bulb) {
$this->bulb = $bulb;
}
}
---------------------------------------------
$myBulb = new Bulb();
$myLamp = new Lamp($bulb);
12. Initializing your OO plugin
// this is the start.php file for a “Lamp” WP plugin...
// require or autoload the “main” class file for your plugin
// and then...
$lamp = new Lamp();
$lamp->run();
// that's it!
14. Abstract classes and methods
abstract class Lamp {
protected $color;
protected $maxSafeWatts;
public function setColor($color) {
$this->color = $color;
return $this->color;
}
abstract public function setMaxSafeWatts($watts);
}
15. Implementing abstract classes
class 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;
}
}
17. Using interfaces: the facade pattern
Functions
A PHP
facade
application
interface
WordPress Drupal Some other
facade facade facade
implementation implementation implementation
18. Interface Example
interface FunctionsFacade {
public function enqueueStylesheet($handle);
}
------------------------------------------------------------------------
class FunctionsFacadeWp implements FunctionsFacade {
public function enqueueStylesheet($handle) {
return wp_enqueue_style($handle);
}
}
// we'll look at how to actually use the interface in a few minutes...
28. NaĂŻve model of a button and lamp
Lamp
Button
+ turnOn()
+ poll()
+ 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”
}
29. Dependency inversion applied
<<interface>>
Button SwitchableDevice
+ poll() + turnOn()
+ turnOff()
Lamp
This is the Abstract Server pattern
30. SwitchableDevice and Lamp
interface SwitchableDevice {
public function turnOn();
public function turnOff();
}
--------------------------------------------------------------
class Lamp implements SwitchableDevice {
public function turnOn() {
// code
}
public function turnOff() {
// code
}
}
31. Button
class Button {
private $switchableDevice;
public function __construct(SwitchableDevice
$switchableDevice) {
$this->switchableDevice = $switchableDevice;
}
public function poll() {
if (/* some condition */) {
$this->switchableDevice->turnOn();
}
}
}
32. Using the Button
// require or autoload the class files, then...
$lamp = new Lamp();
$buttonForLamp = new Button($lamp);
$buttonforLamp->poll();
$motor = new Motor();
$buttonForMotor = new Button($motor);
$buttonForMotor->poll();
33. Dependency Chains
If class A depends on class B,
and class B depends on class C,
class A should be blissfully unaware of class C
www.toppa.com @mtoppa
34. To do this without going insane,
you need an injection container
www.toppa.com @mtoppa
35. 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."
37. When to prefer OOP to procedural programming
www.toppa.com @mtoppa
38. Object oriented programming
for WordPress plugin
development
Mike Toppa
WordCamp NYC
June 9, 2012
www.toppa.com @mtoppa
Skill level
Theory and practice
Won't be an expert
We will be looking at code
39. www.toppa.com @mtoppa
What I'll cover is not specific to WordPress
When you write a plugin you are writing software
Your software should be organized around its use
cases, it should not be organized around WordPress'
architecture
That is, you should not start with something like a
“plugin” class. Your classes should be organized
around the business problem they are trying to
solved, not around the details of WordPress.
40. 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
www.toppa.com @mtoppa
41. Overview
â—Ź Nuts and bolts of classes and objects in PHP
â—Ź The single responsibility principle
â—Ź The dependency inversion principle
â—Ź When to prefer OO to procedural programming
www.toppa.com @mtoppa
43. Simple example
class 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
44. 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.
45. Prefer private properties
class 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.
46. 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.
47. Type Hinting
class Lamp {
private $bulb;
public function __construct(Bulb $bulb) {
$this->bulb = $bulb;
}
}
---------------------------------------------
$myBulb = new Bulb();
$myLamp = new Lamp($bulb);
48. Organizing your classes
www.toppa.com @mtoppa
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
Get a real IDE that autocompletes variable names and
method names
49. Initializing your OO plugin
// this is the start.php file for a “Lamp” WP plugin...
// require or autoload the “main” class file for your plugin
// and then...
$lamp = new Lamp();
$lamp->run();
// that's it!
51. Abstract classes and methods
abstract 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
52. Implementing abstract classes
class 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;
}
}
53. 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.
54. Using interfaces: the facade pattern
Functions
A PHP
facade
application
interface
WordPress Drupal Some other
facade facade facade
implementation implementation implementation
A different implementation of the facade would allow
the PHP application to work outside of WordPress,
without touching the application's code
When you write a class to implement an interface, it
can interact with other classes that know how to talk
to that interface, without those other classes having
to know anything about your particular
implementation of the interface
55. Interface Example
interface FunctionsFacade {
public function enqueueStylesheet($handle);
}
------------------------------------------------------------------------
class FunctionsFacadeWp implements FunctionsFacade {
public function enqueueStylesheet($handle) {
return wp_enqueue_style($handle);
}
}
// we'll look at how to actually use the interface in a few minutes...
56. The SOLID Principles
â—Ź Single Responsibility (SRP)
â—Ź Open-Closed (OCP)
â—Ź Liskov Substitution (LSP)
â—Ź Interface Segregation (ISP)
â—Ź Dependency Inversion (DIP)
www.toppa.com @mtoppa
Introduced by Bob Martin in his book “Agile Software
Development”
57. 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
58. The purpose is to reduce the
complexity and fragility
of a class
www.toppa.com @mtoppa
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
59. But what does it mean to do “one thing”?
www.toppa.com @mtoppa
61. Only one reason to change
www.toppa.com @mtoppa
A typical example is when business logic is entangled
with the user interface. If you want to develop an
RSS feed for a web page, and can't create the feed
without tearing apart or copying-and-pasting code
that's woven into your HTML, then you've got code
that has more than one reason to change.
62. Classes Example
ShashinDisplayer.php
ShashinAlbumDisplayer.php
ShashinAlbumDisplayerPicasa.php
ShashinAlbumDisplayerTwitpic.php
ShashinAlbumDisplayerFlickr.php
ShashinPhotoDisplayer.php
ShashinPhotoDisplayerPicasa.php
ShashinPhotoDisplayerTwitpic.php
ShashinPhotoDisplayerFlickr.php
You can start to see the power of the OO 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
63. Methods Example
class ShashinInstall {
public function run() {
$this->createAlbumTable();
$this->verifyAlbumTable();
$this->createPhotoTable();
$this->verifyPhotoTable();
$this->updateSettings();
return true;
}
// ...
}
64. From LosTechies.com
It's common to see code that hard-wires together all
the parts, when those connections could be made
more flexible and extensible
65. NaĂŻve model of a button and lamp
Lamp
Button
+ turnOn()
+ poll()
+ 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”
}
This solution violates the DIP
â—Ź Button depends directly on Lamp
â—Ź Button is not reusable
â—Ź It can't control, for example, a Motor
66. Dependency inversion applied
<<interface>>
Button SwitchableDevice
+ poll() + 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
67. SwitchableDevice and Lamp
interface SwitchableDevice {
public function turnOn();
public function turnOff();
}
--------------------------------------------------------------
class Lamp implements SwitchableDevice {
public function turnOn() {
// code
}
public function turnOff() {
// code
}
}
68. Button
class Button {
private $switchableDevice;
public function __construct(SwitchableDevice
$switchableDevice) {
$this->switchableDevice = $switchableDevice;
}
public function poll() {
if (/* some condition */) {
$this->switchableDevice->turnOn();
}
}
}
69. Using the Button
// require or autoload the class files, then...
$lamp = new Lamp();
$buttonForLamp = new Button($lamp);
$buttonforLamp->poll();
$motor = new Motor();
$buttonForMotor = new Button($motor);
$buttonForMotor->poll();
70. Dependency Chains
If class A depends on class B,
and class B depends on class C,
class A should be blissfully unaware of class C
www.toppa.com @mtoppa
71. To do this without going insane,
you need an injection container
www.toppa.com @mtoppa
72. 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
73. 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
74. When to prefer OOP to procedural programming
www.toppa.com @mtoppa
This can be subjective. For me, it hurts my brain to
program procedurally, even for small projects.
For a very small project, OO will usually involve more
code
If you can classify the components of an application
into objects with properties and methods, and you
have an idea of what kind of changes will come in
the future, an OO approach is very powerful.
Also, if you want to do unit testing, you need OO code