0% found this document useful (0 votes)
71 views112 pages

Mastering Object Oriented PHP 24

Mastering Object-Oriented PHP by Sarah Savage aims to demystify object-oriented programming principles through practical examples rather than complex theory. The book covers key concepts such as dependency injection, dependency inversion, and various design principles, providing readers with a resource that emphasizes readability and applicability. It also introduces the use of dependency injection containers to simplify the management of dependencies in PHP applications.

Uploaded by

me
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
71 views112 pages

Mastering Object Oriented PHP 24

Mastering Object-Oriented PHP by Sarah Savage aims to demystify object-oriented programming principles through practical examples rather than complex theory. The book covers key concepts such as dependency injection, dependency inversion, and various design principles, providing readers with a resource that emphasizes readability and applicability. It also introduces the use of dependency injection containers to simplify the management of dependencies in PHP applications.

Uploaded by

me
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 112

Mastering Object-

Oriented PHP

SARAH SAVAGE
Copyright
& Credits
The contents of this book, unless otherwise indicated, are copyright © 2022 - 2023 Sarah
Savage, All Rights Reserved.

This version of the book was published on April 30, 2024.

Books like this are made possible by the time and investment of the authors. If you
received this book but did not purchase it, please consider making future books possible
by buying a copy.
Introductioni
— Dependency Injection 2
— Liskov Substitution Principle 18
— The Open/Closed Principle 27
— Interface Segregation Principle 39
— The Single Responsibility Principle 46
— The Law of Demeter 56
— Antipatterns of Object-Oriented Development 73
— Exceptions 90
— Action-Domain-Responder 102
What comes to your mind when you think of object-oriented
programming? Complex? Difficult? Impossible to grasp? I know
that I struggled to grapple with the principles of object-oriented
programming and design for years. Lots of people do. Perhaps you’re
one of them. Perhaps that’s why you’re reading this book.

I feel as though some of object-oriented programming’s detractors


have a point: those of us who teach object-oriented design often do
so from a perspective of using technical terms to describe even more
technical terms. You’ve probably heard them before. Immutable.
Contract. Polymorphism. Indirection.

Books about object-oriented design are often incomprehensible,


filled with theory, and very challenging to digest. That’s why I
wrote this book: to write a book that is practical and modern, that

M A S T E R I N G O B J E C T- O R I E N T E D P H P — i
— INTRODUCTION

establishes once and for all a resource that you can go to for answers.

This book is designed with readability and applicability in mind.


Rather than a deep dive into theory, it’s focused on presenting
the facts. Sure, we’ll discuss theory, but we’ll do so with concrete
examples that highlight exactly what the theory is trying to teach us.

So, dive in. Enjoy the book!

M A S T E R I N G O B J E C T- O R I E N T E D P H P — ii
There is often some confusion between the concepts of dependency
inversion and dependency injection. Dependency injection is the process of
injecting dependencies into an object or function as an argument, avoiding the
need for globals or statics in order to access them. We will discuss dependency
injection in detail in a moment.

Dependency inversion, on the other hand, is the process of inverting the


dependency tree through the use of abstractions, thus ensuring that lower-level
modules depend upon higher-level abstractions rather than higher-level details.

Let’s take a close look at each principle, starting with dependency injection.

DEPENDENCY INJECTION
When we develop an application, we have to decide how we’re going to manage
dependencies in terms of other objects, functions, and methods. We need some

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 2
— DEPENDENCY INVERSION

way of providing some objects to other objects, while maintaining a simple


structure so that we can understand what we’ve done and why.

The simplest way to provide one object to another object is to instantiate it,
whether that be in the constructor or at runtime when the object is needed. You
can accomplish instantiating the object line this:

<?php

class Controller
{
public function __construct()
{
$this->cache = new ApcCache();
}
public function manageData()
{
return $this->cache->get('someKey');
}
}

In the code sample provided, we’re simply instantiating the object we need and
then using it in Controller::manageData().

This has several advantages. It provides easy access to the objects that we need
and allows us to control when those objects are created, ensuring that we gain
access to the right object at the right time. It also streamlines the instantiation
of the Controller class, because all we have to do is instantiate new
Controller().

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 3
— DEPENDENCY INVERSION

There are some drawbacks to this approach as well. Chief among those
drawbacks is that this is difficult if not impossible to test in isolation: we always
get an instance of ApcCache() whenever we instantiate the Controller
class. We’re also tied to a particular implementation of ApcCache; we can’t use
another cache type like MemcacheCache or NullCache for development or
testing.

To solve for this, we can take a couple of strategies. One of them is to use service
location to locate the dependency we want and make it available to our code.

<?php

class Controller
{
public function __construct($cache = 'APCCache')
{
$this->cache = CacheLocator::locate($cache);
}

public function manageData()


{
return $this->cache->get('someKey');
}

In this example, we pass in an argument of which cache type we want. This


solves many of our previous problems: we can now select a different cache type
than ApcCache if we so desire and we can use NullCache in development or
MemcacheCache if we want in production.

However, there are some problems with this approach. The biggest

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 4
— DEPENDENCY INVERSION

problem is that we are now tied directly to the service locator. We have an
inextricable link with the CacheLocator object and the global call to
CacheLocator::locate(). This makes testing possible, but very difficult,
because we have this global involved in our code. In addition, we’re not focused
on extracting the dependency from the Controller class.

What’s the solution then? The answer is dependency injection, a process by


which we inject dependencies into the class. This breaks the dependency upon
outside locators and also allows us to substitute, mock or stub a dependency for
testing purposes. Dependency injection looks like this:

<?php

class Controller
{
public function __construct(APCCache $cache)
{
$this->cache = $cache;
}
public function manageData()
{
return $this->cache->get('someKey');
}
}

Note that now we are injecting the APCCache class. This is dependency
injection in its simplest form: there’s no need to make it any more complicated
because it isn’t.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 5
— DEPENDENCY INVERSION

Dependency injection provides the greatest set of advantages and the least
disadvantages over any of the other methods.

The advantages of dependency injection are numerous. We have the ability to


mock or substitute the object we’re injecting. We can also take advantage of the
injection process to inject an object that behaves similarly but works differently
on the backend (this is part of dependency inversion, which we’ll be discussing
shortly). Because we’re using a type hint as part of our definition, we are
essentially saying, “we want this object, or a type of this object, every time,” and
PHP is enforcing this for us. All injected objects must conform to the APCCache
specification.

Dependency injection isn’t without its disadvantages. Chief among them is that
it’s much more complex to instantiate the Controller class now that we have
dependencies. Previously, we could simply rely upon new Controller() in
both the hard-coded dependency and the service locator dependency models.
Now we must pass in a separate object that must be instantiated, and ultimately
may have its own dependencies. This can lead to long dependency chains,
simply to instantiate the object that we want. Many developers use dependency
inversion containers to help instantiate objects precisely for this reason.

DEPENDENCY INVERSION
Now that we’ve covered dependency injection, let’s cover dependency inversion.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 6
— DEPENDENCY INVERSION

According to the definition, dependency inversion is a principle stating that


“high-level modules should not depend on low-level modules. Both should
depend on abstractions. Also, abstractions should not depend on details. Details
should depend on abstractions.” (Wikipedia)

DEPENDENCE ON ABSTRACTIONS
The core of the dependency inversion principle is the concept of dependence
upon abstractions. Both high- and low-level modules should depend upon
abstractions. But what is an abstraction?

PHP equips us with a concept for abstractions in both the abstract class
construct and the interface construct.

<?php

interface MyInterface
{
public function MyMethod();
}

abstract class MyAbstractClass implements MyInterface


{

Here we have defined a simple interface with a single method. (Interfaces in PHP
are not required to define any methods; you can have an empty interface.) We
have then defined an abstract class that implements the interface but does not

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 7
— DEPENDENCY INVERSION

actually implement the method in the interface (thus requiring the class to be
abstract). Both of these are perfectly valid abstractions.

The dependency inversion principle is essentially stating that we should (must)


depend upon similar abstractions whenever we build lower-level modules in
our code base. For example, if you have a low-level database class, higher-level
modules should not depend on the database class directly; instead, they should
depend upon a low-level abstraction of the database class.

This is where the importance of the “details” clause comes into play. Abstractions
should inform the details that go into your concretion, rather than the other way
around. By removing details from your abstractions, you remove the need for
higher-level modules to know the details. This frees the higher-level modules to
work the way you intend, while being unencumbered by the details of the lower-
level modules. This is a much better way to work.

What does this look like in practical terms? Take for a moment our Controller
class, which we are injecting APCCache into:

<?php

class Controller
{
public function __construct(APCCache $cache)
{
$this->cache = $cache;
}

public function manageData()

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 8
— DEPENDENCY INVERSION

{
return $this->cache->get('someKey');
}
}

The APCCache class is a concrete class. We’re relying on the concretion, rather
than the abstraction. This means that there’s a potential to leak details into the
Controller class, which could cause us to have higher levels of coupling. To
resolve this, let’s define and use a Cache interface:

<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache


public function purge();

}
class Controller
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
public function manageData()
{
return $this->cache->get('someKey');
}
}

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 9
— DEPENDENCY INVERSION

Note the change to the Controller class: the hint on the Cache interface,
rather than on the concrete APCCache class.

How is this better? It provides several advantages. First and foremost, we can
more easily substitute different instances of the Cache interface, realizing
the full potential of dependency injection. It also helps us follow the Liskov
Substitution Principle, which we’ll discuss later. It makes testing easier, and
it promotes low cohesion between classes, which is desirable because low
coupling improves reuse.

For the Cache interface, consider that it’s simply a matter of defining a
VoidCache class to have a cache available for development that doesn’t cache
at all. We simply need to put the class together and it meets the requirements:

<?php

class VoidCache implements Cache {

public function get($key) {


return null;
}
public function set($key, $value) {
return true;
}

public function delete($key) {


return true;
}

// Purge the whole cache

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 10
— DEPENDENCY INVERSION

public function purge() {


return true;
}
}

DEPENDENCY INJECTION CONTAINERS


When we use dependency injection together with dependency inversion,
instantiating objects can become quite complicated in short order. That’s why
many, if not most, modern frameworks in PHP include a dependency injection
container, to provide us with a simple means for injecting our dependencies.

A dependency injection container is relatively simple. Given a class name, it


searches a registry to determine if it knows how to build that particular class. If
it does, great! It builds the class for us and returns a fully constructed class. If it
doesn’t, it can try a few different options, from auto-wiring (using reflection) to
throwing an exception.

