return new static php
Позднее статическое связывание в PHP
Рассмотрим следующий пример:
C:\> php php-late-static-binding.php
My name A!
My name A!
My name A!
My name B!
Так что же здесь происходит? А вот что: когда мы в методе getNameSB() класса А определяем return static::$name мы говорим компилятору PHP, что нам нужно значение из контекста дочернего класса, а не из контекста родительского. Проще говоря, свойство берется не из того места где оно определено изначально, а из того места где оно переопределено, т.е. из дочернего класса. Вот это как раз и называется поздним статическим связыванием в PHP.
Таким образом, если Вам надо получить статические свойства (методы) из родительского класса для дочернего класса Вам необходимо использовать позднее статическое связывание в PHP, реализуемое посредством ключевого слова static.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 0 ):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.
Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.
Позднее статическое связывание — PHP: Погружаясь в классы
Подобное поведение можно рассматривать как ограничение. Фактически оно игнорирует факт наследования. Позднее связывание же открывает доступ к некоторым возможностям, которые широко эксплуатируются в некоторых других языках, таких как Ruby. Прежде чем вдаваться в технические детали, нужно немного разобраться с тем, какую роль играют статические свойства и методы в классах.
Данные в статических свойствах относятся к классу в целом. Они «описывают» его. Самый распространённый пример из веба — это связь сущности с таблицей в базе данных, где она хранится:
Почему эту информацию нужно хранить в статическом свойстве? Потому что она не принадлежит конкретному объекту. Представьте, что вы просто хотите узнать из кода, в какую таблицу будут сохраняться пользователи. Если бы эта информация не была статической, то пришлось бы создавать объект только ради того, чтобы узнать ответ на наш вопрос. Это бессмысленно.
Для сохранения сущности в базу данных обычно используются библиотеки ORM (Object-Relationship Mapping), которые, в том числе, знают, как сохранять сущности в базу и как извлечь их. Большинство из них построено на наследовании. Любая сущность должна наследоваться от специального базового класса, который содержит в себе общую логику для работы с базой данных:
Для сохранения пользователя в базу, недостаточно знать, что сохранять, нужно ещё понимать, куда сохранять. И эта информация записана в статическое свойство.
Готовимся к собеседованию по PHP: ключевое слово «static»
Не секрет, что на собеседованиях любят задавать каверзные вопросы. Не всегда адекватные, не всегда имеющие отношение к реальности, но факт остается фактом — задают. Конечно, вопрос вопросу рознь, и иногда вопрос, на первый взгляд кажущийся вам дурацким, на самом деле направлен на проверку того, насколько хорошо вы знаете язык, на котором пишете.
Попробуем разобрать «по косточкам» один из таких вопросов — что значит слово «static» в PHP и зачем оно применяется?
Ключевое слово static имеет в PHP три различных значения. Разберем их в хронологическом порядке, как они появлялись в языке.
Значение первое — статическая локальная переменная
Однако всё меняется, если мы перед присваиванием поставим ключевое слово static:
Подводные камни статических переменных
Разумеется, как всегда в PHP, не обходится без «подводных камней».
Камень первый — статической переменной присваивать можно только константы или константные выражения. Вот такой код:
с неизбежностью приведет к ошибке парсера. К счастью, начиная с версии 5.6 стало допустимым присвоение не только констант, но и константных выражений (например — «1+2» или «[1, 2, 3]»), то есть таких выражений, которые не зависят от другого кода и могут быть вычислены на этапе компиляции
Камень второй — методы существуют в единственном экземпляре.
Тут всё чуть сложнее. Для понимания сути приведу код:
Такое поведение может быть неожиданным для неподготовленного к нему разработчика и послужить источником ошибок. Нужно заметить, что наследование класса (и метода) приводит к тому, что всё-таки создается новый метод:
Вывод: динамические методы в PHP существуют в контексте классов, а не объектов. И только лишь в рантайме происходит подстановка «$this = текущий_объект»
Значение второе — статические свойства и методы классов
В объектной модели PHP существует возможность задавать свойства и методы не только для объектов — экземпляров класса, но и для класса в целом. Для этого тоже служит ключевое слово static:
Для доступа к таким свойствам и методам используются конструкции с двойным двоеточием («Paamayim Nekudotayim»), такие как ИМЯ_КЛАССА::$имяПеременной и ИМЯ_КЛАССА:: имяМетода().
Само собой разумеется, что у статических свойств и статических методов есть свои особенности и свои «подводные камни», которые нужно знать.
Особенность вторая — static не аксиома!
Обратное не совсем верно:
И кстати, всё написанное выше относится только к методам. Использование статического свойства через «->» невозможно и ведет к фатальной ошибке.
Значение третье, кажущееся самым сложным — позднее статическое связывание
Разработчики языка PHP не остановились на двух значениях ключевого слова «static» и в версии 5.3 добавили еще одну «фичу» языка, которая реализована тем же самым словом! Она называется «позднее статическое связывание» или LSB (Late Static Binding).
Понять суть LSB проще всего на несложных примерах:
Ключевое слово self в PHP всегда значит «имя класса, где это слово написано». В данном случае self заменяется на класс Model, а self::$table — на Model::$table.
Такая языковая возможность называется «ранним статическим связыванием». Почему ранним? Потому что связывание self и конкретного имени класса происходит не в рантайме, а на более ранних этапах — парсинга и компиляции кода. Ну а «статическое» — потому что речь идет о статических свойствах и методах.
Немного изменим наш код:
Теперь вы понимаете, почему PHP ведёт себя в этой ситуации неинтуитивно. self был связан с классом Model тогда, когда о классе User еще ничего не было известно, поэтому и указывает на Model.
Для решения этой дилеммы был придуман механизм связывания «позднего», на этапе рантайма. Работает он очень просто — достаточно вместо слова «self» написать «static» и связь будет установлена с тем классом, который вызывает данный код, а не с тем, где он написан:
Это и есть загадочное «позднее статическое связывание».
Нужно отметить, что для большего удобства в PHP кроме слова «static» есть еще специальная функция get_called_class(), которая сообщит вам — в контексте какого класса в данный момент работает ваш код.
Return new static php
I have implemented enum using late static binding.
interface IEnum <
/**
* Only concrete class should implement this function that should behave as
* an enum.
*
* This method should return the __CLASS__ constant property of that class
*
* @return string __CLASS__
*/
public static function who ();
>
abstract class Enum <
// use of late static binding to get the class.
$class = static:: who ();
class Fruits extends Enum implements IEnum <
public static function who () <
return __CLASS__ ;
>
>
// user input from dropdown menu of fruits list
$input = 3 ;
echo B :: constructor (); // B
?>
Just a quick reminder to always check your syntax. While I love LSB, I thought it wasn’t working:
static::$sKey = not set
…until I realized that I’d completely forgotten to make it a variable variable:
static::$$sKey = is set
…of course this applies anywhere in PHP, but because of the (current) newness late static bindings, I’ve seen lots of code with this particular snafu in it from others.
In the above example (#3) in order to make it work, you can change the child’s method from ‘private’ to ‘protected’ (or public) and it will be called through ‘static’.
class B extends A <
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
>
class C extends A <
protected function foo () < //note the change here
echo ‘hello world!’ ;
>
>
I discovered an interesting thing. The class name string must be accessed directly from «flat» variable. Late static binding code that get’s it’s variable from array that is passed by class instance, throws an syntax error. Bug?
public static function numbers ()
<
return 123 ;
>
$arr = array( ‘class’ => ‘A’ );
This function can be used as a workaround for late static binding in PHP >= 5.1.0. There was another similar version of this function elsewhere, but used eval.
At least as of PHP 5.3.0a2 there’s a function get_called_class(), which returns the class on which the static method is called.
class a <
static public function test () <
print get_called_class ();
>
>
a :: test (); // «a»
b :: test (); // «b»
You can pass arguments to your constructor through your getInstance method, assuming you are running php5.
This would pass the params to your constructor. Love for php.
PHP5.3 unavailable, yet in the need for ‘static’, I did the following.
Any objections? Personally I hate using the the eval() statement.
Here is a small workaround I made for the static inheritance issue. It’s not perfect, but it works.
// Init
$backTrace = debug_backtrace ();
$class = false ;
public static function staticClassName () <
// Get real name
$staticName = self :: getStatic ();
?>
There are two issues with this workaround :
— if you call a static method from global env, you need to declare the name of the class BEFORE calling the method, otherwise the workaround won’t work (see 3rd and 4th examples). But I assume good programming makes few calls to static methods from global scope, so this shouldn’t be long to fix if you use it.
— the workaround fails to access to private or protected static vars, as it uses get_class_vars(). If you find any better solution, let us know.
Позднее статическое связывание в PHP (Часть I)
Позднее Статическое Связывание (Late Static Binding, LSB) является бурно темой обсуждений последние три года в кругах разработчиков PHP (и наконец мы его получили в PHP 5.3). Но зачем оно нужно? В данной статье, как раз и будет рассматриваться, как позднее статическое связывание может значительно упростить ваш код.
На встрече разработчиков PHP, которая проходила в Париже в ноябре 2005 года, тема позднего статического связывания официально обсуждалась основной командой разработчиков. Они согласились реализовать его, наряду со многими другими темами, которые стояли на повестке дня. Детали должны были быть согласованы в ходе открытых дискуссий.
С тех пор как позднее статическое связывание было объявлено как грядущая фишка, прошло два года. И вот наконец LSB стало доступно для использования в PHP 5.3. Но это событие прошло незаметно для разработчиков использующих PHP, из заметок только страничка в мануале.
Если кратко, новая функциональность позднего статического связывания, позволяет объектам все также наследовать методы у родительских классов, но помимо этого дает возможность унаследованным методам иметь доступ к статическим константам, методам и свойствам класса потомка, а не только родительского класса. Давайте рассмотрим пример:
Этот код выдаст такой результат:
Класс Ale унаследовал метод getName(), но при этом self все еще указывает на класс в котором оно используется (в данном случае это класс Beer). Это осталось и в PHP 5.3, но добавилось слово static. И снова рассмотрим пример:
Новое ключевое слово static указывает, что необходимо использовать константу унаследованного класса, вместо константы которая была определена в классе где объявлен метод getStaticName(). Слово static было добавлено, чтобы реализовать новый функционал, а для обратной совместимости self работает также как и в предыдущих версиях PHP.
Внутренне, основное отличие (и, собственно, причина почему связывание назвали поздним) между этими двумя способами доступа, в том, что PHP определят значение для self::NAME во время «компиляции» (когда симовлы PHP преобразуются в машинный код, который будет обрабатываться движком Zend), а для static::NAME значение будет определено в момент запуска (в тот момент, когда машинный код будет выполнятся в движке Zend).
Это еще один инструмент для PHP-разработчиков. Во второй части рассмотрим как его можно использовать во благо.