PHPRefactor

Table of Contents

1 Refactorings

1.1 Add PHPDoc

Smell: An array with elements of generic type or method which can throw Exception

public function printUsersNames(array $users): void
{
    foreach ($users as $user) {
        echo $user->getName();
    }
}

1.1.1 Refactor the code

Add PHPDoc - the Object[] notation in addition to an array type-hint to explain what kind of object is expected

/**
 * @param User[] $users
 */
public function printUsersNames(array $users): void
{
    foreach ($users as $user) {
        echo $user->getName();
    }
}

1.2 Add Type-Hint

Smell: A method with no type defined parameters or return type declaration

public function setNumber($number)
{
    $this->number = $number;
}

1.2.1 Refactor the code

public function setNumber(int $number): void
{
    $this->number = $number;
}

1.2.2 Helper

phpdoc-to-typehint adds automatically scalar type hints and return types to all functions and methods using existing PHPDoc annotations

1.3 Consolidate Conditional Expression

Smell: Sequence of conditionals with the same result

class Sale
{
    public function calculateShipping(Customer $customer)
    {
        if ($customer->isEmployee) return 0;
        if ($customer->isGoldCustomer) return 0;
        if ($customer->isHasACoupon) return 0;
        
        if($isUsa) return 10;
        if($isEurope) return 20;
    }
}

1.3.1 Write a test that pass

1.3.2 Refactor the code

Combine them into a single conditional expression and extract it.

class Sale
{
    public function calculateShipping(Customer $customer): int
    {
       if ($this->isFreeShipping()){
           return 0;
       }

        if($isUsa) {
            return 10;
        }

        if($isEurope) {
            return 20;
        }
    }

    private function isFreeShipping(): bool
    {
        return ($customer->isEmployee || $customer->isGoldCustomer || $customer->isHasACoupon);
    }
}

1.3.3 Pass a test

1.4 Consolidate Duplicate Conditional Fragments

Smell: The same fragment of code is in all branches of a conditional expression.

final class Sale
{
    public function calculateTotal(int $price)
    {
        if ($this->isSpecialDeal()) {
            $total = $price * 0.95;
            $this->setTotal($total);
        }
        else {
            $total = $price * 0.98;
            $this->setTotal($total);
        }
    }
}

1.4.1 Write a test that pass

1.4.2 Refactor the code

Move it outside of the expression.

final class Sale
{
    public function calculateTotal(int $price)
    {
        if ($this->isSpecialDeal()) {
            $total = $price * 0.95;
        }
        else {
            $total = $price * 0.98;
        }

        $this->setTotal($total);
    }
}

1.4.3 Pass a test

1.5 Decompose Conditional

Smell: Complicated conditional (if-else) statement.

class Sale
{
    public $expired_at;
    public $amount;

    public function getAmount()
    {
        if(null !== $this->expired_at && $this->expired_at < time())
        {
            $interest = 10;
            $this->amount = $this->amount + ($this->amount / 100 * $interest);
        }
        else
        {
            $discount = 10;
            $this->amount = $this->amount - ($this->amount / 100 * $discount);
        }
        return $this->amount;
    }
}

1.5.1 Write a test that pass

class SaleTest extends TestCase
{
    public function testAmount()
    {
        $sale = new Sale();
        $sale->amount = 10;
        $sale->expired_at = strtotime('-10 days');
        $this->assertEquals(10 + (10 / 100 * 10), $sale->getAmount());
        $sale = new Sale();

        $sale->amount = 10;
        $sale->expired_at = strtotime('+10 days');
        $this->assertEquals(10 - (10 / 100 * 10), $sale->getAmount());
    }
}

1.5.2 Refactor the code

Extract conditional code in a private method. We name the method isExpired() because our conditional chunk of code checks if the sale is expired. We create the private method isExpired() and, with the technique of extract method, we move chunks of code into the new method. The next step is to move each branch of the condition in a private method. We do the same as we did before for each branch. So we create the private method getAmountWithInterest() for the first branch and the method getAmountWithDiscount() for the second branch.

class Sale
{
    public $expired_at;
    public $amount;

    public function getAmount()
    {
        if ($this->isExpired()) {
            return $this->getAmountWithInterest();
        } else {
            return $this->getAmountWithDiscount();
        }
    }

    private function isExpired()
    {
        return !is_null($this->expired_at) && $this->expired_at < time();
    }

    private function getAmountWithInterest()
    {
        $interest = 10;
        return $this->amount + ($this->amount / 100 * $interest);
    }

    private function getAmountWithDiscount()
    {
        $discount = 10;
        return $this->amount - ($this->amount / 100 * $discount);
    }
}

1.5.3 Pass a test

1.6 Encapsulate Field

Smell: A public field

final class User
{
    /**
     * @var string
     */
    public $name;
}

1.6.1 Write a test that pass

1.6.2 Refactor the code

Make it private and provide accessors.

final class User
{
    /**
     * @var string
     */
    private $name;

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

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

1.6.3 Run a test

1.7 Extract Class

SmellL Large Class

final class User
{
    private $name;
    private $surname;

    private $city;
    private $zipCode;
    private $street;
    private $state;
}

1.7.1 Write a test that pass

1.7.2 Refactor the code

Create a new class and move the relevant fields and methods from the old class into the new class.

final class User
{
    private $name;
    private $surname;

    private $address;
}

final class Address
{
    private $city;
    private $zipCode;
    private $street;
    private $state;
}

1.7.3 Pass a test

1.8 Extract Function

Alias: Extract Method Inverse of: Inline Function Smell: Code fragment that can be grouped together

public function printInvoice(Invoice $invoice): void
{
    echo 'Invoice';
    echo '<br>';
    echo $invoice->getNumber();
   
    echo 'phpRefactor: ';
    echo '<br>';
    echo $invoice->getDate();
}

1.8.1 Write a test that pass

1.8.2 Refactor the code

Turn the fragment into a method whose name explains the purpose of the method

public function printInvoice(Invoice $invoice): void
{
    printInvoiceHeader($invoice);
    printInvoiceFooter($invoice);
}

function printInvoiceHeader(Invoice $invoice): void
{
    echo 'Invoice';
    echo '<br>';
    echo $invoice->getNumber();
}

function printInvoiceFooter(Invoice $invoice): void
{
    echo 'phpRefactor: ';
    echo '<br>';
    echo $invoice->getDate();
}



1.8.3 Run a test

1.8.4 Notes

function printInvoice() {
    $footer=function() {
        echo "phpRefactor.com \n";
        echo "2019";
    };
    
    echo "Header \n";
    $footer();
}
printInvoice();

1.9 Extract Variable

Alias: Introduce Explaining Variable Smell: Complicated expression

if(($stock->checkStatus($order->getItem) > $order->getQuantity()) 
    && ($order->getTotal() > 99) 
    && ($order->getCustomer()->getBillingAddress() === $order->getShippingAddress()));

1.9.1 Write a test that pass

1.9.2 Refactor the code

Put the result of the expression, or part of it in a temporary variable with a name that explains the purpose

$freeShipping = $order->getTotal() > 99;
$stockAvailable = $stock->checkStatus($order->getItem) > $order->getQuantity();
$addressMatches = $order->getCustomer()->getBillingAddress() === $order->getShippingAddress();
if($stockAvailable && $freeShipping && $addressMatches);

1.9.3 Pass a test

1.10 Inline Class

Smell: A class isn't doing very much

final class User
{
    private $name;
    private $surname;

    private $telephoneNumber;
}

final class TelephoneNumber
{
    private $number;
}

1.11 DONE Inline Function

Smell: A function's body is just as clear as it's name

class Circle
{
    public const RADIUS = 2;

    public function getArea(): float
    {
        return $this->getValueOfPI() * self::RADIUS * self::RADIUS;
    }

    private function getValueOfPI(): float
    {
        return pi();
    }
}

1.11.1 Write a test that pass

use PHPUnit\Framework\TestCase;

class CircleTest extends TestCase
{   
    public function testGetArea()
    {
        $circle = new Circle();
        $this->assertEquals( 12.566370614359172, $circle->getArea());
    }
}
PHPUnit 7.4.3 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 53 ms, Memory: 10.00MB

OK (1 test, 1 assertion)

1.11.2 Refactor the code

Put the method's body into the body of its callers and remove the method.

public function getArea(): float
{
    return pi() * $radius * $radius;
}

1.11.3 Pass a test

1.12 Inline Variable

Smell: Variable name doesn't really communicate more than the expression itself or gets in the way of refactoring neighboring code.

let basePrice = anOrder.basePrice;
return (basePrice > 1000);

1.12.1 Refactor the code

return anOrder.basePrice > 1000;

140

1.13 Introduce Parameter Object

Smell: Long Parameter List and parameters that naturally go together

final class Account
{
    public function findAllTransactions(DateTime $start, DateTime $end)
    {
        ...
    }
}

1.13.1 Write a test that pass

1.13.2 Refactor the code

Replace them with an object. #+BEGINSRC php#+ENDSRC

1.13.3 Pass a test

1.14 Optimize Imports

Smell: Imports unused or not in alphabetically order. Multiple use statement

use SomeClass\Worker;
use SomeClass\Foo;
use SomeClass\UnusedClass;

1.14.1 Write a test that pass

1.14.2 Refactor the code

Remove unused imports. Sort imports alphabetically (ascending order). Splits multiple use statement imports into single use statement imports

use SomeClass\{Foo, Worker};

1.14.3 Pass a test

1.15 Move Method

Smell: Method accessing fields and methods in different class

final class Customer
{
    function printInvoice(Order $order)
    {
        echo "Invoice {$order->getId()}";
        echo "Date: {$order->getDate()}";
        echo "Customer: {$this->getName()}";

        $address = $order->getAddress();
        echo "City: {$address->getCity()}";
        echo "Address: {$address->getStreet()}";

        foreach ($order->getItems() as $item){

            echo "Name: {$item->getName()}";
            echo "Price: {$item->getPrice()}";
        }
    }
}   

1.15.1 Write a test that pass

1.15.2 Refactor the code

Move all its features into another class and delete it.

final class Order
{
    function printInvoice()
    {
        echo "Invoice {$this->getId()}";
        echo "Date: {$this->getDate()}";
        echo "Customer: {$this->getCustomer()->getName()}";

        $address = $this->getAddress();
        echo "City: {$address->getCity()}";
        echo "Address: {$address->getStreet()}";

        foreach ($this->getItems() as $item){

            echo "Name: {$item->getName()}";
            echo "Price: {$item->getPrice()}";
        }
    }
}

1.15.3 Pass a test

1.16 Parameterize Method

Smell: Several methods do similar things but with different values contained in the method body.

final class Employee
{
    /**
     * @var float
     */
    private $salary;

    public function setSalary(float $salary)
    {
        $this->salary = $salary;
    }

    public function getSalary(): float
    {
        return $this->salary;
    }

    public function fivePercentRaise()
    {
        $this->salary += $this->salary* (5 / 100);
    }

    public function tenPercentRaise()
    {
        $this->salary += $this->salary* (10 / 100) ;
    }
}

1.16.1 TODO Write a test that pass

1.16.2 Refactor the code

Create one method that uses a parameter for the different values.

final class Employee
{
    /**
     * @var float
     */
    private $salary;

    public function setSalary(float $salary)
    {
        $this->salary = $salary;
    }

    public function getSalary(): float
    {
        return $this->salary;
    }

    public function raise(float $percent)
    {
        $this->salary += $this->salary * ($percent / 100);
    }
}

1.16.3 Pass a test

1.17 Preserve Whole Object

Smell: More than one value from an object are passing as parameters in a method call

class September
{
    /**
     * @var float
     */
    private $highestTemp;

    /**
     * @var float
     */
    private $lowestTemp;

    public function __construct(float $highestTemp, float $lowestTemp)
    {
        $this->highestTemp = $highestTemp;
        $this->lowestTemp = $lowestTemp;
    }

    public function getHighestTemp(): float
    {
        return $this->highestTemp;
    }

    public function getLowestTemp(): float
    {
        return $this->lowestTemp;
    }
}

class Calculator
{
    public function calculateAverageTemperature(float $highestTemp, float $lowestTemp)
    {
        return ($highestTemp + $lowestTemp) / 2;
    }
}

$september = new September(15,5);
$calculator = new Calculator();

$averageTemperature = $calculator->calculateAverageTemperature(
            $september->getHighestTemp(),
            $september->getLowestTemp()
        );

1.17.1 Write a test that pass

public function testCalculateAverageTemperature()
{
        $september = new September(15,5);
        $calculator = new Calculator();

        $averageTemperature = $calculator->calculateAverageTemperature(
            $september->getHighestTemp(),
            $september->getLowestTemp());

        $this->assertEquals(10, $averageTemperature);
}

1.17.2 Refactor the code

Add object as a new parameter. Set it default value to null, that will help to manage the transitions towards the final version of the method.

class Calculator
{
    public function calculateAverageTemperature(float $highestTemp, float $lowestTemp, September $september = null)
    {
        return ($highestTemp + $lowestTemp) / 2;
    }
}

1.17.3 Pass a test

1.17.4 Refactor the code

Replace values with values coming from the whole object

class Calculator
{
    public function calculateAverageTemperature(float $highestTemp, float $lowestTemp, September $september = null)
    {
        return ($september->getHighestTemp() + $september->getLowestTemp()) / 2;
    }
}

1.17.5 Pass a test

1.17.6 Refactor the code

Remove useless parameters and default null value of $september object

class Calculator
{
    public function calculateAverageTemperature(September $september)
    {
        return ($september->getHighestTemp() + $september->getLowestTemp()) / 2;
    }
}

1.17.7 Pass a test

1.18 Pull Up Method

Smell: Subclasses have the same method.

class Employee
{
    /**
     * @var string
     */
    protected $name;
    
    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

final class Salesman extends Employee
{
    public function getName()
    {
        return $this->name;
    }
}

final class Engineer extends Employee
{
    public function getName()
    {
        return $this->name;
    }
}

1.18.1 Write a test that pass

1.18.2 Refactor the code

Move the methods to the super class.

class Employee
{
    /**
     * @var string
     */
    protected $name;

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

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

final class Salesman extends Employee {}

final class Engineer extends Employee {}

1.18.3 Pass a test

1.19 Remove Assignments to Parameters

Smell: Reassign to a parameter

public function discount(int $priceTotal): int
{
    if ($priceTotal > 100) {
        $priceTotal = $priceTotal - 10;
    }
    
    return $priceTotal;
}  

1.19.1 Write a test that pass

1.19.2 Refactor the code

Use a temporary variable instead

public function discount(int $priceTotal): int
{
    $result = $priceTotal;

    if ($priceTotal > 100) {
        $result = $result - 10;
    }

    return $result;
}

1.19.3 Pass a test

1.19.4 Info

The best practice is that if you pass parameters into a method then they should always represent what were passed in and never be reassigned to mean something else. Btw. in Java you can prevent variable’s reassignment by keyword 'final' before a parameter https://stackoverflow.com/questions/500508/why-should-i-use-the-keyword-final-on-a-method-parameter-in-java

1.20 Remove PHPDoc

Smell: PHPDoc is duplicating type-hint information Damage: Adds information which not provides additional value

/**
 * @param int $number
 * @return void
 */
public function setNumber(int $number): void
{
    $this->number = $number;
}  

1.20.1 Write a test that pass

1.20.2 Refactor the code

Remove PHPDoc if it's not provides additional value


1.20.3 Pass a test

1.21 Rename Function

Alias: Rename Method, Change Function Declaration Smell: The name of a method does not reveal it's purpose1

public function getInvcdtlmt()

1.21.1 Write a test that pass

1.21.2 Refactor the code

Change the name of the method

public function getInvoiceableCreditLimit()

1.21.3 Pass a test

1.22 Replace Global with Dependency Injection

Smell: Variable with 'global' keyword

final class Item
{
    public function fetch()
    {
        global $db;
        return $db->query(...);
    }
}

1.22.1 Write a test that pass

1.22.2 Refactor the code

Move global variable in class to the constructor

final class Item
{
    /**
     * @var Database
     */
    private $db;

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

    public function fetch()
    {
        return $db->query(...);
    }
}

1.22.3 Pass a test

1.23 DONE Replace Magic Number With Symbolic Constant

Smell: Number with a particular meaning

final class Circle
{
    /**
     * @var float
     */
    private $radius;
    
    public function __construct(float $radius)
    {
        $this->radius = $radius;
    }
    
