Documentation on clean coding and demonstration of studied clean coding principals with PHP.

Overview

practice-php-clean-code

Documentation on clean coding and demonstration of studied clean coding principals with PHP. The document contained in this repo are summarized from sources: https://medium.com/swlh/the-must-know-clean-code-principles-1371a14a2e75, https://github.com/jupeter/clean-code-php.

1. General Rules (can skip this section)

DRY: Don't Repeat Yourself - write repeated codes inside functions.
KISS: Keep It Simple Stupid - go for the simplest solution first (try linear search before binary search).

1.1 Variable Naming Rules:

  • descriptive, easy to search, easy to pronounce.
  • don't add type information, avoid encodings.
  • replace magic numbers with constants.

1.2 Comments:

  • The code is the best documentation.
  • Use only to clarify code.

1.3 Functions:

  • Place functions in the downward direction.
  • keep it simple and short, do only one thing inside a function. Rule of thumb: whole function should fits in your monitor.
    • keep number of parameters small (<=3),
    • avoid passing booleans (are you trying to do more than one thing?).
    • use language provided library functions (sorting, searching, data structures etc.)
  • AVOID SIDE EFFECTS (changing variables passed by reference)

1.4 Class:

  • use public, private, protected keywords.
  • keep small, and only for single purpose.
  • small number of instance variables.
  • super class should know nothing of it's sub classes.
  • keep utility variables and methods private.
  • avoid redundancy by inheriting from classes.

1.5 Tests:

  • fast to execute.
  • should not depend on other tests.
  • one assert per test.
  • TEST DRIVEN DEVELOPMENT - writing unit tests while you are writing the code (or before writing new code).

2. Variables

2.1 Make variable names clear, searchable. don't encode or minify.

$currTmptr = 24; // bad
$currentTemparature = 24; // good

2.2 Use constants/enums instead of magic hardcoded numbers.

bad:

class User{

    public $type = 'employee'; // what is a 'employee', what are other types??
}

good:

class UserType{
// dedicated class to keep constant values of employee types

    public const ADMIN_USER = 'admin';
    public const EMPLOYEE_USER = 'employee';
    public const MANAGER_USER = 'manager';
}

class User{

    public $type = UserType::ADMIN_USER;
}

3. Functions

3.1 Return as early as possible.

bad:

function fooFunc($fooFlag){

    $returnValue = null;

    if(!$fooFlag) $returnValue = 0;

    else{
        
	// very long and complex code...
	
        $returnValue = 100;
    }

    return $returnValue;
}

good:

function fooFunc($fooFlag){

    $returnValue = null;

    if(!$fooFlag) return 0;

    // very long and complex code...    
    $returnValue = 100;

    return $returnValue;
}

3.2 Avoid deep nesting.

bad:

function isVacation($day, $week){
// weekend is Sundays every week and Saturdays on every alternating week

    if($week%2==0){
        if($day == 'Sunday' || $day == 'Saturday') return true;
        else return false;
    }    

    else{
        if($day == 'Sunday') return true;
        else return false;
    }
}

good:

function isVacation($day, $week){
// weekend is Sundays every week and Saturdays on every alternating week

    $weekendDays = ['Sunday'];
    $alternateWeekendDays = ['Sunday', 'Saturday'];

    if($week%2==0) return in_array($day, $weekendDays);
    else return in_array($day, $alternateWeekendDays);
}

3.3 Avoid functional side effects.

bad:

class FooClass{
    
    public $fooVar=2;

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

function fooFunc($fooObj) {
    
    $fooObj->a = 5; // changes the value of the original '$fooObj' passed by reference
}

good:

class FooClass{
    
