Відчужений static php p. Статичні члени класу. Не дай їм занапастити твій код. Хто любить статичні методи

Reg.ru: домени та хостинг

Найбільший реєстратор та хостинг-провайдер у Росії.

Понад 2 мільйони доменних імен на обслуговуванні.

Просування, пошта для домену, рішення для бізнесу.

Понад 700 тис. клієнтів у всьому світі вже зробили свій вибір.

*Наведіть курсор миші, щоб зупинити прокручування.

Назад вперед

Статичні методи та властивості в PHP

У попередніх матеріалах ми освоїли основні можливості об'єктно-орієнтованого програмування в PHP і зараз переходимо до вивчення більш складних та цікавих аспектів.

До того ми завжди працювали з об'єктами. Ми охарактеризували класи як шаблони, з допомогою яких створюються об'єкти, а об'єкти - як активні компоненти, методи ми викликаємо і властивостям яких отримуємо доступ.

Звідси випливав, що в об'єктно-орієнтованому програмуванні реальна роботавиконується за допомогою екземплярів класів. А класи зрештою - це просто шаблони для створення об'єктів.

Але насправді не все так просто. Ми можемо отримувати доступ і методам, і властивостям у тих класу, а чи не об'єкта. Такі методи та властивості називаються "статичними" і повинні бути оголошені за допомогою ключового слова static.

Class StaticExample ( static public $aNum = 0; static public function sayHello() ( print "Привіт!"; ) )

Статичні методи- це функції, які у контексті класу. Вони самі не можуть отримувати доступ до якихось звичайних властивостей класу, тому що такі властивості належать об'єктам.

Однак із статичних методів, як ви вже напевно здогадалися, можна звертатись до статичних властивостей. І якщо ви зміните статичну властивість, всі екземпляри цього класу зможуть отримати доступ до нового значення.

Оскільки доступ до статичного елемента здійснюється через клас, а чи не через екземпляр об'єкта, нам не потрібна змінна, яка посилається на об'єкт. Натомість використовується ім'я класу, після якого вказується дві двокрапки "::".

Print StaticExample::$aNum; StaticExample::sayHello();

З цим синтаксисом ви вже повинні бути знайомі з основ ООП в PHP. Ми використовували конструкцію "::" у поєднанні з ключовим словом parentщоб отримати доступ до перевизначеного методу батьківського класу.

Зараз, як і тоді, ми звертаємось до класу, а не до даних, які містяться в об'єкті. У коді класу можна використовувати ключове слово parentщоб отримати доступ до суперкласу, не використовуючи ім'я класу.

Щоб отримати доступ до статичного методу або властивості з того самого класу (а не з дочірнього класу), ми будемо використовувати ключове слово self.

Ключове слово selfвикористовується для звернення до поточного класу, а псевдозмінна $this- До поточного об'єкта. Тому з-за меж класу StaticExampleми звертаємось до якості $aNumза допомогою імені його класу.

StaticExample::$aNum;

А всередині класу StaticExampleможна використовувати ключове слово self.

Class StaticExample ( static public $aNum = 0; static public function sayHello() ( self::$aNum++; print "Привіт! (" . self::$aNum . ")\n"; ) )

Зверніть увагу:окрім випадків звернення до перевизначеного методу батьківського класу, конструкція "::" повинна завжди використовуватися лише для доступу до статичних методів чи властивостей.

За визначенням статичні методи не викликаються у тих об'єкта. З цієї причини статичні методи та властивості часто називають змінними та властивостями класу. Як наслідок, не можна використовувати псевдозмінну $thisусередині статичного методу.

А навіщо взагалі використати статичний метод чи властивість?

Ось ми й дійшли до найважливішого питання. Справа в тому, що статичні елементи мають ряд корисних характеристик.

По перше, вони доступні з будь-якої точки сценарію (за умови, що у вас є доступ до класу). Це означає, що ви можете звертатися до функцій не передаючи екземпляр класу від одного об'єкта іншому або, що ще гірше, зберігаючи екземпляр об'єкта у глобальній змінній.