    public function getCircumference(): float
    {
        return $this->radius * 2 * 3.1416;
    }
}

1.23.1 Write a test that pass

use PHPUnit\Framework\TestCase;

class CircleTest extends TestCase
{   
    public function testGetCircumference()
    {
        $circle = new Circle(2);
        $this->assertEquals(12.5664, $circle->getCircumference());
    }
}
PHPUnit 7.5.2 by Sebastian Bergmann and contributors.

.                                                                   1 / 1 (100%)

Time: 151 ms, Memory: 10.00MB

OK (1 test, 1 assertion)

1.23.2 Refactor the code

Create a constant, name it after the meaning, and replace the number with it

final class Circle
{
    /**
     * @var float
     */
    private const PI = 3.1416;

    /**
     * @var float
     */
    private $radius;

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

    public function getCircumference(): float
    {
        return $this->radius * 2 * self::PI;
    }
}

1.23.3 Run a test

1.23.4 Helper:

PHP Magic Number Detector is a tool to detect magic numbers in your PHP code

1.24 Replace Parameter with Method

Smell: A method runs different code depending on the values of parameters

final class EmailNotification
{
    public function send(string $to, string $body, string $from = null)
    {
        if($from){
            $this->mailer->send($to, $body, $from);
        }else{
            $this->mailer->send($to, $body, $this->defaultSender);
        }
    }
}

1.24.1 Write a test that pass

1.24.2 Refactor the code

Create a separate method for each value of the parameter

final class EmailNotification
{
    public function send(string $to, string $body, string $from)
    {
        $this->mailer->send($to, $body, $from);
    }
    
    public function sendFromDefaultSender(string $to, string $body)
    {
        $this->mailer->send($to, $body, $this->defaultSender);
    }
}

1.24.3 Pass a test

1.25 Replace Temp with Query

Smell: Using a temporary variable to hold the result of an expression. Damage: Temporary variable increase the temptation to write longer methods. Temporaries aren’t necessarily bad, but sometimes they attract new code.

public function getTotalPrice(): int
{
    $basePrice = $this->quantity * $this->itemPrice;

    if ($basePrice > self::DISCOUNT_POINT) {
        return $basePrice * self::DISCOUNT;
    }
    return $basePrice;
}

1.25.1 Write a test that pass

1.25.2 Refactor the code

Extract the expression into a method. Replace all references to the temp with the expression. The new method can then be used in other methods.

public function getTotalPrice(): int
{
    if ($this->getBasePrice() > self::DISCOUNT_POINT) {
        return $this->getBasePrice() * self::DISCOUNT;
    }
    return $this->getBasePrice();
}

public function getBasePrice(): int
{
    $this->quantity * $this->itemPrice;
}

1.25.3 Pass a test

1.25.4 Info

Now, but wait, you might say. Isn't this more inefficient? Because if we created the temp the old way, we'd only have to execute the expression once, but if we turn it into a method, we might be calling it many different times. And yes, you're absolutely right, but remember, the pure efficiency of the code is not our first goal in refactoring. Clarity is. The likelihood is that a typical expression you would deal with in this sort of refactoring is going to be so undemanding, it wouldn't be noticeable at all, even having to call it several more times. But if it is an intensive operation, an intensive expression, well you should really be working on that later, after you've refactored using profilers. And other tools to make sure you're not doing pointless, premature optimization. And the real benefit is that by creating this as its own method, we will also have use of it anywhere else in the class, which wasn't the case before. As the original temp was scoped to the original method. So, we won't be tempted to add more code to the original method just to have access to that temp.

1.26 Replacing Type Code with Subclasses

Smell: Immutable type code affecting the class behavior.

final class Account
{
    /**
     * @var int
     */
    private $accountType;

    /**
     * @var float
     */
    private $balance;

    /**
     * @var int
     */
    public const CHECKING = 0;

    /**
     * @var int
     */
    public const SAVINGS = 1;

    /**
     * @var int
     */
    public const INVESTMENT = 2;

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

    public function getAccountType(): int
    {
        return $this->accountType;
    }

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

    public function withdraw(float $amount): void
    {
        switch ($this->accountType){
            case self::CHECKING:
                $this->balance -= $amount;
                break;
            case self::SAVINGS:
                $this->balance -= $amount + 100;
                break;
            case self::INVESTMENT:
                $this->balance -= $amount + 300;
                break;
            default:
                throw new RuntimeException('Unknown Account Type');
        }
    }
}

1.26.1 Write a test that pass

1.26.2 Refactor the code

Replace the type code with subclasses.

abstract class Account
{
    /**
     * @var float
     */
    private $balance;

    public function getBalance(): float
    {
        return $this->balance;
    }
    
    abstract public function withdraw(float $amount): void
}

final class AccountChecking extends Account
{
    public function withdraw(float $amount): void
    {
        $this->balance -= $amount;
    }
}

final class AccountSavings extends Account
{
    public function withdraw(float $amount): void
    {
        $this->balance -= $amount + 100;
    }
}

final class AccountInvestment extends Account
{
    public function withdraw(float $amount): void
    {
        $this->balance -= $amount + 300;
    }
}

1.26.3 Pass a test

1.27 Separate Query from Modifier

Smell: A method that returns a value but also changes the state of an object.

final class Account
{
    /**
     * @var float
     */
    private $balance;
    
    public function withdrawAndGetBalance(float $amount): float
    {
        $this->balance -= $amount;
        return $this->balance;
    }
}

1.27.1 Write a test that pass

1.27.2 Refactor the code

Create two methods, one for the query and one for the modification.

final class Account
{
    /**
     * @var float
     */
    private $balance;

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

    public function withdraw(float $amount): void
    {
        $this->balance -= $amount;
    }
}

1.27.3 Pass a test

1.28 Split Temporary Variable

Smell: Temporary variable is assigned to more than once (overwrite), but is not a loop variable nor a collecting temporary variable.

$temp = $item.getPrice() * item.getQuantity();
echo "Total: $temp";
$temp = order.getTotal() - order.getDiscount();
echo "Price after discount: $temp;"

1.28.1 Write a test that pass

1.28.2 Refactor the code

Make a separate temporary variable for each assignment.

$totalPrice = $item.getPrice() * item.getQuantity();
echo "Total: $totalPrice";
$totalDiscountPrice = order.getTotal() - order.getDiscount();
echo "Price after discount: $totalDiscountPrice";

1.28.3 Pass a test

2 Tutorials

Automated Tests with PHPUnit Anna Filina testing - PHPUnit
Catalog of Refactoring to Patterns Joshua Kerievsky patterns
Catalog of Refactorings Martin Fowler refactoring
Clean Code PHP Piotr Plenik clean code
Detecting Code Smells Patkós Csaba refactoring
Doctrine Best Practices Marco Pivetta (Ocramius) doctrine
Encapsulation and SOLID Mark Seemann SOLID
Extremely Defensive PHP Programming Marco Pivetta (Ocramius) defensive programming
How to Refactor Like a Boss 1 Michael Cheng refactoring
How to Refactor Like a Boss 2 Michael Cheng refactoring
HTTP Smoke Testing Peter Heinz testing - smoke test
Introduction to Testing with PHPUnit Trevor Sawler testing - PHPUnit
Legacy Coderetreat (Java) Adrian Bolboaca refactoring
List of Static Analysis Tools   analysis
PHP: Testing Legacy Applications Chris Hartjes testing - PHPUnit
Programming Foundations: Test-Driven Development Simon Allardice testing - TDD
Refactoring 101 Adam Culp refactoring
Refactoring Legacy Code Patkós Csaba refactoring
Refactoring Legacy Code Adam Culp refactoring
Refactoring to Collections Adam Wathan collections
Solving the N+1 Problem Paul M. Jones database
Steps Toward Modernizing a Legacy Codebase Paul M. Jones refactoring
Techniques for Refactoring Code Patkós Csaba refactoring

2.1 Programming Foundations: Refactoring Code Simon Allardice refactoring

Because using the formal refactoring technique, brings more conscious awareness to that decision.

No single technique is the master refactoring. It is the cumulative, additive impact of many small changes done consistently that is the real improvement here.

Just as important, as you start to get a sense of what refactoring is, is that you stay very aware of what refactoring is not. And it's easy to get the wrong ideas because we're saying things like we're improving code. And that could imply all sorts of possible meanings. So, three things that refactoring is not. One, refactoring is not debugging. Your code already needs to work. Either on the small scale, you have some classes and methods that are already functional, testable and working. Or on the large scale you have an entire shipped application that's already in use.

Now it's true, during the process of refactoring you may notice something that you realize is a problem. But the moment that you start to deal with that. You have stopped refactoring and you have started bug fixing which is a very different mindset.

Number two. Refactoring is not performance. This is another very common misconception. That we do this to improve our code. We're cleaning our code up and that's going to make it faster. No, code performance is not the goal of refactoring nor, let me be quite clear here, is it even an expectation. Refactoring might even make your code technically slower because we're making decisions about the construction of code, unlike building construction. The decisions you would make to build a house as well as possible are not the same as the decisions you would make to build a house as fast as possible. Improving pure performance is something we do in software development but that is a separate process with a different set of questions and a different set of tools. But there is one thing that gets faster with refactoring, us, we do, programmers.

We'll be able to read and understand it, not just individual pieces of code but the bigger application and that can lead to many benefits. Well re-factored code will make us faster. It is not intended to make the code faster. And finally, number three, refactoring is not adding features. If during this process you add a new feature, you support and you use case, you add a single tiny menu item to an application, you are no longer refactoring. Because in refactoring we do not change the behavior of the code.

Because well-structured code will make it so much easier to add new features and new capabilities.

It will make it easier to start analyzing performance. So we're refactoring to get the future roadblocks out of our way. Now, it can often be difficult for teams to get approval to spend time refactoring when it's not part of the culture, because the benefits aren't immediately apparent. Oh, you're not adding features, you're not working on performance, so why bother? We bother so we can do those things easier. We can add features quicker next week, or next month, or next year. So that we can bring new people onto the team and just point them at well-written code, rather than spend three months having to explain everything about what it does. And so that a year, or two, or three down the line, we don't have code that's such a mess.

if you don't have a reason to re-factor, then you don't re-factor.

unforeseen consequences

Bringing multiple sets of eyes to the same code.

Something about this code that just smells bad. And we use it as an indicator we have something to improve.

We're changing code and that brings risk. The single best way to prove that the changes you make when refactoring do what you intend. Only what you intend, and don't unwittingly mess everything else up is to have a set of automated unit tests you can run on your code before and after. So, using a test framework something like j unit for java, n unit for .Net, rspec for Ruby and so on.

2.2 PHPUnit: Testing with a Bite

2.2.1 3 Types of Tests

  1. Unit Test - Test one specific function on a class. Fake any needed database connecnions.
  2. Integration Test - Just like unit test, except it use the real database connection.
  3. Functional Test - Write a test to programmatically command a browser

2.2.2 How much should I be testing my code?

If the feature scares you then test it! Too many tests = wasted time, add little value, slow you down

2.2.3 TDD

  1. Create the test
  2. Write just enough code for the test to pass
  3. Refactor your code

2.3 Behavior-Driven Development

2.3.1 The 5 Ws

  1. As (who, what or where)
  2. I (want)
  3. because (why)

    Example: Who - as a user of Acme.com What - I want to access content on my mobile phone Why - because I might not always have a computer available

    https://cucumber.io/training -> 3 youtube

2.3.2 Throw Over the Wall

  1. Developers would coe and then throw it over the wall for someone else to test

2.3.3 After BDD Transformation

  1. testing cycle went down
  2. defect count went down
  3. time to market went down
  4. team's confidence in code increased and anxiety decreased
  5. manual testing cost went down

    "The hardest part of building a software system is deciding precisely what to build." - Fred Brooks

    If you're having trouble, example mapping helps us keep these conversations short and on track by creating a visual representation of a user story to guide and document the discussion. The process of example mapping is fairly straightforward. Using a four-color pack of index cards, we build a visual representation of our user's story with each color providing a specific piece of information. We build this as we have our conversation to document what is discovered through the discussion about the user's story.

    We begin with a yellow card, which contains the name of our story itself and place this at the very top. Our blue cards represent specific rules that constrain the scope of our story. This is our acceptance criteria. With green cards, we provide concrete examples of the user's story in the context of a specific rule. So we place these under the relevant blue card. And finally, we have our red cards. These cards contain questions that cannot be immediately answered during the discussion, but are captured so that we can move on with the conversation.

  6. Acceptance criteria
    1. addresses what defines a working system
    2. written as pass/fail
  7. Scenario
    1. defines the initial conditions for acceptance criteria
    2. states the trigger of scenario and expected outcome

2.3.4 Gherkin Syntax

  1. Feature: feature story - name of the feature and possibly brief description
    1. Scenario: a user story - single concrete example of how a system should behave - each feature generally have 5-20 scenarios - Scenarios describe the behaviour of the system under specific initial conditions.
    2. Given: some set of initial conditions - describe the context or precondition for the scenario
    3. When: an event occurs - identifies an event or actor enacted by some actor on the system
    4. Then: an outcome is expected - provides the expected outcome to the scenario

2.3.5 Example

  1. Feature : Customer pays with a credit card
    1. As a sales associate
    2. I should be able to process payments
    3. when given a credit card

      Now we're ready to translate our conversation into scenarios that describe the concrete behavior of the application. Ideally, this is done using declared dephrasing, meaning without referencing the particulars of a user interface or sequence of steps. Remember that Gherkin is a way to phrase our acceptance criteria as an executable scenario that describes the behavior of a system. So, our conversations would describe the behavior of the system under specific initial conditions.

    4. Scenario 1
      1. Total charge is over the $2 credit card minimum.
      2. Given: Maria orders $3 of coffee from Li.
      3. When: Maria pays with a credit card.
      4. Then: Lit should process the payment.
    5. Scenario 2
      1. Total charge is under the $2 credit card minimum.
      2. Given: Maria orders $1 of coffee from Li.
      3. When: Maria pays with a credit card.
      4. Then: Li should not process the payment.

2.3.6 Test Automation

Allows for the execution of software tests that compare the expectations for software with the actual outcomes. Testing can be repetitive process and automation can be a boon to productivity.

2.3.7 Living Documentation

Is a system for dynamically generating documentation that contains information which is up to date and accurate. Cucumber provides a system that dynamically generates documentation because of the manner that tests are written. Because Cucumber tests are written in a way that can be easily interpreted by non-technical stakeholders and, at the same time, be executed by a computer, your tests themselves become a living source of documentation that reflect the accuracy of your application. Cucumber was designed to enhance the practice of test-driven development. It provides a single source of truth for an application's lifecycle by merging test automation and test documentation.

2.3.8 Further

  1. https://cucumber.io/school
  2. https://cucumber.io/training
  3. BDD in Action by John Ferguson Smart
  4. The Cucomber Book by Aslak Hellesoy…

2.3.9 Notes

  1. BDD is a process, not a tool
  2. Specialized frameworks can assist in this process by providing a common language for acceptance criteria.

    To types of BDD:

    1. Story - is done with Behat & functional tests
    2. Spec - is done with PHPSpec & Unit tests

    4 Steps to BDD 1.Define Business Value of all big features 2.Prioritize you features 3.Break feature down into user stories 4.Write the code for the feature

    https://github.com/Behatch/contexts

2.4 Test Automation Foundations

Automated testing follows the same steps as manual testing, but it's much quicker. While there is an initial time investment to write the scripts, once the scripts are complete they can be run repeatedly without much additional cost.

  1. There will be maintenance that is required, but it saves time in the long run. This makes automation have a great return on investment. In addition, the exact same steps are executed every time, which reduces any possibility of human error.
  2. Agile Testing Quadrants
  3. Test Pyramid

2.5 Clean Application Development Adam Culp

bad code:

  • is easy
  • fast

    result:

  • wasted time
  • bugs
  • excessive debugging
  • procrastination
  • missed deadlines
  • technical debt
  • financial losses
  • company killer
  • I didn't write it!

Names should be clear - functions and variables should tell a story $elapsed -> $elapsedTimeInDays

Class - nouns - Describe ex - Customer, Account, Product, Company Method - verbs - getCustomer, closeAccount, updateProduct, addCompany Function - 20 lines - 10 lines

Recognizing bad dosen't mean we know how to make good - we know a good/bad, but are not song writers

smells - are indications of problems in your code

CodeSniffer

2.9 Symfony

2.9.2 Upgrade to Symfony4 and Flex!