    public $fooVar=2;

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

function fooFunc($fooObj) {
    
    // create a clone to avoid changing the original object
    // which was passed by reference
    $fooObj = clone $fooObj;

    $fooObj->a = 5;
}

3.4 Avoid passing flags as function arguments, functions should do only one thing.

bad:

function processYear($year, $isYearLeap) {

    if($isYearLeap){
        // process leap year
    }
    else{
        // process not leap year
    }
}

good:

function processLeapYear($year) {
    // process leap year
}

function processNotLeapYear($year) {
    // process not leap year
}

3.5 Do only ONE thing inside a function.

bad:

// the functions 'addAndMultiply()' and 'substractAndDivide()' were too much complicated 
// to forcefully fit the implementation of 'applyFormula()' functions

function addAndMultiply(int $x, int $y, int $z) {

    $result = $x + $y;
    $result = $result*$z;
    return $result;
}

function substractAndDivide(int $x, int $y, int $z) {

    $result = $x - $y;
    $result = $result/$z;
    return $result;
}

function applyFormula(int $x, int $y, int $z){
// apply formula (x+y)*z + (x-y)/z
    
    return addAndMultiply($x, $y, $z) + substractAndDivide($x, $y, $z);
}

good:

// instead create generalized functions to do one task only.

function add(int $x, int $y){
    return $x+$y;
}
function substract(int $x, int $y){
    return $x-$y;
}
function multiply(int $x, int $y){
    return $x*$y;
}
function divide(int $x, int $y){
    return $x/$y;
}

function applyFormula(int $x, int $y, int $z){
// apply formula (x+y)*z + (x-y)/z

        $sumPart1 = multiply(add($x, $y), $z); // calculate (x+y)*z
        $sumPart2 = divide(substract($x, $y), $z); // calculate (x+y)*z
        $result = add($sumPart1, $sumPart2);
        
        return $result;
}

3.6 Specify function arguments variable types and/or return type.

bad:

function add($x, $y) {

    if (! is_numeric($x) || ! is_numeric($y)){
        throw new Exception('Must be a Number');
    }

    return $x + $y;
}

good:

function add(int $x, int $y): int {
    return $x + $y;
}

4. OOP

4.1 Use polymorphism instead of type cheking

bad:

class Authentication{
        
    public $auth_type;

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

    public function googleAuthentication() {
        // logic for google authentication
        return true;
    }

    public function githubAuthentication() {
        // logic for google authentication
        return true;
    }
}

$user_authenticator = new Authentication('github');

// making call to authentication function based on '$user_authentication->type' variable
switch($user_authenticator->auth_type){
    case 'google':
        $user_authenticator->googleAuthentication();
        break;

    case 'github':
        $user_authenticator->githubAuthentication();
        break;

    default:
        throw new Exception('unknon auth type='.$user_authenticator->auth_type);
}

good:

abstract class Authentication{

    abstract function authenticate();
}

class GoogleAuthentication extends Authentication{
    public function authenticate(){
        return true;
    }
}

class GithubAuthentication extends Authentication{
    public function authenticate(){
        return true;
    }
}

Authentication $user_auth = new GoogleAuthentication();  // for Google Authentication
// for Github authentication we would initialize it as 'new GithubAuthentication()' 
// and no changes would be required in the rest of the code

$user_auth->authenticate();

4.2 Always enforce encapsulation

bad:

class Result{
    public $mark;
}
$result = new Result();
$result->mark -= 10; // penalize 10 marks for cheating

good:

class Result{

    private static $MAX_MARK = 100;
    private static $MIN_MARK = 0;

    private $mark;

    public function __construct($mark){
        $this->mark = $mark;
    }
    public function penalize($penalizeMarks){
    // method to access the instance variable $mark
    // and decrease it to non-negative
    
        $this->mark-=$penalizeMarks;
        $this->mark = max(Result::$MIN_MARK, $this->mark);
    }
    public function reward($bonusMarks){
    // method to access the instance variable $mark
    // and increase it within a fixed limit
    
	$this->mark+=$bonusMarks;
        $this->mark = min(Result::$MAX_MARK, $this->mark);
    }
}

$result = new Result(85);
$result->penalize(5); // penalize 5 marks for cheating

5. SOLID

S: Single Responsibility

One class should be responsible for ONE and ONLY ONE thing.

bad:

// Character class stores information of a character and also codes regarding actions made by the character
class Character{
    private string $name;
    private float $healthPoints;
    private int $positionX, $positionY;

    public function __construct(
        $name='temp_player', 
        $healthPoints=100.00, 
        $positionX=0,
        $positionY=0)
    {
            $this->name = $name;
            $this->healthPoints = $healthPoints;
            $this->positionX = $positionX;
            $this->positionY = $positionY;   
    }
    
