PHP
Raiting:
17

Clean code in PHP


image
These are the principles of software development, taken from the Robert Martin Clean Code book and adapted for PHP. This manual is not a programming style, but to create a readable, reusable and refactoring-friendly PHP code.
Not each of these principles should be strictly observed, and with even fewer will all agree. These are just recommendations, not more, but they are all codified in the author's many years of collective experience.
The article is inspired by clean-code-javascript .
Contents

Variables
Functions
Data Objects and Structures
Classes

S: The Principle of Single Responsibility (SRP)
O: Principle of openness / closure (Open / Closed Principle, OCP)
L: The substitution principle of Liskov Substitution Principle (LSP)
I: The principle of interface separation (Interface Segregation Principle, ISP)
D: The Principle of Dependency Inversion Principle (DIP)

Variables
Use meaningful and pronounced variable names
Bad:
$ymdstr = $moment->format('y-m-d'); Good:
$currentDate = $moment->format('y-m-d');
For one type of variables, use a single dictionary
Bad:
getUserInfo();
getClientData();
getCustomerRecord();
Good:
getUser();
Use names that make it easy to search for
We will read more code than we will ever write. Therefore, it is important to write code that is readable and search-friendly. But by giving names to variables, useless for understanding our program, we are hindering future readers. Use names that are convenient to look for.
Bad:
// What the heck is 86400 for?
addExpireAt(86400);
Good:
// Declare them as capitalized `const` globals.
interface DateGlobal {
const SECONDS_IN_A_DAY = 86400;
}

addExpireAt(DateGlobal::SECONDS_IN_A_DAY);
Use the explanatory variables
Bad:
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches[1], $matches[2]);
Not bad:
This is better, but we are still heavily dependent on the regular expression.
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);

list(, $city, $zipCode) = $matches;
saveCityZipCode($city, $zipCode);
Good:
Using the naming of subparts, we reduce the dependence on the regular expression.
$address = 'One Infinite Loop, Cupertino 95014';
$cityZipCodeRegex = '/^[^,\\]+[,\\\s]+(?<city>.+?)\s*(?<zipCode>\d{5})?$/';
preg_match($cityZipCodeRegex, $address, $matches);

saveCityZipCode($matches['city'], $matches['zipCode']);
Avoid mental mapping
Do not force those who will read your code to translate the values ​​of variables. It is better to write explicitly than implicitly.
Bad:
$l = ['Austin', 'New York', 'San Francisco'];

for ($i = 0; $i < count($l); $i++) {
$li = $l[$i];
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
// Wait, what is `$li` for again?
dispatch($li);
}
Good:
$locations = ['Austin', 'New York', 'San Francisco'];

foreach ($locations as $location) {
doStuff();
doSomeOtherStuff();
// ...
// ...
// ...
dispatch($location);
});
Do not add unnecessary context
If the name of your class / object is associated with something, do not project this association to the variable name.
Bad:
$car = [
'carMake' => 'Honda',
'carModel' => 'Accord',
'carColor' => 'Blue',
];

function paintCar(&$car) {
$car['carColor'] = 'Red';
}
Good:
$car = [
'make' => 'Honda',
'model' => 'Accord',
'color' => 'Blue',
];

function paintCar(&$car) {
$car['color'] = 'Red';
}
Instead of abbreviated or conditional, use the default arguments
Bad:
function createMicrobrewery($name = null) {
$breweryName = $name ?: 'Hipster Brew Co.';
// ...
}
Good:
function createMicrobrewery($breweryName = 'Hipster Brew Co.') {
// ...
}
Functions
Arguments of functions (ideally two or less)
It is extremely important to limit the number of function parameters, because this simplifies testing. More than three arguments lead to a "combinatorial explosion" when you need to test a bunch of different cases for each argument.
Ideal - without any arguments at all. One or two are normal too, but three should be avoided. If they get more, then you need to combine to reduce the amount. Usually, if you have more than two arguments, the function does too much. In cases where this is not the case, it is often sufficient to use a higher-level object as an argument.
Bad:
function createMenu($title, $body, $buttonText, $cancellable) {
// ...
}
Good:
class MenuConfig
{
public $title;
public $body;
public $buttonText;
public $cancelLabel = false;
}

$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancelLabel = true;