  1. Upgrade to Symfony 3.4
  2. See depractions in toolbar
    1. remove $kernel->loadClassCache(); from app.php & appdev.php
    2. "Symfony\Component\Security\Guard\AuthenticatorInterface::supports()"
    3. logoutonuserchange

2.11 Behat

2.11.1 BDD, Behat, Mink and other Wonderful Things

  • Write the behavior for a feature first
  • Code until the behavioral tests pass
  1. Two types of BDD
    1. Story: is done with Behat & functional tests
    2. Spec: is done with PHPSpec & Unit tests
  2. 4 Steps to BDD
    1. Define Business Value of all big features
    2. Prioritize your features
    3. Break feature down into user stories
    4. Write the code for the feature

3 Books

3.1 Others

author title topic info year  
Andrew Hunt, David Thomas The Pragmatic Programmer: From Journeyman to Master overall skip 1999 a lot of tips & tricks but too old
William J. Brown, Raphael C. Malveau, Hays W. "Skip" McCormick, Thomas J. Mowbray Antipatterns. Refactoring Software, Archtectures and Projects in Crisis     1998  
  Refactoring in Large Software Projects     2006  
Phillip A. Laplante, Colin J. Neill Antipatterns: Identification, Refactoring, and Management     2005  
  Fifty Quick Ideas To Improve Your Tests tests      
  Growing Object-Oriented Software, Guided by Tests        
  Working Effectively with Unit Tests        
  Scalable Internet Architectures        
  SQL for Smarties: Advanced SQL Programming database   2005  
  Mastering the SPL Library        
  Anthology The Thoughtworks Anthology - Essays on Software Technology and Innovation     2008  
  Anthology The ThoughtWorks Anthology 2 - More Essays on Software Technology and Innovation     2012  
Beck, Kent Extreme Programming Explained: Embrace Change overall can be read 1999  
Bernstein, David Scott Beyond Legacy Code     2015 Want to read
Bhargava, Aditya Grokking Algorithms: An illustrated guide for programmers and other curious people   must read 2017 Fully illustrated, friendly, easy to read guide, worth to read. When you will stumble upon a problem, you will know how to recognize it and chose the right Algorithm
Bloch, Joshua Effective Java     2018  
Buenosvinos, Carlos Domain-Driven Design in PHP     2016  
Bugayenko, Yegor Elegant Objects     2016 Want to read
Fowler, Martin Refactoring: Improving the Design of Existing Code refactoring must have 1999  
Fowler, Martin Patterns of Enterprise Application Architecture     2012  
Francesco Trucchia, Jacopo Romei Pro PHP Refactoring     2010 Old, without PHP 7 but still the best PHP book about refactoring. Great examples. Worth to have.
Ganesh, Samarthyam; Tushar, Sharma Refactoring for Software Design Smells: Managing Technical Debt     2015 Catalog of smells. Focus on smell point of view. Also they introduce smell classification scheme, naming scheme for design smells which helps to increase awerness of smells. So definetly must read. (Java)
Halladay, Steve Principle-Based Refactoring: Learning Software Design Principles by Applying Refactoring Rules     2012 Want to read
Jones, Paul M. Modernizing Legacy Apps In PHP     2014  
Junade, Ali Mastering PHP Design Patterns     2016 Little bit inmature, poorly written. Having a lot of tips and information about broad variaty of things which is a good place on website but not in the book. Code examples could be much better.
Karwin, Bill SQL Antipatterns - Avoiding The Pitfalls of Database Programming database   2010  
Kerievsky, Joshua Refactoring to Patterns     2004  
Rahman, Mizanur PHP 7 Data Structures and Algorithms refactoring must have 2017  
Scott J Ambler and Pramod J. Sadalage Refactoring Databases - Evolutionary Database Design refactoring must read 2006 It's catalog of refactorings for database
Stephane Faroult, Pascal L'Hermite Refactoring SQL Application database   2008  
Tornhill, Adam Your Code as a Crime Scene - Use Forensic Techniques to Arrest Defects, Bottlenecks, and Bad Design in Your Programs     2015  
West, David Object Thinking     2004  
Zandstra, Matt PHP Objects, Patterns, and Practice   can be read 2008 Pretty old. But written with precise language and with great examples

3.2 xUnit Test Patterns - Refactoring Test Code LOOKING FOR

3.3 Object Design Style Guide Matthias Noback

3.4 Build APIs You Won't Hate

3.5 Joe Celko’s Trees and Hierarchies in SQL for Smarties

3.6 Wake, William C.; Refactoring Workbook; 2003

3.7 Feathers, Michael; Working Effectively with Legacy Code; 2005

  • To me, legacy code is simply code without tests. I’ve gotten some grief for this definition. What do tests have to do with whether code is bad? To me, the answer is straightforward, and it is a point that I elaborate throughout the book: 'Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.'
  • being able to confidently make changes in any code base.
  • Preserving existing behavior
  • Changes in a system can be made in two primary ways. I like to call them Edit and Pray and Cover and Modify. Unfortunately, Edit and Pray is pretty much the industry standard. When you use Edit and Pray, you carefully plan the changes you are going to make, you make sure that you understand the code you are going to modify, and then you start to make the changes. When you’re done, you run the system to see if the change was enabled, and then you poke around further to make sure that you didn’t break anything. The poking around is essential. When you make your changes, you are hoping and praying that you’ll get them right, and you take extra time when you are done to make sure that you did.
  • …testing is a tough problem, and people are often seduced by the idea that they can test through a GUI or web interface without having to do anything special to their application. It can be done, but it is usually more work than anyone on a team is prepared to admit. In addition, a user interface often isn’t the best place to write tests. UIs are often volatile and too far from the functionality being tested. When UI-based tests fail, it can be hard to figure out why. Regardless, people often spend considerable money trying to do all of their testing with those sorts of tools.
  • Pay now or pay more later

3.8 Fowler, Martin; 2018

  • With any introductory example, however, I run into a problem. If I pick a large program, describing it and how it is refactored is too complicated for a mortal reader to work through.(…) However, if I pick a program that is small enough to be comprehensible, refactoring does not look like it is worthwhile.
  • Thus, if I’m faced with modifying a program with hundreds of lines of code, I’d rather it be structured into a set of functions and other program elements that allow me to understand more easily what the program is doing. If the program lacks structure, it’s usually easier for me to add structure to the program first, and then make the change I need.
  • If the code works and doesn’t ever need to change, it’s perfectly fine to leave it alone. It would be nice to improve it, but unless someone needs to understand it, it isn’t causing any real harm. Yet as soon as someone does need to understand how that code works, and struggles to follow it, then you have to do something about it.
  • Refactoring changes the programs in small steps, so if you make a mistake, it is easy to find where the bug is.
  • Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
  • The true test of good code is how easy it is to change it.
  • Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior
  • Refactoring (verb): to restructure software by applying a series of refactorings without changing its observable behavior.
  • Refactoring is very similar to performance optimization, as both involve carrying out code manipulations that don’t change the overall functionality of the program. The difference is the purpose: Refactoring is always done to make the code “easier to understand and cheaper to modify.” This might speed things up or slow things down. With performance optimization, I only care about speeding up the program, and am prepared to end up with code that is harder to work with if I really need that improved performance.
  • Without refactoring, the internal design—the architecture—of software tends to decay.As people change code to achieve short­term goals, often without a full comprehensionof the architecture, the code loses its structure. It becomes harder for me to see thedesign by reading the code. Loss of the structure of code has a cumulative effect. Theharder it is to see the design in the code, the harder it is for me to preserve it, and themore rapidly it decays. Regular refactoring helps keep the code in shape.
  • It reminds me of a statement Kent Beck often makes about himself: “I’m not a great programmer; I’m just a good programmer with great habits.” Refactoring helps me be much more effective at writing robust code.
  • Branches. As I write this, a common approach in teams is for each team member to work on abranch of the code base using a version control system, and do considerable work onthat branch before integrating with a mainline (often called master or trunk) sharedacross the team. Often, this involves building a whole feature on a branch, notintegrating into the mainline until the feature is ready to be released into production.Fans of this approach claim that it keeps the mainline clear of any in­process code,provides a clear version history of feature additions, and allows features to be revertedeasily should they cause problems.There are downsides to feature branches like this. The longer I work on an isolatedbranch, the harder the job of integrating my work with mainline is going to be when I’mdone. Most people reduce this pain by frequently merging or re­basing from mainlineto my branch. But this doesn’t really solve the problem when several people areworking on individual feature branches. I distinguish between merging and integration.If I merge mainline into my code, this is a oneway movement—my branch changes butthe mainline doesn’t. I use “integrate” to mean a two­way process that pulls changesfrom mainline into my branch and then pushes the result back into mainline, changingboth. If Rachel is working on her branch I don’t see her changes until she integrateswith mainline; at that point, I have to merge her changes into my feature branch, whichmay mean considerable work. The hard part of this work is dealing with semanticchanges. Modern version control systems can do wonders with merging complexchanges to the program text, but they are blind to the semantics of the code. If I’vechanged the name of a function, my version control tool may easily integrate mychanges with Rachel’s. But if, in her branch, she added a call to a function that I’verenamed in mine, the code will fail.The problem of complicated merges gets exponentially worse as the length of featurebranches increases. Integrating branches that are four weeks old is more than twice ashard as those that are a couple of weeks old. Many people, therefore, argue for keepingfeature branches short—perhaps just a couple of days. Others, such as me, want themeven shorter than that. This is an approach called Continuous Integration (CI), alsoknown as Trunk­Based Development. With CI, each team member integrates withmainline at least once per day. This prevents any branches diverting too far from eachother and thus greatly reduces the complexity of merges. CI doesn’t come for free: Itmeans you use practices to ensure the mainline is healthy, learn to break large featuresinto smaller chunks, and use feature toggles (aka feature flags) to switch off any in­process features that can’t be broken down.Fans of CI like it partly because it reduces the complexity of merges, but the dominantreason to favor CI is that it’s far more compatible with refactoring. Refactorings ofteninvolve making lots of little changes all over the code base—which are particularlyprone to semantic merge conflicts (such as renaming a widely used function). Many ofus have seen feature­branching teams that find refactorings so exacerbate mergeproblems that they stop refactoring. CI and re­factoring work well together, which iswhy Kent Beck combined them in Extreme Programming.I’m not saying that you should never use feature branches. If they are sufficiently short,their problems are much reduced. (Indeed, users of CI usually also use branches, butintegrate them with mainline each day.) Feature branches may be the right techniquefor open source projects where you have infrequent commits from programmers whoyou don’t know well (and thus don’t trust). But in a full­time development team, thecost that feature branches impose on refactoring is excessive. Even if you don’t go to fullCI, I certainly urge you to integrate as frequently as possible. You should also considerthe objective evidence [Forsgren et al.] that teams that use CI are more effective insoftware delivery.

131

3.9 Martin, Robert C.; Clean Code: A Handbook of Agile Software Craftsmanship; 2009

  • There are two parts to learning craftsmanship: knowledge and work. You must gain the knowledge of principles, patterns, practices, and heuristics that a craftsman knows, and you must also grind that knowledge into your fingers, eyes, and gut by working hard and practicing.
  • Do not refer to a grouping of accounts as an accountList unless it’s actually a List. The word list means something specific to programmers. If the container holding the accounts is not actually a List, it may lead to false conclusions (even if the container is a List, it’s probably better not to encode the container type into the name). So accountGroup or bunchOfAccounts or just plain accounts would be better.
  • A truly awful example of disinformative names would be the use of lower-case L or uppercase O as variable names, especially in combination. The problem, of course, is that they look almost entirely like the constants one and zero, respectively.
  • Certainly a loop counter may be named i or j or k (though never l!) if its scope is very small and no other names can conflict with it. This is because those single-letter names for loop counters are traditional.
int a = 1;
if ( O == 1 )
a = O1;
else
l = 01;

3.9.1 Naming

  1. Use Intention-Revealing Names
    $d; //elapsed time in days
    

    The name d reveals nothing. It does not evoke a sense of elapsed time, nor of days. We should choose a name that specifies what is being measured and the unit of that measure- ment:

    $elapsedTimeInDays;
    $daysSinceCreation;
    $daysSinceModification;
    $fileAgeInDays;
    
  2. Make Meaningful Distinctions
    public function copyChars(array $a1, array $a2): void {
        for ($i = 0; i < count($a1); i++) {
            a2[i] = a1[i];
        }
    }
    

    This function reads much better when source and destination are used for the argument names.

  3. Use Pronounceable Names

    If you can’t pronounce it, you can’t discuss it without sounding like an idiot.

    class DtaRcrd102 {
        private Date genymdhms;
        private Date modymdhms;
        private final String pszqint = "102";
        /* ... */
    };
    
    class Customer {
        private Date generationTimestamp;
        private Date modificationTimestamp;;
        private final String recordId = "102";
        /* ... */
    };
    
  4. Use Searchable Names
    • Single-letter names and numeric constants have a particular problem in that they are not easy to locate across a body of text.
    • My personal preference is that single-letter names can ONLY be used as local variables inside short methods.
    • The length of a name should correspond to the size of its scope.
  5. Interfaces and Implementations
    • These are sometimes a special case for encodings. For example, say you are building an ABSTRACT FACTORY for the creation of shapes. This factory will be an interface and will be implemented by a concrete class. What should you name them? IShapeFactory and ShapeFactory? I prefer to leave interfaces unadorned. The preceding I, so common in today’s legacy wads, is a distraction at best and too much information at worst. I don’t want my users knowing that I’m handing them an interface. I just want them to know that it’s a ShapeFactory. So if I must encode either the interface or the implementation, I choose the implementation. Calling it ShapeFactoryImp , or even the hideous CShapeFactory, is pref- erable to encoding the interface.
    • PSR Naming Conventions - Interfaces MUST be suffixed by Interface: e.g. Psr\Foo\BarInterface.
  6. Avoid Mental Mapping

    Readers shouldn’t have to mentally translate your names into other names they already know. This problem generally arises from a choice to use neither problem domain terms nor solution domain terms.

  7. Class Names

    Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb.

3.10 Martin, Robert C.; Agile Software Development: Principles, Patterns, and Practices; 2002

4 Database

4.1 Refactorings

4.1.1 Add CRUD Methods

Introduce stored procedures (methods) to implement the creation, retrieval, update, and deletion (CRUD) of the data representing a business entity.

4.1.2 Add Foreign Key Constraint

Add a foreign key constraint to an existing table to enforce a relationship to another table.

4.1.3 Add Lookup Table

Create a lookup table for an existing column.

4.1.4 Add Mirror Table

Create a mirror table, an exact duplicate of an existing table in one database, in another database.

4.1.5 Add Parameter

Existing method needs information that was not passed in before.

4.1.6 Add Read Method

Introduce a methodin this case, a stored procedureto implement the retrieval of the data representing zero or more business entities from the database.

4.1.7 Add Trigger For Calculated Column

Introduce a new trigger to update the value contained in a calculated column.

4.1.8 Apply Standard Codes

Apply a standard set of code values to a single column to ensure that it conforms to the values of similar columns stored elsewhere in the database.

4.1.9 Apply Standard Type

Ensure that the data type of a column is consistent with the data type of other similar columns within the database.

4.1.10 Consolidate Conditional Expression

Combine sequence of conditional tests into a single conditional expression and extract it.

4.1.11 Consolidate Key Strategy

Choose a single key strategy for an entity and apply it consistently throughout your database.

4.1.12 Decompose Conditional

Extract methods from the condition.

4.1.13 Drop Column

Remove a column from an existing table.

4.1.14 Drop Column Constraint

Remove a column constraint from an existing table.

4.1.15 Drop Default Value

Remove the default value that is provided by a database from an existing table column.

4.1.16 Drop Foreign Key Constraint

Remove a foreign key constraint from an existing table so that a relationship to another table is no longer enforced by the database.

4.1.17 Drop Non-Nullable

Change an existing non-nullable column so that it accepts null values.

4.1.18 Drop Table

Remove an existing table from the database.

4.1.19 Drop View

Remove an existing view.

4.1.20 Encapsulate Table With View

Wrap access to an existing table with a view.

4.1.21 Extract Method

Turn the code fragment into a method whose name explains the purpose of the method.Insert Data (page 296): Insert data into an existing table.

4.1.22 Introduce Calculated Column

Introduce a new column based on calculations involving data in one or more tables.

4.1.23 Introduce Calculation Method

Introduce a new method, typically a stored function, which implements a calculation that uses data stored within the database.

4.1.24 Introduce Cascading Delete

Ensure that the database automatically deletes the appropriate "child records" when a "parent record" is deleted.

4.1.25 Introduce Column Constraint

Introduce a column constraint in an existing table.

4.1.26 Introduce Common Format

Apply a consistent format to all the data values in an existing table column.

4.1.27 Introduce Default Value

Let the database provide a default value for an existing table column.

4.1.28 Introduce Hard Delete

Remove an existing column which indicates that a row has been deleted and instead actually delete the row.

4.1.29 Introduce Index

Introduce a new index of either unique or non-unique type.

4.1.30 Introduce New Column

Introduce a new column in an existing table.

4.1.31 Introduce New Table

Introduce a new table in an existing database.

4.1.32 Introduce Read-Only Table

Create a read-only data store based on existing tables in the database.

4.1.33 Introduce Soft Delete

Introduce a flag to an existing table which indicates that a row has been deleted instead of actually deleting the row.