There are several very simple containers on the market that simply take
arguments and return a result. There are also significantly more complex
containers available that use reflection to determine the correct parameters and
can construct objects using parts previously defined, even if the object itself is
not defined. Choosing the right container is up to you.

Let’s build ourselves a dependency injection container to learn how they work.
Let’s start with storing data in the container.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 11
— DEPENDENCY INVERSION

<?php

class Container
{
/**
* @var array
*/
protected $registry = [];

/**
* @param $className
* @param callable $builder
*/
public function register($className, callable
$builder) {
$this->registry[$className] = $builder;
}
}

Here we have a very simple process for registering a new object definition. Our
first argument is the classname. This is best as a fully qualified class name, but
can also be a “named service,” e.g., “DatabaseConnection”. Second, we
accept a callable. We’re type hinting for a callable to make sure we always receive
one. We use a callable here because we don’t want to create the object right
away; we want to lazy load the object on demand. A callable lets us define the
code we’re going to use, without actually running it until it’s needed (sometimes
DI containers can have hundreds or thousands of definitions; we don’t want to
instantiate those unless we need them!).

Now we can define some dependencies:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 12
— DEPENDENCY INVERSION

<?php

$container = new Container;

$container->register(SomeClass::class, function() {
return new SomeClass();
});

We also want to be able to build our objects by calling the callable (or the
invokable if we so choose), so we need a method that builds out our objects.
Here’s where we need to make some decisions.

Some containers will have the ability to use reflection and determine the
parameters, then go out and instantiate those objects automagically for you. For
a simple DI container, however, we aren’t interested in doing that. We probably
want to throw an exception in the case where the class we’re trying to build is not
found inside our DI container.

Let’s add our build method now:

<?php

class Container
{
/**
* @var array
*/
protected $registry = [];

/**
* @param $className

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 13
— DEPENDENCY INVERSION

* @param callable $builder


*/
public function register($className, callable
$builder) {
$this->registry[$className] = $builder;
}
/**
* @param $className
* @return mixed
*/
public function build($className) {
if (!isset($this->registry[$className])) {
throw new \InvalidArgumentException(
$className . ' is not registered');
}

return $this->registry[$className]();
}
}

This is relatively simple and straightforward as well: we accept a classname (or


a service name if you’ve provided one) and look it up in the registry. If it’s not
found, we get an InvalidArgumentException, but if it is found, we invoke
it and return the resulting object.

Now that we have our build method, we can construct some more complex
objects, including using the container to provide dependencies when they’re
required.

<?php

$container = new Container;

$container->register(Database::class, function() {

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 14
— DEPENDENCY INVERSION

return new Database();


}

$container->register(Controller::class,
function() use ($container) {
return new Controller(
$container->build(Database::class)
);
});

Notice how we’re passing the container into the closure, allowing the container
to be used to build our dependency and provide it directly to the Controller
when it was needed.

There are some important antipatterns to watch out for when using a DI
container. First is that a DI container is mainly a piece of infrastructure, and it
shouldn’t at all be involved with the heart of your application. For example,
if you’re passing the DI container into your controllers for locating your
dependencies, you’re probably not using the DI container in the way it was
intended.

In addition, it’s important to note that service location is not the same thing as
dependency injection. Service location relates to the locating of dependencies
on demand and is accomplished by passing the DI container into the class that
requires it or by using a static call to a globally available container. Neither
approach is a good one: you’re tied directly to the DI container in both, and
you can’t effectively type hint or evaluate your dependencies for correctness at
runtime.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 15
— DEPENDENCY INVERSION

CONCLUSION
Dependency inversion is an essential component of the SOLID principles,
offering us the foundation upon which we will build all the other components.
With dependency inversion we can know that our dependencies are properly
abstracted and that our application is architected in a reasonable way.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 16
One of the advantages to using dependency inversion is that it prepares us
for the possibility of substituting objects of similar type for one another. This is
what the Liskov Substitution Principle is all about: the substitution of one object
for another object of the same type (or subtype), without breaking the program.

LISKOV SUBSTITUTION, DEFINED


The Liskov Substitution Principle was developed by a woman named Barbara
Liskov, along with Jeannette Wing. It is a particular type of subtyping, focused
on the suitability of subtypes to replace other types of the same object. When
following the Liskov Substitution Principle, we can assert that any object in a
program should be subtype of object D, then A can replace instances of D in the
program.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 18
—LISKOV SUBSTITUTION

This was a major breakthrough in computer science and today remains a crucial
principle in object-oriented design and development. The
creation of a class of objects that behave
BARBARA LISKOV AND
similarly but are internally unique is a JENETTE WING

breakthrough from one-off or unique Barbara Liskov introduced


objects. The focus is on creating object the Liskov Substitution
classes. Essentially, we are asserting that Principle in a 1988 keynote
we should program to a particular interface, address called Data
rather than to a specific implementation. abstraction and hierarchy.
It describes a semantic
rather than merely syntactic
DESIGN BY CONTRACT relationship, and guarantees
The Liskov Substitution Principle is closely semantic interoperability
related to another principle by Bertrand of types. Together with
Meyer, called design by contract. This Jeannette Wing, the
principle asserts that we should define principle was further
contracts for our programs, thus ensuring described in a 1994 paper.
consistency throughout the application The formal requirement
development process. establishes mathematic
proof for subtype objects.
What should a contract contain? The
contract should contain a formal, precise, verifiable specification for software
components.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 19
—LISKOV SUBSTITUTION

The formal specification piece comes from the concept of defining an interface
in the first place. For example, we defined an interface for our Cache class in
the last chapter, which is a formal interface by Meyer’s standard.

<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache


public function purge();

The interface we have defined is also precise. It specifies all the preconditions,
postconditions, and invariants that we need to worry about right in the
specification. The precision of our interface can be improved by adding type
hints and return type hints; while that would be impractical with the example
we have provided, it is a valuable thing to do for other specifications and only
enhances the overall contract.

Finally, our specification is verifiable. We can test it, and we can do so easily. Any
cache I drop into a set of unit tests written for this interface will pass, assuming
that the preconditions and postconditions are preserved. If I have broken any
of the rules of the interface, the tests will fail, and I will know that I have made a

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 20
—LISKOV SUBSTITUTION

mistake. In other words, I am guaranteed that my tests will verify adherence to a


given specification for any variant.

An important part of designing by contract and the Liskov Substitution Principle


is the concept of inheritance and how an object behaves in relationship to
inheritance. There are three basic principles that must be followed:

1. Preconditions cannot be strengthened in a subtype.


2. Postconditions cannot be weakened in a subtype.
3. Invariants of the supertype must be preserved in a subtype.

This is why, when it comes to precondition strengthening, method signatures


must match their interface precisely (with very few exceptions) in PHP. When
you have defined a method signature, you are creating a contract and that
contract must be applied across all subtypes of that object. You cannot simply
say in the interface that you’ll accept a string as an argument type, and then
in a subtype assert that it must be an object; that would make substitution
impossible for the subtypes that could not accept an object.

The same is true for postconditions. Return types are part of the postcondition:
they enforce what will be returned. You should use return types as part of your
interfaces to enforce what is being returned. Not only does this help further
define the interface specification, but it also cleans up your code. For example,
in many applications it’s common to return a string or an object on success, with
a boolean on failure. This is not possible with return type declarations. Instead,

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 21
—LISKOV SUBSTITUTION

you must throw an exception and return only the type you’re expecting. This
results in much better, bug-free code that’s easier to read.

Designing by contract makes it much easier to maintain and work with our code,
as well as to follow the Liskov Substitution Principle.

WHEN THE CONTRACT IS BROKEN


But what happens when we, knowingly or unknowingly, break the contract?
What does it look like to “break the contract”?

The Liskov Substitution Principle asserts that we should be able to substitute


any subtype of an object for the original. That is, any object that implements
the Cache interface should be a suitable replacement for any other object that
implements the Cache interface.

Take the following controller for example:

<?php

class User_Controller
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}

public function getUserContacts()

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 22
—LISKOV SUBSTITUTION

{
if ($contacts = $cache->get('user_contacts')) {

return $contacts;
}
}
}

This simple controller takes Cache as an argument and uses the cache to look
up the user contacts if they exist in the cache.

Presume for the moment that we are using Memcache as our cache type. (We
could be using any other cache but for this implementation we’ll focus on
Memcache.) Our implementation makes sense, except for a persistent bug:
Memcache keeps failing to stay connected. In that case, we want to add a
MemcacheCache::reconnect() method to the cache object:

<?php

class MemcacheCache implements Cache


{
// all the other methods

public function reconnect()


{
$this->memcache->connect($this->host, $this-
>port);
}
}

This simple method will reconnect to the server for us, ensuring that we always
have a connection. Great! Now let’s add this to our User_Controller class:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 23
—LISKOV SUBSTITUTION

<?php

class User_Controller
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}

public function getUserContacts()


{
$cache->reconnect();
if ($contacts = $cache->get('user_contacts')) {
return $contacts;
}
}
}

But now we have a problem: we’re type hinting on the Cache interface, but
we’re depending on details that are specific to the MemcacheCache class
(particularly the MemcacheCache::reconnect() method).

This breaks our contract. The Liskov Substitution Principle no longer applies
because we can no longer rely upon the Cache interface to tell us if we’re
meeting the requirements of the class. In addition to breaking the contract,
we are also breaking the Dependency Inversion Principle, because we are now
dependent on the details of the MemcacheCache class, rather than dependent
on the abstraction in our Cache class.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 24
—LISKOV SUBSTITUTION

CONCLUSION
The Liskov Substitution Principle is a powerful component of the concept
of reuse and replacement of objects, and it allows us to achieve great things
through substitution and replacement. However, there are pitfalls along the
way: it’s possible to create objects that pass a type hint, but don’t actually follow
the principle as a whole.

Expressing a contract and then breaking that contract produces chaos in an


application and results in unpredictable behavior when attempting to apply the
Liskov Substitution Principle. In addition, extending the interface violates the
Dependency Inversion Principle, since we now are dependent on the details,
rather than the interface (abstraction). Both of these are important concepts to
remember and consider when choosing to extend the interface of an abstraction
in any meaningful way.

Yet it’s impossible to completely avoid extending the interface in real world
application development. So what are we to do? In the next chapter we’re going
to discuss how we create contracts that honor the Liskov Substitution Principle
(and the Dependency Inversion Principle), and how we ensure that our objects
are able to be modified and extended.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 25
A TALE OF TWO PRINCIPLES
If you look up the definition of the open/closed principle on Wikipedia, it will
tell you that “software entities should be open for extension, but closed for
modification.” What it doesn’t tell you, at least not at first, is that this is one of
the least understood, most violated, and most challenging principles in the
entire SOLID world.