function createMenu(MenuConfig $config) {
// ...
}
Functions must do something one
This is certainly the most important rule in software development. When functions do more than one thing, they are more difficult to compose, test, and justify. And if you can give functions only by some single actions, then they will be easier to refactor, and your code will become much cleaner. Even if you do not follow any other recommendation, except this one, you'll still be ahead of many other developers.
Bad:
function emailClients($clients) {
foreach ($clients as $client) {
$clientRecord = $db->find($client);
if ($clientRecord->isActive()) {
email($client);
}
}
}
Good:
function emailClients($clients) {
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
}

function activeClients($clients) {
return array_filter($clients, 'isClientActive');
}

function isClientActive($client) {
$clientRecord = $db->find($client);
return $clientRecord->isActive();
}
Function names must be
Bad:
function addToDate($date, $month) {
// ...
}

$date = new \DateTime();

// It's hard to tell from the function name what is added
addToDate($date, 1);
Good:
function addMonthToDate($month, $date) {
// ...
}

$date = new \DateTime();
addMonthToDate(1, $date);
Functions must be only one level of abstraction
If you have several levels of abstraction, then the function is assigned too many tasks. Splitting functions allows you to reuse code and make testing easier.
Bad:
function parseBetterJSAlternative($code) {
$regexes = [
// ...
];

$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
// ...
}
}

$ast = [];
foreach($tokens as $token) {
// lex...
}

foreach($ast as $node) {
// parse...
}
}
Good:
function tokenize($code) {
$regexes = [
// ...
];

$statements = split(' ', $code);
$tokens = [];
foreach($regexes as $regex) {
foreach($statements as $statement) {
$tokens[] = /* ... */;
});
});

return $tokens;
}

function lexer($tokens) {
$ast = [];
foreach($tokens as $token) {
$ast[] = /* ... */;
});

return $ast;
}

function parseBetterJSAlternative($code) {
$tokens = tokenize($code);
$ast = lexer($tokens);
foreach($ast as $node) {
// parse...
});
}
Remove the duplicate code
Try to completely get rid of duplicate code. It's bad that if you need to change the logic, you'll have to do it in several places.
Imagine that you own a restaurant and monitor whether there are any products: tomatoes, onions, garlic, spices, etc. If you have several lists with the contents of refrigerators, then you have to update them all when you are preparing a dish. And if the list is one, then it is necessary to make changes only in it.
Often, duplicate code arises because you are doing two or more things that have a lot in common. But a small difference between them makes you write a few functions, and they mostly do the same thing. Removing duplicate code means that you create an abstraction that can handle all the differences with a single function / module / class.
The correct choice of abstraction is critically important, so follow the SOLID principles described in the "Classes" section. Bad abstractions can be worse than duplicate code, so be careful! But if you can write good, then do it! Do not repeat, otherwise it turns out that every time you change, you need to update the code in several places.
Bad:
function showDeveloperList($developers) {
foreach($developers as $developer) {
$expectedSalary = $developer->calculateExpectedSalary();
$experience = $developer->getExperience();
$githubLink = $developer->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];

render($data);
}
}

function showManagerList($managers) {
foreach($managers as $manager) {
$expectedSalary = $manager->calculateExpectedSalary();
$experience = $manager->getExperience();
$githubLink = $manager->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];

render($data);
}
}
Good:
function showList($employees) {
foreach($employees as $employe) {
$expectedSalary = $employe->calculateExpectedSalary();
$experience = $employe->getExperience();
$githubLink = $employe->getGithubLink();
$data = [
$expectedSalary,
$experience,
$githubLink
];

render($data);
}
}
Do not use flags as parameters for
Flags tell your users that the functions do more than one thing. And they have to do one thing. Separate your functions if they go to different branches of code in accordance with Boolean logic.
Bad:
function createFile($name, $temp = false) {
if ($temp) {
touch('./temp/'.$name);
} else {
touch($name);
}
}
Good:
function createFile($name) {
touch($name);
}

function createTempFile($name) {
touch('./temp/'.$name);
}
Avoid the side effects of
A function can introduce side effects if it not only receives a value and returns another value / value, but does something else. A side effect may be writing to a file, changing a global variable, or randomly sending all your money to a stranger.
But sometimes side effects are needed. For example, the same entry in the file. It is better to do this centrally. Do not select several functions and classes that write to any one file, use a single service for this. Only.
The main task is to avoid common errors like the general state for several objects without any structure; Use of mutable data types that can be overwritten by something; absence of centralized treatment of side effects. If you can do this, you will be happier than the vast majority of other programmers.
Bad:
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
$name = 'Ryan McDermott';

