Раннее и позднее связывание

Раннее связывание

ClassA

namespace App;
class ClassA
{
  protected static string $name = 'A';
  
  public function getName() : string
  {
    return self::$name; // self - определяет класс к которому относится метод
                        // не наследуется (not extend)
  }
}

ClassB

namespace App;
class ClassB extends ClassA
{
  protected static string $name = 'B';
}

Связывание

echo \App\ClassA::getName() . PHP_EOL; //вывод А
echo \App\ClassB::getName() . PHP_EOL; //вывод А

// так происходит потому что статическое связывание происходит во время компиляции (до выполнения)

Позднее связывание

ClassA

namespace App;
class ClassA
{
  protected string $name = 'A';
  
  public function getName() : string
  {
    return $this->name; // this в отличии от self работает с "текущим" классом 
                        // наследуется и зависит от времени (порядка) выполнения
  }
}

ClassB

namespace App;
class ClassB extends ClassA
{
  protected string $name = 'B';
}

Связывание

require_once __DIR__ .'/../vendor/autoload.php';

$classA = new \App\ClassA();
$classB = new \App\ClassB();

echo $classA->getName() . PHP_EOL; //вывод А
echo $classB->getName() . PHP_EOL; //вывод B

//тут и происходит связывание основанное на времени выполнеия (порядке выполнения)

Позднее статическое связывание с переопределением

ClassA

namespace App;
class ClassA
{
  protected static string $name = 'A';
  
  public function getName() : string
  {
    return static::$name; 
  }
}

Связывание

echo \App\ClassA::getName() . PHP_EOL; //вывод А
echo \App\ClassB::getName() . PHP_EOL; //вывод B

//в данном случае static:: работает с учетом времени выполнения