Бескорыстный static php p. Статические методы PHP

Mar 23, 2010 dec5e

В PHP 5.3 появилась такая интересная возможность, как позднее статическое связывание (late static binding). Дальше немного вольный перевод описания из официального мануала .

Начиная с PHP 5.3.0 в языке реализована возможность, называемая поздним статическим связыванием, которая может использоваться для ссылки на вызываемый класс в контексте статического наследования.

Эту возможность назвали «позднее статическое связывание». «Позднее связывание» говорит о том, что static:: будет разрешаться не относительно класса, где определен метод, но будет вычисляться во время выполнения. «Статическое связывание» означает, что оно может быть использовано в вызовах статических методов (но не ограничивается только ими).

Ограничения self::

Пример № 1: использование self::

Пример выведет:

Использование позднего статического связывания

Позднее статическое связывание пытается решить это ограничение, вводя ключевое слово, ссылающееся на класс, первоначально вызванный в процессе выполнения. То есть, ключевое слово, которое позволит сослаться на B из test() в предыдущем примере. Было решено не вводить новое слово, а использовать уже зарезервированное static .

Пример № 2: простое использование static::

Пример выведет:

Замечание: static:: не работает как $this для статических методов! $this-> следует правилам наследования, а static:: нет. Это различие уточняется ниже.

Пример № 3: использование static:: в нестатическом контексте

test(); ?>

Пример выведет:

Замечание: Позднее статическое связывание останавливает процесс разрешения вызова. Статические вызовы с использованием ключевых слов parent:: или self:: передают информацию о вызове дальше.

Пример № 4: Передача и непередача вызовов

Пример выведет

Крайние случаи

Существует множество различных способов вызвать метод в PHP, такие как коллбэки или магически методы. Поскольку позднее статическое связывание разрешается во время выполнения, это может привести к неожиданным результатам в так называемых крайних случаях.

Пример № 5 Позднее статическое связывание в магических методах

foo; ?>

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 . C помощью SQLite определим таблицу products следующим образом:

CREATE TABLE products (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, firstname 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;

Попытка присвоить константе значение после того, как она была объявлена, приведет к ошибке на этапе синтаксического анализа..

Константы следует использовать, когда свойство должно быть доступным для всех экземпляров класса и когда значение свойства должно быть фиксированным и неизменным.

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

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


  • Tutorial

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

Попробуем разобрать «по косточкам» один из таких вопросов - что значит слово «static» в PHP и зачем оно применяется?

Ключевое слово static имеет в PHP три различных значения. Разберем их в хронологическом порядке, как они появлялись в языке.

Значение первое - статическая локальная переменная

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

В PHP переменные локальны. Это значит, что переменная, определенная и получившая значение внутри функции (метода), существует только во время выполнения этой функции (метода). При выходе из метода локальная переменная уничтожается, а при повторном входе - создается заново. В коде выше такой локальной переменной является переменная $a - она существует только внутри функции foo() и каждый раз при вызове этой функции создается заново. Инкремент переменной в этом коде бессмысленен, поскольку на следующей же строчке кода функция закончит свою работу и значение переменной будет потеряно. Сколько бы раз мы не вызвали функцию foo(), она всегда будет выводить 0…

Однако всё меняется, если мы перед присваиванием поставим ключевое слово static:

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, можно выполнять в статическом контексте. Однако вы получите предупреждение «Non-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(), которая сообщит вам - в контексте какого класса в данный момент работает ваш код.

Удачных собеседований!



В мире существуют две разновидности PHP-разработчиков. Одни предпочитают статические методы, потому что с ними легко работать, другие же, напротив, считают статические методы - зло и не используют их в своей практике.
В этой статье попробую, используя опыт работы с несколькими фреймворками, объяснить, почему некоторые разработчики игнорируют лучшие практики и используют целую кучу статических методов.

Кто любит статические методы?

Особенно часто их применяют разработчики, которые когда-либо использовали в своей работе фреймворк CodeIgniter .

Также, в число последователей статистических методов входят большинство Kohana- и Laravel-разработчиков.
Тут нельзя не упомянуть тот факт, что программисты, которые решают начать писать собственные вещи, обычно отказываются от использования CodeIgniter.

Почему, спросите вы?

CodeIgniter поддерживал PHP 4 до того, как статические методы были добавлены в PHP 5. Кроме того, CodeIgniter использует «супер-объект», который предоставляет равный доступ всем классам, назначенным контроллеру. Таким образом они становится доступными к использованию в рамках всей системы.

Это означает, что классы могут быть доступны из любой модели с помощью метода __get(), который будет искать запрашиваемое свойство с помощью get_instance()->{$var} . Ранее, когда поддержки функции __get() не было в PHP 4, для этого использовалась конструкция foreach через параметры CI_Controller, а затем они назначались переменной $this в модели.

В библиотеке вы должны вызвать get_instance. Библиотека не наследует класс в принудительном порядке, поэтому нет никакого способа обойти функцию __get().

Объем…

Получается довольно громоздкая конструкция для доступа к коду. Точно такой ​​же функциональности без дополнительных усилий можно достичь с помощью статического метода.

Да и нет особого смысла рассуждать о такой конструкции. Хорошо, вы можете получить доступ к данным сессии в своей модели. Но зачем вам это делать?

«Решение»

Kohana-разработчики были первыми, кто серьезно поработал над статическими методами. Они внесли следующие изменения:
//было $this->input->get("foo"); // стало Input::get("foo");
Для многих CodeIgniter-разработчиков с их устаревшим PHP 4, которые переехали на фреймфорк Kohana, чтобы пользоваться всеми прелестями PHP 5, в этом ничего необычного нет. Но ведь, чем меньше символов, тем лучше, так?

И в чем же проблема?

Многие PHP-разработчики (особенно те, кто хорошо разбирается в Symfony и Zend) скажут: « Это же очевидно - используй Внедрение зависимостей!» Но не многие разработчики в сообществе CodeIgniter имеют реальный опыт работы с этим процессом, так как он довольно сложен.

Еще один факт о фреймворке Fuel PHP - пока в основном статические методы выступают в качестве интерфейса. Например, логика все еще имеет проблемы со статикой, особенно когда задействуется концепция HMVC.

Это псевдокод, который я не использовал в FuelPHP, начиная с версии 1.1:
class ControllerA extends Controller { public function action_foo() { echo Input::get("param"); } }
Довольно стандартный код. Этот метод будет выводить значение ?bar= в методе.

Что происходит, когда мы делаем HMVC-запрос к данному методу?
class ControllerB extends Controller { public function action_baz() { echo Input::get("param"); echo " & "; echo Request::forge("controllera/foo?param=val1")->execute(); } }
Вызвав в браузере controllerb/baz , вы увидите вывод «val1», но если вы наберете controllerb/baz?param=override , то получите оба вызова для получения метода, возвращающего то же самое значение.

Актуальность

Глобальный код не даст вам какого-либо отношения. Пример лучше любых слов:
$this->request->input->get("param");
Запрошенный объект будет содержать совершенно новый экземпляр для каждого запроса, тогда ввод объекта будет создаваться для каждого запроса, который содержит только входные данные для конкретного запроса. Это справедливо для FuelPHP 2.0 plans to work и решает проблему Внедрения зависимостей, а также проблемы с HMVC.

Как быть с грубым синтаксисом?

Symfony- или Zend- разработчики таким не страдают, а вот тем, кто использует CodeIgniter, долго будут сниться кошмары о «возвращении к PHP 4».

$this всегда обращается к «текущему» объекту, и точно не стоит использовать ее для доступа к глобальному коду.

$this->request->input->get() может выглядеть как удлиненная форма CodeIgniter-синтаксиса, но на самом деле мы просто находимся в контроллере. Когда контроллер создает экземпляр нового вложенного в него запроса, конструктор запроса также получает экземпляр на входе.

Если вы находитесь в модели или другом классе, то доступ вида $this->request->input->foo() не будет работать, потому что $this - не контроллер.

Конструкция Input::get("foo") создает фасад для экземпляров логики в фоновом режиме. Но это не решает вопросов, связанных с работой глобального кода. Самые ленивые при тестировании приложений могут переключаться между двумя режимами без необходимости полностью использовать новый фреймворк.

Есть отличное видео от Тейлора Отвелла (creator или laravel 4), в котором он описывает, как вы можете заменить статический код с единичным экземпляром, проверяемым через его DiC-контейнер.

Laravel 4 - IoC Controller Injection & Unit Testing from UserScape on Vimeo .

Это отличная презентация того, как можно обойтись без использования статических методов в Laravel. Хотя некоторые современные фреймворки, на первый взгляд, очень напоминают Kohana, они абсолютно по-разному решают даже самые стандартные задачи.

На этой печальной ноте…

Сейчас я занимаюсь преобразованием PyroCMS с CodeIgniter на Laravel. Пытаюсь перейти прямо от глобального кода PHP 4 к совершенному внедрению зависимостей - это абсолютное самоубийство. Промежуточный шаг перед использованием загрузчика CI - использование PHP 5, автозагружаемого кода PSR-2 с кучей статичных методов. Ну а пока мы все еще находимся в CodeIgniter.

Переход от статики к DiC-коду можно будет легко показать, когда мы, наконец, сделаем переход на Laravel.

Переход от сильносвязанного кода CodeIgniter к тестируемому PSR-2 - главная задача. Команда Pyro уже в пути - и это будет эпично.

В PHP есть возможность определить метод как статический. Статический метод не имеет доступа к свойствам объекта. Такие методы могут быть вызваны только в контексте класса, но не в контексте объекта.

Самое важно что нужно понять - статические свойства и методы пренадлежат классам, а не объектам.

На примере станет сразу понятно. Давайте создадим обект Math (сокращённое названием математики в английском).

Статические <a href="/php-magicheskie-metody-get-getter-y-i-setter-y-magiya-kotoraya.html">методы PHP</a>
"; $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 = new Math(); $math_2 = new Math(); $math_3 = new Math(); $math_4 = new 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, избегая создания объекта.

mob_info