function splitIntoFirstAndLastName() {
global $name;
$name = preg_split('/ /', $name);
}

splitIntoFirstAndLastName();

var_dump($name); // ['Ryan', 'McDermott'];
Good:
$name = 'Ryan McDermott';

function splitIntoFirstAndLastName($name) {
return preg_split('/ /', $name);
}

$newName = splitIntoFirstAndLastName($name);

var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];
Do not write to global functions
Clogging with globals is a bad habit in any language, because you can conflict with another library, and users of your API will not know about it until they receive an exception in production. Here is an example: you need a configuration array. You write a global function like config (), but it can conflict with another library trying to do the same. Therefore, it is better to use a singleton design pattern and a simple configuration.
Bad:
function config() {
return [
'foo' => 'bar',
]
}
Good:
class Configuration {
private static $instance;
private function __construct() {/* */}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Configuration();
}
return self::$instance;
}
public function get($key) {/* */}
public function getAll() {/* */}
}

$singleton = Configuration::getInstance();
Encapsulating Conditional Expressions
Bad:
if ($fsm->state === 'fetching' && is_empty($listNode)) {
// ...
}
Good:
function shouldShowSpinner($fsm, $listNode) {
return $fsm->state === 'fetching' && is_empty($listNode);
}

if (shouldShowSpinner($fsmInstance, $listNodeInstance)) {
// ...
}
Avoid negative conditional expressions
Bad:
function isDOMNodeNotPresent($node) {
// ...
}

if (!isDOMNodeNotPresent($node)) {
// ...
}
Good:
function isDOMNodePresent($node) {
// ...
}

if (isDOMNodePresent($node)) {
// ...
}
Avoid conditional expressions
Probably, it seems impossible. For the first time this is heard, many people say: "How can I do anything without expression?" The second common question: "Well, it's fine, but why should I do this?" The answer lies in the pure code rule discussed above: the function should do something one. If you have classes and functions that contain an expression, then you tell your users that the function does more than one thing. Do not forget - you need to leave one thing.
Bad:
class Airplane {
// ...
public function getCruisingAltitude() {
switch ($this->type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
Good:
class Airplane {
// ...
}

class Boeing777 extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getPassengerCount();
}
}

class AirForceOne extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude();
}
}

