BIG 4

Константы

class Transaction {
 private const STATUS_PAID = '1';
 public const STATUS_PENDING = '2';
 
 public function __construct(){
   echo Transaction::STATUS_PENDING;
   echo self::STATUS_PENDING; // self - текущий класс или вызываемый 
 }
}

/******/
//где то в другом месте кода

Transaction::STATUS_PENDING; // 1, вполне допустимо
// :: - оператор разрешения области видимости
// позволяет обращаться к константе, статическому свойству или статическому методу класса или одному из его родителей.

// если у нас уже есть экземпляр класса
$transaction = new Transaction();
$transaction::STATUS_PENDING; // 1, вполне себе вариант

$transaction::class; // выведет полное имя класса с namespace

Статические свойства и методы

// подходит для ситуаций где нам не нужен экземпляр класса, к примеру
// Format::dateToFormat(); но считается "такой себе" практикой
// либо для закрытия доступа к $this 

class Transaction {
  public static int $count = 0; // доступ как к константе. не привязаны к экземпляру
                                // работает глобально
  public float $amount = 0;
                          
  __construct(){
    self::$count++;  // будет работать при создании нового экземпляра и сохранять значение
  }
  
  public static function getCount(): int {
  
  echo $this->amount; // за это бан, в статических методах нельзя привязываться к экземплярам
   
  return self::$count;
  }
}

/****/

echo Transaction::$count; // только для статических свойств

echo Transaction::$amount; // за это бан, не статическое свойство же

Инкапсуляция

//объединение данных и методов в один объект, например в класс
//скрывая его части, защищая его целостность
//гарантирует что объект управляет своим состоянием и никто не сможет его изменить
//без соответствующего разрешения

//get и set не особо к инкапсуляции относится, да и вообще фигня
// самое важное, не давать возможностей менять то что должно быть закрытым и стабильным

Наследование

// наследуются public, protected, methods, properties, constants
// extend - скорее расширение класса чем "наследование"

// private не наследуется и не может быть переопределено
// нельзя понижать видимость, к примеру public -> private / за это бан

class Toaster {
  public array $slices = [];
  public int $size = 2;

  public function addSlice(string $slice): void {
    if (count($this->slices) <  $this->size){
      $this->slices[] = $slice;
    }
  }
  
  public function toast(){
    foreach($this->slices as $i => $slice){
      echo ($i + 1) . ': Toasting ' . $slice . PHP_OEL;
    }
  }

}

class ToasterPro extends Toaster { // расширение класса Toaster 
  //ToasterPro наследует все что есть в Toaster (открытая часть)
  public int $size = 4;
  
  public function toastBagel(){
    foreach($this->slices as $i => $slice){
      echo ($i + 1) . ': Toasting ' . $slice . ' with bagels option' . PHP_OEL;
    }
  }
  
}

/****/
$toaster = new Toaster();

$toaster->addSlice('bread');
$toaster->addSlice('bread');
$toaster->addSlice('bread');
$toaster->toast();
/* 
1: Toasting bread
2: Toasting bread
влезло только 2, вышли за лимит
 */
 
/****/
$toaster = new ToasterPro();
$toaster->addSlice('bread');
$toaster->addSlice('bread');
$toaster->addSlice('bread');
$toaster->toastBagel();

/*
1: Toasting bread with bagels option
2: Toasting bread with bagels option
3: Toasting bread with bagels option
*/

__construct в наследовании

class Toast {

  protected array $slices;
  protected int $size;
  
  public function __construct(){ // к слову можно и без него
    $this->slices = [];
    $this->size = 2;
  }

}

class ToasterPro {
  public function __construct(){
    parent::__construct(); //и только так
                           //иначе бан, т.к. родительский __construct не вызывается
                           //при создании экземпляра потомка
                           // и по этому свойства родителя могут быть недоступны
    
    $this->size = 4;
  }
  
  //метод должен полностью совпадать с родительским, и типы и атрибуты и возвращаемое значение
  public function addSlice(){
    parent::addSlice(); // на случаей если мы переопределяем родительский метод
                        // и при этом хоти чтобы он все же сработал
                        // без parent будет работать только этот метод
  }
}

Ограничения наследования

final class Toaster{
  // этот класс не может быть расширен и extends Toaster в бан
  
  final public function addSlice(){
    // этот метод нельзя переопределять в дочерних классах
  }
}

Абстрактные классы и методы

//как понятие, ближе к инкапсуляции
//основа: внутренние детали реализации не важны для реализации задачи
// и не должны быть видны в том же месте

//иными словами следует делать так чтобы внутренная реализация методов не мешала их использованию
//и изменения внутренней работы не сказывались на внешнем коде

// абстрактные классы нельзя создать, можно только наследовать
// не должны содержать реализацию методов (знает что сделать но не знает как)
namespace App;

abstract class Field {
  public function __construct(protected string $name){
  
  }
  
  abstract public function render(): string; // только сигнатура, без реализации
  //теперь все наследуемые классы должны реализовывать метод render
}

class Text extends Field {
  public function render(): string {
    return <<<HTML
    <input type="text" name="{$this->name}" />
    HTML;
  }
}

abstract class Boolean extends Field {
//в наследуемых абстрактных классах не обязательно
}

class Checkbox extends Boolean {
  public function render(): string {
    return <<<HTML
    <input type="checkbox" name="{$this->name}" />
    HTML;
  }
}

class Radio extends Boolean {
  public function render(): string {
    return <<<HTML
    <input type="radio" name="{$this->name}" />
    HTML;
  }
}

// вот теперь поабстрагируем

$fields = [
  // new \App\Field('baseField'), За это бан, он жеж абстрактный
  new \App\Text('textField'),
  // new \App\Boolean('booleanField'), так ведь и за это бан
  new \App\Checkbox('checkboxField'),
  new \App\Radio('radioField'),
];

foreach($fields as $field) {
  echo $field->render(). "<br />"; // 
}

Полиморфизм

// душный вариант
//это способность обьекта использовать методы производного класса, который не существует на момент создания базового. 

//подробнее в следующем разделе