  1. Motivation

    preserve all application data, typically for historical means.

  2. Tradeoffs
    1. more data - table have to store valid rows and those mark as deleted
    2. performace - more data means reduction in query performace - the additional work of distinguishing between deleted and nondeleted rows
    3. code complexity
      1. mostly in every query we would have to remember add 'where isDeleted = false'
      2. You may have associations going from Table A to Table B and when you soft delete something from Table A, you need to ensure that independent queries on Table B take care of that fact.
  3. How
    1. Introduce New Column
      1. isDeleted
        1. bool not null false
        2. date/timestamp null
    2. Update
      1. application
      2. database trigger - it is simple and it avoids the risk that the applications will not update the column properly
  4. Opposite

    Introduce Hard Delete

4.1.34 Introduce Surrogate Key

Replace an existing natural key with a surrogate key.

4.1.35 Introduce Trigger For History

Introduce a new trigger to capture data changes for historical purposes.

  1. Motivation

    delegate the tracking of data changes to the database itself

4.1.36 Introduce Variable

Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

4.1.37 Introduce View

Create a view based on existing tables in the database.

  1. Motivation
    1. Summarize data for reporting. Many reports require summary data, which can be generated via the view definition.
    2. Replace redundant reads. Several external programs, or stored procedures for that matter, often implement the same retrieval query. These queries can be replaced by a common readonly table or view.
    3. Data security. A view can be used to provide end users with read access to data but not update privileges.
    4. Encapsulate access to a table. Some organizations choose to encapsulate access to tables by defining updateable views that external programs access instead of the source tables. This enables the organization to easily perform database refactorings such as Rename Column (page 109) or Rename Table (page 113) without impacting the external applications, because views add an encapsulation layer between your tables and your application.
    5. Reduce SQL duplication. When you have complex SQL queries in an application, it is common to discover that parts of the SQL are duplicated in many places. When this is the case, you should introduce views to extract out the duplicate SQL, as shown in the example section.
  2. Tradeoffs
    1. poor performance of the joins
    2. increases the coupling with database schemas

4.1.38 Make Column Non-Nullable

Change an existing column so that it does not accept any null values.

4.1.39 Merge Columns

Merge two or more columns within a single table.

4.1.40 Merge Tables

Merge two or more tables into a single table.

4.1.41 Migrate Method From Database

Rehost an existing database method (a stored procedure, stored function, or trigger) in the application(s) which currently invoke it.

4.1.42 Migrate Method To Database

Rehost existing application logic in the database.

4.1.43 Move Column

Migrate a table column, with all of its data, to another existing table.

4.1.44 Move Data

Move the data contained within a table, either all or a subset of its columns, to another existing table.

4.1.45 Parameterize Method

Create one method that uses a parameter for the different values.

4.1.46 Remove Control Flag

Use return or break instead of a variable acting as a control flag.

4.1.47 Remove Middle Man

Get the caller to call the method directly.

4.1.48 Remove Parameter

Remove a parameter no longer used by the method body.

4.1.49 Rename Column

Rename an existing table column with a name that explains its purpose.

4.1.50 Rename Method

Rename an existing method with a name that explains its purpose.

4.1.51 Rename Table

Rename an existing table with a name that explains its purpose.

4.1.52 Rename View

Rename an existing view with a name that explains its purpose.

4.1.53 Reorder Parameters

Change the order of the parameters of a method.

4.1.54 Replace Column

Replace an existing non-key column with a new one.

4.1.55 Replace LOB With Table

Replace a large object (LOB) column that contains structured data with a new table or in the same table.

4.1.56 Replace Literal With Table Lookup

Replace code constants with values from database tables.

4.1.57 Replace Method(s) With View

Create a view based on one or more existing database methods (stored procedures, stored functions, or triggers) within the database.

4.1.58 Replace Nested Conditional With Guard Clauses

Remove nested if conditions with a series of separate IF statements.

4.1.59 Replace One-To-Many With Associative Table

Replace a one-to-many association between two tables with an associative table.

4.1.60 Replace Parameter With Explicit Methods

Create a separate method for each value of the parameter.

4.1.61 Replace Surrogate Key With Natural Key

Replace a surrogate key with an existing natural key.

4.1.62 Replace Type Code With Property Flags

Replace a code column with individual property flags, usually implemented as Boolean columns, within the same table column.

4.1.63 Replace View With Method(s)

Replace an existing view with one or more existing methods (stored procedures, stored functions, or triggers) within the database.

4.1.64 Split Column

Split a column into one or more columns within a single table.

4.1.65 Split Table

Vertically split (e.g., by columns) an existing table into one or more tables.

4.1.66 Split Temporary Variable

Make a separate temporary variable for each assignment.

4.1.67 Substitute Algorithm

Replace the body of the method with the new algorithm.

4.1.68 Update Data

Update data within an existing table.

4.1.69 Use Official Data Source

Use the official data source for a given entity, instead of the current one you are using.

4.2 MySQL vs. PostgreSQL

MySQL PostgreSQL
relational database management system object-relational database management system
   

similar to a relational database, but with an object-oriented database model: objects, classes and inheritance are directly supported in database schemas and in the query language. In addition, just as with pure relational systems, it supports extension of the data model with custom data types and methods.

The characteristic properties of ORDBMS are 1) complex data, 2) type inheritance, and 3) object behavior. Complex data creation in most SQL ORDBMSs is based on preliminary schema definition via the user-defined type (UDT). Hierarchy within structured complex data offers an additional property, type inheritance. That is, a structured type can have subtypes that reuse all of its attributes and contain additional attributes specific to the subtype. Another advantage, the object behavior, is related with access to the program objects

4.3 ACID

4.3.1 Atomicity

Transactions are often composed of multiple statements. Atomicity guarantees that each transaction is treated as a single "unit", which either succeeds completely, or fails completely: if any of the statements constituting a transaction fails to complete, the entire transaction fails and the database is left unchanged. An atomic system must guarantee atomicity in each and every situation, including power failures, errors and crashes.

4.3.2 Consistency

Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key - foreign key relationship.

4.3.3 Isolation

Transactions are often executed concurrently (e.g., multiple transactions reading and writing to a table at the same time). Isolation ensures that concurrent execution of transactions leaves the database in the same state that would have been obtained if the transactions were executed sequentially. Isolation is the main goal of concurrency control; depending on the method used, the effects of an incomplete transaction might not even be visible to other transactions. Durability

4.3.4 Durability

guarantees that once a transaction has been committed, it will remain committed even in the case of a system failure (e.g., power outage or crash). This usually means that completed transactions (or their effects) are recorded in non-volatile memory.

4.5 views

select y.tableschema, y.tablename, y.viewlen, y.referencedviews views, cast((y.viewlen - y.wofrom) / 4 - 1 as unsigned) subqueries, cast((y.viewlen - y.wounion) / 5 as unsigned) unions, cast((y.viewlen - y.wodistinct) / 8 as unsigned) distincts, cast((y.viewlen - y.wogroup) / 5 as unsigned) groups from (select x.tableschema, x.tablename, x.viewlen, cast(x.referencedviews as unsigned) referencedviews, length(replace(upper(x.viewdefinition), 'FROM', '')) wofrom, length(replace(upper(x.viewdefinition), 'UNION', '')) wounion, length(replace(upper(x.viewdefinition), 'DISTINCT', '')) wodistinct, length(replace(upper(x.viewdefinition), 'GROUP', '')) wogroup from (select v1.tableschema, v1.tablename, v1.viewdefinition, length(v1.viewdefinition) viewlen, sum(case when v2.tablename is not null then (length(v1.viewdefinition)

  • length(replace(v1.viewdefinition, v2.tablename, '')))

/length(v2.tablename) else 0 end) referencedviews from informationschema.views v1 left outer join informationschema.views v2 on v1.tableschema = v2.tableschema where v1.tablename <> v2.tablename group by v1.tableschema, v1.tablename, v1.viewdefinition) x group by x.tableschema, x.tablename) y order by 1, 2;

4.6 indexes

select t.tablename, t.tablerows, count(distinct s.indexname) indexes, case when min(s.unicity) is null then 'N' when min(s.unicity) = 0 then 'Y' else 'N' end uniqueindex, sum(case s.columns when 1 then 1 else 0 end) singlecolumn, sum(case when s.columns is null then 0 when s.columns = 1 then 0 else 1 end) multicolumn from informationschema.tables t left outer join (select tableschema, tablename, indexname, max(seqinindex) columns, min(nonunique) unicity from informationschema.statistics where tableschema = schema( ) group by tableschema, tablename, indexname) s on s.tableschema = t.tableschema and s.tablename = t.tablename where t.tableschema = schema( ) group by t.tablename, t.tablerows order by 3, 1;

4.7 copy

If you want to copy the table structure including its keys, then you should use:

 CREATE TABLE `new_table_name` LIKE `old_table_name`;
 CREATE TABLE `new_table_name` SELECT * FROM `old_table_name`;
 (select * from A) except (select * from B) union (select * from B) except select * from A)

4.8 dump

mysqldump -R –triggers -u root -p culture > file.sql

4.9 actions

4.9.1 Add Auto-Increment ID to existing table?

ALTER TABLE `table` ADD COLUMN `id` INT AUTOINCREMENT UNIQUE FIRST;

4.9.2 How to change collation of a column?

ALTER TABLE <tablename> MODIFY <columnname> VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4unicodeci;

5 Smells

5.1 Booch’s fundamental design principles

5.1.1 abstraction

  1. Missing Abstraction

    Clumps of data or encoded strings are used instead of creating a class or an interface

    PROBLEMS:

    • it can expose implementation details to different abstractions, violating the principle of encapsulation.
    • When data and associated behavior are spread across abstractions, it can

    lead to tight coupling between entities, resulting in brittle and non-reusable code. Hence, not creating necessary abstractions also violates the principle of modularization.

    POTENTIAL CAUSES:

    Inadequate design analysis When careful thought is not applied during design, it is easy to overlook creating abstractions and use primitive type values or strings to “get the work done.” In our experience, this often occurs when software is developed under tight deadlines or resource constraints.

    Lack of refactoring As requirements change, software evolves and entities that were earlier represented using strings or primitive types may need to be refactored into classes or interfaces. When the existing clumps of data or encoded strings are retained as they are without refactoring them, it can lead to a Missing Abstraction smell.

    Misguided focus on minor performance gains This smell often results when designers compromise design quality for minor performance gains. For instance, we have observed developers using arrays directly in the code instead of creating appropriate abstractions since they feel that indexing arrays is faster than accessing members in objects. In most contexts, the performance gains due to such “optimizations” are minimal, and do not justify the resultant trade-off in design quality.

    https://steemit.com/php/@crell/php-use-associative-arrays-basically-never

    REFACTOR - The refactoring for this smell is to create abstraction(s) that can internally make use of primitive type values or strings. For example, if a primitive type value is used as a “type-code,” then apply “replace type-code with class”.

    ALIAS: Primitive Obsession — This smell occurs when primitive types are used for encoding dates, currency, etc. instead of creating classes.

    Data clumps — This smell occurs when there are clumps of data items that occur together in lots of places instead of creating a class.

  2. Imperative Abstraction

    Consider the case of a large-sized financial application. This application employs classes named CreateReport, CopyReport, DisplayReport, etc. to deal with its report generation functionality. Each class has exactly one method definition named create, copy, display, etc., respectively, and suffers from Imperative Abstraction smell. The data items relating to a report such as name of the report, data elements that need to be displayed in the report, kind of report, etc. are housed in a “data class” named Report. The smell not only increases the number of classes (in this case there are at least four classes when ideally one could have been used), but also increases the complex- ity involved in development and maintenance because of the unnecessary separation of cohesive methods

    Reification “Reification” is the promotion or elevation of something that is not an object into an object. When we reify behavior, it is possible to store it, pass it, or transform it. Reifica- tion improves flexibility of the system at the cost of introducing some complexity [52]. Many design patterns [54] employ reification. Examples:

    • State pattern: Encoding a state-machine.

    • Command pattern: Encoding requests as command objects. A permitted excep- tion for this smell is when a Command pattern has been used to objectify method requests.

    • Strategy pattern: Parameterizing a procedure in terms of an operation it uses.

    In other words, when we consciously design in such a way to elevate non-objects to objects for better reusability, flexibility, and extensibility (i.e., for improving design quality), it is not a smell.

    ALIASES “Operation class” [51,52] — This smell occurs when an operation that should have been a method within a class has been turned into a class itself.

  3. Incomplete Abstraction

    An abstraction (entity or interface) does not support complementary or interrelated methods completely For example, if we need to be able to add or remove elements in a data structure, the type abstracting that data structure should support both add() and remove() methods. Supporting only one of them makes the abstraction incomplete and incoherent in the context of those interrelated methods.

    missing “complementary and symmetric” methods,

    Min/max Open/close Create/destroy Get/set Read/write Print/scan First/last Begin/end Start/stop Lock/unlock Show/hide Up/down First/last Push/pull Enable/disable Left/right On/off

    Sometimes, a designer may make a conscious design decision to not provide sym- metric or matching methods. For example, in a read-only collection, only add() method may be provided without the corresponding remove() method. In such a case, the abstraction may appear incomplete, but is not a smell.

    Sometimes, APIs choose to replace symmetrical methods with a method that takes a boolean argument (for instance, to enforce a particular naming convention such as naming convention that requires accessors to have prefixes “get,” “is,” or “set”). For example, classes such as java.awt.MenuItem and java.awt.Component originally supported disable() and enable() methods. These methods were dep- recated and are now replaced with setEnabled(boolean) method. Similarly, java. awt.Component has the method setVisible(boolean) that deprecates the methods show() and hide(). One would be tempted to mark these classes as Incomplete Abstractions since they lack symmetric methods, i.e., getEnabled() and getVisi- ble() respectively. However, since there is no need for corresponding getter methods (as these methods take a boolean argument), these classes do not have Incomplete Abstraction smell.

    ALIASES This smell is also known in literature as:

    • “Class supports incomplete behavior” [18]—This smell occurs when the public interface of a class is incomplete and does not support all the behavior needed by objects of that class.

    • “Half-hearted operations” [63]—This smell occurs when interrelated methods provided in an incomplete or in an inconsistent way; this smell could lead to runtime problems.

  4. Multifaceted Abstraction

    This smell arises when an abstraction has more than one responsibility assigned to it.

    In particular, the Single Responsibility Principle says that an abstraction should have a single well-defined responsibility and that responsibility should be entirely encapsulated within that abstraction.

    ALIASES This smell is also known in literature as:

    • “Divergent change” [7]—This smell occurs when a class is changed for differ- ent reasons.

    • “Conceptualization abuse” [30]—This smell occurs when two or more non- cohesive concepts have been packed into a single class of the system.

    • “Large class” [7,24,57,58]—This smell occurs when a class has “too many” responsibilities.

    • “Lack of cohesion” [59]—This smell occurs when there is a large type in a design with low cohesion, i.e., a “kitchen sink” type that represents many abstractions.

  5. Unnecessary Abstraction

    An abstraction that is not needed ALIASES: Irrelevant class - class does not have any meaningful behavior in the design Lazy class / Freeloader — class does “too little” Small class - class has no (or too few) variables or no (or too few) methods in it Mini-class - a public, non-nested class defines less than three methods and less than three attributes (including constants) in it No responsibility - class has no responsibility associated with it Agent classes - class serve as an “agent” (i.e., they only pass messages from one class to another), indicating that the class may be unnecessary

  6. Unutilized Abstraction

    UNUTILIZED ABSTRACTION An abstraction is left unused (either not directly used or not reachable). This smell manifests in two forms:

    • Unreferenced abstractions—Concrete classes that are not being used by anyone • Orphan abstractions—Stand-alone interfaces/abstract classes that do not have any derived abstractions

    This smell violates the principle YAGNI (You Aren’t Gonna Need It), which recommends not adding functionality until deemed necessary [53]

    When an abstraction is left unused in design, it does not serve a meaningful purpose in design, and hence violates the principle of abstraction.

    POTENTIAL CAUSES: Leftover garbage during maintenance or refactoring. Speculative generality - abstractions are introduced speculating that they may be required sometime in future.

    REFACTORING: remove the Unutilized Abstraction from the design.

    IMPACT: pollutes the design space and increases cognitive load. This impacts understandability. UNUTILIZED ABSTRACTION Two or more abstractions have identical names or identical implementation or both

    POTENTIAL CAUSES:

    Copy-paste programming The “get-the-work-done” mindset of a programmer leads him to copy and paste code instead of applying proper abstraction. Ad hoc maintenance When the software undergoes haphazard fixes or enhancements over many years, it leaves “crufts”6 with lots of redundant code in it. Lack of communication Often, in industrial software, code duplication occurs because different people work on the same code at different times in the life cycle of the software. They are not aware of existing classes or methods and end up re-inventing the wheel.

    REFACTORING: For identical name form, the suggested refactoring is to rename one of the abstrac- tions to a unique name. In the case of the identical implementation form of Duplicate Abstraction, if the implementations are exactly the same, one of the implementations can be removed. If the implementations are slightly different, then the common implementation in the duplicate abstractions can be factored out into a common class.

    IMPACT: it affects understandability of the design. Developers of client code will be confused and unclear about the choice of the abstraction that should be used by their code.

    identical implementation (i.e., they have duplicate code), it becomes difficult to maintain them. In summary, this smell indicates a violation of the DRY (Don’t Repeat Yourself) principle. If the DRY principle is not fol- lowed, a modification of an element within the system requires modifications to other logically unrelated elements making maintainability a nightmare. Since there is dupli- cation among abstractions in the design, this smell is named Duplicate Abstraction.

    3.7.6 ALIASES This smell is also known in literature as:

    • “Alternative classes with different interfaces” [7]—This smell occurs when classes do similar things, but have different names. • “Duplicate design artifacts” [74]—This smell occurs when equivalent design artifacts are replicated throughout the architecture.

  7. Duplicate Abstraction

    UNUTILIZED ABSTRACTION Two or more abstractions have identical names or identical implementation or both

    POTENTIAL CAUSES:

    Copy-paste programming The “get-the-work-done” mindset of a programmer leads him to copy and paste code instead of applying proper abstraction. Ad hoc maintenance When the software undergoes haphazard fixes or enhancements over many years, it leaves “crufts”6 with lots of redundant code in it. Lack of communication Often, in industrial software, code duplication occurs because different people work on the same code at different times in the life cycle of the software. They are not aware of existing classes or methods and end up re-inventing the wheel.

    REFACTORING: For identical name form, the suggested refactoring is to rename one of the abstrac- tions to a unique name. In the case of the identical implementation form of Duplicate Abstraction, if the implementations are exactly the same, one of the implementations can be removed. If the implementations are slightly different, then the common implementation in the duplicate abstractions can be factored out into a common class.

    IMPACT: it affects understandability of the design. Developers of client code will be confused and unclear about the choice of the abstraction that should be used by their code.

    identical implementation (i.e., they have duplicate code), it becomes difficult to maintain them. In summary, this smell indicates a violation of the DRY (Don’t Repeat Yourself) principle. If the DRY principle is not fol- lowed, a modification of an element within the system requires modifications to other logically unrelated elements making maintainability a nightmare. Since there is dupli- cation among abstractions in the design, this smell is named Duplicate Abstraction.

    3.7.6 ALIASES This smell is also known in literature as:

    • “Alternative classes with different interfaces” [7]—This smell occurs when classes do similar things, but have different names. • “Duplicate design artifacts” [74]—This smell occurs when equivalent design artifacts are replicated throughout the architecture.