    public function fly() {
        $this->positionX+=5;
        $this->positionY+=5;
    }

    public function sleep() {
        $this->healthPoints+=100.00;
    }
}

good:

// 'Character' class only holds information 
// and a separate 'CharacterAction' class holds implementions of action by the character

class Character{
    private string $name;
    private float $healthPoints;
    private int $positionX, $positionY;

    public function __construct(
        $name='temp_player', 
        $healthPoints=100.00, 
        $positionX=0,
        $positionY=0)
    {
        $this->name = $name;
        $this->healthPoints = $healthPoints;
        $this->positionX = $positionX;
        $this->positionY = $positionY;   
    }
}

class CharacterAction{

    public function fly(Character $character) : Character
    {
        $character->positionX+=5;
        $character->positionY+=5;
        return $character;
    }

    public function sleep(Character $character) : Character
    {
        $character->healthPoints+=100.00;      
        return $character;
    }
}

O: Open-Closed Principal

Open for extension, closed for modification. Make update by extending existing classes and not by modifying them by adding new instance variables and/or method.

bad:

// everytime we implement 'insert()' for a new database source we need to add new method to the Database class

class Database{

    public function insertToFirebase(){
        // implementation
    }
    
    public function insertToMysql(){
        // implementation
    }
}

good:

// everytime we implement 'insert()' for a new database source we need to just extend the Database class and add the implementation.

abstract class Database{

    abstract function insert();
}

class FirebaseDatabase extends Database{
    // overriden method
    function insert(){
        // implementation
    }
}

class MySqlDatabase extends Database{
    // overriden method
    function insert(){
        // implementation
    }
}

L: Liskov Substitution Principal

All super class instances should be completely replacable by their subclasses. Generally, this can be achieved through an additional level of abstraction (addding another higher level interface/abstract class) see the traditional "Rectangle-Square" example: https://github.com/jupeter/clean-code-php#liskov-substitution-principle-lsp

I: Interface Segragation

Keep interfaces short and meaningful, don't force the implementing classes to override irrelevant methods.

bad:

// characters who are only supposed to farm or attack or heal would be forced to override the other methods
interface Action{
    function farm();
    function attack();
    function heal();
}

good:

// character classes can implement corresponding interfaces based on their defined abbilities
interface VillagerAction{
    function farm();
}

interface AttackerAction{
    function attack();
}

interface HealerAction{
    function heal();
}

D: Dependency Inversion

  • high-level modules should not depend on low-level modules. Both should depend on abstractions.
  • abstractions should not depend upon details. Details should depend on abstractions.

The coding example below only contains demo for point (ii)

bad:

abstract class Insect{

    protected int $positionX, $positionY, $positionZ;

    abstract function crawl();
    abstract function fly();
}

class Cockroach extends Insect{

    function crawl(){
        // code for crawling
    }

    function fly(){
        // code for flying
    }
}

class Scorpion extends Insect{
    
    function crawl(){
        // code for crawling
    }

    // scorpions can't fly!
    // abstract class Insect contains too much details about insects (that they can crawl and fly)
    function fly(){
        // code for flying
    }
}

good (maybe...):

abstract class Insect{

    protected int $positionX, $positionY, $positionZ;
}

interface Flyable{
    function fly();
}

interface Crawlable{
    function crawl();
}

class Cockroach extends Insect implements Flyable, Crawlable{

    function crawl(){
        // code for crawling
    }

    function fly(){
        // code for flying
    }
} 

class Scorpion extends Insect implements Crawlable{