По-другеСтатична властивість доступна кожному екземпляру об'єкта цього класу. Тому можна визначити значення, які мають бути доступні для всіх об'єктів даного типу.

І наприкінці, по-третєСам факт, що не потрібно мати екземпляр класу для доступу до його статичної властивості або методу, дозволить уникнути створення екземплярів об'єктів виключно заради виклику простої функції.

Щоб продемонструвати це, давайте створимо статичний метод для класу ShopProduct, який автоматично створюватиме екземпляри об'єктів ShopProduct. За допомогою SQLite визначимо таблицю productsнаступним чином:

CREATE TABLE products (ID INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, 1-й TEXT, mainname TEXT, title TEXT, price float, numpages int, playlength int, discount int)

Тепер створимо метод getInstance(), якому передається ідентифікатор рядка та об'єкт типу PDO. Вони будуть використовуватися для вилучення рядка з таблиці бази даних, на підставі якої формується об'єкт типу ShopProduct, що повертається в програму, що викликає.

Ми можемо додати ці методи до класу ShopProduct, який був створений нам у ранніх матеріалах. Як ви, напевно, знаєте, PDO розшифровується як PHP Data Object (об'єкти даних PHP). Клас PDO забезпечує універсальний інтерфейс для різних додатківбаз даних.

// Клас ShopProduct private $id=0; public function setID($id) ( $this->id = $id; ) // ... public static function getInstance($id, PDO $pdo) ( $stmt = $pdo->prepare("select * from products where id=?"); $result = $stmt->execute(array($id)); $row = $stmt->fetch(); if (empty($row)) ( return null; ) if ($ row["type"] == "book") ( $product = new BookProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"] , $row["numpages"]); ) else if ($row["type"] == "cd") ( $product = new CdProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"], $row["playlength"]); ) else ( $product = new ShopProduct($row["title"], $row["firstname"], $row["mainname"], $row["price"]); ) $product->setId($row["id"]); $product->setDiscount($row["discount"]); return $ product;) // ...

Як бачите, метод getInstance()повертає об'єкт типу ShopProduct, причому він достатньо "розумний" для того, щоб на підставі значення поля typeстворювати об'єкт із необхідними характеристиками.

Я спеціально опустив код обробки помилок, щоб приклад був наскільки можна лаконічним. Наприклад, у реально працюючій версії цього коду нам не можна бути надто довірливими і припускати, що переданий PDO-об'єкт був коректно проініціалізований і підключений до необхідної бази даних.

Насправді нам, мабуть, слід укласти PDO-об'єкт у клас, який гарантує таку поведінку. До цього питання ми ще повернемося до одного з майбутніх матеріалів.

Метод getInstance()корисніший у контексті класу, ніж у контексті об'єкта. Він дозволяє легко перетворити дані, що знаходяться в базі даних, на об'єкт, причому для цього нам не потрібно мати окремий екземпляр об'єкта типу ShopProduct.

У цьому методі не використовуються жодні методи або властивості, що вимагають окремого екземпляра об'єкта, тому немає жодної причини, щоб не оголосити статичним. Тоді, маючи коректний PDO-об'єкт, ми можемо викликати даний методз будь-якого місця застосування.

$dsn = "sqlite://home/bob/projects/products.db"; $ pdo = new PDO ($ dsn, null, null); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $obj = ShopProduct::getInstance(1, $pdo);

Подібні методи працюють як "фабрики", оскільки вони беруть "сирі" матеріали (наприклад, дані, отримані з рядка бази даних або конфігураційного файлу) і використовують їх для створення об'єктів.

Термін "фабрика" відноситься до коду, призначеного для створення екземплярів об'єктів. З прикладами таких "фабрик" ми ще зустрінемося з вами далі.


Постійні властивості

Деякі властивості не повинні змінюватись. Наприклад, такі елементи, як коди помилок або коди стану програми, зазвичай задаються вручну в класах. Хоча вони повинні бути загальнодоступними та статичними, код клієнта не повинен мати можливість змінювати їх.

І тому можна визначити постійні властивості всередині класу. Як глобальні константи, константи класу не можна змінювати після того, як вони були визначені. Постійну властивість оголошують за допомогою ключового слова const.

На відміну від звичайних властивостей перед ім'ям постійної якості не ставиться знак долара. За прийнятою угодою їм часто призначають імена, що складаються лише з великих літер, як у наступному прикладі:

Class ShopProduct (const AVAILABLE = 0; const OUT_OF_STOCK = 1; // ...

Постійні властивості можуть містити лише значення, що стосуються елементарного типу. Константі не можна присвоїти об'єкт.

Як і статичним властивостям, доступом до постійним властивостям здійснюється через клас, а чи не через екземпляр об'єкта. Подібно до того, як константа визначається без знака долара, при зверненні до неї також не потрібно використовувати ніякий символ попереду.

Print ShopProduct::AVAILABLE;

Спроба надати константі значення після того, як вона була оголошена, призведе до помилки на етапі синтаксичного аналізу.

Константи слід використовувати, коли властивість має бути доступним всім екземплярів класу і коли значення властивості має бути фіксованим і незмінним.

На цьому цю статтю я завершую, а в наступному йдеться про .

Сподобався матеріал і хочете віддячити?
Просто поділіться з друзями та колегами!


Дуже важливою особливістю ООПє наявність статичних властивостей та методів. Головне, що необхідно одразу зрозуміти, що такі властивості та методи належать не об'єкту, а класу. Це треба зрозуміти з самого початку, а ось застосування статичних властивостей та методів у PHPя розгляну у цій статті.

Найкласичніший приклад - це клас, який відповідає за математичні функції. Якщо хтось знає Java, то він знає, що там є клас MathJavaScriptтакий клас теж є), що містить безліч математичних функцій. І там методи статичні. Це означає, що для того, щоб порахувати якийсь синус або експоненту, не потрібно створювати об'єкт даного класу, що дуже зручно.

Давайте ми з Вами напишемо зменшену копію цього класу, але тільки для PHP:

class Math (
private static $ count = 0;
public function __construct() (
self::$count++;
}
public static function calcSin($x) (
return sin($x);
}
public static function calcSQRT($x) (
return sqrt($x);
}
public static function getCount() (
return self::$count;
}
}
echo Math::calcSin(1);
echo "
";
echo Math::calcSQRT(9);
echo "
";
$ Math = New Math ();
$ math_2 = новий Math ();
echo Math::getCount();
?>

У цьому коді я показав використання статичних методів та властивостей. Зверніть увагу, що я реалізував класичний спосіб лічильника об'єктів. Це вийшло лише завдяки тому, що поле countє статичним, і вона має те саме значення всім об'єктів.

Ще один популярний приклад використання статичних методів та властивостей– це ведення лога. Усі записи додаються через статичні методи. Також дуже часто роблять клас, що складається з безлічі налаштувань, і там також всі поля статичні. Як бачите, прикладів використання статичних методів та властивостей у PHPта інших мовах більш ніж достатньо, тому вміти з ними працювати треба обов'язково.

(Late Static Binding, LSB) є бурхливо темою обговорень останні три роки у колах розробників PHP (і нарешті ми його отримали у PHP 5.3). Але навіщо воно потрібне? У цій статті якраз і розглядатиметься, як пізніше статичне зв'язуванняможе спростити ваш код.

На зустрічі розробників PHP, що проходила в Парижі у листопаді 2005 року, тема пізнього статичного зв'язування офіційно обговорювалася основною командою розробників. Вони погодилися реалізувати його, поряд з багатьма іншими темами, що стояли на порядку денному. Деталі мали бути узгоджені під час відкритих дискусій.

З тих пір як пізніше статичне зв'язуваннябуло оголошено як майбутня фішка, минуло два роки. І ось нарешті LSB стало доступним для використання в PHP 5.3. Але ця подія пройшла непомітно для розробників, які використовують PHP, з нотаток тільки сторінка в мануалі.

Якщо коротко, нова функціональність пізнього статичного зв'язування, дозволяє об'єктам все також успадковувати методи у батьківських класів, але крім цього дає можливість успадкованим методам мати доступ до статичних константів, методів та властивостей класу нащадка, а не лише батьківського класу. Давайте розглянемо приклад:

Class Beer ( const NAME = "Beer!"; public function getName() ( return self::NAME; ) ) class Ale extends Beer ( const NAME = "Ale!"; ) $beerDrink = new Beer; $aleDrink = new Ale; echo "Beer is:". $beerDrink->getName() ."\n"; echo "Ale is:". $aleDrink->getName() ."\n";

Цей код видасть такий результат:

Beer is: Beer! Ale is: Beer!

Клас Aleуспадкував метод getName(), але при цьому selfвсе ще вказує на клас, в якому воно використовується (у даному випадкуце клас Beer). Це залишилося і в PHP 5.3, але додалося слово static. І знову розглянемо приклад:

Class Beer ( const NAME = "Beer!"; public function getName() ( return self::NAME; ) public function getStaticName() ( return static::NAME; ) ) class Ale extends Beer ( const NAME = "Ale!" ;) $beerDrink = new Beer; $aleDrink = new Ale; echo "Beer is:". $beerDrink->getName() ."\n"; echo "Ale is:". $aleDrink->getName() ."\n"; echo "Beer is actually: " . $beerDrink->getStaticName() ."\n"; echo "Ale is actually: " . $aleDrink->getStaticName() ."\n";

Нове ключове слово staticвказує, що необхідно використовувати константу успадкованого класу, замість константи, яка була визначена в класі, де оголошений метод getStaticName(). Слово staticбуло додано, щоб реалізувати новий функціонал, а для зворотної сумісності selfпрацює також як і в попередніх версіях PHP.

Внутрішньо, основна відмінність (і, власне, причина чому зв'язування назвали пізнім) між цими двома способами доступу, в тому, що PHP визначать значення для self::NAMEпід час «компіляції» (коли символи PHP перетворюються на машинний код, який оброблятиметься двигуном Zend), а для static::NAMEзначення буде визначено в момент запуску (у той момент, коли машинний код буде виконуватись у двигуні Zend).

Це ще один інструмент для PHP-розробників. У другій частині розглянемо як його можна використати на благо.

У PHP можна визначити метод як статичний. Статичний метод немає доступу до властивостей об'єкта. Такі методи можуть бути викликані лише у контексті класу, але не контексті об'єкта.

Найважливіше що треба зрозуміти – статичні властивості та методи належать класам, а не об'єктам.

На прикладі стане одразу зрозуміло. Давайте створимо об'єкт Math (скорочене назвою математики англійською).

Статичні методи PHP
$math_1 = new Math(); $math_2 = new Math(); $math_3 = new Math(); $math_4 = new Math(); echo "Створено об'єктів: " . Math::getCount(); ?>

Цей клас надає інструменти для роботи з математичними функціями без створення об'єкта. У класі є конструктор, який збільшує статичну властивість $count на одиницю. Клас запам'ятовує значення цієї якості, оскільки воно статичне.

До речі, для оголошення методу чи властивості статичним використовується слово static, а для доступу до статичної властивості використовується слово self з подвійною двокрапкою "::".

Все це краще зрозуміти порівняно, особливо порівняно робочого прикладу з помилковим. Давайте трохи розширимо наш приклад.

Статичні методи PHP counter++; ) public static function calcSin($x) ( return sin($x); ) public static function calcSQRT($x) ( return sqrt($x); ) public static function getCount() ( return self::$count; ) public function getCounter() ( return $this->counter; ) ) echo Math::calcSin(1); echo "
"; echo Math::calcSQRT(9); echo "
$math_1 = новий Math(); $math_2 = новий Math(); $math_3 = новий Math(); $math_4 = новий Math(); echo "Створено об'єктів: " . Math::getCount(); echo "
"; echo "Створено об'єктів: " . $math_4->getCounter(); ?>