This is all for a good reason: depending on who you ask, there is more than one
open/closed principle. In fact, there are two.

The open/closed principle was originally developed by Bertrand Meyer as part


of his 1988 book Object-Oriented Software Construction. Mr. Meyer is a brilliant
computer scientist who contributed lots to our understanding of object-
oriented design and theory, including the open/closed principle. The problem
is, most often we don’t follow Meyer’s open/closed principle anymore. Instead,
M A S T E R I N G O B J E C T- O R I E N T E D P H P — 27
— OPEN/CLOSED PRINCIPLE

we follow a principle called the Polymorphic Open/Closed Principle.

Confused yet? That’s okay. We’ll explore both and see how each principle
applies, both historically and practically, to the object-oriented code that we
write today.

MEYER’S OPEN/CLOSED PRINCIPLE


When Bertrand Meyer was writing about the open/closed principle, software
was very different than it is today. Creating a class that could be subclassed
or used by different consumers was a time-consuming process involving
compilation of your code and providing that code on physical media for
distribution. There was no real concept of open source. Simply put, in Meyer’s
world, software was much more challenging than it is today.

As a result, Meyer’s Open/Closed Principle is a bit more cumbersome than


perhaps we need today. It asserts that software entities (classes in particular)
are open for extension through inheritance, but closed to internal modification
once they are set.

In other words, I can extend a class all day long, but I cannot change the
internals or the behavior of that class once it has been set. The act of inheriting
is what gives me the ability to extend and modify that class. But the original is
set in stone.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 28
— OPEN/CLOSED PRINCIPLE

I’ll let Meyer tell it in his own words:

“A class is closed, since it may be compiled, stored in a library, baselined, and


used by client classes. But it is also open, since any new class may use it as
parent, adding new features. When a descendant class is defined, there is no
need to change the original or to disturb its clients.” - Bertrand Meyer (Object-
Oriented Software Construction, p. 229)

What Meyer is asserting here is that the ability to inherit from a parent class
gives you the ability to have an open class, because the child may implement
new features.

However, Meyer’s Open/Closed Principle is impractical from several


perspectives. First and foremost, we prefer composition, not inheritance
(covered in another chapter). In addition, in the world in which we now operate
the ability to distribute software has changed. We can resolve the issues of code
reuse and inheritance through semantic versioning. Simply put, this principle is
not as practical or useful anymore as it was when it was originally devised.

T H E P O LY M O R P H I C O P E N / C L O S E D P R I N C I P L E
So, if we reject Meyer’s Open/Closed Principle as impractical, do we reject
the “O” in SOLID altogether? Absolutely not. The computer science world has
shifted, and the “O” in SOLID has shifted right along with it.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 29
— OPEN/CLOSED PRINCIPLE

Enter the Polymorphic Open/Closed Principle.

According to the Polymorphic Open/Closed Principle, it is the interface, not the


internals, that make an object, and it is therefore the interface that cannot be
changed in later classes. We know this is consistent with the Liskov Substitution
Principle, because when we changed the interface, we broke the ability to
substitute. Remember back to this code:

<?php

class User_Controller
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}

public function getUserContacts()


{
$cache->reconnect();
if ($contacts = $cache->get('user_contacts')) {
return $contacts;
}
}
}
The Open/Closed Principle tells us that we should not have extended the
interface or relied on the details of our implementation. This is a violation of the
open/closed principle.

The reason that this violates the open/closed principle has to do with object
type. An object’s type refers to its behavior, which is defined in the public

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 30
— OPEN/CLOSED PRINCIPLE

methods that an object exposes to the rest of the world. The computer is
uninterested in the class name we’ve given an object. It only cares what public
methods are accessible and what we’ve given the object as its behaviors.

When we extend the interface, we change the inherent type of the object. Even
though it might pass a class name type hint, we have still changed the type. It’s
important to recognize that PHP is not type safe! PHP will allow objects that are
technically of a different type to pass a class-name-based type hint, but it will
not check true types. We must do this ourselves as developers.

So, what is a good developer to do in order to preserve type or honor the


interface? We need the MemcacheCache::reconnect() method, but we
don’t want to break the rules, or, at least, break all of the rules.

The answer lies in making a tradeoff. Here, in this case, we want to type hint
on MemcacheCache specifically. This technically ignores the Dependency
Inversion Principle because we’re technically depending on details, but it allows
us to honor two other principles. By definition, MemcacheCache is unique, so
it’s not substitutable (which honors Liskov Substitution), and, by extending it
and acknowledging the extension, is creating a new type, we honor the Open/
Closed Principle.

<?php

class User_Controller
{
public function __construct(MemcacheCache $cache)

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 31
— OPEN/CLOSED PRINCIPLE

{
$this->cache = $cache;
}

public function getUserContacts()


{
$cache->reconnect();
if ($contacts = $cache->get('user_contacts')) {
return $contacts;
}
}
}

A SIDE NOTE ABOUT SOLID PRINCIPLES


Honoring the SOLID principles in the code that you write is extremely
important. But the rules as explained here are neither hard nor fast. They are
principles, intended to guide your development of application code to the best
practices we have established over time.

It is impossible to follow the SOLID principles, or any set of rules, 100% of the
time. That’s why they’re considered guiding principles. They are not laws; they
do not apply all the time. It’s important to recognize this fact.

In my own development I am able to honor each of the SOLID principles in


about 80% of the code that I write. For the other 20%, there are tradeoffs that
must be made. You will experience these tradeoffs as well. The key is not to
avoid tradeoffs when they are necessary. Carefully consider what you’re trading
off and understand what the consequences of that decision might be.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 32
— OPEN/CLOSED PRINCIPLE

So, when you find yourself faced with a situation where you must use a concrete
class over an abstraction, or you need to break the interface, accept the
tradeoff, and move on. This isn’t permission to ignore the principles at will. It’s
acceptance that real life doesn’t always follow the prim and proper science of a
computer science classroom.

THE RETURN TYPE


Beginning in PHP 7, we gained the ability to define a return type for our classes
and interfaces. PHP would never be the same again, and it also would never be
easier to implement the open/closed principle. This is because the return type
is part of the interface, and we should treat it as such.

The return type refers to the type of response from a method or function (e.g.
string, object, array). This is part of the interface, because it helps us to identify
how a function behaves. A function that returns an array and only an array and
suddenly starts returning a string will only confuse our code.
If you are writing PHP 7 code, you should be using return types as often as
possible with your objects. Return types are easy to define and look something
like this:

<?php

interface Cache {

public function get($key) : string;

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 33
— OPEN/CLOSED PRINCIPLE

public function set($key, $value) : bool;

public function delete($key) : bool;

// Purge the whole cache


public function purge() : bool;

As you can see, we define a return value for our methods. These return values
are important because they inform our use of the interface and make things
consistent across our code. We can’t and we won’t suddenly start returning
arrays or objects from the Cache::get() method.

But what about cases where you are returning a string on success and a null on
failure? Cases like this:

<?php

function addNumbers($a, $b)


{
if (! $a || ! $b) {
return null;
}

return $a + $b;
}

This is a classic old style error handling mechanism, whereby we return null
as quickly as possible when we can’t execute the code. But this is broken for a
number of reasons.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 34
— OPEN/CLOSED PRINCIPLE

First, this is broken because we have more than one return type, meaning we
have to test for a particular return type. Consider the following checking code:

<?php

$result = addNumbers($a, $b);


if (is_null($result)) {
exit;
} else {
// Do something
}

Because I have to test whether or not this is null, my code gets pretty ugly pretty
fast. And as a result, my code is littered with lots of if(is_something())
references.

In addition, it’s impossible for me to predict with certainty what the behavior
of this function will be. I cannot know with certainty that I’ll get back a valid
response each and every time the function completes.

What I should do instead is use exceptions. When I’m unable to process the
addition for any reason, that’s an exceptional event and I should throw an
exception that the calling code can handle or pass up to its consuming code.
Now my code looks like this:

<?php

function addNumbers($a, $b)

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 35
— OPEN/CLOSED PRINCIPLE

{
if (! $a || ! $b) {
throw new \InvalidArgumentException;
}
return $a + $b;
}

If my arguments are invalid, I raise the exception; the only time I get a return
value from the function is when the function worked properly.

My consuming code now needs to be changed as well, to use a try-catch block:

<?php

try {
$result = addNumbers($a, $b);
// do something
} catch (\InvalidArgumentException $e) {
$this->log($e->getMessage());
}

At first glance, this might appear to be almost the same as the is_null()
check, but it’s not. First and foremost, there’s not a break between the call
of the function and the work being done after getting the return value. And
second, by catching an exception we have the ability to trigger more than one
exception if necessary and bubble up problems to calling code, or to even halt
the application entirely.

Some might argue that PHP 7.1’s inclusion of nullable return type declarations
means that returning null is valid. I disagree. I feel strongly that returning

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 36
— OPEN/CLOSED PRINCIPLE

null OR something else changes the behavior of an object and makes it


unpredictable. In addition, this isn’t about null per se; this is about ALL variable
return types. For example, returning integer on success but false on failure
would violate the same rules outlined here.

The bottom line is, the return type is part of the object’s definition, and it
should be consistent across all calls. You should use return type declarations as
often as possible to cement the return type you expect and make your objects
predictable.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 37
We know now that extending the interface is not a preferred way to
increase the functionality of our application. But if we can’t extend the
interface, what kind of interfaces should we be building in the first place?

The answer is that we should be building small interfaces. Our interfaces should
be discrete.

SMALLER INTERFACES ARE BETTER


The interface segregation principle states that no object should be forced to
depend upon or implement methods it doesn’t use. Put another way, every
method of your interface should be useful in the class that implements that
interface and provide services to that object.
What does this mean practically?

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 39
—INTEGRATION SEGREGATION

It’s easy to define an interface that contains every conceivable option within
it. For example, going back to our prior example, we might decide that the
reconnect() method really does need to be a part of the Cache interface,
so we might define it thusly:

<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache


public function purge();

public function reconnect();


}

But what happens now, when we go to implement a cache that doesn’t require
connection with a server? Consider a file-based cache or APC: neither of them
requires a connection with the server. So, our code ends up looking like this:

<?php

class APCCache implements Cache


