Что такое позднее связывание php
Позднее статическое связывание в 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-разработчиков. Во второй части рассмотрим как его можно использовать во благо.
Позднее статическое связывание в 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
Reg.ru: домены и хостинг
Крупнейший регистратор и хостинг-провайдер в России.
Более 2 миллионов доменных имен на обслуживании.
Продвижение, почта для домена, решения для бизнеса.
Более 700 тыс. клиентов по всему миру уже сделали свой выбор.
Бесплатный Курс «Практика HTML5 и CSS3»
Освойте бесплатно пошаговый видеокурс
по основам адаптивной верстки
на HTML5 и CSS3 с полного нуля.
Фреймворк Bootstrap: быстрая адаптивная вёрстка
Пошаговый видеокурс по основам адаптивной верстки в фреймворке Bootstrap.
Научитесь верстать просто, быстро и качественно, используя мощный и практичный инструмент.
Верстайте на заказ и получайте деньги.
Что нужно знать для создания PHP-сайтов?
Ответ здесь. Только самое важное и полезное для начинающего веб-разработчика.
Узнайте, как создавать качественные сайты на PHP всего за 2 часа и 27 минут!
Создайте свой сайт за 3 часа и 30 минут.
После просмотра данного видеокурса у Вас на компьютере будет готовый к использованию сайт, который Вы сделали сами.
Вам останется лишь наполнить его нужной информацией и изменить дизайн (по желанию).
Изучите основы HTML и CSS менее чем за 4 часа.
После просмотра данного видеокурса Вы перестанете с ужасом смотреть на HTML-код и будете понимать, как он работает.
Вы сможете создать свои первые HTML-страницы и придать им нужный вид с помощью CSS.
Бесплатный курс «Сайт на WordPress»
Хотите освоить CMS WordPress?
Получите уроки по дизайну и верстке сайта на WordPress.
Научитесь работать с темами и нарезать макет.
Бесплатный видеокурс по рисованию дизайна сайта, его верстке и установке на CMS WordPress!
Хотите изучить JavaScript, но не знаете, как подступиться?
После прохождения видеокурса Вы освоите базовые моменты работы с JavaScript.
Развеются мифы о сложности работы с этим языком, и Вы будете готовы изучать JavaScript на более серьезном уровне.
*Наведите курсор мыши для приостановки прокрутки.
Позднее статическое связывание: ключевое слово static
После того, как мы рассмотрели абстрактные классы и интерфейсы, самое время снова обратиться к статическим методам.
Вы уже знаете, что статический метод можно использовать в качестве фабрики, создающей экземпляры объектов того класса, в котором содержится этот метод. Если вы такой же ленивый программист, как и я, то вас должны раздражать повторы кода наподобие тех, что приведены ниже.
Сначала я создал суперкласс под именем DomainObject. Само собой разумеется, что в реальном проекте в нем будет находиться функциональность, общая для всех дочерних классов.
После этого я создал два дочерних класса User и Document. Я хотел, чтобы в каждом из моих конкретных классов находился метод create().
* Примечание
Почему для создания конкретного объекта я воспользовался статическим методом-фабрикой, а не оператором new и конструктором объекта?
Дело в том, что существует шаблон под названием Identity Map (материал по нему скоро будет доступен) и он создает и инициализирует новый объект только в том случае, если объект с аналогичными отличительными особенностями еще не создан. Если таковой объект существует, то возвращается просто ссылка на него.
Статический метод-фабрика наподобие рассмотренного выше метода create() является отличными кандидатом для реализации подобной функциональности.
Ну что ж, все выглядит круто! Теперь весь общий код сосредоточен в одном месте, и чтобы обратиться к текущему классу, я воспользуюсь ключевым словом self.
Однако насчет ключевого слова self я сделал допущение, что оно должно так работать. На самом деле оно не работает для классов так же, как псевдопеременная $this для объектов.
С помощью ключевого слова self нельзя сослаться на вызывающий объект. Оно используется только для разрешения ссылок на содержащий класс, в контексте которого вызывается метод. Поэтому при попытке запуска приведенного выше примера получим следующее сообщение об ошибке:
Переводится это так: невозможно создать экземпляр абстрактного класса.
Таким образом, ключевое слово self трансформируется в ссылку на класс DomainObject, в котором определен метод create(), а не на класс Document, для которого этот метод должен быть вызван.
До появления PHP 5.3 это было серьезным ограничением языка, которое породило массу неуклюжих обходных решений.
В PHP 5.3 впервые введена концепция позднего статического связывания (late static bindings). Самым заметным ее проявлением является введение нового (в данном контексте) ключевого слова static. Оно аналогично ключевому слову self, за исключением того, что относится к вызывающему, а не содержащему классу.
Итак, теперь я смогу воспользоваться всеми преимуществами наследования в статическом контексте.
В результате будет выведено следующее:
Ключевое слово static можно использовать не только для создания объектов. Так же, как и self и parent, его можно использовать как идентификатор для вызова статических методов даже из нестатического контекста.
Например, я хочу реализовать идею группировки моих классов DomainObject. По умолчанию все классы попадают в категорию «default». Но для некоторых веток иерархии наследования моих классов мне нужно это переопределить.
Здесь в класс DomainObject я ввел конструктор, в котором используется ключевое слово static для вызова метода getGroup(). Стандартное значение группы сосредоточено в классе DomainObject, но оно переопределяется в классе Document.
Я также создал новый класс SpreadSheet, расширяющий класс Document. Вот что получим в результате.
Все происходящее с классом User не настолько очевидно, поэтому требует пояснений.
В конструкторе класса DomainObject вызывается метод getGroup(), который интерпретатор находит в текущем классе. Несмотря на это, в случае с классом SpreadSheet поиск метода getGroup() начинается не с класса DomainObject, а с класса SpreadSheet, для которого из метода create() был вызван стандартный конструктор.
Поскольку в классе SpreadSheet реализиция метода getGroup() не предусмотрена, интерпретатор вызывает аналогичный метод класса Document (т.е. идет вверх по иерархии объектов).
Таким образом, до появления PHP 5.3 и позднего статического связывания, здесь у нас возникала проблема из-за использования ключевого слова self, которое находило метод getGroup() только в классе DomainObject. Теперь же этот вопрос можно считать решенным.
На этом я завершаю данную статью. В следующем материале мы поговорим об обработке ошибок в PHP.
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Позднее статическое связывание — PHP: Погружаясь в классы
Подобное поведение можно рассматривать как ограничение. Фактически оно игнорирует факт наследования. Позднее связывание же открывает доступ к некоторым возможностям, которые широко эксплуатируются в некоторых других языках, таких как Ruby. Прежде чем вдаваться в технические детали, нужно немного разобраться с тем, какую роль играют статические свойства и методы в классах.
Данные в статических свойствах относятся к классу в целом. Они «описывают» его. Самый распространённый пример из веба — это связь сущности с таблицей в базе данных, где она хранится:
Почему эту информацию нужно хранить в статическом свойстве? Потому что она не принадлежит конкретному объекту. Представьте, что вы просто хотите узнать из кода, в какую таблицу будут сохраняться пользователи. Если бы эта информация не была статической, то пришлось бы создавать объект только ради того, чтобы узнать ответ на наш вопрос. Это бессмысленно.
Для сохранения сущности в базу данных обычно используются библиотеки ORM (Object-Relationship Mapping), которые, в том числе, знают, как сохранять сущности в базу и как извлечь их. Большинство из них построено на наследовании. Любая сущность должна наследоваться от специального базового класса, который содержит в себе общую логику для работы с базой данных:
Для сохранения пользователя в базу, недостаточно знать, что сохранять, нужно ещё понимать, куда сохранять. И эта информация записана в статическое свойство.
Позднее статическое связывание в PHP (Часть II: Практика)
Первую часть читайте здесь.
Теперь приступим к практике. Наиболее показательным примером использования LSB, по-моему, является случай, когда у вас есть набор классов выполняющих похожие действия. В терминах веб-разработки мы часто встречаемся с такими задачами при обращениях к таблицам базы данных, особенно в ORM системах. Все ваши объекты для работы с таблицами будут похожи по сути, но при этом будут иметь собственный функционал ( и, соответственно, свои подклассы).
Допустим, у нас в системе есть класс Storable, который реализует (как вы догадались) (прим. переводчика: я не догадался 🙂 ) паттерн Storable. Мы определяем классы, которые наследуются от класса Storable и задаем имена таблиц в конструкторах. Вот как это выглядит:
Можете пропустить подробности конструктора, он приведен только для того, чтобы вы представляли как работает класс Storable. Как вы уже успели понять, это сэкономит нам немного времени и позволит не тратить его на такие мелочи как простые запросы SELECT, INSERT и UPDATE.
В нашей системе, помимо основной таблицы Статьи (ArticleEntry), будут использоваться еще несколько таблиц, которые содержат мета-данные (в отношении многие-к-одному), например: теги, вложения. Мне также нужен простой способ удаления данных из под-таблиц, прежде чем обновлять данные в основной таблице (проще удалить мета-данные и создать заново, чем беспокоится о синхронизации данных). Итак, чтобы смоделировать код наиболее приближенный к схеме базы данных, я остановился на такой реализации:
Весь фокус в методе purge(). Свойство $this->tableName является защищенным свойством, которое мы получили от класса Storable. Вот пример использования:
У меня есть куча таких мелких классов для работы с таблицами мета-данных. К сожалению, поскольку наша система использует PHP версии 5.2, я не мог использовать функциональность LSB описанную в первой части статьи. И чтобы удалять мета-данные, я должен писать:
Если посмотрите выше, как определен метод purge(), то увидите, что tableName он получает из класса Storable, который получает его из конструкторов классов-потомков. Помимо этих тривиальных (но абсолютно необходимых) данных, а также получения объекта базы данных в $this->db, мы не достигли ничего создавая объект класса articleentryattachment. Код был бы намного понятнее и чище ( и конечно эффективнее), если бы можно было бы вызвать метод purge() статически. Рассмотрим такой код:
Первое, что, я надеюсь, вы заметили, это то что ArticleEntryAttachment стал намного проще. Теперь нет необходимости переопределять конструктор для подклассов, потому, что конструктор родительского класса самодостаточен. И теперь можно использовать метод purge() (используя LSB):
Поскольку, теперь purge() может получать имя таблицы, которое определяется в момент выполнения, мы можем сделать его статическим. В итоге, код — чище, выполнение — эффективней, поддержка (например, добавление новых под-классов) — пустяковая, потому как избыточность полностью убрана. Спасибо разработчикам PHP за то что это стало возможным!