5.1.2 encapsulation

5.1.3 modularization

5.1.4 hierarchy

5.2 Normal

5.2.1 Alias Duplicates

use App\Model\Category\Query as CategoryQuery;
use App\Model\Product\Contract\Query as ProductQueryInterface;
use App\Contract\Query as

5.2.2 Alternative Classes With Different Interfaces

5.2.3 Comments

If you need a comment to explain what a block of code does, try Extract Function (106). If the method is already extracted but you still need a comment to explain what it does, use Change Function Declaration (124) to rename it. If you need to state some rules about the required state of the system, use Introduce Assertion (302).

5.2.4 Data Class

5.2.5 Data Clumps

Fix: Use Extract Class (182) on the fields to turn the clumps into an object.

5.2.6 Divergent Change

Within Classes

5.2.7 Duplicated Code

Within Classes Same code structure in more than one place Don't Repeat Yourself (DRY)

  1. Same expression in different places

    The simplest duplicated code problem is when you have the same expression in two methods of the same class. Then all you have to do is Extract Function and invoke the code from both places.

5.2.8 Feature Envy

Between Classes A method accesses the data of another object more than its own data

5.2.9 Global Data

"Global data is especially nasty when it’s mutable. Global data that you can guarantee never changes after the program starts is relatively safe—if you have a language that can enforce that guarantee." Fix: Encapsulate Variable

5.2.10 Insider Trading

5.2.11 Large Class

Within Classes A class contains too many fields, methods, lines of code. Fix: Extract Class, Extract Superclass, Replace Type Code with Subclass

5.2.12 Lazy Element

It may be a function that’s named the same as its body code reads, or a class that is essentially one simple function. Fix: Inline Function, Inline Class, Collapse Hierarchy

5.2.13 Long Function

Within Classes Fix: Extract Function

5.2.14 Long Parameter List

Within Classes Fix: Replace Parameter with Query, Preserve Whole Object, INtroduce PArameter Object, Remove Flag Argument, Combine Functions into Class

5.2.15 Loops

Fix: Replace Loop with Pipeline

5.2.16 Message Chains

You see message chains when a client asks one object for another object, which the client then asks for yet another object, which the client then asks for yet another another object, and so on. 95

5.2.17 Middle Man

Fix: Remove Middle Man, Inline Function, Replace Superclass with Delegate, Replace Subclass with Delegate

5.2.18 Mysterious Name

Fix: Change Function Declaration, Rename Variable, Rename Field

5.2.19 Primitive Obsession

Primitive types: integers, floating point numbers and strings. Money, coordinates or ranges. Fix: Replace Primitive with Object

5.2.20 Refused Bequest

5.2.21 Repeated Switches

5.2.22 Shotgun Surgery

5.2.23 Speculative Generality

“Oh, I think we’ll need the ability to do this kind of thing someday”

5.2.24 Temporary Field

6 Codebases

Name Lines of Code Description Tutorials
Video Rental Shop 132 From Martin Fowler book "Refactoring: Improving the Design of Existing Code" converted to PHP Refactoring 101
Legacy Code Retreat - Trivia Game 196 Designed for Legacy Code Retreat events Refactoring Legacy Code
      Techniques for Refactoring Code
      Legacy Coderetreat (Java)
      GitHub
       

6.1 Video Rental Shop

phploc Size Lines of Code (LOC) 132 Comment Lines of Code (CLOC) 12 (9.09%) Non-Comment Lines of Code (NCLOC) 120 (90.91%) Logical Lines of Code (LLOC) 48 (36.36%) Classes 40 (83.33%) Average Class Length 13 Minimum Class Length 6 Maximum Class Length 24 Average Method Length 2 Minimum Method Length 1 Maximum Method Length 19 Functions 0 (0.00%) Average Function Length 0 Not in classes or functions 8 (16.67%)

Structure Namespaces 0 Interfaces 0 Traits 0 Classes 3 Abstract Classes 0 (0.00%) Concrete Classes 3 (100.00%) Methods 11 Scope Non-Static Methods 11 (100.00%) Static Methods 0 (0.00%) Visibility Public Methods 11 (100.00%) Non-Public Methods 0 (0.00%) Functions 0 Named Functions 0 (0.00%) Anonymous Functions 0 (0.00%) Constants 3 Global Constants 0 (0.00%) Class Constants 3 (100.00%)

Cyclomatic Complexity Average Complexity per LLOC 0.17 Average Complexity per Class 3.67 Minimum Class Complexity 1.00 Maximum Class Complexity 9.00 Average Complexity per Method 1.73 Minimum Method Complexity 1.00 Maximum Method Complexity 9.00

Dependencies Global Accesses 0 Global Constants 0 (0.00%) Global Variables 0 (0.00%) Super-Global Variables 0 (0.00%) Attribute Accesses 15 Non-Static 15 (100.00%) Static 0 (0.00%) Method Calls 14 Non-Static 14 (100.00%) Static 0 (0.00%)

6.2 Legacy Code Retreat - Trivia Game

phploc Size Lines of Code (LOC) 196 Comment Lines of Code (CLOC) 0 (0.00%) Non-Comment Lines of Code (NCLOC) 196 (100.00%) Logical Lines of Code (LLOC) 99 (50.51%) Classes 88 (88.89%) Average Class Length 88 Minimum Class Length 88 Maximum Class Length 88 Average Method Length 7 Minimum Method Length 1 Maximum Method Length 17 Functions 1 (1.01%) Average Function Length 1 Not in classes or functions 10 (10.10%)

Cyclomatic Complexity Average Complexity per LLOC 0.26 Average Complexity per Class 25.00 Minimum Class Complexity 25.00 Maximum Class Complexity 25.00 Average Complexity per Method 3.18 Minimum Method Complexity 1.00 Maximum Method Complexity 10.00

Dependencies Global Accesses 0 Global Constants 0 (0.00%) Global Variables 0 (0.00%) Super-Global Variables 0 (0.00%) Attribute Accesses 115 Non-Static 115 (100.00%) Static 0 (0.00%) Method Calls 21 Non-Static 21 (100.00%) Static 0 (0.00%)

Structure Namespaces 0 Interfaces 0 Traits 0 Classes 1 Abstract Classes 0 (0.00%) Concrete Classes 1 (100.00%) Methods 11 Scope Non-Static Methods 11 (100.00%) Static Methods 0 (0.00%) Visibility Public Methods 11 (100.00%) Non-Public Methods 0 (0.00%) Functions 1 Named Functions 1 (100.00%) Anonymous Functions 0 (0.00%) Constants 0 Global Constants 0 (0.00%) Class Constants 0 (0.00%)

7 Definitions

7.1 Anemic Domain Model

  • focus on data
  • structured
  • easy to implement and to maintain
  • contains little or no logic
  • no guarantee to be valid or consisten

7.1.1 use

  • prototyping
  • easy of use
  • easily generated

7.2 Rich Domain Model

  • combines data and logic
  • valid by design
  • easy to test
  • defined state transistions

7.2.1 use

  • clean code
  • testability
  • truly OOP

7.3 Characterization Test

Test that characterizes the actual behavior of a piece of code. It acts as a change detector, protecting legacy code from unintended changes.

7.4 Polymorphism

describes a pattern in object oriented programming in which classes have different functionality while sharing a common interface.

7.5 Parameter vs. Argument

A parameter is the variable which is part of the method’s signature (method declaration). An argument is an expression used when calling the method.

Consider the following code:

void Foo(int i, float f) { // Do things }

void Bar() { int anInt = 1; Foo(anInt, 2.0); }

Here i and f are the parameters, and anInt and 2.0 are the arguments.

https://stackoverflow.com/questions/1788923/parameter-vs-argument

7.6 Principle vs. Practices

  • The Boy scout motto – “Be prepared”–is a timeless principle. “Buy a plunger before you need a plunger” is a practice that applies this principle in a memorable way.
  • Principles are good ideas or good values stated in a context-independent manner. Practices are applications of theses principles stated in a context-dependent way.
  • Principle: a fundamental, primary or general law or truth
  • Practice: the action or process of performing or doing something

7.7 Logical operators

They are used for different purposes and in fact have different operator precedences. The && and || operators are intended for Boolean conditions, whereas and and or are intended for control flow.

For example, the following is a Boolean condition:

if ($foo = $bar && $baz ! $quxx) {

This differs from control flow:

doSomething() or die();

7.8 Value Object

  • measures, quantifies or describe a thing in the domain
  • identity is based on composition of values
  • immutable
  • compared using all values
  • no side effects

7.9 Service

an object that dose work

7.10 Technical Dept

is the debt that accumulate when you knowingly or unknowingly make wrong or non-optimal design decisions.

http://blog.insight.sensiolabs.com/2014/11/04/technical-debt-relevant-projects.html

7.11 Dead Code

  • code that is never used
  • data never delivered to the user

8 Practices

8.1 Always Use 'declare(strictypes=1)'

declare(stric_types=1);

$var  = '0';
if ( !$var ) {
   echo 'negation';
}

8.2 Avoid Else, Return Early (Guard Clause)

Return as soon as you know your method cannot do any more meaningful work.

 public function foo(int $x): string
    {
        $result = null;

        if ($x === 1) {
            $result = 'a';
        } elseif ($x === 2) {
            $result = 'b';
        } else {
            $result = 'c';
        }

        return $result;
    }
 public function foo(int $x): string
    {
        if ($x === 1) {
            return 'a';
        }

        if ($x === 2) {
            return 'b';
        }

        if ($x === 3) {
            return 'c';
        }
    }

Refactoring: Replace Nested Conditional with Guard Clauses Guard clause provides an early exit from a subroutine. Removing one level of nesting and resulting in flatter code.

https://stackoverflow.com/questions/4838828/why-should-a-function-have-only-one-exit-point

8.3 Avoid Negative Conditionals

It is much easier, for the human mind, to comprehend positive reasoning. So if you can avoid negative conditionals, you should always take that path.

if (!count){
...
}
if (count == 0){
...
}

8.4 Don't Use 'clone'

8.4.2 TODO show example with Doctrine

8.5 Don't Use 'else if'

Else if is not compatible with the colon syntax for if|elseif blocks. For this reason, use elseif for conditionals

<?php if ($user) { ?>
    <span>OK. Your registration is successful</span>
<?php } else { ?>
    <span>Something went wrong! Please try again later! </span>
<?php } ?>
 if ($user) : ?>
    <div>OK. Your registration is successful</div>
<?php else : ?>
    <div>Something went wr+ong! Please try again later!</div>
<?php endif ?>

Most of the time the alternative (endif) syntax is used in view scripts. It's often hard to see/notice the end of an if statement since a curly brace only takes up one character, when you're at the bottom of a file, it's hard to tell if it's the end of an if or a foreach. For example:

<?php if ($condition): ?>

    <div>a huge block of html</div>

<?php endif; ?>

https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP And the latter has poorer performance.

// This:
if ( $foo == 'bar' ) {
    echo 'Hello world';
} else if ( $foo == 'Bar' ) {
    echo 'Hello world';
} else if ( $baz == $foo ) {
    echo 'Hello baz';
} else {
    echo 'Eh?';
}

// Is actually equivalent to:
if ( $foo == 'bar' ) {
    echo 'Hello world';
} else {
    if ( $foo == 'Bar' ) {
        echo 'Hello world';
    } else {
        if ( $baz == $foo ) {
            echo 'Hello baz';
        } else {
            echo 'Eh?';
        }
    }
}

8.6 Don't Use Globals

EVIL in pure form ;)

8.7 Don't Use Magic Numbers

Raw numbers in code. Like 86400 - that's a number of seconds per day. But it is not so obvious for every one whats that number means Refactoring: Replace Magic Number with Symbolic Constant

8.8 Don't Use Method Chaining Syntax

Violating CQS

8.9 Don't Use Optional Dependencies

Use Constructor Injection not Setter Injection. Constructor Injection gives you a valid object with all its dependencies, upon construction.

8.10 Don't Use Switch Parameter

A method runs different code depending on the values of an parameter Refactoring: Replace Parameter with Method

8.11 Don't Use Traits

8.12 Eliminate or Reduce Number of Comments

8.13 Eliminate or Reduce Number of Parameters

Functions should have a small number of arguments. No argument is best, followed by one, two, and three. More than three is very questionable and should be avoided with prejudice.

8.15 Make Class Constants & Variables Always Private

8.16 Make Classes Always Final

8.17 Sprout Class

8.18 Sprout Method

When adding new functionality, write the code in a new method with TDD and then call this method from the old code. So even if you can’t test the code where your method is being called, at least the new code has tests.

  1. Identify where you need to make your code change.
  2. If the change can be formulated as a single sequence of statements in one place in a method, write down a call for a new method that will do the work involved and then comment it out. (I like to do this before I even write the method so that I can get a sense of what the method call will look like in context.)
  3. Determine what local variables you need from the source method, and make them arguments to the call.

4.Determine whether the sprouted method will need to return values to source method. If so, change the call so that its return value is assigned to a variable.

  1. Develop the sprout method using test-driven development.
  2. Remove the comment in the source method to enable the call.

8.19 Use parentheses

Parentheses help clarify the order of operators…etc. PHP won’t get confused if you don’t use parentheses because it knows the order of operators table very well. However, a person looking at your program has to figure out which is done first, and parentheses help group operations together.

if (age < 20 || sales < 1200 && hrsWorked > 15) {}
if ((age < 20) || ((sales < 1200) && (hrsWorked > 15))) {}

8.20 Wrap Class

Choosing to use Wrap Class is a whole other issue. There is a higher thresh- old for this pattern. Generally two cases tip me toward using Wrap Class: 1. The behavior that I want to add is completely independent, and I don’t want to pollute the existing class with behavior that is low level or unre- lated. 2. The class has grown so large that I really can’t stand to make it worse. In a case like this, I wrap just to put a stake in the ground and provide a roadmap for later changes.

8.21 Wrap Method

8.22 Naming

8.22.1 variable

8.22.2 method

  • isX() or hasX() for bool return type

8.22.3 snakecase

  1. Clients must use API with snakecase (keys in JSON object sent in request body are in snakecase).
  2. Database column names are also snakecase.

    Based on an eye tracking study on camelCase and snakecase (PDF) from 2010, snakecase is 20% easier to read than camelCase!

    name psr comunnity description
    method names camelCase() 21/22. lowerunder 1/22 camelCase()  
    Properties names $StudlyCaps or $camelCase or $underscore $camelCase  

    XMLHttpRequest is still a great tragedy

    Based on an (PDF) from 2010, snakecase is 20% easier to read than camelCase! eye tracking study on camelCase and snakecase https://stitcher.io/blog/have-you-thought-about-casing http://www.cs.loyola.edu/~binkley/papers/icpc09-clouds.pdf?fbclid=IwAR2lZJWpYcV-TK2HNpy-JmB3A9g0nSeCeSaDr2ZVh30hSlS41n5O48YoiWc

9 Principles

9.1 SOLID

There is a strong relationship beetween each of those principles. If you take each of those principles in isolation and try to apply them in your codebase you will get limited benefit out of that. it's only when you take enterly package apply it in it entarity. Applying it all at once. Strong sinergy efect.

9.1.1 Single Responsibilty (SRP)

A class should have only one reason to change - doing one thing, and do it well The object should be able to do the job completely The class has only one well-defined responsibility which is exclusively handling user data. No more, no less.

9.1.2 Open/Closed Principle (OCP)

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. Interface is open for extension but closed for modification.

9.1.3 Liskov Substitution (LSP)

Metody do klas bazowych, muszą być w stanie używać również obiektów klas dziedziczących po klasach bazowych. Program to an, interface, not an implementation.

9.1.4 Interface Segregation (ISP)

A client should never be forced to implement an interface that it doesn't use or clients shouldn't be forced to depend on methods they do not use.

9.1.5 Dependency Inversion (DIP)

Depend on abstractions, not concretions Program to an, interface, not an implementation.

9.2 GRASP General Responsibility Assignment Software Patterns

  • Craig Larman 1997 - Applying UML and Patterns (BN)

9.3 Design

9.3.1 Separation of Concerns (SoC)

The no.1 - the most important

  1. opposite

    Big Ball of Mud

9.3.2 Big Ball of Mud

9.3.3 Command Query Separation (CQS)

9.3.4 Composition Over Inheritance

9.3.5 YAGNI

9.3.6 Don't Repeat Yourself (DRY)

9.3.7 The Law of Demeter (LoD)

Each unit should only talk to its friends; don't talk to strangers.

  • Object Calisthenics - One Arrow Per Line

9.4 Architectural

9.4.1 Model-View-Controller

• The Model: Captures and centralizes all the domain model behaviour. This layer manages all the data, logic and business rules independently of the data representation. It can be said that the Model layer is the heart and soul of every MVC application. • The Controller: Orchestrates interactions between the other layers. Triggers actions on the model in order to update its state and refreshes the representations associated to the model. Additionally, the Controller can also send messages to the View layer in order to change the specific Model representation. • The View: A layer whose main purpose is to expose the differing representations of the Model layer and to give a way to trigger changes on the Model’s state.

9.4.2 Api

  1. Tools
  2. underscore vs camelCase vs kebab
  3. Info

    Pluralization of resource names in REST URIs is the widely adopted standard followed by the overwhelming majority of public and private APIs. Beyond being "the standard approach" it also makes sense and is the most simple.

    For example:

    GET /resources returns a list of resource items POST /resources creates one or many resource items PUT /resources updates one or many resource items PATCH /resources partially updates one or many resource items DELETE resources deletes all resource items And for single resource items: GET /resources:id returns a specific resource item based on :id parameter POST resources:id creates one resource item with specified id (requires validation) PUT resources:id updates a specific resource item PATCH resources:id partially updates a specific resource item DELETE resources:id deletes a specific resource item

    1. Sorting

      We can also add a sort parameter to sort by field. The sort field in turn contains a list of comma separated columns to sort on; the first in the list is the highest sort priority. In order to negatively sort you prefix a column with a negative sign - GET /tickets?sort=-amount: sort orders by descending order of amount (highest first). GET /tickets?sort=-amount,createdat: sort orders by descending order of amount (highest first). Within those amounts (with orders of equal amounts), older orders are listed first.

    2. Searching

      We can then search using a simple parameter that applies a search query that can then be routed through a search service (for example, ElasticSearch). Suppose we want to search orders for the phrase refund, we can define a field for search queries: GET /orders?q=refund

    3. Limiting fields

      Additionally, using a fields parameter we can query for specific fields: GET /orders?fields=amount,createdat,customername,shippingaddress

  4. How to pack data

    https://jsonapi.org/format/

    A JSON object MUST be at the root of every JSON:API request and response containing data. This object defines a document’s “top level”.

    A document MUST contain at least one of the following top-level members:

    data: the document’s “primary data” errors: an array of error objects meta: a meta object that contains non-standard meta-information.

    The members data and errors MUST NOT coexist in the same document.

  5. Links

10 Paradigm

10.1 Imperative

  • uses statements that change a program's state
  • focuses on describing how a program operates
  • The term is often used in contrast to declarative programming, which focuses on what the program should accomplish without specifying how the program should achieve the result.

10.2 Declaraive

10.2.1 Functional Programming

that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm in that programming is done with expressions or declarations[1] instead of statements.

10.3 OOP

data before action vs. procedual action before data https://www.youtube.com/watch?v=lbXsrHGhBAU

10.3.1 definition

  1. class (data type definition)
    1. field (data member)
    2. method (function member) - function associate woth class

      object/instance (piece of data)

10.3.2 Principles

  1. encapsulution - methods act as 'interface' to object's fields.

    Fields of an object and instance should only be read or written by methods of that instances class.

  2. inheritance

    My design goals are :

    • The simples code that does what he needs to.
    • complexity has to justyfiy itself

    OOP Tutorial

    OOP Tutorial 4 APIE Abstraction Polymorphism Innheritance Encapsulation

    Object-Oriented Programming is Bad

    procedural & imperative (the default) procedural & functional (minimize state) object-oriented & imperative (segregate state) object-oriented & functional (do both)

    Can't find the answear on internet. I would like to play offline, set broadcast to lan, play offline. Then when I join the server I see only console. How I can jump in to the game?

10.4 Procedural

10.5 Sources

10.5.1 4 Programming Paradigms In 40 Minutes

  1. OOP
    • everything is an object
    • state (properties) & behaviour (methods)
    • modyfing it's own internal state
  2. Functional
    • function take data & output data
    • Pure Functional
      • don't store state
      • don't mutate incoming data

11 Patterns

  • Patterns are formalized best practices.
  • is a named description of a problem and solution that can be applied to new contexts

11.1 Software design patterns

11.1.1 Creational

Creation of objects themselves - separate the construction of a complex object from its representation

  1. Factory

    Separating the creation of an object from the actual implementation. If creating the object is a complicated job you can do all of the work in the factory, instead of repeating it every time you want to create a new instance.

    1. Simple Factory
      class Bicycle
      {
      }
      
      class SimpleFactory
      {
          public function createBicycle(): Bicycle
          {
              return new Bicycle();
          }
      }      
      
      class SimpleFactoryTest extends TestCase
      {
          public function testCanCreateBicycle()
          {
              $bicycle = (new SimpleFactory())->createBicycle();
              $this->assertInstanceOf(Bicycle::class, $bicycle);
          }
      }
      
    2. Method Factory
      interface CarFactory 
      {
          public function makeCar();
      }
      
      interface Car 
      {
          public function getType();
      }
      
      class SedanFactory implements CarFactory 
      {
          public function makeCar() 
          {
              return new Sedan();
          }
      }
      
      class Sedan implements Car 
      {
          public function getType() 
          {
              return 'Sedan';
          }
      }
      
      /* Client */
      $factory = new SedanFactory();
      $car = $factory->makeCar();
      
    3. Abstract Factory
      1. https://stackoverflow.com/questions/2280170/why-do-we-need-abstract-factory-design-pattern
    4. Links
  2. Builder
    class Car
    {
        ...
    }
    
    class CarBuilder
    {
        public function __construct()
        {
            $car = new Car;
        }
        
        public function build(): Car
        {
            return $car;   
        }
    
        public function setSeats(int $seats)
        {
            $car->setSeats($seats);
        }
    }
    
    1. links
  3. Prototype
    abstract class Prototype{
        protected $name;
     
        public function __construct($name) {
            $this->name=$name;
        }
        abstract function __clone();
        public function getName() {
            return $this->name;
        }
    }
     
    class ConcretePrototype extends Prototype{
     
        public function __construct($name) {
            parent::__construct($name);
        }
        public function __clone() {}
    }
     
    // testy
    $prototype = new ConcretePrototype("nazwa");
    echo  $prototype->getName(); // wyswietli "nazwa"
    $prototype2 = clone $prototype;
    echo  $prototype2->getName(); // wyswietli "nazwa"
    
    
  4. Dependency Injection

    A class receives its dependencies from external sources rather than creating them itself. That is, following the Dependency Inversion principle.

    1. Types of injection:
      1. Construction injection:

        dependencies are injected when creating the object. It can be used for required and optional dependencies.

      2. Setter injection:

        dependencies are injected through setters, and they are always optional.

      3. Property injection:

        dependencies are injected directly through public properties. This type of injection is not recommended as there is no control at all of what is being injected.

    2. Links

11.1.2 Structural

They act as interconnectors between entities. It serves as a blueprint for how basic classes can be combined to form bigger entities. Structural patterns describe the static architecture of a design;

  1. Bridge

    The Bridge pattern can be quite straightforward; it effectively allows us to decouple an abstraction from an implementation so the two can vary independently.

    interface FormatterInterface
    {
        public function format(string $text);
    }
    
    class PlainTextFormatter implements FormatterInterface
    {
        public function format(string $text)
        {
            return $text;
        }
    }
    
    class HtmlFormatter implements FormatterInterface
    {
        public function format(string $text)
        {
            return sprintf('<p>%s</p>', $text);
        }
    }
    
    abstract class Service
    {
        /**
         * @var FormatterInterface
         */
        protected $implementation;
    
        /**
         * @param FormatterInterface $printer
         */
        public function __construct(FormatterInterface $printer)
        {
            $this->implementation = $printer;
        }
    
        /**
         * @param FormatterInterface $printer
         */
        public function setImplementation(FormatterInterface $printer)
        {
            $this->implementation = $printer;
        }
    
        abstract public function get();
    }
    
    class HelloWorldService extends Service
    {
        public function get()
        {
            return $this->implementation->format('Hello World');
        }
    }
    
    class BridgeTest extends TestCase
    {
        public function testCanPrintUsingThePlainTextPrinter()
        {
            $service = new HelloWorldService(new PlainTextFormatter());
            $this->assertEquals('Hello World', $service->get());
    
            // now change the implementation and use the HtmlFormatter instead
            $service->setImplementation(new HtmlFormatter());
            $this->assertEquals('<p>Hello World</p>', $service->get());
        }
    }
    
  2. Facade
    • A Facade is meant to decouple a client and a sub-system by embedding many (but sometimes just one) interface, and of course to reduce complexity.
    • That’s why a good facade has no new in it. If there are multiple creations for each method, it is not a Facade, it’s a Builder or a [Abstract|Static|Simple] Factory [Method].
    • The best facade has no new and a constructor with interface-type-hinted parameters. If you need creation of new instances, use a Factory as argument.
    • A Facade design pattern works providing a single class that in itself instantiates other classes and provides a simple interface to use those functions. A warning when using such pattern is that, as classes are instantiated within the Facade, you are essentially tightly coupling the classes that it utilizes. There are cases where you want this, but there are cases where you do not. Where do you do not want this behavior, you are better suited to using dependency injection. I have found this to be useful when wrapping a set of poor APIs into a single unified API. It reduces external dependencies, allowing complexity to be internalized; this process can make your code more readable. In other situations, where the various classes were loosely coupled together, we may find it better to use dependency injection. By injecting objects that perform various actions into the ToyFactory class we can benefit from making testing easier by being able to inject fake classes that the ToyFactory class can manipulate. Personally, I am a huge believer in making code as easily testable as possible; hence why I don't like this approach.
    class Facade
    {
        /**
         * @var OsInterface
         */
        private $os;
    
        /**
         * @var BiosInterface
         */
        private $bios;
    
        /**
         * @param BiosInterface $bios
         * @param OsInterface   $os
         */
        public function __construct(BiosInterface $bios, OsInterface $os)
        {
            $this->bios = $bios;
            $this->os = $os;
        }
    
        public function turnOn()
        {
            $this->bios->execute();
            $this->bios->waitForKeyPress();
            $this->bios->launch($this->os);
        }
    
        public function turnOff()
        {
            $this->os->halt();
            $this->bios->powerDown();
        }
    }
    
    interface OsInterface
    {
        public function halt();
    
        public function getName(): string;
    }
    
    interface BiosInterface
    {
        public function execute();
    
        public function waitForKeyPress();
    
        public function launch(OsInterface $os);
    
        public function powerDown();
    }
    
    class FacadeTest extends TestCase
    {
        public function testComputerOn()
        {
            /** @var OsInterface|\PHPUnit_Framework_MockObject_MockObject $os */
            $os = $this->createMock('DesignPatterns\Structural\Facade\OsInterface');
    
            $os->method('getName')
                ->will($this->returnValue('Linux'));
    
            $bios = $this->getMockBuilder('DesignPatterns\Structural\Facade\BiosInterface')
                ->setMethods(['launch', 'execute', 'waitForKeyPress'])
                ->disableAutoload()
                ->getMock();
    
            $bios->expects($this->once())
                ->method('launch')
                ->with($os);
    
            $facade = new Facade($bios, $os);
    
            // the facade interface is simple
            $facade->turnOn();
    
            // but you can also access the underlying components
            $this->assertEquals('Linux', $os->getName());
        }
    }
    
    
  3. Composite

    To treat a group of objects the same way as a single instance of the object.

    Composite Imagine an audio system consisting of individual songs and also playlists of songs. Yes, playlists consist of songs, but we want both to be treated individually. Both are types of music, both can be played. The Composite design pattern can help here; it allows us to ignore the differences between compositions of objects and individual objects. It allows us to treat both with identical or nearly-identical code. Let's put together a little example; a song is our example of a leaf, with playlists being composites. Music is our abstraction of playlists and songs; therefore, we can call this our component. The client of all this is our index.php file. By not discriminating between leaf-nodes and branches, our code becomes less complex and therefore less error prone.

    interface RenderableInterface
    {
        public function render(): string;
    }
    
    /**
     * The composite node MUST extend the component contract. This is mandatory for building
     * a tree of components.
     */
    class Form implements RenderableInterface
    {
        /**
         * @var RenderableInterface[]
         */
        private $elements;
    
        /**
         * runs through all elements and calls render() on them, then returns the complete representation
         * of the form.
         *
         * from the outside, one will not see this and the form will act like a single object instance
         *
         * @return string
         */
        public function render(): string
        {
            $formCode = '<form>';
    
            foreach ($this->elements as $element) {
                $formCode .= $element->render();
            }
    
            $formCode .= '</form>';
    
            return $formCode;
        }
    
        /**
         * @param RenderableInterface $element
         */
        public function addElement(RenderableInterface $element)
        {
            $this->elements[] = $element;
        }
    }
    
    class InputElement implements RenderableInterface
    {
        public function render(): string
        {
            return '<input type="text" />';
        }
    }
    
    class TextElement implements RenderableInterface
    {
        /**
         * @var string
         */
        private $text;
    
        public function __construct(string $text)
        {
            $this->text = $text;
        }
    
        public function render(): string
        {
            return $this->text;
        }
    }
    
    class CompositeTest extends TestCase
    {
        public function testRender()
        {
            $form = new Composite\Form();
            $form->addElement(new Composite\TextElement('Email:'));
            $form->addElement(new Composite\InputElement());
            $embed = new Composite\Form();
            $embed->addElement(new Composite\TextElement('Password:'));
            $embed->addElement(new Composite\InputElement());
            $form->addElement($embed);
    
            // This is just an example, in a real world scenario it is important to remember that web browsers do not
            // currently support nested forms
    
            $this->assertEquals(
                '<form>Email:<input type="text" /><form>Password:<input type="text" /></form></form>',
                $form->render()
            );
        }
    }
    
  4. Adapter (Wrapper)
    // Concrete Implementation of PayPal Class
    class PayPal {
         
        public function __construct() {
            // Your Code here //
        }
         
        public function sendPayment($amount) {
            // Paying via Paypal //
            echo "Paying via PayPal: ". $amount;
        }
    }
     
    // Simple Interface for each Adapter we create
    interface paymentAdapter {
        public function pay($amount);
    }
     
    class paypalAdapter implements paymentAdapter {
         
        private $paypal;
     
        public function __construct(PayPal $paypal) {
            $this->paypal = $paypal;
        }
         
        public function pay($amount) {
            $this->paypal->sendPayment($amount);
        }
    }
    
    // Client Code
    $paypal = new paypalAdapter(new PayPal());
    $paypal->pay('2629');
    

    getData and setData (not as “pretty”).

  5. Container
    1. Benchmark

11.1.3 Behavioral

They work to explain how objects interact with each other; how they can send messages between each of the objects and how you can divide the steps of various tasks up among classes. Describe a flowing process.

  1. Chain Of Responsibility

    Is an object oriented version of the if … else if … else if ……. else … endif idiom

    interface PurchaserInterface
    {
        public function setNextPurchaser(PurchaserInterface $nextPurchaser): void;
    
        public function buy($price): void;
    }
    
    final class AssociatePurchaser implements PurchaserInterface
    {
        /**
         * @var PurchaserInterface
         */
        private $nextPurchaser;
    
        public function setNextPurchaser(PurchaserInterface $nextPurchaser): void
        {
            $this->nextPurchaser = $nextPurchaser;
        }
    
        public function buy($price): void
        {
            if ($price < 100) {
                echo('Associate purchased');
                return;
            }
    
            if ($this->nextPurchaser) {
                $this->nextPurchaser->buy($price);
                return;
            }
    
            echo 'Associate could not buy';
        }
    }
    
    final class ManagerPurchaser implements PurchaserInterface
    {
        /**
         * @var PurchaserInterface
         */
        private $nextPurchaser;
    
        public function setNextPurchaser(PurchaserInterface $nextPurchaser): void
        {
            $this->nextPurchaser = $nextPurchaser;
        }
    
        public function buy($price): void
        {
            if ($price < 200) {
                echo('Manager purchased');
                return;
            }
    
            if($this->nextPurchaser) {
                $this->nextPurchaser->buy($price);
                return;
            }
    
            echo 'Manager could not buy';
        }
    }
    
    final class DirectorPurchaser implements PurchaserInterface
    {
        /**
         * @var PurchaserInterface
         */
        private $nextPurchaser;
    
        public function setNextPurchaser(PurchaserInterface $nextPurchaser): void
        {
            $this->nextPurchaser = $nextPurchaser;
        }
    
        public function buy($price): void
        {
            if ($price < 300) {
                echo('Director purchased');
                return;
            }
    
            if($this->nextPurchaser) {
                $this->nextPurchaser->buy($price);
                return;
            }
    
            echo 'Director could not buy';
        }
    }
    
    class ChainOfResponsibilityTest extends TestCase
    {
        public function testOne()
        {
            $this->expectOutputString('Director purchased');
    
            $associate = new AssociatePurchaser();
            $manager = new ManagerPurchaser();
            $director = new DirectorPurchaser();
            $associate->setNextPurchaser($manager);
            $manager->setNextPurchaser($director);
    
            $associate->buy(299);
        }
    }
    
    
  2. Observer

    The Observer design pattern essentially allows an object (the subject) to maintain a list of observers that are automatically notified when the state of the that object changes. This pattern applies a one-to-many dependency between objects; there is always one subject that updates many observers. This pattern applies a one-to-many dependency between objects; there is always one subject that updates many observers.

    class User implements \SplSubject
    {
        /**
         * @var string
         */
        private $email;
    
        /**
         * @var \SplObjectStorage
         */
        private $observers;
    
        public function __construct()
        {
            $this->observers = new \SplObjectStorage();
        }
    
        public function attach(\SplObserver $observer)
        {
            $this->observers->attach($observer);
        }
    
        public function detach(\SplObserver $observer)
        {
            $this->observers->detach($observer);
        }
    
        public function changeEmail(string $email)
        {
            $this->email = $email;
            $this->notify();
        }
    
        public function notify()
        {
            /** @var \SplObserver $observer */
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    }
    
    class UserObserver implements \SplObserver
    {
        /**
         * @var User[]
         */
        private $changedUsers = [];
    
        /**
         * It is called by the Subject, usually by SplSubject::notify()
         *
         * @param \SplSubject $subject
         */
        public function update(\SplSubject $subject)
        {
            $this->changedUsers[] = clone $subject;
        }
    
        /**
         * @return User[]
         */
        public function getChangedUsers(): array
        {
            return $this->changedUsers;
        }
    }
    
    class ObserverTest extends TestCase
    {
        public function testChangeInUserLeadsToUserObserverBeingNotified()
        {
            $observer = new UserObserver();
    
            $user = new User();
            $user->attach($observer);
    
            $user->changeEmail('foo@bar.com');
            $this->assertCount(1, $observer->getChangedUsers());
        }
    }
    
  3. Strategy

    Allow us to alter the behavior of an object at runtime. We defined a family of algorithms, bound by one common interface These algorithms are interchangeable; they can be swapped in and out without affecting the client implementation We encapsulated each algorithm within a class

    class Customer
    {
        /**
         * @var float
         */
        private $bill;
    
        /**
         * @var BillingStrategyInterface
         */
        private $strategy;
    
        public function __construct(BillingStrategyInterface $strategy)
        {
            $this->strategy = $strategy;
        }
    
        public function setStrategy(BillingStrategyInterface $strategy)
        {
            $this->strategy = $strategy;
        }
    
        public function addBeer(float $price): void
        {
            $this->bill += $this->strategy->getPrice($price);
        }
    
        public function printBill(): void
        {
            echo "Total: $this->bill";
        }
    }
    
    interface BillingStrategyInterface
    {
        public function getPrice(float $rawPrice): float;
    }
    
    class NormalStrategy implements BillingStrategyInterface
    {
        public function getPrice(float $rawPrice): float
        {
            return $rawPrice;
        }
    }
    
    class HappyHourStrategy implements BillingStrategyInterface
    {
        public function getPrice(float $rawPrice): float
        {
            return $rawPrice * 0.5;
        }
    }
    
    class StrategyTest extends TestCase
    {
        public function testOne()
        {
            $this->expectOutputString('Total: 15');
    
            //Normal billing
            $customer = new Customer(new NormalStrategy());
            $customer->addBeer(10);
    
            //Start Happy Hour
            $customer->setStrategy(new HappyHourStrategy());
            $customer->addBeer(10);
    
            $customer->printBill();
        }
    }
    
  4. Command

    Command objects encapsulate an action and its parameters. We have an Invoker and a Receiver

  5. Iterator

    Iterators are used to access the elements of an aggregate object sequentially without exposing its underlying representation

  6. Null Object

    Designed to act as a default value of an object

  7. Template method:

    Describes the program skeleton of a program

11.1.4 Concurrency

11.2 Architectural patterns

This is not strictly a design pattern (but the Gang of Four didn't cover Architectural patterns in their book); but it is incredibly relevant for PHP developers due to the web-oriented nature of PHP. Architectural patterns address various different constraints in computer systems through addressing performance limitations, high availability, and also minimization of business risk. Most developers will be familiar with the Model-View-Controller architecture when it comes to web frameworks, more recently other architectures have started to emerge; for example, a microservices architecture works by a set of RESTful APIs that are independent and interconnected. Some people believe microservices move problems from the software development layer to the systems architecture layer. The opposite of microservices often referred to as a monolithic architecture, is where all the code is together in one application.

11.2.1 Active Record

Problem: Accessing data in a database prevent duplication & centralize access don't have to add any properties to class, just add new column to table objects are tightly coupled to the database schema - hard to test without actually using database breaking SOLID's Single Responsibility Principle - object is responsible for knowing how to create, retrieve, update and delete database entry

<?php declare(strict_types=1);

class User
{

    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    public function getId(): int
    {
        return $this->id;
    }

    public function setId(int $id)
    {
        $this->id = $id;
    }

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

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

}

$id = 1;
$pdo = new PDO('sqlite:test.db');
$sth = $pdo->prepare("SELECT * FROM user WHERE id =:id");
$sth->bindParam(":id", $id, PDO::PARAM_INT);
$sth->execute();
$row = $sth->fetch(PDO::FETCH_ASSOC);
$user = new User();
$user->SetId((int) $row['id']);
$user->SetName($row['name']);

cons: hardcoded columns name, every time we add new column to database we have to manually add it in code in multiple places, hard to maintain,

<?php declare(strict_types=1);

class User
{
    private $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }
    public function load(int $id)
    {
        $sth = $this->pdo->prepare("SELECT * FROM user WHERE id =:id");

        $sth->bindParam(":id", $id, PDO::PARAM_INT);
        $sth->execute();
        $row = $sth->fetch(PDO::FETCH_ASSOC);

        $i = 0;
        foreach($row as $column => $value) {

            $meta = $sth->getColumnMeta($i);

            if($meta['sqlite:decl_type'] === 'INTEGER')
            {
                $this->$column = (int) $value;
            }else{
                $this->$column = $value;
            }

            $i++;
        }
    }
}

$pdo = new PDO('sqlite:test.db');
$user = new User($pdo);
$user->load(1);

pros: you don’t have to specify the properties of the object and how they relate to the database. The model is able to determine the properties automatically by looking at the schema of the database.

11.2.2 Data Transfer Object DTO

"a class with public variables and no functions" clean code p. 100. But in PHP we still can't declare type of variable, just type-hint parameters of methods. Like setters, so we can do it indirectly. Another great benefit it the standard rule is to not have public properties so we can stay consisten with this one. But more of great adventage of if we can make our DTO object immutable. So create only getters, without setters. Add all in constructor.

class IssResponse
{
    /**
     * @var float
     */
    private $latitude;

    /**
     * @var float
     */
    private $longitude;

    public function __construct(array $array)
    {
        $this->latitude = $array['latitude'];
        $this->longitude = $array['longitude'];
    }

    public function getLatitude(): float
    {
        return $this->latitude;
    }


    public function getLongitude(): float
    {
        return $this->longitude;
    }

}

final class PositionDTO
{
    /**
       @var float
    */
    private $latitude;

    /**
       @var float
    */
    private $longitude;

    public function __construct(float $latitude, float $longitude)
    {
        $this->latitude = $latitude;
        $this->longitude = $longitude;
    }

    public function getLatitude(): float
    {
        return $this->latitude;
    }

    public function getLongitude(): float
    {
        return $this->longitude;
    }
}

11.2.4 MVC

  1. Controllers
    1. are what the user interacts with
    2. they receive a request from the user, decide what to do, and send request back
    3. it's the only component that interacts with the models
  2. Models
    1. are where an anpplication's data are stored
    2. responsible for storing and retrieving data
    3. kno nothing about the user interface
  3. Views
    1. are what the user sees on the screen
    2. they present the data to the user
    3. kno nothing about the models
  4. Why use MVC?
    1. business logic separate from presentation: Separation Of Concerns
    2. developer specialisation
      1. designers can focus on the front end without worrying about the business logic
      2. developers of the models can focus on the business logic or back end without worrying about the look and feel
  5. Front controller
    1. provide a central entry point for all request. All request are sent through one page
  6. Action suffix
    1. _call is exectued for a non-existent or non-public method call
    2. Stack

      No it wasn't just a naming convention. It was used to execute some code before or after every controller 'action' method. Like checking is user has logged in. It is based on magic _call function which is executed for a non-existent or non-public method call.

        $controller = new Posts();
        $controller->index();
      
        class Posts
        {
          public function __call($name, $args)
        {
        //run code before
        call_user_func_array()[$this, "$nameAction"], $args);
        //run code after
        }
          public function indexAction()
        {
        }
        }
      

      tutorial ended on 31 video

11.2.5 MVVM

Magento was here.

11.3 Antipatterns

God objects Essentially, a God object is an object with either too many methods or too many properties; essentially, it's a class that knows too much or does too much. The God object soon becomes tightly coupled to (referenced by) lots of other bits of code in the application. So what's actually wrong with this? Well, in short, when you have one bit of code tied into every single other bit of code, you quickly find a maintenance disaster. If you adjust the logic for a method in a God object for one use case, you might find it having unintended consequences for another element.

The flip side to God objects being an anti-pattern is when developing embedded systems. Embedded systems are used to process data on anything from a calculator to LED signage; they are small chips that are essentially self-contained computers and quite low cost. In this use case, with restricted computational power you can often find that programming elegance and maintainability become peripheral concerns. Slight performance increase and centralization of control can be more important, meaning using God objects can be somewhat sensible. Fortunately, PHP is incredibly seldom used to program embedded systems, so you are incredibly unlikely to find yourself in this particular situation.

Another anti-pattern, called Fear of Adding Classes,

Singleton

<?php class{ Singleton private static $instance; public static function getInstance() { if (null = static::$instance) { static::$instance = new static(); } } return static::$instance; protected function _construct() { } private function _clone() { } private function _wakeup() { } }

So here are the reasons why this should be avoided: They are inherently tightly coupled meaning they are difficult to test, for example using unit tests. They even maintain their state throughout the life cycle of the application. They violate the Single Responsibility Principle by controlling their own creation and life cycle. Fundamentally, it results in you hiding the dependencies of your application in a global instance. You can no longer effectively follow your dependencies around your code as you can't follow where they are injected as function arguments. They make it ineffective to find the dependency chain should you need to analyze it.

Objects should typically be self-contained; they should only know problems about themselves and also should only solve one set of problems, its own problems. Anything that isn't relevant to this aim doesn't belong in that class.

Database as IPC Let me clear this up for you; your database isn't a message queuing system. You don't use it schedule jobs or queue up tasks to be completed. If you need something to do that, use a queuing system. Your database is for data…the clue is in the name; don't shove temporary messages in there. There are many reasons why this is a bad idea. One major issue is the fact that in databases there is no real way to not enforce a policy by which you can guarantee that a double-read will not occur, and that is by utilizing row locks. This in turn, results in processes (either incoming out outgoing) being blocked, which in turn results in processing only being able to be done in a serial fashion. Furthermore, in order to check if there is any work to do you end up essentially counting the rows of data in the database to see if there is work to do; you run this on a continuous basis. MySQL doesn't support push notifications; unlike PostgreSQL it doesn't have the NOTIFY command to pair with a LISTEN channel. Also note that when you merge a job queue with a database table that stores real data, you also invalidate the cache every time you complete a job and update a flag, in turn making MySQL far slower. In short, it results in your database performing worse and can force it to slow critical messages to a standstill. You must be careful not to turn your database into a job queue by having this functionality sneak up on you; instead, use the database exclusively for data, and bear this in mind when extending your database. RabbitMQ provides an open source queuing system with some great PHP SDKs.

Interface Bloat Interfaces shouldn't contain thousands of methods that reference internal operations of the class. They should be lightweight and considered a way of guaranteeing that when something is queried that it is definitely there.

Interfaces should be used sparingly; do you actually need an interface if the class is only ever going to be implemented once and once alone (and realistically, no one is never going to need to tamper with such code?). If so, you might want to consider avoiding an interface in such a situation.

So, let me draw you to one implementation of Interface Bloat. Let's take a look at the Pheanstalk interface class in the Pheanstalk open source library (note I have stripped the comments to make it more readable): <?php namespace Pheanstalk; interface PheanstalkInterface { const DEFAULTPORT = 11300; const DEFAULTDELAY = 0; const DEFAULTPRIORITY = 1024; const DEFAULTTTR = 60; const DEFAULTTUBE = 'default'; public function setConnection(Connection $connection); public function getConnection(); public function bury($job, $priority = self::DEFAULTPRIORITY); public function delete($job); [ 49 ] Anti-Patterns public function ignore($tube); public function kick($max); public function kickJob($job); public function listTubes(); public function listTubesWatched($askServer = false); public function listTubeUsed($askServer = false); public function pauseTube($tube, $delay); public function resumeTube($tube); public function peek($jobId); public function peekReady($tube = null); public function peekDelayed($tube = null); public function peekBuried($tube = null); public function put($data, $priority = self::DEFAULTPRIORITY, $delay = self::DEFAULTDELAY, $ttr = self::DEFAULTTTR); public function putInTube($tube, $data, $priority = self::DEFAULTPRIORITY, $delay = self::DEFAULTDELAY, $ttr = self::DEFAULTTTR); public function release($job, $priority = self::DEFAULTPRIORITY, $delay = self::DEFAULTDELAY); public function reserve($timeout = null); public function reserveFromTube($tube, $timeout = null); public function statsJob($job); public function statsTube($tube); public function stats(); public function touch($job); public function useTube($tube); public function watch($tube); public function watchOnly($tube); } Yuck! Notice how even constants have been put in the implement, the one thing you might actually want to change. Clearly, this is an interface for a class that can only be implemented one way, making the Interface useless.

Bloated optimization Often, developers may trip over themselves trying to optimize their code or their design artifacts to a ridiculous extent, often before their code even performs basic functions, or even before any code has been created at all. This can rapidly perform issues in production. In this section, I wish to discuss three anti-patterns specifically relating to this topic: Analysis paralysis Bikeshedding Premature optimization [ 62 ] Anti-Patterns Analysis paralysis In short, this is where a strategy is over-analyzed to the point where progress is slowed down, or even stopped entirely in extreme cases. Not only can such solutions become obsolete rapidly, they can be made in under-educated circumstances, for example, in a meeting where an over-analytic boss tries to dig too deep into detail in advance without allowing their developers to actually do some research. Over-analyzing a problem and seeking a perfect solution upfront just does not work; programmers should seek to refine their solution, not come up with the refined solution up front. Bikeshedding Essentially, this is where analysis paralysis can occur on the basis of some very trivial decisions, for example, the color of a log in page. The only fix that's required is to not waste time on trivial decisions. Avoid design by committee where possible as the majority of people, regardless of how good they think their design skills are, are largely incompetent at design. Premature optimization In this section, so far, I've largely beaten up project managers; no time to beat up developers. Often, developers will seek to optimize their code prematurely without having educated data-led conclusions to drive where and when optimizations should be made. Writing clean and readable code is your first priority; then you can use some great profiling tools to determine where your bottlenecks are. XDebug and New Relic are just some of the tools that are good at this. That said, there are some cases where optimization must be done, particularly on some long computational tasks where it can be mission-critical to reduce something from O(N2) time to O(N). This said, most simple PHP web apps will have no real need to use this consideration.

11.6 Enum

https://stitcher.io/blog/php-enums composer require myclabs/php-enum

12 Tests

12.3 TDD

Tests Focus on Require Speed Complexity Setup Needed
Unit Tests a class/method the source code very fast low no
Integration Tests a component/service part of the running system slow medium yes
           

12.4 Notes

Two Approach Test First Test After

Test doubles are tools you can use when you need to create substitute dependencies]

