This repo contains my notes on Object oriented programming using php.
There are two directories of interest:
./snippets
: contains code snippets. Currently, only UltiSnips vim./src
: contains examples for every section in this file, it can be used while solving challenges.
For contribution see here
OOP is a programming style in which we group methods and variables of a particular topic into single class. For example, the code that relate users will be in User class. OOP is heavily adopted because it support code organization, provides modularity and reduces the need to repeat ourselves.
- Encapsulation: data and the operations that manipulate the data (the code) are both encapsulated in the object.
- Inheritance: A class can inherit from another class and take advantage of the methods and properties defined by the superclass. (is a)
- Polymorphism: Similar objects can respond to the same messages (method) in different ways. more on this
- Composition: an object is built from other objects. embedding classes in other classes. (has a)
Classes are used to group the code that handles a certain topic into one place. It is a template for creating objects, providing initial values for state (properties/attributes) and implementations of behavior (methods.
A person can be seen as an object defined by two components: attributes (such as eye color, age, height) and behaviors (such as walking, talking, breathing). In its basic definition, an object is an entity that contains both data and behavior. Each person is different in terms of there age and in how they walk or talk. But they all instance of a class that organize their attributes and behavior for example Human class.
In OOP, objects are the building blocks, they are instances of a some class. A program that leverages OO style is basically a collection of objects. The behavior of an object represents what the object can do and the data stored within an object represents the state of the object.
$this
keyword are used to interact with/refer to a class method or properties
from within the class. Among different uses of $this
keyword, there is
chaining methods and properties.
- We use
public
if we want the methods or properties to be accessed on public scope as well as within the class. - We use
private
if we want the methods or properties to be accessed within the class only. - We use
protected
if we want the methods or properties to be accessed within the class and class child.
To access private
properties from outside the class, we use publicly defined
setters and getters. Using private properties limit the possible interaction to
our private properties from public scope. This is useful when for example we
want to define a hook each time a method is called to get the model of the
object, such as save the request in a log.
Inheritance is central concept in OOP, they enable us to reduce code
duplications by creating a parent/master class with properties and method that
can be inherited by child classes. In php, and many other languages, we use extends
keyword to inherit from another class.
Put simply, an abstract class is a class with at least one abstract method and with a abstract keyword in front of it. They get used for multiple reasons:
- When we want be commit to writing certain class methods, or when we are only sure of there names.
- When we want child classes to define these methods.
Abstract classes cannot be instantiated, and whatever non-abstract class derived from it must include actual implementations of all inherited abstract methods and properties.
An interface can be seen as an outline of what a particular object can do. They are considered one of the main building blocks of the SOLID pattern. With interfaces we can create code which specifies which methods a class must implement, without having to define how these methods are implemented.
A lot of people may find interface to be similar to abstract classes, or doesn't know which one to choice, here a few notes on that:
Interfaces are contract, we "implement" them to provide code and behavior that fit the description of the interface. In the other hand, Abstract Classes are behavior, we "extend" them and add additional behaviors, sometimes we are required to add specific behavior left that are left out by the class (methods marked with "abstract").
Interface maybe used when multiple classes need to define the same methods. However, abstract class might be appropriate when we need the share code between subclasses
Interface | Abstract Class |
---|---|
An interface cannot have concrete methods in it i.e. methods with definition. | An abstract class can have both abstract methods and concrete methods in it. |
All methods declared in interface should be public | An abstract class can have public, private and protected etc methods. |
Multiple interfaces can be implemented by one class. | One class can extend only one abstract class. |
Put simply, Polymorphism is a principle that state that methods in different classes doing similar things should have the same name.
Type hinting is used to specify the expected data type for an argument. It is used for better code organization and improved error messages.
// Class
function funName(ClassName $object) { }
// Strings
function funName(string $arg) { }
// Array
function funName(array $arg) { }
// Boolean
function funName(bool $arg) { }
// Integers
function funName(int $arg) { }
// Floats
function funName(float $arg) { }
To specify the output type of a function, we add after (expected type)
: int
Static methods and properties are those properties with static
keyword
in front of them. They enable us to approach methods and properties of a class
without the need to first create an object out of the class. They are used
mainly as utilities. The following are the main use cases for them:
-
As counters, to save the last value that has been assigned to them. For example, the method
add1ToCars()
adds 1 to the$numberOfCars
property each time they are invoked. -
As utilities for the main classes. Utility methods can perform all kinds of tasks, such as: conversion between measurement systems (kilograms to pounds), data encryption, sanitation, and any other task that is not more than a service for the main classes. The example given below is of a static method with the name of redirect that redirects the user to the URL that we pass to it as an argument.
class Utilis {
static public function redirect($url) {
header("Location: $url");
exit;
}
}
Utilis::redirect("http://www.phpthusiast.com");
Traits are a mechanism for code reuse. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.
A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own.
Some people refer to traits as "like an automatic CTRL+C/CTRL+V for your classes". You specificity some methods in a trait and "import" them into your class. It will make your code behave like the methods were written inside your class.
When using a trait, we should be on the lookout for code duplication and for naming conflicts that are the result of calling the methods in different traits with the same name.
Namespaces and code integration are used when the project grow in complexity, and we need to start organizating our code and integrate them from different resources. Or when we have multiple class or methods with the same name.
Setting up namespace:
- Create an index.php file in the root directory of the site.
- create a class with the name of CarIntro and place it in
src/Car
folder, as followssrc/Car/CarIntro.php
is convention to have file names as same as class name - Define a namespace at the top of the file:
<?php namespace Acme/Car;
it as customary to give the src directory theAcme
namespace, or maybe brand name, and imitate the directory structure.
Using a namespace:
- open up the file we want to use the namesapce in.
- require the namespaced file:
require "src/Car/CarIntro.php;"
- import the namespace
use Acme\Car\CarIntro;
- now we can use the class by
$carintro = new Acme\Car\CarIntro()
- or use an alias with
use Acme\Car\CarIntro as Intro;
Dependency injection is the process whereby we input dependencies that the
application needs directly into the object itself. When class A
cannot do its job
without class B
, we say that class A
is dependent on class B
.
When writing a class, it's natural to use other dependencies; perhaps a database model class. So with dependency injection, instead of a class having its database model created in itself, you can create it outside that object and inject it in.
Lets look at the two ways where we make one class dependent on other:
a Car is dependent on Human Driver, so we create HumanDriver
object from the
in the constructor of the Car
class.
class HumanDriver
{
// Method to return the driver name.
public function sayYourName($name)
{
return $name;
}
}
class Car
{
protected $driver;
// Create the driver object in the constructor
public function __construct() {
$this->driver = new HumanDriver();
}
// A getter method that returns the driver object
public function getDriver() {
return $this->driver;
}
}
Tight coupling between classes become a real issue when we need to switch dependencies. In our example, say we have a robot driving the car instead of a human. In fact, when we do tight coupling between classes, we violate a fundamental principle of well designed code called the “single responsibility principle” (SRP), according to which a class should have only one responsibility.
First, rewrite Car
class so it can set its own $driver
property that is
passed as a parameter to the constructor.
// The Car class gets the driver object injected
// to its constructor
class Car
{
protected $driver;
// The constructor sets the value of the $driver
public function __construct($driver)
{
$this->driver = $driver;
}
// A getter method that returns the driver object
// from within the car object
public function getDriver()
{
return $this->driver;
}
}
Then, we inject dependency by first creating the Driver
object and then
injecting this object into the newly created Car
object through the
constructor:
$humanDriver = new HumanDriver();
$car = new Car($humanDriver);
$robotDriver = new RobotDriver();
$car = new Car($robotDriver);
Exception handling is an elegant way to handle errors which are beyond the program’s scope. For example, if our application fails to contact the database, we can use exception handling to contact another data source or to provide instructions to the users that they need to contact technical support.
In php we can handle exception with making the use of Exception
class
Rules to follow when implementing exceptions handling
- Know what to do with an exception before throwing it.
- If you have no idea what to do with a caught exception then it shouldn't be caught.
OOP supports the idea of creating classes that are complete packages, encapsulating the methods and properties of a single entity. So, a class can be represent a logical component. The following are general design guidelines to be followed when developing classes:
- Mimic the real world: When creating classes, we should design them in a
way that represents the true behavior of the object. For example,
User
class andWebapp
class model a real-world entity. TheUser
andWebapp
objects encapsulate their data and behavior, and they interact through each other’s public interfaces. - Identify the public interfaces: perhaps the most important issue when designing a class is to keep the public interface to a minimum. The entire purpose of building a class is to provide something useful and concise. The goal is to provide the user with exact interface to do the job right.
- Design robust constructor: constructors that deals with how will the class be constructed. A constructor should put an object into an initial, safe state. This includes issues such as properties initialization and memory management.
- Design Error Handling: As with the design of constructors, designing how a class handles errors is of vital importance.
- Document the class and using comments: A lack of proper documentation and comments can undermine the entire system. One of the most crucial aspects of a good design, whether it’s a design for a class or something else, is to carefully document the process.
- Design with reuse in mind: OO coding enable us to easily reuse our code in different systems. Make sure when you design classes, to keep reusability in mind, attempt to predict all the possible scenarios in which it will be used.
- Design with extensibility in mind: Adding new features to a class might be as simple as extending an existing class, adding a few new methods, and modifying the behavior of others. If you have just written a Person class, you must consider the fact that you might later want to write an Employee class or a Customer class. Thus, having Employee inherit from Person might be the best strategy; in this case, the Person class is said to be extensible. You do not want to design Person so that it contains behavior that prevents it from being extended by classes such as Employee or Customer(assuming that in your design you really intend for other classes to extend Person).
- Design with maintainability in mind: Designing useful and concise classes promotes a high level of maintainability. Just as you design a class with extensibility in mind, you should also design with future maintenance in mind. The process of designing classes forces you to organize your code into many (ideally) manageable pieces. Separate pieces of code tend to be more maintainable than larger pieces of code (at least that’s the idea). One of the best ways to promote maintainability is to reduce interdependent code.
- Use Object Persistence: Object persistence is another issue that must be addressed in many OO systems. Persistence is the concept of maintaining the state of an object. When you run a program, if you don’t save the object in some manner, the object dies, never to be recovered. These transient objects might work in some applications, but in most business systems, the state of the object must be saved for later use.
If you like to contribute, it's awesome! Just keep in mind this project using phpcs to achieve unified code style. So make sure you run
composer install
to install the dev dependencies
Then you can run code checks by using the composer script
composer phpcs
or use the code beautifier to auto fix violations.
composer phpcbf
Please run phpcs and solve all issues before creating a pull request
(i) This project is not about a real code lib. It's about OOP examples for better understanding. That's why the ruleset.xml exclude 2 fundamental PSR1 rules.
Areas of contribution
- Adding Examples
- Adding notes to better understand Examples
- Adding challenges with there solutions in
./src/challenges
- Revising, rephrasing or adding new notes in readme to better clarify something.