    function crawl(){
        // code for crawling
    }
}
You might also like...
Test and enforce architectural rules in your Laravel applications. Keep your app's architecture clean and consistent!
Test and enforce architectural rules in your Laravel applications. Keep your app's architecture clean and consistent!

Laravel Arkitect Laravel Arkitect lets you test and enforce your architectural rules in your Laravel applications, and it's a PHPArkitect wrapper for

A wrapper around symplify/config-transformer used to update recipes and using easy coding standard for generating readable config files.

Symfony Recipes Yaml to PHP Converter This is a wrapper around the symplify/config-transformer used to convert Symfony core recipes which uses .yaml c

PHP Simple M3U Parser, it clean the playlist and remove duplicate

SimpleM3UParser PHP Simple M3U Playlist Parser, it clean, remove duplicate, and group the playlist. Usage see example.php ?php require_once "M3UPars

A super simple, clean and pretty error handler that replace the default error handler of PHP. You need only include this file!

php-custom-error-handler A super simple, clean and pretty error handler that replace the default error handler of PHP. You need just include only this

PHPCheckstyle is an open-source tool that helps PHP programmers adhere to certain coding conventions.

PHPCheckstyle Overview PHPCheckstyle is an open-source tool that helps PHP programmers adhere to certain coding conventions. The tools checks the inpu

A tool to automatically fix PHP Coding Standards issues
A tool to automatically fix PHP Coding Standards issues

PHP Coding Standards Fixer The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards; whether you want to follow PHP codi

Clean Architecture, DDD and CQRS using Symfony 6

Task manager system using Clean Architecture, DDD and CQRS. Environment setup Install Docker Clone the project: git clone https://github.com/k0t9i/Tas

A tool to automatically fix Twig Coding Standards issues

Twig CS Fixer Installation This standard can be installed with the Composer dependency manager. Add the coding standard as a dependency of your projec

Magento 1.x Coding Standard

Magento Extension Quality Program Coding Standard ⚠️ Versions 3.0.0 and above of the MEQP Coding Standard are for Magento 1.x code only. To check Mage

Owner
Ferdous Islam
Ferdous Islam
A story about SQLinject and a demonstration of some vulnerabilities and tools

Предысловие Если не умру,буду дальше развивать эту тему Идея которая пришла мне в голову,<<А почему бы не рассказать об уязвимостях SQL?>>.Поэтому я б

null 0 Jun 11, 2022
Demonstration of OOP concepts and usage of Abstract class & Interfaces

Learn OOP Demonstration of OOP concepts and usage of Abstract class & Interfaces Usage clone this repo run composer install run php index.php Code str

M N Islam Shihan 3 Sep 14, 2021
Coding-standard - Magento PHP CodeSniffer Coding Standard

ECG Magento Code Sniffer Coding Standard ECG Magento Code Sniffer Coding Standard is a set of rules and sniffs for PHP_CodeSniffer tool. It allows aut

Magento ECG 309 Jan 3, 2023
A list of documentation and example code to access the University of Florida's public (undocumented) API

uf_api A list of documentation and example code to access the University of Florida's public (undocumented) API Courses Gym Common Data (admissions an

Rob Olsthoorn 49 Oct 6, 2022
Testing your OpenApi documentation and your code easily.

Raven - How to test your API documentation and behavior. This library was written to allow testing OpenAPI documentation easily. It also allows verify

CH Studio 30 Dec 6, 2022
Simple IT Documentation Solution for MSPs

SimpleMSPDoc RC 1.0 I wasn't happy with what other IT documention software had. I felt they over complicated things and required so much clicky clicky

null 4 Jun 5, 2022
Generate API documentation for humans from your Laravel codebase.✍

Scribe v3 is out now! Scribe helps you generate API documentation for humans from your Laravel/Lumen/Dingo codebase. See a live example at demo.scribe

Knuckles 1k Jan 9, 2023
Disclaimer: The documentation of this plugin is English at the moment, but I might go for Latin later down the line, just for the fun of it.

Quiritibus Plugin This repository is storing the custom plugin developed for the Quiritibus Latin Magazine website, currently being developed at: http

Alkor András 1 Jan 19, 2022
PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.

PHP_CodeSniffer is a set of two PHP scripts; the main phpcs script that tokenizes PHP, JavaScript and CSS files to detect violations of a defined coding standard, and a second phpcbf script to automatically correct coding standard violations. PHP_CodeSniffer is an essential development tool that ensures your code remains clean and consistent.

Squiz Labs 9.9k Jan 5, 2023
Clean Code concepts adapted for PHP - A guide for producing readable, reusable, and refactorable PHP software

Clean Code concepts adapted for PHP - A guide for producing readable, reusable, and refactorable PHP software

Fabio Soares 172 Dec 25, 2022