Legacy codebases are not ready for isolated unit tests.

ARRANGE-ACT-ASSERT a pattern for arranging and formatting code in UnitTest methods: Each method should group these functional sections, separated by blank lines: Arrange all necessary preconditions and inputs. Act on the object or method under test. Assert that the expected results have occurred.

'bit by bit you rewrite your code to be testable'

A double is a substitue for a real dependency

Assertions base test - most framework

dummies, fakes, spies, stubs, mocks

dummies - a stand-in for a dependency when class signatures or functionality don't matter, like PHP stdObject - but fail on type hints :/ fakes - dependency built without the use of framework tools stubs - substitue for a dependency and it matches the class signature, use stubs if you use type hints mocks - a stub that uses the framework tools - most common

feedback loop

'your decision to not build systems to catch mistakes will cost you more money

Chris???

https://github.com/opencfp/opencfp

Dependencies that you are unable to set at runtime can cause a problem ->

What to test ? Critical Functionality - First the most valuable parts - like register form if it ncessary. Bug Fixes - Every Bug that was found. New Features - New Functionality

https://github.com/kahlan/kahlan

  • use real dependencies when you can
  • when you can't use real dependencies, try test doubles
  • if you have significant amounts of code that would be burdensome to refactor, consider monkey patching
  • keep doubles in sync with real dependencies