{
public function get($key)
{
return apc_fetch($key);
}

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 40
—INTEGRATION SEGREGATION

public function set($key, $value)


{
return apc_store($key, $value);
}

public function delete($key)


{
return apc_delete($key);
}

// Purge the whole cache


public function purge()
{
return apc_clear_cache();
}

public function reconnect()


{
throw new \BadMethodCallException(
'This function is not implemented');
}
}

Notice how we now have a method that we are not implementing, that
generates an exception at runtime, for no reason other than it’s not needed.
This violates the interface segregation principle, because our object is
dependent upon a method that it does not care about or need to implement.

This is exactly the reason that small interfaces are better. The smaller the
interface, the less likely it is that you’ll find yourself needing to implement
methods that you don’t need.

Take the interfaces of the Standard PHP Library, for example.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 41
—INTEGRATION SEGREGATION

A SMALL EXAMPLE
Within the Standard PHP Library (SPL) there are a number of different interfaces
offering a variety of different functionality. For example, you could create an
object that functions identically to an array using the interfaces provided within
the SPL.

When talking about small interfaces, take for example the Countable
interface:

<?php

interface Countable
{
public function count();
}

This simple interface does one thing for us: implements the code needed so
that when we call count() an object, we get back an integer that represents
the count.

We might want to take this a step further and implement a full array. There’s an
interface for that, as well. It’s called Iterator and it’s available to implement
the behaviors of an array:

<?php

interface Iterator extends Traversable


{
public function current();

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 42
—INTEGRATION SEGREGATION

public function key();

public function next();

public function rewind();

public function valid();


}

This interface, when applied to an object, makes the object iterable. In other
words, we can use the object in a foreach loop.

There may be cases where we want our iterable object to be countable as well.
In that case, we can combine the Iterator and Countable interfaces to
create an object that implements both:

<?php

class ArrayClass implements Iterator, Countable


{
public $collection = [];

public function count()


{
return count($this->collection);
}

public function current()


{
return current($this->collection);
}

public function key()


{

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 43
—INTEGRATION SEGREGATION

return key($this->collection);
}

public function next()


{
return next($this->collection);
}
public function rewind()
{
reset($this->collection);
}

public function valid()


{
return (bool) $this->current();
}
}

Notice how we took two smaller interfaces and combined them together to
make a larger interface, where no methods needed to be omitted.

This is one of the powers of interfaces: the ability to create larger classes from
smaller interfaces. For type-hinting purposes, we would say that if we care
about both Iterator and Countable we would typehint on ArrayClass.
We can also typehint on either of the specific interfaces if those individual sets
of methods are what we need to consider for our application.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 44
The Single Responsibility Principle is responsible for a paradox: more
writing has been done about this principle than any other, yet people
understand this principle the least.

In seeking to understand why this is so, I came across something that


shocked me: most of the misunderstanding of this principle stems from
misunderstandings that teachers have perpetrated for years. Some of those
misunderstandings are mine.

My goal, then, in this chapter, is to set the record straight. Hopefully I can bring
some clarity to the concept of the Single Responsibility Principle.

So, what is the Single Responsibility Principle all about?

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 46
— SINGLE RESPONSIBILITY

ONE CLASS, ONE JOB, RIGHT?


The Single Responsibility Principle boils down to saying that each class
should have a single responsibility, and that responsibility should be entirely
encapsulated by that class. All the services of that class should be narrowly
aligned with that responsibility.

Simple enough, yes?

Not so much.

This has oft been boiled down to saying, “one class, one job.” In other words,
each single class should have a single job that it performs, no more, and no less.
This seems on its face to be what the Single Responsibility Principle is all about,
right?

Wrong.

The definition I provided above doesn’t mention the concept of “jobs” at all.
It mentions responsibilities. Specifically, it mentions the responsibilities that
a class has, and how those responsibilities are encapsulated and aligned. It is
silent on the notion of jobs and the like.

This is a good thing, because the first thing most developers get hamstrung
by is the notion of “what’s a job, anyway?” And then we get into a technical
discussion of what constitutes a job, listing out specific jobs, rather than

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 47
— SINGLE RESPONSIBILITY

focusing on the point, which is the overall responsibility.

How then are we to evaluate this definition down to something we can easily
understand? The definition is distillable, like the “one class, one job” definition,
but we have to look for it.

I prefer, “one class, one responsibility.”

This little tweak has a big impact. Specifically, we are now focused on the
responsibility, or to use another word, the domain of a particular class. What
domain does the class rule over? What domain is the class responsible for? How
does the domain impact the application as a whole, and how can we make sure
that we’re managing that domain properly?

One thing of note to bring up here: when I’m talking about domain I’m talking
about an area of responsibility, not the domain layer of the application. They are
separate and distinct components.

Another important distinction in the “job” versus “responsibility” discussion


is that any class can have more than one job - as long as it fits squarely within
the domain. For example, a service might be responsible for both fetching data
from the database and saving the data back to the database - both are narrowly
aligned with the responsibility of managing that data. Different jobs, same
responsibility.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 48
— SINGLE RESPONSIBILITY

AN EXAMPLE FROM PDO


PHP’s built-in PDO class is a classic example of the Single Responsibility
Principle in action and the concept of having more than one job in a class, even
if the jobs narrowly align with a single responsibility.

Consider for a moment the base PDO class in PHP. It has several jobs. First, it
begins and ends transactions with the database. This is an important task that
belongs at the connection-level management component, which happens to
be PDO’s second job. The PDO class is also capable of sending simple queries to
the database and returning result sets. Finally, PDO is responsible for preparing
complex queries and returning a PDOStatement object. (Note: this isn’t
discretely a job of the domain, except that a connection is required).

The PDOStatement object has its own distinct set of jobs. It represents
a single PDO statement or query; it’s responsible for maintaining the state,
allowing for binding of values, etc., along with executing the statement against
the database; and finally, it’s responsible for returning the results back to the
consuming code. Each of these are separate jobs, but they fall under a common
set of responsibilities. Because they all fall under common responsibilities, they
belong within the various classes that PDO offers.

Imagine for a moment that we had broken out the jobs into individual classes.
We’d have tons of objects to work with! We’d have separate objects for
returning results and running simple queries. We’d have separate objects for
preparing queries and managing the database connection. All these objects

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 49
— SINGLE RESPONSIBILITY

would be tightly coupled together, because they are all dependent upon one
another. That’s not ultimately what we seek to achieve by applying the Single
Responsibility Principle.

Instead, we are focused on domain. The domain of the PDO class is connection
management. All the features of the object are narrowly aligned with that
domain save one: the ability to send simple queries. Even that feature is a matter
of convenience rather than a core behavior. The domain of the PDOStatement
object is to represent a specific query and provide functionality around using
that specific query to send or receive data from a database.

When we focus on domain rather than on discrete jobs, we come away with
far more robust objects. We also decouple our code, because the resulting
code is not dependent on a large number of related objects. When we use our
dependency, it’s the only dependency that we need because it is capable of
doing the entire job. This is called “encapsulation”.

E N T I R E LY E N C A P S U L AT E D
Remember back to our definition of the Single Responsibility Principle: “…that
responsibility should be entirely encapsulated by that class.” We asserted that
we should encapsulate all the duties pertaining to a particular responsibility
within a single class. But what exactly does this mean?

The definition I like best lies with Robert Martin, who said that you want to group

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 50
— SINGLE RESPONSIBILITY

together code that has “a single reason to change.” Martin states in a blog post
that we should “gather together the things that change for the same reasons.
Separate those things that change for different reasons.”

So, what does this mean in practical terms?

It provides us with another way to determine whether or not a job belongs


in a class, having little to do with its domain and more to do with whether
or not things change consistently through the class. For example, within the
PDO object, things change when the state of the connection is changed. In the
PDOStatement class, things change when the query itself changes. All the jobs
hinge on changing for that particular reason and no other.

Contrast that with a class that incorrectly does double duty. Take for example
a front controller that also acts as a router. The front controller is responsible
for instantiating the controller object, dispatching the controller object, and
determining what route was called so that the proper controller object can
be loaded. Those are three discrete jobs. One could argue that they fall under
a common domain, but where the class fails is when things change. Things
change for the router component when the route changes. Things change for
the instantiation and dispatching of the controller when the controller changes.
Those are distinct and different reasons to change.

In light of that, it makes sense to abstract the router away from the front
controller and into its own class. The router can change when the route path

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 51
— SINGLE RESPONSIBILITY

changes. Refactoring this away also enhances the possible functionality of the
router, providing a greater ability to develop new features for the component
that is no longer constrained by the front controller. The front controller, on
the other hand, is now free of the router logic and can be focused solely on
instantiating and dispatching the controller when the controller is properly
identified by a third-party service (i.e., the router as an object).

S P OT T I N G S I N G L E R E S P O N S I B I L I T Y V I O L AT I O N S
Sometimes, it can be difficult to identify when and where the Single
Responsibility Principle is being violated. Being close to a problem often
introduces a kind of blindness that can make it hard to see when things ought
not be grouped together or should be refactored apart. There are several
techniques you can use to identify these kinds of mistakes and fix them in your
own code. It’s important to note that none of these are hard and fast rules,
but guidelines aimed at helping you to identify Single Responsibility Principle
violations in your own code.

The first thing I look for is the number of dependencies being passed into a
particular object. Large numbers of dependencies (say, more than 5) usually
indicate that the object is doing far too much and should be refactored. There
may indeed be legitimate reasons for having a large number of dependencies in
a particular class, but that doesn’t mean that it doesn’t merit a look and see if
perhaps there’s an opportunity for refactoring or restructuring of the underlying
code.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 52
— SINGLE RESPONSIBILITY

In some cases, you’ll see only a few dependencies injected into an object, but
that object will not use all the dependencies in all the operations. As another
general rule, dependencies should be utilized by the operations of the class.

For example, if you are writing a service that inserts data into a database and
retrieves data from the same database, it makes sense to inject the data storage
component into the service for persistence. What doesn’t make sense is for you
to add a part of the class that formats the data and doesn’t utilize the database
at all. That belongs in a separate helper class that you should create outside the
service and potentially inject into the service.

Another way to tell if you’ve violated the Single Responsibility Principle is


checking how many methods are a part of the class itself. Good classes are
limited in the number of methods, usually because they don’t need many. A
count of four to seven non-getter/setter methods is usually the right limit for
most classes. Classes containing more methods than this may be doing too
much and have too many responsibilities. This is perhaps the fuzziest rule so
far because it’s entirely subjective: there is no concrete way to know how many
methods absolutely constitute a Single Responsibility Principle violation or not.
Still, it’s a good rule to consider when looking for opportunities to refactor.