class Cessna extends Airplane {
// ...
public function getCruisingAltitude() {
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
Avoid type checking (part 1)
PHP is not typed, that is, your functions can accept arguments of any type. Sometimes such freedom even hinders and there is a temptation to perform type checking in functions. But there are many ways to avoid this. The first thing to consider is a consistent API.
Bad:
function travelToTexas($vehicle) {
if ($vehicle instanceof Bicycle) {
$vehicle->peddle($this->currentLocation, new Location('texas'));
} else if ($vehicle instanceof Car) {
$vehicle->drive($this->currentLocation, new Location('texas'));
}
}
Good:
function travelToTexas($vehicle) {
$vehicle->move($this->currentLocation, new Location('texas'));
}
Avoid type checking (part 2)
If you work with basic primitives (like string, integer) and arrays, then you can not use polymorphism. But if it seems that you still need type checking, then apply the type declaration or strict mode. This will give you a static typing over the standard PHP syntax. The problem of manual type checking is that its qualitative implementation implies such verbosity that the resulting artificial "type of security" does not compensate for the loss of code readability. Keep your PHP clean, write good tests and conduct quality code revisions. Or do it all, but with a strict PHP-type declaration or in strict mode.
Bad:
function combine($val1, $val2) {
if (is_numeric($val1) && is_numeric($val2)) {
return $val1 + $val2;
}

throw new \Exception('Must be of type Number');
}
Good:
function combine(int $val1, int $val2) {
return $val1 + $val2;
}
Remove the dead code
It's bad like the duplicate code. You do not need to keep it in the code base. If something is not called, get rid of it! If that, the dead code can be obtained from the version history.
Bad:
function oldRequestModule($url) {
// ...
}

function newRequestModule($url) {
// ...
}

$req = new newRequestModule($requestUrl);
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
Good:
function requestModule($url) {
// ...
}

$req = new requestModule($requestUrl);
inventoryTracker('apples', $req, 'www.inventory-awesome.io');
Data Objects and Structures
Use getters and setters
In PHP, you can set public, protected, and privial keywords for methods. With their help you will manage the change of properties of the object.

If you need to not only receive an object property, it is not necessary to find and change each accessor method in the code base.
Thanks to it's easier to add validation.
You can encapsulate an internal representation.
With the help of getters and setters, it's easy to add logging and error handling.
When inheriting this class, you can override the default functionality.
You can lazily load the properties of the object, for example, getting them from the server.

It is also part of the principle of openness / closure, which is part of a set of object-oriented design principles.
Bad:
class BankAccount {
public $balance = 1000;
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->balance -= 100;
Good:
class BankAccount {
private $balance;

public function __construct($balance = 1000) {
$this->balance = $balance;
}

public function withdrawBalance($amount) {
if ($amount > $this->balance) {
throw new \Exception('Amount greater than available balance.');
}
$this->balance -= $amount;
}

public function depositBalance($amount) {
$this->balance += $amount;
}

public function getBalance() {
return $this->balance;
}
}

$bankAccount = new BankAccount();

// Buy shoes...
$bankAccount->withdrawBalance($shoesPrice);

// Get balance
$balance = $bankAccount->getBalance();
Objects should have personal / protected components (members)
Bad:
class Employee {
public $name;

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

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->name; // Employee name: John Doe
Good:
class Employee {
protected $name;

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

public function getName() {
return $this->name;
}
}

$employee = new Employee('John Doe');
echo 'Employee name: '.$employee->getName(); // Employee name: John Doe
Classes
The Single Responsibility Principle (SRP)
As the Clean Code says: "There can never be more than one reason to change a class". Sometimes it is tempting to stuff a class with all sorts of functionality, as we do with bags and backpacks that are allowed to be taken as hand luggage on a plane. The problem is that your class will not be conceptually cohesive, and therefore there are many reasons to change it. It is important to minimize the number of times you need to change a class. And it is important because when there is an excess of functionality in the class and you need to change its part, it can be difficult to understand how this will affect the dependent modules in the code base.
Bad:
class UserSettings {
private $user;
public function __construct($user) {
$this->user = user;
}

public function changeSettings($settings) {
if ($this->verifyCredentials()) {
// ...
}
}

private function verifyCredentials() {
// ...
}
}
Good:
class UserAuth {
private $user;
public function __construct($user) {
$this->user = user;
}

protected function verifyCredentials() {
// ...
}
}

class UserSettings {
private $user;
public function __construct($user) {
$this->user = $user;
$this->auth = new UserAuth($user);
}

public function changeSettings($settings) {
if ($this->auth->verifyCredentials()) {
// ...
}
}
}
Open / Closed Principle (OCP)
As Bertrand Meyer said: "Program entities (classes, modules, functions, etc.) should be open for expansion, but they are closed for modification." What does this mean? Allow users to add new functionality without changing the code.
Bad:
abstract class Adapter {
protected $name;
public function getName() {
return $this->name;
}
}

class AjaxAdapter extends Adapter {
public function __construct() {
parent::__construct();
$this->name = 'ajaxAdapter';
}
}

class NodeAdapter extends Adapter {
public function __construct() {
parent::__construct();
$this->name = 'nodeAdapter';
}
}

class HttpRequester {
private $adapter;
public function __construct($adapter) {
$this->adapter = $adapter;
}

public function fetch($url) {
$adapterName = $this->adapter->getName();
if ($adapterName === 'ajaxAdapter') {
return $this->makeAjaxCall($url);
} else if ($adapterName === 'httpNodeAdapter') {
return $this->makeHttpCall($url);
}
}

protected function makeAjaxCall($url) {
// request and return promise
}

protected function makeHttpCall($url) {
// request and return promise
}
}
Good:
abstract class Adapter {
abstract protected function getName();
abstract public function request($url);
}

class AjaxAdapter extends Adapter {
protected function getName() {
return 'ajaxAdapter';
}

public function request($url) {
// request and return promise
}
}

class NodeAdapter extends Adapter {
protected function getName() {
return 'nodeAdapter';
}

public function request($url) {
// request and return promise
}
}

class HttpRequester {
private $adapter;
public function __construct(Adapter $adapter) {
$this->adapter = $adapter;
}

public function fetch($url) {
return $this->adapter->request($url);
}
}
The principle of substitution of Liskov Substitution Principle (LSP)
Behind this frightening term is a very simple idea. Formal definition: "If S is a subtype of T, then objects of type T can be replaced by objects of type S (for example, objects of type S can be substituted for objects of type T) without changing any properties of the program (correctness, problems, ) ". Even more frightening definition.
It can be explained more simply: if you have a parent and a child class, then they can be used interchangeably without getting incorrect results. Consider a classic example with a square and a rectangle. From the point of view of mathematics, a square is a rectangle, but if you model this relationship is-a by inheritance, then you will have problems.
Bad:
class Rectangle {
private $width, $height;

public function __construct() {
$this->width = 0;
$this->height = 0;
}

public function setColor($color) {
// ...
}

public function render($area) {
// ...
}

public function setWidth($width) {
$this->width = $width;
}

public function setHeight($height) {
$this->height = $height;
}

public function getArea() {
return $this->width * $this->height;
}
}

class Square extends Rectangle {
public function setWidth($width) {
$this->width = $this->height = $width;
}

public function setHeight(height) {
$this->width = $this->height = $height;
}
}

function renderLargeRectangles($rectangles) {
foreach($rectangle in $rectangles) {
$rectangle->setWidth(4);
$rectangle->setHeight(5);
        $ area = $ rectangle-> getArea (); // Bad: Will return 25 for Square. Should be 20.
$rectangle->render($area);
});
}

$rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($rectangles);
Good:
abstract class Shape {
private $width, $height;

abstract public function getArea();

public function setColor($color) {
// ...
}

public function render($area) {
// ...
}
}

class Rectangle extends Shape {
public function __construct {
parent::__construct();
$this->width = 0;
$this->height = 0;
}

public function setWidth($width) {
$this->width = $width;
}

public function setHeight($height) {
$this->height = $height;
}

public function getArea() {
return $this->width * $this->height;
}
}

class Square extends Shape {
public function __construct {
parent::__construct();
$this->length = 0;
}

public function setLength($length) {
$this->length = $length;
}

public function getArea() {
return $this->length * $this->length;
}
}

function renderLargeRectangles($rectangles) {
foreach($rectangle in $rectangles) {
if ($rectangle instanceof Square) {
$rectangle->setLength(5);
} else if ($rectangle instanceof Rectangle) {
$rectangle->setWidth(4);
$rectangle->setHeight(5);
}

$area = $rectangle->getArea();
$rectangle->render($area);
});
}

$shapes = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles($shapes);
Interface Segregation Principle (ISP)
According to the ISP, "Clients should not depend on interfaces that do not use".
A good example of demonstrating the principle: classes that require large objects of settings (settings objects). It is recommended not to require clients to configure many parameters, because for the most part they do not need them. If you make them optional, this will help to avoid bloating the interface.
Bad:
interface WorkerInterface {
public function work();
public function eat();
}

class Worker implements WorkerInterface {
public function work() {
// ....working
}
public function eat() {
// ...... eating in launch break
}
}

class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}

public function eat() {
//.... eating in launch break
}
}

