When developers are introduced to Object Oriented Programming, one of the first things that happens is that they are taught that nouns turn into objects, verbs into methods, and Dog is a subclass of Animal. OOP is more than just turning things into classes and objects and showing that both Boats and Cars have motors, and that Dogs and Cats both speak(). Let's look at OOP in real world settings and go beyond cars and dogs, and see how to use Object Oriented Programming properly in PHP. Traits, Composition, Inheritance, none of it is off limits!
Unlocking the Future of AI Agents with Large Language Models
OOP is More Than Just Animals
1. OOP is More Than Cats
and Dogs
Chris
Tankersley
LonestarPHP
2015
LonestarPHP
2015
1
2. Who Am I
• PHP
Programmer
for
over
10
years
• Work/know
a
lot
of
different
languages,
even
COBOL
• Primarily
do
Zend
Framework
2
• hFps://github.com/dragonmantank
LonestarPHP
2015
2
3. Quick Vocabulary Lesson
• Class
–
DefiniOon
of
code
• Object
–
InstanOaOon
of
a
Class
• Member
–
Variable
belonging
to
a
class
• Method
–
FuncOon
belonging
to
a
class
There
will
be
more
as
we
go
along
LonestarPHP
2015
3
4. LonestarPHP
2015
4
Class
class Employee {
protected $name; // This is a member
protected $number;
// This is a Method
public function setData($data) {
$this->name = $data['name'];
$this->number = $data['number'];
}
public function viewData() {
echo <<<ENDTEXT
Name: {$this->name}
Number: {$this->number}
ENDTEXT;
}
}
7. Let’s count the reasons
• Because
we’re
told
to,
procedural
programming
leads
to
spagheX
code
• We
deal
with
objects
every
day,
so
it
shouldn’t
be
too
hard
• We
want
to
allow
for
code
re-‐use
• We
want
to
group
like
code
together
• We
want
to
easily
extend
our
code
• We
want
to
be
able
to
easily
test
our
code
LonestarPHP
2015
7
8. Getting OOP Right can be Complicated
• Animals
can
speak,
and
have
legs
and
fur.
Let’s
make
objects
based
on
that
• Dogs
are
animals,
and
so
are
cats
• What
about
Fish?
• Cars
have
doors,
engines,
and
colors.
They
only
go
so
fast.
Let’s
model
that
• Motorcycles
aren’t
Cars,
so
let’s
refactor
Cars
• Holy
crap
there
are
a
lot
of
engines…
and
wheels…
LonestarPHP
2015
8
9. Can a Dog have Wheels?
• Discuss
(or
listen
to
me
talk
some
more)
LonestarPHP
2015
9
11. What we’re all taught
• Classes
are
“things”
in
the
real
world
• We
should
construct
class
members
based
on
AFributes
• Number
of
wheels
• Sound
it
makes
• We
should
construct
class
methods
based
on
“AcOons”
• Running
• Speaking
• Jumping
LonestarPHP
2015
11
12. New Vocabulary
• Parent
Class
–
Class
that
is
extended
• Child
Class
–
Class
that
is
extending
another
class
In
PHP,
a
class
can
be
both
a
Child
and
a
Parent
at
the
same
Ome
LonestarPHP
2015
12
15. The Employee Class
LonestarPHP
2015
15
abstract class Employee {
protected $name; // Employee Name
protected $number; // Employee Number
public function setData($data) {
$this->name = $data['name'];
$this->number = $data['number'];
}
public function viewData() {
echo <<<ENDTEXT
Name: {$this->name}
Number: {$this->number}
ENDTEXT;
}
}
16. The Manager Class
LonestarPHP
2015
16
class Manager extends Employee {
protected $title; // Employee Title
protected $dues; // Golf Dues
public function setData($data) {
parent::setData($data);
$this->title = $data['title'];
$this->dues = $data['dues'];
}
public function viewData() {
parent::viewData();
echo <<<ENDTEXT
Title: {$this->title}
Golf Dues: {$this->dues}
ENDTEXT;
}
}
17. The Scientist Class
LonestarPHP
2015
17
class Scientist extends Employee {
protected $pubs; // Number of Publications
public function setData($data) {
parent::setData($data);
$this->pubs = $data['pubs'];
}
public function viewData() {
parent::viewData();
echo <<<ENDTEXT
Publications: {$this->pubs}
ENDTEXT;
}
}
19. What does this teach us?
• Inheritance
• Makes
it
easier
to
group
code
together
and
share
it
amongst
classes
• Allows
us
to
extend
code
as
needed
• PHP
allows
Single
inheritance
LonestarPHP
2015
19
20. We use it all the time
namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewModelViewModel;!
!
Class IndexController extends AbstractActionController!
{!
public function indexAction()!
{!
/** @var VendorVendorService $vendor */!
$vendor = $this->serviceLocator->get('VendorVendorService');!
!
$view = new ViewModel();!
return $view;!
}!
}
LonestarPHP
2015
20
21. Why it Works (Most of the time, Kinda)
• Allows
us
to
extend
things
we
didn’t
necessarily
create
• Encourages
code
re-‐use
• Allows
developers
to
abstract
away
things
LonestarPHP
2015
21
22. How to use it
• Understand
the
difference
between
Public,
Protected,
and
Private
• Public
–
Anyone
can
use
this,
even
children
• Protected
–
Anything
internal
can
use
this,
even
children
• Private
–
This
is
mine,
hands
off
• Abstract
vs
Concrete
Classes
• Abstract
classes
cannot
be
instanOated
directly,
they
must
be
extended
LonestarPHP
2015
22
23. The Employee Class
LonestarPHP
2015
23
abstract class Employee {
protected $name; // Employee Name
protected $number; // Employee Number
public function setData($data) {
$this->name = $data['name'];
$this->number = $data['number'];
}
public function viewData() {
echo <<<ENDTEXT
Name: {$this->name}
Number: {$this->number}
ENDTEXT;
}
}
24. The Manager Class
LonestarPHP
2015
24
class Manager extends Employee {
protected $title; // Employee Title
protected $dues; // Golf Dues
public function setData($data) {
parent::setData($data);
$this->title = $data['title'];
$this->dues = $data['dues'];
}
public function viewData() {
parent::viewData();
echo <<<ENDTEXT
Title: {$this->title}
Golf Dues: {$this->dues}
ENDTEXT;
}
}
25. An Example
<?php!
// Cannot do this. This will throw the following error:!
// Fatal error: Cannot instantiate abstract class Employee!
$employee = new Employee(); !
// We can do this though!!
$manager = new Manager(); !
// Name is protected, so we can't do this. This throws:!
// Fatal error: Cannot access protected property Manager::$name!
$manager->name = 'Bob McManager’;!
// setData is public, so we can use that!
$manager->setData(['name' => 'Bob McManager’,'number' => 1]);!
// We can also view the data, since it's public!
$manager->viewData();
LonestarPHP
2015
25
26. Why can Inheritance Be Bad
• PHP
only
allows
Single
Inheritance
on
an
Class
• You
can
have
a
series
of
Inheritance
though,
for
example
CEO
extends
Manager,
Manager
extends
Employee
• Long
inheritance
chains
can
be
a
code
smell
• Private
members
and
methods
cannot
be
used
by
Child
classes
• Single
Inheritance
can
make
it
hard
to
‘bolt
on’
new
funcOonality
between
disparate
classes
LonestarPHP
2015
26
28. The General Idea
• Classes
contain
other
classes
to
do
work
and
extend
that
way,
instead
of
through
Inheritance
• Interfaces
define
“contracts”
that
objects
will
adhere
to
• Your
classes
implement
interfaces
to
add
needed
funcOonality
LonestarPHP
2015
28
29. Interfaces
interface EmployeeInterface {!
protected $name;!
protected $number;!
!
public function getName();!
public function setName($name);!
public function getNumber();!
public function setNumber($number);!
}!
!
interface ManagerInterface {!
protected $golfHandicap;!
!
public function getHandicap();!
public function setHandicap($handicap);!
}
LonestarPHP
2015
29
30. Interface Implementation
class Employee implements EmployeeInterface {!
public function getName() {!
return $this->name;!
}!
public function setName($name) {!
$this->name = $name;!
}!
}!
class Manager implements EmployeeInterface, ManagerInterface {!
// defines the employee getters/setters as well!
public function getHandicap() {!
return $this->handicap; !
}!
public function setHandicap($handicap) {!
$this->handicap = $handicap;!
}!
}
LonestarPHP
2015
30
31. This is Good and Bad
• “HAS-‐A”
is
tends
to
be
more
flexible
than
“IS-‐A”
• Somewhat
easier
to
understand,
since
there
isn’t
a
hierarchy
you
have
to
backtrack
• Each
class
must
provide
their
own
ImplementaOon,
so
can
lead
to
code
duplicaOon
LonestarPHP
2015
31
32. Traits
• Allows
small
blocks
of
code
to
be
defined
that
can
be
used
by
many
classes
• Useful
when
abstract
classes/inheritance
would
be
cumbersome
• My
Posts
and
Pages
classes
should
need
to
extend
a
Slugger
class
just
to
generate
slugs.
LonestarPHP
2015
32
33. Avoid Code-Duplication with Traits
trait EmployeeTrait {!
public function getName() {!
return $this->name;!
}!
public function setName($name) {!
$this->name = $name;!
}!
}!
class Employee implements EmployeeInterface {!
use EmployeeTrait;!
}!
class Manager implements EmployeeInterface, ManagerInterface {!
use EmployeeTrait;!
use ManagerTrait;!
}
LonestarPHP
2015
33
36. What is Coupling?
• Coupling
is
how
dependent
your
code
is
on
another
class
• The
more
classes
you
are
coupled
to,
the
more
changes
affect
your
class
LonestarPHP
2015
36
37. <?php!
!
namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewModelViewModel;!
!
class MapController extends AbstractActionController!
{!
public function indexAction()!
{!
// Position is an array with a Latitude and Longitude object!
$position = $this->getServiceLocator()->get('MapService’)!
->getLatLong('123 Main Street', 'Defiance', 'OH');!
echo $position->latitude->getPoint();!
}!
}
LonestarPHP
2015
37
40. What is Dependency Injection?
• InjecOng
dependencies
into
classes,
instead
of
having
the
class
create
it
• Allows
for
much
easier
tesOng
• Allows
for
a
much
easier
Ome
swapping
out
code
• Reduces
the
coupling
that
happens
between
classes
LonestarPHP
2015
40
41. Method Injection
class MapService {!
public function getLatLong(GoogleMaps $map, $street, $city, $state) {!
return $map->getLatLong($street . ' ' . $city . ' ' . $state);!
}!
!
public function getAddress(GoogleMaps $map, $lat, $long) {!
return $map->getAddress($lat, $long);!
}!
}
LonestarPHP
2015
41
42. Constructor Injection
class MapService {!
protected $map;!
public function __construct(GoogleMaps $map) {!
$this->map = $map;!
}!
public function getLatLong($street, $city, $state) {!
return $this!
->map!
->getLatLong($street . ' ' . $city . ' ' . $state);!
}!
}!
!
LonestarPHP
2015
42
43. Setter Injection
class MapService {!
protected $map;!
!
public function setMap(GoogleMaps $map) {!
$this->map = $map;!
}!
public function getMap() {!
return $this->map;!
}!
public function getLatLong($street, $city, $state) {!
return $this->getMap()->getLatLong($street . ' ' . $city . ' ' . $state);!
}!
}!
LonestarPHP
2015
43
45. Single Responsibility Principle
• Every
class
should
have
a
single
responsibility,
and
that
responsibility
should
be
encapsulated
in
that
class
LonestarPHP
2015
45
46. What is a Responsibility?
• Responsibility
is
a
“Reason
To
Change”
–
Robert
C.
MarOn
• By
having
more
than
one
“Reason
to
Change”,
code
is
harder
to
maintain
and
becomes
coupled
• Since
the
class
is
coupled
to
mulOple
responsibiliOes,
it
becomes
harder
for
the
class
to
adapt
to
any
one
responsibility
LonestarPHP
2015
46
47. An Example
/**
* Create a new invoice instance.
*
* @param LaravelCashierContractsBillable $billable
* @param object
* @return void
*/
public function __construct(BillableContract $billable, $invoice)
{
$this->billable = $billable;
$this->files = new Filesystem;
$this->stripeInvoice = $invoice;
}
/**
* Create an invoice download response.
*
* @param array $data
* @param string $storagePath
* @return SymfonyComponentHttpFoundationResponse
*/
public function download(array $data, $storagePath = null)
{
$filename = $this->getDownloadFilename($data['product']);
$document = $this->writeInvoice($data, $storagePath);
$response = new Response($this->files->get($document), 200, [
'Content-Description' => 'File Transfer',
'Content-Disposition' => 'attachment; filename="'.$filename.'"',
'Content-Transfer-Encoding' => 'binary',
'Content-Type' => 'application/pdf',
]);
$this->files->delete($document);
return $response;
}
LonestarPHP
2015
47
hFps://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php
48. Why is this Bad?
• This
single
class
has
the
following
responsibiliOes:
• GeneraOng
totals
for
the
invoice
(including
discounts/coupons)
• GeneraOng
an
HTML
View
of
the
invoice
(Invoice::view())
• GeneraOng
a
PDF
download
of
the
invoice(Invoice::download())
• This
is
coupled
to
a
shell
script
as
well
• Two
different
displays
handled
by
the
class.
Adding
more
means
more
responsibility
• Coupled
to
a
specific
HTML
template,
the
filesystem,
the
Laravel
Views
system,
and
PhantomJS
via
the
shell
script
LonestarPHP
2015
48
49. How to Improve
• Change
responsibility
to
just
building
the
invoice
data
• Move
the
‘output’
stuff
to
other
classes
LonestarPHP
2015
49
51. This is not a testing talk
• Using
Interfaces
makes
it
easier
to
mock
objects
• Reducing
coupling
and
following
Demeter’s
Law
makes
you
have
to
mock
less
objects
• Dependency
InjecOon
means
you
only
mock
what
you
need
for
that
test
• Single
Responsibility
means
your
test
should
be
short
and
sweet
• Easier
tesOng
leads
to
more
tesOng
LonestarPHP
2015
51
53. We can make a dog with wheels!
• Abstract
class
for
Animal
• Class
for
Dog
that
extends
Animal
• Trait
for
Wheels
• With
the
write
methodology,
we
could
even
unit
test
this
In
the
real
world,
we
can
now
represent
a
crippled
dog
LonestarPHP
2015
53