Finally, there’s what I call The Conjunction Test. If you can define your class
using the word “and” to separate responsibilities, it’s probably time for a
refactor. For example, if you were to say, “My class manages database data
and formats data,” that would indicate that it’s time to refactor for a Single

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 53
— SINGLE RESPONSIBILITY

Responsibility violation.

The problem with this rule, of course, is that there are many cases where we
can describe classes with “and” while getting away with it. For example: “PDO
maintains the database connection and begins/ends transactions.” When
coming across cases like this, dive deep to determine whether or not you’re
faced with a concern about responsibilities or jobs. In PDO’s case, we have a
mix of responsibilities and jobs: “PDO maintains the database connection” is a
responsibility, while “…and begins/ends transactions” is a job. Be careful that,
when using The Conjunction Test, you’re certain that you’re evaluating apples to
apples.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 54
The five SOLID principles are a great starting point for understanding
object-oriented design. They provide some anchors for organizing and writing
our code, and they offer us a basic understanding of object-oriented best
practices. Adhering to these principles alone would dramatically improve most
of the code most of us write, but there are a few other important rules and laws
for us to adhere to, laws like the Law of Demeter.

The Law of Demeter (also called the principle of least knowledge) is a rule
devised by Ian Holland at Northeastern University. It states the following
principles:

1. Each object should have limited knowledge of other objects.


2. Each object should only talk to its friends; never talk to
strangers.
3. Only talk to your immediate friends.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 56
—LAW OF DEMETER

LIMITED KNOWLEDGE

The Law of Demeter is about what an object can know and who an object can
talk to. It’s important to note that this principle is not about how to organize
your code, but about how your code should talk to other bits of your code. That
makes the Law of Demeter different.

The first principle is that each object should only have limited knowledge of
other objects. More specifically, it should only have knowledge of objects that
are closely related to the subject.

There are any number of ways to violate this first principle. For example, take
the following code sample:

<?php

class MyController
{
public function __construct(\PDO $pdo)
{
$this->model = new Model($pdo);
}
}

In this example, we’re passing a PDO instance into the controller and
instantiating a model. This is a very common code example from many
different types of applications. The problem here is that whenever we require a
dependency, we are essentially making that dependency known to the subject

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 57
—LAW OF DEMETER

(MyController). By making PDO known, we’re introducing the two objects.


They are no longer strangers.

This violates the first principle by giving our object knowledge of another object
that it doesn’t need to know; it’s not closely related. Instead of passing in PDO,
we should inject the Model directly, like this:

<?php

class MyController
{
public function __construct(Model $model)
{
$this->model = $model;
}
}

Now the object only knows about a relation it needs to know about: the model
class that it’s using.

It’s generally not a good idea to pass in construction materials (e.g.,


dependencies to construct another object); rather, you should pass in finished
products (objects that your object needs and uses).

Another common mistake is passing in dependencies that are unused in one or


more methods. If I am passing a dependency that is unused in the method I am
calling, I am teaching my object about a dependency that it does not need to be
aware of. This creates problematic code. As discussed in the previous chapter,

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 58
—LAW OF DEMETER

this is generally poor practice and should be considered for a refactor.

DON’T TALK TO STRANGERS


Another element important to the Law of Demeter is not talking to “strangers.”
In this context, strangers are any objects that are not immediately known to the
calling object. For example:

<?php

class MyController
{
public function someMethod()
{
$this->object
->someParameter
->someOtherParameter
->someMethod();
}
}

In this example, we call through three different objects in order to find the
method that we want to call. The first object (“object”) is known to us. That
is a familiar object, and we should feel comfortable communicating with that
object.

The second and third objects, however, are not familiar; they are foreign.
In order to communicate with them we must learn the internal API of the
MyController::$object parameter. This API would be subject to change,

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 59
—LAW OF DEMETER

making it possible that our code would break later just from trying to call a non-
object on an object.

The second and third objects are “strangers.” We should not talk to them.
Talking to them makes them dependencies of our object and requires us to
maintain the internal state of the object as well as our own.

This is also a significant problem with public properties, over getters/setters.


I prefer getters/setters because they define an explicit, verifiable API structure
that I can test against, versus the transient nature of public/private properties.

O N LY TA L K T O C L O S E F R I E N D S
But what happens if we’re in a situation where a getter returns an object for us
to use? Is that okay? Generally speaking, that still presents problems for our
object.

When we return an object and we interrogate that object for useful information
or behavior, we have introduced a dependency that’s not handled through
dependency inversion, setter injection, or some other means. We can’t type hint
on that object, either, meaning we’re at the mercy of the producer to produce
an object that matches our expectations. (Some of this is controlled by the fact
that the producer was type-hinted itself.)

Consider the following scenario:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 60
—LAW OF DEMETER

<?php

class MyController
{
public function checkUserAuth()
{
$user = $this->user->getUser();
return $user->authenticate(
$this->username,
$this->password
);
}
}

We are relying upon the MyController::$user to return a User object


from the getUser() method. But how can we be assured that this is returned
and valid?
There is a philosophy in object-oriented programming that says you should only
“use one dot,” as a dot is a common separator for many languages. For example,
a.b() is valid; a.b.c() is not. When driving a car, you do not command the
wheels to turn; you invoke the engine, and the engine turns the wheels. The
code would read car.forward() rather than car.wheels.forward().

A MORE FORMAL LAW OF DEMETER


The Law of Demeter’s rules as to whom an object may speak can be
complicated and somewhat overbearing from time to time. However, it’s really
simple and straightforward to break down the rules and understand them with
a more formal and precise definition.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 61
—LAW OF DEMETER

In general, an object many speak with:


1. The object itself.
2. The method’s parameters.
3. Any objects created or instantiated within the method.
4. The object’s dependent and component objects.

The object itself


An object may interact with any part of the object itself, including other
methods and other objects. There is no limitation to how often the method in
question may invoke the methods and properties of the object itself.

The method’s parameters


Methods may have parameters passed in and those parameters may be objects.
If they are, a method may access those methods on the parameters in search of
information or to invoke behaviors.

What a method cannot do, however, is invoke a method or a property on a


parameter in order to invoke a secondary method or property on the result of
the first. This is “talking to strangers” and is not permitted by the rules of the
Law of Demeter.

Any objects created or instantiated within the method


Most developers will focus on using dependency injection and avoid creating
objects within a method, but the fact remains that it may still happen from time
to time. For example, value objects like DateTime may still be created within

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 62
—LAW OF DEMETER

the method and are permitted to be accessed from within the method.

Except in factories, it’s considered an antipattern to pass in parameters to a


method or an object with the explicit purpose of constructing a new object. For
example, if objects A and B are passed in, they should not be passed to new
C(A, B), except in a factory.
The object’s dependent and component objects
If you are using dependency injection, you’ll likely have dependencies that
were injected into your object. Your method may access all of them, and their
front-line API, as much as is needed to complete the behavior assigned to that
method. Feel free to communicate with those objects as friends; they are not
strangers.

ADVANTAGES AND DISADVANTAGES


There are many advantages and disadvantages to the Law of Demeter and its
application. For example, applications developed using the Law of Demeter as
a primary rule tend to be more maintainable and adaptable. This makes sense,
because applications that do not rely on the internal API and internal structure
of other objects can be more easily decoupled and more easily refactored than
objects that do. It stands to reason as well that objects maintaining a superficial
relationship with dependencies are less tightly coupled to those dependencies.

The fewer calls you make to other objects can also result in fewer software bugs.
This makes sense from a logical perspective: the fewer calls in general, the less

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 63
—LAW OF DEMETER

complex your code, and the less likely you are to produce a bug. Of course, no
code is perfectly bug-free, and no developer is going to be able to produce code
that’s 100% free of all noted bugs, but it makes sense that the simpler a code
base, the lower the number of bugs overall.

Disadvantages to the Law of Demeter exist as well. In many cases, the Law
of Demeter increases the number of wrapper functions that are necessary to
accomplish the goals of the system. For example, rather than invoking the
wheels directly, a method must be written to wrap that functionality for the
driver of the car. CCar.wheels.forward() becomes Car.forward()
which essentially duplicates the forward() behavior without adding any new
functionality.

In addition, while the Law of Demeter limits the size and scope of the method’s
API knowledge, it increases the size of class APIs by forcing the development of
these wrapper calls or calls to internal members of the class. For example, a Car
interface without the Law of Demeter might have only a few methods. With the
Law of Demeter, that same class is going to have a few dozen methods in order
to provide support for consuming classes.

Disadvantages aside, I believe the Law of Demeter offers something useful and
provides adherents with better code overall. It’s impossible to adhere to the
Law of Demeter all the time (and not all libraries make it possible), but it’s still a
good practice to follow in your own code.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 64
The six principles we have discussed to this point serve as shining stars
for how to build lasting, maintainable, understandable object-oriented
applications. They represent best practices and present us with opportunities
for improvement in all our applications and how we deal with them.

But what the areas that serve as “black holes” to object-oriented design,
areas where best practices are challenged? These antipatterns of software
development serve to illustrate these bad practices, so that you can identify and
avoid them in your own code.

SINGLETON
The Singleton is perhaps the most famous of all design patterns, owing to

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 66
— ANTIPATTERNS

its simple implementation and the fact that it is the only design pattern to
specifically represent itself in code. But the Singleton pattern is fraught with
challenges and should be avoided at nearly all costs.

A Singleton is defined as being a design pattern that restricts instantiation of


a class to just one object per runtime. This is useful in cases where you need
exactly one object to coordinate actions across the system. For example,
Singletons were very popular in the early 2000’s as database handlers, offering a
global connection for the purposes of ensuring a single connection per runtime.

The Singleton pattern looks something like this:

<?php

class MySingleton {

private static $instance;

private function __construct() {}

private function __clone() {}

public static function getInstance() {


if (!(self::$instance instanceof MySingleton))
{
self::$instance = new MySingleton();
}

return self::$instance;
}
}
Because the Singleton is designed the way it is, there are limited ways to

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 67
— ANTIPATTERNS

instantiate one in PHP. You must first mark the __construct() and __
clone() methods as private. In addition, you provide a static function (in this
case MySingleton::getInstance()) for the creation or retrieval of the
Singleton object. Within that method, you check to see if a singleton has been
generated; if not, you create it and then return it. Otherwise, you return the
existing object.

Singletons have several problems. First and foremost, it violates many of