class Manager {
/** @var WorkerInterface $worker **/
private $worker;

public function setWorker(WorkerInterface $worker) {
$this->worker = $worker;
}

public function manage() {
$this->worker->work();
}
}
Good:
interface WorkerInterface extends FeedableInterface, WorkableInterface {
}

interface WorkableInterface {
public function work();
}

interface FeedableInterface {
public function eat();
}

class Worker implements WorkableInterface, FeedableInterface {
public function work() {
// ....working
}

public function eat() {
//.... eating in launch break
}
}

class Robot implements WorkableInterface {
public function work() {
// ....working
}
}

class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}

public function eat() {
//.... eating in launch break
}
}

class Manager {
/** @var $worker WorkableInterface **/
private $worker;

public function setWorker(WorkableInterface $w) {
$this->worker = $w;
}

public function manage() {
$this->worker->work();
}
}
Principle of Dependency Inversion Principle (DIP)
This principle states:

High-level modules should not depend on low-level modules. Both types should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions.

At first, this can be difficult to understand, but if you worked with PHP frameworks (like Symfony), you already met the implementation of this principle in the form of Dependency Injection (DI). However, these principles are not identical, DI shields high-level modules from the details of their low-level modules and their settings. This can be done by DI. The huge advantage is that the coupling is reduced between the modules. Clutching is a very bad design pattern, making it hard to refactor the code.
Bad:
class Worker {
public function work() {
// ....working
}
}