У цьому прикладі ми додали класу звичайну властивість $counter, вона також збільшувалася на одиницю в конструкторі. Але звичайна властивість належить об'єкту, тому вона зберігається між викликами об'єктів. При будь-якому створенні екземпляра класу (об'єкта) властивість дорівнює нулю, в конструкторі воно буде збільшено на одиницю.

Статична властивість належить класу, тому його значення зберігається.

Нижче ще кілька прикладів, які розкривають роботу статичних властивостей та методів.

Спроба використовувати в статичному методі змінну $this призведе до помилки (Fatal error: Using $this when not in object context).

Статичні методи PHP age. "old."; // це помилка "Using $this when not in object context". ) ) $ TestClass = new TestClass (); $TestClass->sayHello(); ?>

До речі, без рядка:

$TestClass->sayHello();

помилки не буде, але як тільки ви в коді спробуєте запустити статичний метод зі змінною $this, ви відразу отримаєте повідомлення про помилку.

Якщо в цьому прикладі забрати слово static, то помилки не буде.

Якщо зі статичного методу звернутися до властивості об'єкта, це призведе до помилки. Можна звертатися лише до статичних властивостей за допомогою конструкції self::$age. Зверніть увагу, що є знак $ перед ім'ям змінної, на відміну конструкції $this->age .

Статичні методи PHP sayHello(); ?>

Якщо у цьому прикладі прибрати слово static перед ім'ям властивості, виникне помилка "Access to undeclared static property".

Статичні характеристики відсутні в об'єктах класу.

Статичні методи PHP "; print_r($TestClass); echo ""; ?>

Статичний метод можна викликати, використовуючи конструкцію self::метод() . Приклад:

Статичні методи PHP printHello(); ?>

Статичну властивість можна отримати в контексті класу, використовуючи синтаксис:

echo TestClass::$age;

Причому спроба звернення до звичайної властивості таким чином призведе до помилки: "Fatal error: Access to undeclared static property".

Статичну властивість можна змінювати в контексті класу, використовуючи синтаксис:

TestClass::$age += 20; // наприклад

Ще приклад коду зі статичними методами та властивостями

У цьому прикладі ще прості варіантивикористання статичних методів та властивостей. Чим більше простого кодуви зрозумієте, тим краще запам'ятайте матеріал.

Статичні методи PHP ".TestClass::$age; // echo TestClass::$txt; // Помилка: Fatal error: Access to undeclared static property. echo "
"; TestClass::sayHi(); echo "
"; TestClass::sayHello(); // А ось так мені вдалося отримати доступ до статичної змінної через об'єкт ($obj::$age)... echo "
"; $obj = new TestClass; echo "Отримуємо поступ до статичної змінної через об'єкт: " . $obj::$age; ?>

Зверніть увагу, і це важливо, у цьому прикладі ми звернулися до нестатичного методу sayHi(), використовуючи синтаксис звернення до статичних елементів класу.

Резюме

  • Головне: статичні властивості належать класам, а чи не об'єктам.
  • Зі статичного методу не можна звернутися до звичайних властивостей і методів класу, $this->name не працює тут.
  • Зі статичного методу можна звернутися до статичних властивостей використовуючи self::$name .
  • Статичні характеристики класу не доступні об'єктам.
  • Звичайний метод може звернутися до статичної властивості, використовуючи self::$name .
  • Статичну властивість можна отримати в контексті класу, використовуючи синтаксис: TestClass::$age .
  • Звичайний метод можна викликати в контексті об'єкта ($object->метод()), і класу використовуючи синтаксис TestClass::метод() .
  • За допомогою синтаксису $object::$age мені вдалося отримати доступ до статичної властивості через об'єкт.

Паралелі з JavaScript

JavaScript має такий клас Math, що містить дуже багато різних математичних функцій.

Для проведення математичних обчислень (розрахунок синуса або експоненти) JavaScript не потрібно створювати об'єкт класу Math, так як його методи є статичними. До вивчення PHP я ніяк не міг зрозуміти, що це таке, і тільки вивчивши класи та об'єкти в PHP у мене в голові все стало на свої полички.

Насправді це дуже зручно мати прямий доступ до математичних методів класу Math, уникаючи створення об'єкта.

Не секрет, що на співбесідах люблять ставити підступні питання. Не завжди адекватні, які завжди мають відношення до реальності, але факт залишається фактом - задають. Звичайно, питання розбіжності, і іноді питання, на перший погляд здавалося вам безглуздим, насправді спрямований на перевірку того, наскільки добре ви знаєте мову, якою пишете.

Спробуємо розібрати «по кісточках» одне з таких питань. що означає слово "static" у PHP і навіщо воно застосовується?

Ключове слово static має у PHP три різних значення. Розберемо їх у хронологічному порядку, як вони з'являлися у мові.

Значення перше – статична локальна змінна

function foo() ( $a = 0; echo $a; $a = $a + 1; ) foo(); // 0 foo(); // 0 foo(); // 0

У PHP змінні локальні. Це означає, що змінна, певна і що отримала значення всередині функції (методу), існує лише під час виконання цієї функції (методу). При виході з методу локальна змінна знищується, а при повторному вході створюється заново. У коді вище такої локальної змінної є змінна $a - вона існує тільки всередині функції foo() і щоразу виклик цієї функції створюється заново. Інкремент змінної в цьому коді безглуздий, оскільки на наступному рядку коду функція закінчить свою роботу і значення змінної буде втрачено. Скільки б разів ми не викликали функцію foo(), вона завжди виводитиме 0…

Однак все змінюється, якщо ми перед привласненням поставимо ключове слово:

Function foo() ( static $a = 0; echo $a; $a = $a + 1; ) foo(); // 0 foo(); // 1 foo(); // 2

Ключове слово static, написане перед наданням значення локальної змінної, призводить до наступних ефектів:

  1. Привласнення виконується лише один раз, при першому виклику функції
  2. Значення позначеної таким чином змінної зберігається після закінчення функції
  3. При наступних викликах функції замість привласнення змінна отримує збережене раніше значення
Таке використання слова static називається статична локальна змінна.
Підводне каміння статичних змінних
Зрозуміло, як завжди в PHP, не обходиться без «підводного каміння».

Камінь перший – статичній змінній присвоювати можна лише константи чи константні вирази.Ось такий код:
static $a = bar();
з неминучістю призведе до помилки парсера. На щастя, починаючи з версії 5.6, стало допустимим присвоєння не тільки констант, а й константних виразів (наприклад - «1+2» або ""), тобто таких виразів, які не залежать від іншого коду і можуть бути обчислені на етапі компіляції

Камінь другий – методи існують в єдиному екземплярі.
Тут все трохи складніше. Для розуміння суті наведу код:
class A ( public function foo() ( static $x = 0; echo ++$x; ) ) $a1 = new A; $a2 = new A; $a1->foo(); // 1 $a2->foo(); // 2 $a1->foo(); // 3 $a2->foo(); // 4
Всупереч інтуїтивному очікуванню «різні об'єкти - різні методиМи наочно бачимо на цьому прикладі, що динамічні методи в PHP «не розмножуються». Навіть якщо у нас буде сто об'єктів цього класу, метод існуватиме лише в одному екземплярі, просто при кожному виклику в нього буде прокидатися різний $this.

Така поведінка може бути несподіваною для непідготовленого до неї розробника і стати джерелом помилок. Слід зазначити, що успадкування класу (і методу) призводить до того, що створюється новий метод:

Class A ( public function foo() ( static $x = 0; echo ++$x; ) ) Class B extends A ( ) $a1 = new A; $ b1 = new B; $a1->foo(); // 1 $ b1-> foo (); // 1 $a1->foo(); // 2 $b1->foo(); // 2

Висновок: динамічні методи у PHP існують у контексті класів, а не об'єктів. І тільки в рантаймі відбувається підстановка "$this = поточний об'єкт"

Значення друге - статичні властивості та методи класів

В об'єктній моделі PHP існує можливість задавати властивості та методи не тільки для об'єктів – екземплярів класу, але й для класу загалом. Для цього теж є ключове слово static:

Class A ( public static $x = "foo"; public static function test() ( return 42; ) ) echo A::$x; // "foo" echo A:: test (); // 42
Для доступу до таких властивостей та методів використовуються конструкції з подвійною двокрапкою («Paamayim Nekudotayim»), такі як ІМ'Я_КЛАСУ::$ім'яЗмінною та ІМ'Я_КЛАСУ:: ім'яМетоду().

Само собою зрозуміло, що у статичних властивостей і статичних методів є свої особливості та свої «підводні камені», які потрібно знати.

Особливість перша, банальна - немає $this.Власне це походить із самого визначення статичного методу - оскільки він пов'язаний з класом, а не об'єктом, в ньому недоступна псевдозмінна $this, що вказує в динамічних методах на поточний об'єкт. Що цілком логічно.

Однак, потрібно знати, що на відміну від інших мов, PHP не визначає ситуацію "в статичному методі написано $this" на етапі парсингу або компіляції. Подібна помилка може виникнути лише в рантаймі, якщо ви спробуєте виконати код із $this усередині статичного методу.

Код типу такого:
class A ( public $id = 42; static public function foo() ( echo $this->id; ) )
не призведе до жодних помилок, доки ви не спробуєте використовувати метод foo() неналежним чином:
$a = new A; $a->foo(); (і відразу отримаєте "Fatal error: Using $this when not in object context")

Особливість друга – static не аксіома!
class A ( static public function foo() ( echo 42; ) ) $a = new A; $a->foo();
Так, так. Статичний метод, якщо він містить у коді $this, можна викликати у динамічному контексті, як метод об'єкта. Це не помилка в PHP.

Зворотне не зовсім вірно:
class A ( public function foo() ( echo 42; ) ) A::foo();
Динамічний метод, який не використовує $this, можна виконувати у статичному контексті. Однак ви отримаєте попередження "No-static method A::foo() should not be called statically" рівня E_STRICT. Тут вирішувати вам - або суворо дотримуватися стандартів коду, або придушувати попередження. Перше, зрозуміло, краще.

І до речі, все написане вище стосується лише методів. Використання статичної властивості через "->" неможливе і веде до фатальної помилки.

Значення третє, що здається найскладнішим – пізнє статичне зв'язування

Розробники мови PHP не зупинилися на двох значеннях ключового слова "static" і у версії 5.3 додали ще одну "фічу" мови, яка реалізована тим самим словом! Вона називається "пізніше статичне зв'язування" або LSB (Late Static Binding).

Зрозуміти суть LSB найпростіше на нескладних прикладах:

Class Model ( public static $table = "table"; public static function getTable() ( return self::$table; ) ) echo Model::getTable(); // "table"
Ключове слово self у PHP завжди означає «ім'я класу, де це слово написано». У цьому випадку self замінюється на клас Model, а self::$table - на Model::$table.
Така мовна можливість називається раннім статичним зв'язуванням. Чому раннім? Тому що зв'язування self та конкретного імені класу відбувається не в рантаймі, а на більш ранніх етапах – парсингу та компіляції коду. Ну а «статичне» - тому що йдеться про статичні властивості та методи.

Трохи змінимо наш код:

Class Model ( public static $table = "table"; public static function getTable() ( return self::$table; ) ) class User extends Model ( public static $table = "users"; ) echo User::getTable() ; // "table"

Тепер ви розумієте, чому PHP поводиться в цій ситуації неінтуїтивно. self був пов'язаний із класом Model тоді, коли про клас User ще нічого не було відомо, тому і вказує на Model.

Як бути?

Для вирішення цієї дилеми було придумано механізм зв'язування «пізнього», на етапі рантайму. Працює він дуже просто - достатньо замість слова "self" написати "static" і зв'язок буде встановлений з тим класом, який викликає цей код, а не з тим, де він написаний:
class Model ( public static $table = "table"; public static function getTable() ( return static::$table; ) ) class User extends Model ( public static $table = "users"; ) echo User::getTable() ; // "users"

Це і є загадкове «пізнє статичне зв'язування».

Потрібно зазначити, що для більшої зручності в PHP крім слова «static» є ще спеціальна функція get_called_class(), яка повідомить вам - у контексті якого класу Наразіпрацює ваш код.

Вдалих співбесід!