the principles we just learned about in the first few chapters. For example, it
cannot easily be type hinted, so it violates the Liskov Substitution Principle
and the Dependency Inversion Principle. It accesses the global state of the
application, violating separation of concerns. And a Singleton has domain over
both instantiating itself and doing something else (like managing the database
connection), violating the Single Responsibility Principle.1

Remember back to our description of the Singleton as a connection manager


for databases? Let’s delve a bit deeper into the problems that Singletons
presented to this approach. Developers quickly discovered that Singletons were
unsuited to handling database connections because they eventually needed
more than one in a read-write database setup. Singletons were non-performant
for this purpose, and much of the code needed to be refactored, which led to
considerable heartache for the developers involved.

1
Instantiating objects is technically a responsibility, so objects that create other objects are responsible
for that behavior and should not be given additional responsibilities. This rule does not apply to creat-
ing value objects or simple internal-use-only objects.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 68
— ANTIPATTERNS

The reality is that most problems can be solved without using a Singleton. Given
that fact, most developers should not rely on the Singleton pattern as a pattern
of choice and should consider it an antipattern.

TIGHT COUPLING
We have alluded to tight coupling in the past when discussing The Law of
Demeter, but we have not discussed the subject specifically or in depth. Tight
coupling is essentially a measure of how dependent objects are upon one
another. Tightly coupled objects are incredibly

interdependent upon one another, and this violates both the Law of Demeter
and the SOLID principles.

Tight coupling is a problem in a code base because it slowly strangles the code
base to death. When objects are tightly coupled, you must maintain a larger
mental map of the code base in order to function in it. You must also be more
careful to make changes, avoiding violating one of the conditionals that is

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 69
— ANTIPATTERNS

present in another object. This is an impossible task as a code base grows in


both size and complexity. Tight coupling therefore produces nothing but grief.

There is no way to specifically measure tight coupling in an object-oriented


application, except to say that you know it when you see it. It looks like having
many (more than five or six) objects passed into the constructor or through
setter injection. It looks like instantiation of multiple objects. It looks like having
several base classes all with their own constructors or setter injection.

Tight coupling also makes testing that much harder. When objects are tightly
coupled together, it becomes difficult or impossible to extricate one object from
another for testing or mocking. Testing in isolation is a fundamental element of
being able to test code.

It would be impossible to have entirely loosely coupled code, because objects


do need to interact and work together to function. The ultimate question is not
whether to couple objects together, but which objects to couple together and
why. For example, if you are injecting several dependencies, consider whether
those dependencies might be better wrapped together in a class that’s more
suited to do the work needed. It may add another layer to your application,
but in the end, from a testing and operational standpoint, it may be a better
outcome.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 70
— ANTIPATTERNS

UNTESTABLE CODE
Many developers consider tests as an afterthought. But the best developers
consider tests as the finished product, the way of proving what they sought
to build and demonstrating their completed goals. Viewing tests this way also
helps in writing code that’s ultimately testable and able to be tested through
automated means.

We discussed in the last section how tight coupling produces code that is hard
or impossible to test. This is a reality of tight coupling and a hallmark of this
kind of coding approach. But tight coupling isn’t the only coding approach that
produces difficult-to-test code.

One of the main reasons we use dependency injection is that it makes testing
possible in many cases where otherwise it might not be. For example, take this
code:

<?php

class Database
{
public function __construct()
{
$this->cache = new ApcCache();
}
}

If we try and test this, we’ll be hamstrung by the fact that the ApcCache class is
hardwired into the code. We won’t be able to mock that or get away from that,

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 71
— ANTIPATTERNS

so we’ll only be able to write a functional test at best. Many popular frameworks
today are built this way: they allow you to write functional tests, but never unit
tests,2 in order to test your code. I think this is a major mistake.

We can fix this using dependency injection and inject the object we want. This
will allow us to inject a mock, a substitute object, in order to avoid using the APC
cache (which is inconveniently not available on the command line anyway).

<?php