Manual or Automate Testing

-avoid regressions

Permutations != Execution Paths - you only nee to write a test case for each execution path, not for each permutation. The number of possible paths grows with each new decision branch.

https://www.brandonsavage.net/dont-write-useless-unit-tests/

http://www.ifdattic.com/mock-test-double-using-prophecy/

http://stakeholderwhisperer.com/posts/2015/1/economy-of-tests

First, create the test. Second, write the minimum amount of code to get that test to pass. And third, now that your tests are passing, you can safely refactor your code to make it fancier.

Test. Code. Refactor.

You should write the tests first and then write your code.

unit - test one specific method on a class. Fake any needed database connections. One class in complete isolation.

integration - just like a unit test. Except it uses the real database connection.

functional - write a test to command a browser. Browser surfs your site, clicks links, fills out forms & asserts things is sees on the page. Testing the interface that users actually use!

Mock services. But don't mock simple model objects.

Let me say it a different way: if you're organizing your code well, then all classes will fall into one of two types. The first type - a model class - is a class whose job is basically to hold data… but not do much work. Our entities are model classes. The second type - a service class - is a class whose main job is to do work, but it doesn't hold much data, other than maybe some configuration. DinosaurFactory is a service class.

As a rule, you will want to mock service classes, but you do not need to mock model classes. Why not? Well, you can… but usually it's overkill. Since model classes tend to be simple and just hold data, it's easy enough to create those objects and set their data to whatever you want.

https://github.com/humbug/humbug

https://github.com/beberlei/assert

You need tests; yes, automated tests can be slow to write, but they are crucial for ensuring things don't break when you rewrite or refactor them.

Isoloate One Method

$this->getMockBuilder(Object:class) ->setMethodExcept(['someMethod']) ->getMock();

Pinpointing the problem

12.5 Behat

12.5.1 install

behat/mink-extension

12.5.2 config

baseurl: 'http://localhost' Remember that have to be with http://

13 Tools

13.1 Symfony

13.1.2 Form

'https://webmozart.io/blog/2015/09/09/value-objects-in-symfony-forms/#data-mappers', 'https://stovepipe.systems/post/rethinking-form-development', 'https://blog.martinhujer.cz/symfony-forms-with-request-objects/', 'https://speakerdeck.com/webmozart/symfony-forms-101', 'http://verraes.net/2013/04/decoupling-symfony2-forms-from-entities/'

13.1.3 Problems

After update to Symfony 4 - blank page - have to remove var/bootstrap.php.cache

13.4 Git

  • git reset HEAD~1 - move back to recent commit

13.5 Twig

13.5.1 comma

{% for role in user.roles %} {{ role.name }} {% if not loop.last %},{% endif %} {% endfor %}

13.6 Linux

sudo lsof -i -P -n | grep LISTEN sudo lsof -i:22 ## see a specific port such as 22 ##

14 Style

15 Jokes

  • There is so much spaghetti I might as well open a restaurant!
  • I echo all of my code as a string in the command line and then use ">" to redirect it in to a new file. I don't have time to open an edit
  • We draw exceptional people from exception

16 Metrics

16.1 Cyclomatic Complexity Number (CCN)

Counts the available decision paths in a source code to determine it's complexity. Each decision path starts with one of the conditional statements from the following list:

  • ?
  • &&
  • ||
  • or
  • and
  • xor
  • case
  • catch
  • elseif
  • for
  • foreach
  • if
  • while

Cyclomatic Complexity Number is never less than 1, because there’s always at least one code path.

  • 1-4 has low complexity.
  • 5-7 is moderate and still easy to understand.
  • 6-10 has a high complexity.
  • 10+ is very complex and hard to understand.
final class CyclomaticComplexityNumber
{
    // Class Cyclomatic Complexity = 1
}

final class CyclomaticComplexityNumber
{

    public function one()
    {
        // Function Cyclomatic Complexity = 1
    }

    public function two()
    {
        // Function Cyclomatic Complexity = 1
    }

    // Class Cyclomatic Complexity = 1
}

final class CyclomaticComplexityNumber
{

    public function one()
    {
        if(true){

        }
        // Function Cyclomatic Complexity = 2
    }

    public function two()
    {
        // Function Cyclomatic Complexity = 1
    }

    // Class Cyclomatic Complexity = 1 + 1 = 2
}

final class CyclomaticComplexityNumber
{

    public function one()
    {
        if(true){

        }
        // Function Cyclomatic Complexity = 2
    }

    public function two()
    {
        if(true){

        }
        // Function Cyclomatic Complexity = 2
    }

    // Class Cyclomatic Complexity = 1 + 1 + 1 = 3
}

16.2 NPath Complexity

function foo($a, $b)
{
    if ($a > 10) {
        echo 1;
    } else {
        echo 2;
    }
    if ($a > $b) {
        echo 3;
    } else {
        echo 4;
    }
}

So here we have function with 4 possible outcomes, since we have 2 statements that have 2 possible outcomes each (2 * 2 = 4). That means that the functions Npath complexity is 4. If we would add another statement with 2 possible outcomes we would get a complexity of 8 since 2 * 2 * 2 = 8.

17 Notepad

  1. Accurate predictions are an oxymoron in the real world. You can't predict accurately for things that aren't certain, and in almost all cases, developers won't know the systems they are dealing with fully enough. Moreover, they don't know their own personal efficiency from day to day; it just can't be foreseen accurately.
  2. A good property of software is the ability to change
  3. Anyone can take a chance at improving code, but refactoring brings a discipline

of safely making changes (with tests) and leveraging the knowledge accumulated by the software development community (through refactorings).

  • After all, like beauty, complexity is in the eye of the beholder.
  • Using new programming languages will bring you new insights and ideas to already known languages. You get a better understanding.
  • It's not about clever, crAFTY, PREETY CODE. iT'S ABOUT FAST AND EFFECTIVE COMMUNICATION. It's about communication beetween people.
  • Any time someone sees some code that isn't as clear as it should be, they should take the opportunity to fix it right there and then — or at least within a few minutes. This opportunistic refactoring is referred to by Uncle Bob as following the boy-scout rule — always leave the code behind in a better state than you found it.

17.1 Routing

from most specific at the top to less specific at the bottom

17.1.1 yml vs. annotations

It's always have been a war about this. But I would simply divide them in to two options.

Using annotations is quickier and kind of easier to use but it's increase noise in the code.

I think it's great for prototyping small application, when you absolutly sure that app would grow.

Annotations become a tottaly mess when you have big application, it's hard to manage all routing compare to central routing like yaml.

So I would go with word prototiping, Doctrine it's kind the same. It's great for prototyping but when you application finally reach maximum of what can you squize from ORM, you would have to leave it to pure SQL.

18 Data Structures

19 Functions Core

19.1 SPL Library

Great job on the implementation!

For larger training jobs, wondering if it's worthwhile to investigate implementing SplFixedArray in to the class? While I'm not sure of the PHP7 impacts, for PHP < 7 this resulted in a memory reduction of 144 bytes per array element to 56 bytes per array element.

If you were working with 50M data points total, this would reduce the memory requirements from 7.2GB to 2.8GB.

19.2 As opposed with isset(), propertyexists() returns TRUE even if the property has the value NULL.

19.3 isset() vs arraykeyexist

In simple terms, isset() checks whether a variable exists or its been declared or not. This can be anything, an array, a particular key in an array or a variable. It also checks if a declared variable, array or array key has null value, if it does, isset() returns false, it returns true otherwise.

On the other hand, arraykeyexists() checks whether a particular key or index exists in an array. It does not evaluate the value of the key for any null values. It returns false if it does not find the key in the array and true otherwise.

from the PHP manual: isset() does not return TRUE for array keys that correspond to a NULL value, while arraykeyexists() does

19.4 foreach vs arraymap

https://stackoverflow.com/questions/18144782/performance-of-foreach-array-map-with-lambda-and-array-map-with-static-function https://www.reddit.com/r/PHP/comments/65ihba/re_array_map_vs_foreach/

<?php

// test a simple array_map in the real world.
function test_array_map($data){
    return array_map(function($row){
        return array(
            'productId' => $row['id'] + 1,
            'productName' => $row['name'],
            'desc' => $row['remark']
        );
    }, $data);
}

// Another with local variable $i
function test_array_map_use_local($data){
    $i = 0;
    return array_map(function($row) use ($i) {
        $i++;
        return array(
            'productId' => $row['id'] + $i,
            'productName' => $row['name'],
            'desc' => $row['remark']
        );
    }, $data);
}

// test a simple foreach in the real world
function test_foreach($data){
    $result = array();
    foreach ($data as $row) {
        $tmp = array();
        $tmp['productId'] = $row['id'] + 1;
        $tmp['productName'] = $row['name'];
        $tmp['desc'] = $row['remark'];
        $result[] = $tmp;
    }
    return $result;
}

// Another with local variable $i
function test_foreach_use_local($data){
    $result = array();
    $i = 0;
    foreach ($data as $row) {
        $i++;
        $tmp = array();
        $tmp['productId'] = $row['id'] + $i;
        $tmp['productName'] = $row['name'];
        $tmp['desc'] = $row['remark'];
        $result[] = $tmp;
    }
    return $result;
}

$data = array_fill(0, 10000, array(
    'id' => 1,
    'name' => 'test',
    'remark' => 'ok'
));

$tests = array(
    'array_map' => array(),
    'foreach' => array(),
    'array_map_use_local' => array(),
    'foreach_use_local' => array(),
);

for ($i = 0; $i < 100; $i++){
    foreach ($tests as $testName => &$records) {
        $start = microtime(true);
        call_user_func("test_$testName", $data);
        $delta = microtime(true) - $start;
        $records[] = $delta;
    }
}

// output result:
foreach ($tests as $name => &$records) {
    printf('%.4f : %s '.PHP_EOL, 
           array_sum($records) / count($records), $name);
}

https://3v4l.org/lon7l#output

19.5 create an array

range(1,10);

19.6 foreach

foreach (range(1, 100) as $i)

19.7 array

19.7.1 flatten

public function flatten(array $array) {
    $return = array();
    array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
    return $return;
}

19.7.2 sort by value of a given key

//ascending
usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

//descending
usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

20 Links

21 Quirks

22 Topics

23 About

It's a simple site about refactoring in PHP.

You can contact me at slawomir.grochowski@gmail.com, https://twitter.com/s_grochowski, https://www.facebook.com/SlawomirGrochowski You can support this site: send some BitCoins 1D8xeRkxssTTLESfGZtPVoqVJDq7MJSqNx , send/buy one of the books I would like to read or just say Hello!

Author: slk500

Created: 2019-09-30 Mon 20:19

Validate