Hi there everyone
Something I’m often faced with, having lots of friends with varying degrees of programming experience, is how do (PHP) developers move from sub 25k roles to the higher end of the spectrum, the 35-60k developer roles.
Generally, in my experience, the are some key differences in the salary expectations and the skills you can expect for a developer demanding those salaries. These can be broadly summed up as below:
- True understanding of object orientated programming
- Knowledge and application of programming principles
- Exposure to multiple technologies and ability to move around within them
- Web application development vs website development (the difference between relying on browsers and being able to do things like offloading, queueing, sharding, true separation of concerns, performance optimisation, caching, all that stuff)
To this end I’ve had a decent number of developers ask me how to start working with classes, and work in an object oriented way. So I thought I would do a tutorial on this. I’m going to cover some design patterns, some PHP functionality and various other things.
This is going to be a long and wordy tutorial, but hopefully what it will do is give you some understanding in the differences between procedural and OO programming.
All code samples are available in this project on my GitHub
Step One: The Hardest Part
The first thing I am going to disclaim is simple: Please do not try to have a half procedural, half OOP project or system. It’s going to be an absolute nightmare to maintain!
Now that’s out of the way, let’s talk about some design basics. Avoid god classes! A god class is a class which has many, many responsibilities, it can do everything. In terms of a practical application of a God class think of an ecommerce system: if a single class is responsible for checking stock, adding items to your basket, emptying your basket, and the checkout process – it has far too much responsibility.
I always think it’s a good idea to follow the Single Responsibility Principle – to those new to this I simple explain it as follows:
A class should be responsible for a single job. If you can’t tell me, in a sentence, what it does; then it is almost definitely doing too much.
As such if you have a requirement for an ecommerce system as defined above, checking stock levels would be it’s own class. The point of this is so that:
- The class can be used throughout your project, anywhere that you need to check stock levels
- The class can be modified and know that all stock checking functionality happens through a single place
- Any business logic can be contained in a single place
- The class could be swapped out if needs be, again you know all functionality is encapsulated here
Encapsulation: Goes hand in hand with “DRY” (don’t repeat yourself). Basically bringing everything to do with a certain concern (i.e. stock checking) into a single place, rather than leaving it scattered throughout your code.
So you now have a basic idea of what you use a class for, and in what scenarios you would create a new class – basically, any time you need to get something done.
Now working with objects, as opposed to working with a bunch of variables, has some real perks.
NB: Throughout this article I am going to refer to “classes” and “objects”. For all intents and purposes a class is defined, an object is instantiated. Therefore my User class, once it physically exists, becomes an object, until that point it is a class.
If I have an “Order” object rather than a whole heap of variables or a massive multi-dimensional array, I can put functionality in there which I need, I can do decisions and logic based on information contained within that order. What you’re doing is neatly organising everything into it’s own compartments within your code.
The user may be hitting a button to “add to cart”, but in practice you might be doing all kinds of things; checking the stock level, applying voucher codes, modifying the stock level, calculating the value of the cart so far, all sorts. So this separation becomes invaluable.
However, the point of this part of the article is simple. It’s going to be really difficult to follow, and make almost no sense, to have a bunch of objects floating around a procedural execution. The reason for this is, again, simple; if you have some stuff procedural, and some wrapped in classes and objects; how the heck could I possibly know where to look?
Learning Point Two: Using and understanding the syntax
In this point we’re going to cover some basic concepts:
- Inheritance – abstracting and extending classes
- Interfaces – implementation and usages
- Properties, Methods, Privacy and Scope
Firstly, one of the beautiful things about classes is inheritance. Let’s take a basic example of a User. A User might be a Guest, a Member, a Moderator and an Administrator; but they almost definitely share a bunch of common functionalities, like having a user ID for example (though a guest’s would be 0 or null). You don’t want to have to write a whole heap of code to get the user ID a bunch of times, when it’s the same functionality. You want all of your different types of users to share this functionality, this is where inheritance comes in.
Inheritance
<?php abstract class User { protected $userId = 0; protected $isLoggedIn = true; protected $isStaff = false; public function getUserId() { return $this->userId; } public function isLoggedIn() { return $this->isLoggedIn; } } class Guest extends User { protected $isLoggedIn = false; } class Member extends User{} class Moderator extends Member { protected $isStaff = true; public function hasPermission($permission) { // Some logic here and return TRUE or FALSE } } class Administrator extends Moderator { public function hasPermission($permission) { return true; // Administrators can do anything } }
The handy thing about this is that everyone is a User. So if ever I try to manage some dependency and state that a User is required as an object, I can do this really easily, because everything extends off of User, or one of its derivatives.
Also everyone from Guest to Administrator has a getUserId method, which is quite handy and an isLoggedIn method, so it doesn’t matter if my factory returns me a Guest or an Administrator, the functionality is going to work.
Just to clarify some bits here. Guest::isLoggedIn is false, but Member::isLoggedIn (and everyone who extends Member, or Moderator) returns true. Moderator::isStaff returns true as does Administrator::isStaff (because of the inheritance).
Site note: You could never do: $user = new User(); Because User is defined as an abstract class, as such you could do $administrator = new Administrator(); (or any of the other classes)
Interfaces
An interface is, the best way I’ve heard it described, is the difference between plugging a socket into the wall, vs having to wire in your lamp by hand. You can define an interface on an object, to ensure it conforms to certain standards, basic example now of an Emailable interface.
This interface ensures that the entity, whatever it is (User, Customer, Employee, Organisation, Website) can be emailed, by specifying it must have some methods available to it.
In this example we can make anything we want emailable, by simply adding the methods defined and stating that this class implements the interface – now we can send an email to the fridge if we wish, as long as it can define those methods!
<?php interface Emailable { public function getRecipientName(); public function getEmailAddress(); public function acceptsHtmlEmail(); } class Client implements Emailable { public function getRecipientName() { return 'Very Important Company Plc'; } public function getEmailAddress() { return 'someone.somewhere@clientwebsite.com'; } public function acceptsHtmlEmail() { return true; } } class Employee implements Emailable { public function getRecipientName() { return 'John Doe'; } public function getEmailAddress() { return 'john.doe@ourcorporateemail.com'; } public function acceptsHtmlEmail() { return false; } }
Implementing an interface: Simply means that we have defined an interface, and the class which implements that interface conforms to it. Then if we define Emailable as a type hint, PHP will force not only that the class implementing the interface, but also that any object parsed into an Emailable type hinted parameter conforms. Otherwise it’ll throw a hissy fit and not work
Scope!
The big one that catches a lot of new-to-OOP developers out is variable and method scope. So here is a quick and simple one:
- Public – these methods and properties can be accessed (as long as the object is instantiated) from anywhere that has access to the object
- Protected – these methods and properties can be accessed within this object (or derivatives)
- Private – These can only be accessed specifically within this class
- Static – These can be accessed from the class itself, without needing the instantiation of an object
- Constants – As in PHP itself, these never change
That’s the long and short of it. Word of warning! Always assume your code is going to be copied, recreated and used throughout a system and if it is open source by anyone anywhere in any way they feel like it. So be very careful what you expose as public, once it’s public you have to assume code is relying on it, and as such ensure you are backward compatible – what I’m saying is it is easier to change $myProperty and myMethod to be public later, if they were protected before, than to change them from public to protected – because who knows what you might break!
Accessing properties can be done as follows:
Please do not try to run this code, it won’t work 🙂
<?php class Scope { // I will never change const GITHUBURL = 'https://github.com/johnothecoder'; // I can be called from the class, without instantiation, and can be shared across multiple instances public static $fullName = 'Matt Johnson'; // I can be access from anywhere the object exists public $alias = 'JohnoTheCoder'; // I can only be accessed from within Scope or a class which extends Scope protected $name = 'Matt Johnson'; // I can be called from anywhere public function getAlias() { return $this->alias; } // I can be only be called within Scope (or classes which extend scope) protected function getName() { return $this->name; } } // Executing some code echo Scope::GITHUBURL; echo Scope::$fullName; $scope = new Scope(); echo $scope->alias; echo $scope->getAlias(); // But I can't do this echo $scope->name; // Or this echo $scope->getName();
As always, sample available on GitHub
I can’t really talk you through the full spec of this one, as there’s not much to talk through, really it’s just a way of showing you what can and can’t be done within the scopes of an object.
Hopefully this article has been of some use to those of you looking to get into the big wide world of object orientated programming with PHP. Next time I will be covering how to use Dependency Injection, the Factory and Service locator pattern and polymorphism to your advantage 🙂
Thanks for reading!