class Database
{
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
One way that you can ensure your code is testable is by writing tests beforehand
and making your code pass them before you move on to the next test. This is
called Test-Driven Development and is incredibly
popular. It’s worth taking a read of Robert Martin’s The Clean

Coder, where he describes in detail what it means to do test-driven development


to achieve this goal.

2
There are essentially three kinds of testing: unit testing, integration testing, and functional testing. Unit
testing, the kind of testing we’re focused on here, tests individual code units to see how they perform in
isolation. Integration testing tests the integration points between objects in the code. Functional test-
ing tests a system end-to-end. It’s the least precise level of testing but offers the broadest spectrum of
testability.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 72
— ANTIPATTERNS

P R E M AT U R E O P T I M I Z AT I O N
We all want to write code that is performant and meets the needs of the people
who have hired us. This is an important part of our job, and we should work hard
to develop this kind of code. However, there exists a temptation to try and make
our code extra performant as we write it, without considering the implications of
that performance tuning from a production standpoint.

Resist this temptation with all your might.

It’s not that performance tuning in and of itself is bad. It’s that you need hard
and real numbers in order to do it well. Nobody is omniscient, which makes it
impossible for us to accurately and adequately predict what the consequences
of our tweaks might be, especially related to performance. We need real
numbers to guide our decision making, instead of guessing at changes that
might or might not improve performance.

In addition, when we are writing code, we are not faced with real-world
conditions against which we are testing. We are instead facing the subset of
nearly ideal conditions in which we are writing our code. We need the real-world
conditions of latency, concurrency, race conditions, and the like before we’re
going to be fully able to grasp the performance implications of the work we’re
doing.

That’s not to say you should be sloppy in the work that you do. Surely not. You
should strive to avoid the most common performance pitfalls already known

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 73
— ANTIPATTERNS

(using a while loop in a while loop, etc.), but you should not go farther than that.
You should not guess that something won’t perform. You should prove it.

Similarly, it’s easy to be tempted to consider what are known as “micro-


optimizations.” These were popular in the late 2000’s with developers insisting
that single quotes were faster than double quotes for processing or that you
should avoid certain functions in PHP because they were slow. The truth is that
for most applications, micro-optimizations don’t make much of a difference.
There’s no reason why you should attempt to micro-optimize your application at
any point in time.

D O N ’ T R E P E AT Y O U R S E L F
Have you ever come across code that looked eerily familiar to other code in the
same application? Perhaps it used the same variable names; surely, it used the
same structure. You have encountered duplicated code, an insidious type of
code that makes its way into applications of all shapes and sizes.

Duplicated code is a hallmark of a variety of programmatic problems, from tight


coupling to poor architecture. When you see it, it should serve as a warning
sign that something is amiss somewhere in the code base. Duplicated code
represents one of the biggest challenges to well-factored object-oriented code
there is.

Consider: object-oriented code exists to eliminate duplication. Objects exist

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 74
— ANTIPATTERNS

to encapsulate behavior and provide services for a particular responsibility


or domain. Duplicated code, therefore, is simply impossible in a well-factored
application.

Duplicated code comes with several challenges. The first is maintenance. It’s
difficult, if not impossible, to maintain code when there is rampant duplication.
Inevitably, you’ll fix something in one part of the application and find out that
you forgot to fix it someplace else. You’re forever chasing the duplicated code
trying to squash bugs.

In addition, duplicated code makes for duplicated tests. Your tests (assuming
you have some) will be duplicated in order to ensure coverage of the areas of
duplication. This creates testing overhead and, potentially, cascade failures
when some bit of the code changes and breaks your tests. This makes your tests
less reliable because several different failures are harder to analyze and fix than
one single failure.

Fixing duplication is hard but possible. It starts with accepting the duplication
and setting about to fix it. Fixing it requires identifying areas where duplication
exists and factoring the duplicated code into submodules, domain components,
or the like until the code is sufficiently de-duplicated. In cases where there is a
large amount of duplication except for small components or routines that are
unique, look to patterns like the Strategy Pattern to solve these sorts of issues.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 75
— ANTIPATTERNS

NONDESCRIPTIVE NAMING
Let’s take a look at some code:

<?php

$dr = $d->fetch($s);

foreach ($dr as $ir)


{
// do something
}

What is going on in the code sample above? There are a few things we can
tell right away: $d->fetch() returns an array, because we’re using it in a
foreach loop. But we don’t know where the source of the data is coming from,
and we don’t know much about the context.

This is a classic case of nondescriptive naming. Specifically, we have names for


variables that don’t make any sense for what we’re trying to accomplish. This is
a tragedy, because code communicates intent. Here, we have no intent.

Let’s rewrite that code sample to be more aptly named:

<?php

$databaseResult = $database->fetch($sqlQuery);

foreach ($databaseResult as $individualResult)


{
// do something
}

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 76
— ANTIPATTERNS

This makes more sense! We have a database that we’re fetching data from using
a SQL query. We get back an array of database results, which we pass into a
foreach loop and apply some algorithm or routine.

Notice how simply changing the names to be explicit enabled us to accomplish


much in terms of improving the understanding of the code. It’s important to
remember that code is not about the computer; if it were, we’d write code in
1’s and 0’s, going back to the old punch card method of developing software
applications.

Instead, we use languages, and increasingly modern ones. We use these


languages because they’re expressive. They communicate intent. That’s
what writing code is about: communicating intent. Far from being a technical
exercise, writing code should be more like writing prose, like composing a book.

When I sat down to write this book, I took the opportunity to apply word to
paper in a way that would communicate something extraordinary to the reader.
I wanted to share new insights and be clear and concise. I hope that I have
achieved that with this book, and I implore you to do the same with your code.
Agonize over names and structures. Consider using idioms that make sense for
the environment you’re writing for. Develop themes. Be consistent, clear, and
concise.

Remember that code is written for other humans to read, not for computers
to parse. The importance of writing code for these other humans cannot be

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 77
— ANTIPATTERNS

understated. It should not be messy, badly named, or nondescriptive. It should


be clear and precise. You should consider every element of the code as a means
of communicating your point, which is the goal of the application.

Part of making sure that you communicate purpose is picking names that
matter. In our first code sample, we had a long list of names that did not matter.
They were one or two characters long and they communicated nothing. The
code was syntactically correct; it would compile and run. But it would not inform
a reader as to what the code’s purpose or intent was.

By picking names that matter, you project your thought process into the code
and make it clear to another person how you arrived at your decisions. Source
code spends approximately 80% of its life in maintenance. It’s important to
carefully name things so that other developers maintaining your code can
understand what you intended. Naming a variable $deleteUserCommand
is explicit; $duc is not, even if they have the same purpose. Pick names that
matter and make sense for the purpose that you’re trying to communicate.

Another way to achieve communication in code is to use actual words within


docblocks. Sometimes we generate docblocks as a means of producing
documentation, but we do so in such a rote way: we produce a docblock that
simply documents the arguments and provides no real insight:

<?php

/**

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 78
— ANTIPATTERNS

* @param $xdd
*/
public function processXdd($xdd)
{
// do something
}

Does this docblock provide any insight that I couldn’t have gotten simply by
reading the signature of the method? The simple answer is no, it doesn’t. But
what if I’m more explicit?

<?php

/**
* Processes a single XDD for removal and archive.
*
* @param $xdd
*/
public function processXdd($xdd)
{
// do something
}

Now I’ve used actual words and my docblock communicates purpose and intent
for what the method is trying to achieve. Now we’re getting somewhere!
I am not a fan of inline code comments, because code is liable to change,
while the presence or absence of a method, and the intent of the method, is
more likely to be stable for a long period of time. Thus, it is safer to comment a
docblock with the intent of the method (not the means of the method) than it is
to comment inline in the code.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 79
When it comes to error handling in object-oriented applications, there
is really one chief way we do it: we use exceptions. Exceptions are built-
in objects in PHP that are designed for carrying error messages and halting
execution until they are resolved. Exceptions provide a flexible framework for
resolving problems in our applications.

In the past, applications might have used trigger_error() in PHP to trigger


an error to take place. Exceptions provide a way around this older-style behavior
and equip us to move into the next generation of error handling.

EXCEPTIONS ARE OBJECTS


The first thing to understand about exceptions is that they are simply objects:
their behavior is governed by the rules of the PHP object-oriented engine as well

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 81
—EXCEPTIONS

as some special rules that govern all exceptions.

Let’s take a deeper look at an exception:

<?php

$object = new Exception;


var_dump($object);

Notice the new keyword used to instantiate the exception. This is because
exceptions are simply objects in PHP and can be instantiated like any other
object. When we dump the exception, we get the following data:

object(Exception)[1]
protected 'message' => string '' (length=0)
private 'string' => string '' (length=0)
protected 'code' => int 0
protected 'file' => string '/Users/sarah/Sites/test.
php' (length=29)
protected 'line' => int 3
private 'trace' =>
array (size=0)
empty
private 'previous' => null
The first thing to note is the fact that an exception is an object, and PHP
recognizes it as such inside var_dump(). We have several properties as wel:

1. message: the exception message, passed as the first argument


to the exception constructor;

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 82
—EXCEPTIONS

2. code: the exception code, which is the second argument passed


to the exception constructor;
3. file: what file the exception came from; and
4. line: what line the exception came from.

Exceptions are objects and can be treated as objects for the purpose of
accessing data and type hinting.

The exception API contains several helpful methods for managing exceptions
and accessing the internal data of exceptions:

<?php

Exception implements Throwable {


/* Properties */
protected $message;
protected $code;
protected $file;
protected $line;
/* Methods */

public __construct ($message = "", int $code = 0,


?Throwable $previous = NULL)
final public getMessage() : string
final public getPrevious() : Throwable
final public getCode() : mixed
final public getFile() : string
final public getLine() : int
final public getTrace() : array
final public getTraceAsString() : string
public __toString() : string
final private __clone() : void
}

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 83
—EXCEPTIONS

USING EXCEPTIONS
Since exceptions are simply objects, they can be extended the same way that
any other object can be extended: inheritance. Why would you extend an
exception? To create exceptions of specific types, so that we can type hint.

To extend an exception, you must inherit from either the base


Exception class or from one of the built-in exceptions in PHP like
InvalidArgumentException. Extending an exception works just like any
other extended object:

<?php

class MyException extends \Exception {}

Now that we have our special exception type, we can throw and catch the
exception. Exceptions in PHP are thrown, meaning they are injected, into the
runtime and become the most important object in play. It must be handled, or
the entire runtime grinds to a halt.

Exceptions are handled by catching them. A caught exception can be ignored


or handled in some way to ensure that the problem is resolved. For example, a
missing connection might generate an exception in a long-running process and
catching the exception allows you to reestablish the connection and carry on.

To throw an exception you use the throw keyword:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 84
—EXCEPTIONS

<?php

throw new MyException('Something went wrong');

Catching an exception takes place inside a try-catch block. A try-catch block is a


special block that allows you to specify which exceptions are caught, ensuring
that only the exceptions you want to handle are in fact handled.

<?php

try {
throw new MyException(‘Something went wrong’);
} catch (MyException $e) {
echo $e->getMessage();
}

Notice how in the catch block we specify the type of the exception we want
to handle. This is how we differentiate between different exceptions and
specify the exception that we wish to catch. This is part of why creating custom
exceptions is important: it allows us to be specific about the type of error that
we are catching.

But what if we want to catch multiple types of errors within the same try-catch
block?

E XC E P T I O N C ATC H I N G P R E C E D E N C E
It’s possible within a try-catch block to catch more than one exception type and
to specify multiple exception types. However, there are rules around how to

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 85
—EXCEPTIONS

achieve this that must be followed, or you can get unpredictable results.

To catch multiple types of exceptions, you need only add an additional catch
block to the try-catch block:

<?php

try {
throw new MyException('Something went wrong');
} catch (MyException $e) {
echo $e->getMessage();
} catch (Exception $e) {
echo "Caught a base exception.";
}
Notice now that we are catching any exception that matches MyException or
the base Exception class.

An important rule to observe is that PHP will only trigger one catch block,
and it will trigger the first catch block that it finds immediately following the
throwing of the exception. This makes ordering very important. Recall how
MyException extends from the base Exception class. You might expect
both catch blocks to be triggered but they won’t be: only the first block that
prints the message will be triggered. The second will never be triggered.

Ordering is especially important when exceptions inherit from one another, or


you have an extensive inheritance tree. For example:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 86
—EXCEPTIONS

<?php

class MyException extends Exception {}


class MyExceptionTwo extends MyException {}

Here we have two exceptions, and MyExceptionTwo extends from


MyException, which in turn extends from Exception. Consider then the
following example and which block will be triggered first:

<?php

try {

throw new MyExceptionTwo('test');


} catch (MyException $e) {
echo 'MyException';
} catch (MyExceptionTwo $e) {
echo 'MyExceptionTwo';
} catch (Exception $e) {
echo 'Exception';
}

If you guessed that the first block would be triggered, you are correct. Why?
Because MyExceptionTwo matches the type hint criteria for MyException
in all cases, even if it’s not the most specific type hint. In order to properly type
hint, you must reverse the order of the example:

<?php

try {

throw new MyExceptionTwo('test');

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 87
—EXCEPTIONS

} catch (MyExceptionTwo $e) {


echo 'MyExceptionTwo';
} catch (MyException $e) {
echo 'MyException';
} catch (Exception $e) {
echo 'Exception';
}

In this example, MyExceptionTwo is the first exception we type hint for, and
so the proper error handling will take place, as we expect.

It’s also possible to type hint multiple exceptions on the same line. For example,
you can designate that all instances of MyException and MyExceptionTwo
get handled the same way, with the same catch block, using the pipe operator
between the exception names, like so:

<?php

try {

throw new MyExceptionTwo('test');

} catch (MyExceptionTwo | MyException $e) {


echo 'MyExceptionTwo';
} catch (Exception $e) {
echo 'Exception';
}

F I N A L LY
PHP supports a new keyword as of PHP 5.5 called finally. The finally keyword

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 88
—EXCEPTIONS

allows you to include code that is executed regardless of whether or not an


exception is handled or, in fact, whether an exception is triggered at all.

<?php

try {

throw new MyExceptionTwo('test');

} catch (MyExceptionTwo $e) {


echo 'MyExceptionTwo';
} catch (MyException $e) {
echo 'MyException';
} catch (Exception $e) {
echo 'Exception';
} finally {
echo "This will always be executed.";
}

Notice how the finally keyword appears at the end of the list of exception type
hints and provides code to be executed. Note that this code will be executed in
all cases, whether an exception is generated or not and even if an exception is
unhandled.

ERRORS AS EXCEPTIONS
Starting with PHP 7, fatal errors became exceptions. This means considerably
more in terms of flexibility for error handling in PHP 7.

First and foremost, all errors now extend from a Throwable base interface.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 89
—EXCEPTIONS

Exceptions do as well.

It is possible in PHP 7 to catch errors that should be fatal and suppress them.
This is a poor practice except for logging purposes. Fatal errors should remain
fatal and should terminate your application, while exceptions that you generate
can be handled and resolved appropriately.

If you want to catch all errors (for example, to log fatal errors), you can type hint
on the Throwable interface.

It’s important to note that you cannot implement Throwable in userland


directly. You’ll receive the following fatal error if you try:

Fatal error: Class ExceptionA cannot implement interface

Throwable, extend Exception or Error instead

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 90
Most of us are familiar with the pattern known as Model-View-Controller
(MVC). It’s a standard pattern popularized by Ruby on Rails, and provides the
foundation for much of the modern framework world in which we operate.

The problem with MVC is that it presents a number of issues that make using
MVC largely unworkable. As a substitute in steps Action-Domain-Responder,
which solves many of these issues and provides a solution to the pitfalls of
Model-View-Controller.

Let’s take a deeper look at Action-Domain-Responder through the lens of MVC.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 92
— ACTION-DOMAIN-RESPONDER

A DEEP DIVE INTO MVC


As mentioned, the current paradigm for modern frameworks is Model-View-
Controller or MVC. The MVC framework is extremely popular, both in the PHP
world and in other spheres, because it simplifies the process of developing
web applications and factors different elements into different places in the
application.

Current MVC frameworks look something like this:

A user makes a request, which is dispatched to a controller (usually by a


front controller). The controller makes a request to the model for data, which
then populates the view with data, and the view is presented to the user for
manipulation or data presentation.

This is well and good, except for the fact that MVC doesn’t actually do this. This
isn’t the exact pattern we follow. It’s closer to this:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 93
— ACTION-DOMAIN-RESPONDER

Note the difference here. Instead of the controller passing data to the model
which in turn passes the data to the view, the data is passed straight away into
the view via the controller. The model and the controller communicate directly
with each other, and the view gets its data straight from the controller.

But this still isn’t quite right. We assume a view layer here, which is fine and
well, but the fact remains that most of the time we don’t have a true view layer.
A view layer contains logic for presenting the template to the end user; in most
cases we simply manage the templating process in the controller, like so:

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 94
— ACTION-DOMAIN-RESPONDER

This model most accurately represents the state of MVC in the modern PHP
world. Controllers do most of the work, dispatching requests to various other
pieces in order to gather their information, and then handling the bulk of the
request itself.

This leads to several problems. The first problem is that controllers end up
bloated and filled with cruft. In addition, most of the algorithms that really
belong in the domain end up in the controller; this leads to controllers being
procedural rather than objects in their own right.

Consider the assignment of responsibilities. In a typical MVC setup, the HTTP


request is handled in the controller. The majority of the business logic is
handled here as well; the database logic is shunted off to the “model” but the
algorithms and business logic itself is handled by the controller. The templating
system is managed by the controller, as is authentication for the application.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 95
— ACTION-DOMAIN-RESPONDER

The list is long and extensive and continues on, but we get the point.

Ultimately this is a huge amount of responsibilities to pass off on the controller.


It leads to controllers that are overworked and under-tested, and leads to
situations where isolated testing is impossible and we must do functional
testing because it’s the only thing left.

There has to be a better way, and there is.

A BETTER WAY
Introducing Action-Domain-Responder, or ADR. A second generation model for
representing objects and managing application logic, ADR provides a pattern
that solves many of the harsh realities of MVC without sacrificing the flexibility
that MVC offers the end user.

Under Action-Domain-Responder, we eliminate the templating layer entirely.


We replace it with a Responder whose job it is to determine the type of
response, but that response may not necessarily be a HTTP HTML response.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 96
— ACTION-DOMAIN-RESPONDER

The Responder gets its data from the Action, which provides the data from the
Domain. The Domain receives data directly from the Action as well, but does not
return simple unstructured data; it returns a Payload object that the Action
passes straight into the Responder.

And here’s the coolest detail: the action is only three or four lines long at most. It
contains no conditionals, and provides no templating instructions. The Action’s
sole purpose is to pass data from Point A to Point B and invoke a service/
command/other request. That’s it.

Let’s take a deeper look at each of these components and understand how they
work.

THE ACTION
Actions are the first component we’ll examine. Actions are simple,
straightforward objects that do very little practical work. Most of the work is
done elsewhere, delegated by the Action but not managed or processed by the
Action.

The Action is most similar to the Controller, but it has a few distinct differences.
First and foremost, while a controller may have several related methods (get,
update, delete, etc.), Actions have single methods that are specific to a
particular request. This is because an Action is a discrete block of code for a
discrete purpose; it doesn’t need multiple related actions. Consider the Action

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 97
— ACTION-DOMAIN-RESPONDER

almost a command of sorts.

In addition, while Controllers interact with both the model and the presentation
layer (template), Actions interact only with the model, also known as the
Domain. The extent of the Action’s interaction with the Responder is by passing
the Responder an object for it to use in preparing the response. The Action
contains no template code, and no rendering logic.

Actions are primarily responsible for shuffling request data into the domain, and
shuffling response data out. This stands in stark contrast with many Controllers,
which contain much of the business logic that an application needs to execute.
The Action-Domain-Responder model believes strongly that an Action should be
as small and compact as possible. Consider:

<?php

class ScenerySign
{
/**
* @var ServerRequestInterface
*/
protected $request;

/**
* @var ValidateAndSignRequest
*/
protected $service;

public function __construct(


ServerRequestInterface $request,
ValidateAndSignRequest $service

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 98
— ACTION-DOMAIN-RESPONDER

) {
$this->request = $request;
$this->service =$service;
}

public function __invoke()


{
$body = $this->request->getBody();
$payload = $this->service-
>validateAndSign($body);
return $payload;
}
}

These 29 lines are a full action, representing the full behavior of the
ScenerySign action. The rules are simple enough: the object accepts a
ServerRequestInterface object compatible with PSR-7, as well as a
service called ValidateAndSignRequest. It pulls the body out of the
HTTP request, passes it into the service, and returns the payload. Simple, easy,
straightforward.

It’s worth noting that the Action stands as the last layer in which knowledge of
the request is permissible. Data passed into the Domain should be agnostic to
where the data came from; it shouldn’t care whether the data was from an HTTP
request or a CLI request or some other kind of request. Thus it’s important to
include any logic for extracting data from the request inside the Action.

Next, let’s take a look at the Domain.

M A S T E R I N G O B J E C T- O R I E N T E D P H P — 99
— ACTION-DOMAIN-RESPONDER

THE DOMAIN
Clasically, the Domain is also known as “the Model”. However, a Domain is
much more than a model. We are not simply modeling data, but business
relationships and behavior. Domains are complex elements that are unique for
each and every application you will ultimately work with.

This isn’t a domain book, so we won’t dive too deeply into an understanding
of what a domain is or how to create one. Creating a domain could fill an entire
series of books (and it does, in books like Domain Driven Design and others).
However, you should feel free to be creative in your domain design, creating a
domain that fits your business needs and solves your business problems.

One of the things that makes the Domain unique in Action-Domain-Responder is


the fact that the Domain does not interact directly with the Responder. In classic
MVC the Model informs the View of what has changed and the View presents it
to the requestor. That is not the case here; the Domain passes data back to the
Action who passes it onto the Responder.

This is an important difference. The Domain doesn’t care about formatting or


layout. The Domain cares about presenting data, answering a question, and
solving an issue. The Domain is agnostic to both the request and the response,
meaning that the Domain doesn’t know anything about how the data arrived, or
how the data is being consumed.

A well-designed Domain could be plucked out of your application and dropped

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 100
— ACTION-DOMAIN-RESPONDER

into a standard MVC application or any other kind of application and “just
work.” This is the ultimate goal: to have agnostic domains capable of doing work
without knowledge of the underlying architecture.

THE RESPONDER
Of the three components of the Action-Domain-Responder paradigm, perhaps
none is as challenging to grasp as the Responder. That’s because when people
think of the Responder they immediately assume “the thing that presents
HTML.” They could not be more wrong.

The Responder is responsible for rendering the response. It is not responsible


for how that response is ultimately rendered. What I mean is the Responder
can configure the template, but some other entity is responsible for actually
presenting HTML to the end user.

Consider the following example:

<?php

class Index implements HtmlGenerator


{
public function __construct(
View $template
) {
$this->template = $template;
}

public function checkContentResponseType()


{

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 101
— ACTION-DOMAIN-RESPONDER

return [
'text/html' => 'generateHtml',
];
}

public function generateHtml(PayloadInterface


$payload)
{
$this->template->setView('index');

$response = new HtmlResponse($this->template->__


invoke());
return $response;
}
}

This is a Responder that generates HTML. Notice how the Responder works.
The Responder is responsible for telling a templating engine which template to
use, and calls the templating engine to invoke the code. It also returns an HTML
response. But it doesn’t actually generate the HTML. The HTML is generated by
the templating engine!

This is vitally important, because the Responder must be capable of generating


many different kinds of responses, not just HTML responses. It should be
capable of generating responses to JSON requests, CLI requests, etc. For
example:

<?php

class ScenerySign implements JsonGenerator


{
public function checkContentResponseType()
{
return [

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 102
— ACTION-DOMAIN-RESPONDER

'application/json' => 'generateJson',


];
}

public function generateJson(PayloadInterface


$payload)
{
$output = $payload->getOutput();

$response = new JsonResponse(


$output,
200,
[],
JSON_UNESCAPED_SLASHES);
$response = $response->withStatus($payload-
>getStatus());
return $response;
}
}
In this case, the Responder is capable of and fully responsible for producing
a JSON response. Sometimes a Responder must know how to respond to two
different kinds of request based on content type or other factors.

There are some important ways in which Responders differ from Controllers. For
example, Responders have a 1:1 relationship with specific actions. For a given
action there will be a given responder. Only the Responder knows anything
about the response logic; the Action is ignorant of these details and shouldn’t be
aware of them at all.

PAYLOAD OBJECTS
When passing messages between the Domain and the Responder, it’s important

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 103
— ACTION-DOMAIN-RESPONDER

to encapsulate that message in some sort of predictable fashion. The fashion I


recommend is the Payload Object.

Payload objects are what they sound like: objects that represent state, and
transmit the payload from the Domain into the Responder. Aura framework has
a Payload object interface, which I highly recommend.

Within that Payload interface, Paul has included a spot to store input variables,
output values, messages, extras (miscellaneous stuff), and a status code. The
status code is useful for presenting the Payload object for inspection, because
the status code can be used to toggle the response you provide.

Payload objects are incredibly useful, far more useful than simply passing arrays
around, and more predictable.

CONCLUSION
Separating out the concerns using Action-Domain-Responder helps us to
separate out the responsibilities of our components as well. Now we have a
well-factored system of actions, responders, and the domain at large.

Going back to our list, where do things belong now? The request belongs in the
Action. Our business logic belongs firmly in the Domain, since it’s now separated
from the Action. The templating system goes squarely into the Responder. And
authentication?

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 104
— ACTION-DOMAIN-RESPONDER

There are a few tricky elements to Action-Domain-Responder and


authentication is one of them. Knowing whether a user is logged in is generally
the responsibility of the Action. But knowing whether a user is authorized to
do something is the responsibility of the Domain. An unauthorized user should
generate an exception which is handled and properly represented in the payload
provided to the Responder.

In all, Action-Domain-Responder provides a new approach to understanding


and developing applications. Action-Domain-Responder isn’t perfect, but it’s far
better than Model-View-Controller for your applications.

M A S T E R I N G O B J E C T - O R I E N T E D P H P — 105
About the Author

Around 2004, Sarah realized that she could automate tasks by writing a small program in
PHP. Her first application, an online roleplaying game, inspired her to continue learning
how to use PHP. After graduating in 2007 from University of the Pacific in California,
Sarah became a full-time software developer. But Sarah quickly discovered that most
businesses didn’t really struggle with software, they struggled with people and process.

Sarah learned that solving business problems required thinking differently about how
to approach them. Introducing software is expensive, and Sarah realized that solving
these problems needed solutions beyond the common ones proposed by most people.
By taking an in-depth outsider look at a business, Sarah found that she could tackle the
actual people and process problems facing businesses.

Sarah started Carved Path Consulting in 2021 to consolidate and focus her work by
consulting for businesses that want to solve their process and people problems. Sarah’s
expertise, combined with twenty years of experience in the technology world, helps
Sarah solve these problems and build businesses that run themselves, giving their
owners more free time and even the opportunity to take vacations.
Find Bugs?
Every effort was made to make this book as accurate as possible. But it’s possible that
bugs, typos and other errata may still exist. If you find something, please file a bug!

https://github.com/tailwindsllc/masteringobjectorientedphp

You might also like