class Manager {
/** @var Worker $worker **/
private $worker;

public function __construct(Worker $worker) {
$this->worker = $worker;
}

public function manage() {
$this->worker->work();
}
}

class SuperWorker extends Worker {
public function work() {
//.... working much more
}
}
Good:
interface WorkerInterface {
public function work();
}

class Worker implements WorkerInterface {
public function work() {
// ....working
}
}

class SuperWorker implements WorkerInterface {
public function work() {
//.... working much more
}
}

class Manager {
/** @var Worker $worker **/
private $worker;

public function __construct(WorkerInterface $worker) {
$this->worker = $worker;
}

public function manage() {
$this->worker->work();
}
}
Merge methods into chains
This is a very useful and common template used in many libraries, for example in PHPUnit and Doctrine. It makes your code base more expressive and less verbose. Therefore, I recommend combining the methods into chains (chaining), and you will see how clean your code becomes. At the end of each class function, simply return this - and you can attach the next class method to it.
Bad:
class Car {
private $make, $model, $color;

public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}

public function setMake($make) {
$this->make = $make;
}

public function setModel($model) {
$this->model = $model;
}

public function setColor($color) {
$this->color = $color;
}

public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}

$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();
Good:
class Car {
private $make, $model, $color;

public function __construct() {
$this->make = 'Honda';
$this->model = 'Accord';
$this->color = 'white';
}

public function setMake($make) {
$this->make = $make;

// NOTE: Returning this for chaining
return $this;
}

public function setModel($model) {
$this->model = $model;

// NOTE: Returning this for chaining
return $this;
}

public function setColor($color) {
$this->color = $color;

// NOTE: Returning this for chaining
return $this;
}

public function dump() {
var_dump($this->make, $this->model, $this->color);
}
}

$car = (new Car())
->setColor('pink')
->setMake('Ford')
->setModel('F-150')
->dump();
Composition is better than inheritance
As they say in the well-known book " Design patterns " Gangs of four, as far as possible, you need to choose a composition, not inheritance. There are many good reasons to use both inheritance and composition. The main goal of this maxim is if you instinctively lean toward inheritance, then try to imagine if the composition can better solve your problem. In some cases this is actually a more suitable option.
You ask: "And when is it better to choose inheritance?" Everything depends on the specific task, but you can focus on this list of situations when inheritance is preferable to composition:

Your inheritance is the relationship is-a, not has-a. Example: Person → Animal vs. User → User Details.
You can reuse the code from the base classes. (People can move like animals.)
You want to make global changes to derived classes by changing the base class. (Change in caloric intake in animals while driving.)

Bad:
class Employee {
private $name, $email;

public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}

// ...
}

// Bad because Employees "have" tax data.
// EmployeeTaxData is not a type of Employee

class EmployeeTaxData extends Employee {
private $ssn, $salary;

public function __construct($ssn, $salary) {
parent::__construct();
$this->ssn = $ssn;
$this->salary = $salary;
}

// ...
}
Good:
class EmployeeTaxData {
private $ssn, $salary;

public function __construct($ssn, $salary) {
$this->ssn = $ssn;
$this->salary = $salary;
}

// ...
}

class Employee {
private $name, $email, $taxData;

public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}

public function setTaxData($ssn, $salary) {
$this->taxData = new EmployeeTaxData($ssn, $salary);
}
// ...
}
KlauS 7 october 2017, 10:27
Vote for this post
Bring it to the Main Page
 

Comments

Leave a Reply

B
I
U
S
Help
Avaible tags
  • <b>...</b>highlighting important text on the page in bold
  • <i>..</i>highlighting important text on the page in italic
  • <u>...</u>allocated with tag <u> text shownas underlined
  • <s>...</s>allocated with tag <s> text shown as strikethrough
  • <sup>...</sup>, <sub>...</sub>text in the tag <sup> appears as a superscript, <sub> - subscript
  • <blockquote>...</blockquote>For  highlight citation, use the tag <blockquote>
  • <code lang="lang">...</code>highlighting the program code (supported by bash, cpp, cs, css, xml, html, java, javascript, lisp, lua, php, perl, python, ruby, sql, scala, text)
  • <a href="http://...">...</a>link, specify the desired Internet address in the href attribute
  • <img src="http://..." alt="text" />specify the full path of image in the src attribute