phparchitect-2019-08

(Rick Simeone) #1
http://www.phparch.com \ August 2019 \ 41

Generated Singletons


Internal Apparatus


Explanation


This seems weird to the point of overkill, but it also explains
what went wrong with our refactored Singleton implementa-
tion. Imagine the situation where we include every inherited
class, the whole thing, in every child class. We saw this is
precisely the case.


Can you imagine the code bloat when class A extends class
B which extends class C which extends class D? Imagine class
B has hundreds of child classes. For example, class A and its
hundreds of siblings might be a model class for a specific
database table. Hundreds of database tables might mean
hundreds of child classes, with each child class containing
the generated code for the parent class, its parent class, and
so on. What a mess, with copies of the same generated code
everywhere!


We just saw this appears to be true. Fortunately, it is not!
Let’s look at this from a different direction.


We just saw the generated code, for a given class, does not
change. It can be generated once and cached. Each section of
generated code gets its own pointer (identifier). As we follow
PHP code from file to file, the PHP virtual machine does
something similar, moving from one section of generated
code to the next.


The generated code is immutable. It’s in a read-only cache.
Once generated, it doesn’t change. So, instead of the bloated
mess, we have a well-organized series of code caches corre-
sponding to the PHP files we see.
We, therefore, need only have a single copy of the StatePark
parent class’s generated code. It looked to us like CamdenState-
Park has a copy of that generated code, and CarleyStatePark
also has a copy of that generated code. But there’s only one
copy of that parent class’s generated code. They both refer-
ence the same parent class, and they both reference the same
cache of the parent class’s generated code.


Our parent class uses its own static variable $instance. It’s
only one single variable. Because it’s declared static, there’s
only one single copy of that variable. Since both child classes
incorporate the same parent class and the same (and only)
copy of its generated code, both classes are using the very same
variable. They are stepping on top of each other. That’s the
mistake we made in refactoring our classes to remove code
duplication.


If we had kept static $instance in each of the child classes
(which was our starting point), we would not have the error.
We introduced the problem by moving static $instance to a
shared parent class. Oops!
In all cases we come down to this line of generated code:

L2 (14): V0 = FETCH_STATIC_PROP_R string("instance") (self)
(exception)

That code is part of the parent class, in all cases, and there-
fore in all cases, we are using the same variable.
The first time we invoke getInstance(), from any child
class, we create the object and store its pointer in the parent
c l a s s’s self::$instance. In all future calls, the class has already
been created, and we return that pointer to the same object.
That is how the Singleton pattern should work! Unfortu-
nately, the way we designed this class, we make no distinction
between which child class called getInstance(). The result is
misleading and surely not what we intended.
This whole discussion is one of the reasons we consider the
Singleton Design Pattern an anti-pattern. It’s easy to intro-
duce errors which are difficult to recognize and understand.
We produced the wrong class instance, true, but we also
produced unit tests which affect each other. The second unit
test accidentally uses the object created during the first unit
test. That’s another indication of bad design.

Summary
We took a careful look at exactly how our Singleton design
works under the covers. We practiced identifying how a PHP
generated-code listing is structured. Knowing the structure,
we practiced eliminating the uninteresting parts and focusing
on the few lines of interest. We learned that parent classes
become part of each child class’s generated code.
It’s common practice to refactor working code to remove
duplication. We (intentionally) made a mistake. We found
code using static variables has traps for the unwary.
Internal Apparatus is concluded (for now). We’ll make
immediate use of our hard-won knowledge of how Singletons
work, next month, with Pragmatic PHP: Studying Singletons.

Ed Barnard had a front-row seat when
the Morris Worm took down the Internet,
November 1988. He was teaching CRAY-1
supercomputer operating system internals to
analysts as they were being directly hit by the
Worm. It was a busy week! Ed continues to
indulge his interests in computer security
and teaching software concepts to others.
@ewbarnard

Output 4. Diff between classes

3c3
< ; /PragmaticPHP/pragma/src/GeneratedSingleton
/CamdenStatePark.php:1-12
---
> ; /PragmaticPHP/pragma/src/GeneratedSingleton
/CarleyStatePark.php:1-12
12c12
< L8 (10): DECLARE_INHERITED_CLASS_DELAYED
string("app\generatedsingleton\camdenstatepark") V1
---
> L8 (10): DECLARE_INHERITED_CLASS_DELAYED
string("app\generatedsingleton\carleystatepark") V1

Related Reading


Free download pdf