php обращение к константе внутри класса
constant
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
constant — Возвращает значение константы
Описание
Функция constant() полезна, если вам необходимо получить значение константы, но неизвестно её имя. Например, если оно хранится в переменной или возвращается функцией.
Данная функция также работает с константами классов.
Список параметров
Возвращаемые значения
Ошибки
Примеры
Пример #1 Пример функции constant()
echo MAXSIZE ;
echo constant ( «MAXSIZE» ); // результат аналогичен предыдущему выводу
interface bar <
const test = ‘foobar!’ ;
>
class foo <
const test = ‘foobar!’ ;
>
Смотрите также
User Contributed Notes 18 notes
The constant name can be an empty string.
define(«», «foo»);
echo constant(«»);
If you are referencing class constant (either using namespaces or not, because one day you may want to start using them), you’ll have the least headaches when doing it like this:
class Foo <
const BAR = 42 ;
>
?>
namespace Baz ;
use \ Foo as F ;
echo constant ( F ::class. ‘::BAR’ );
?>
since F::class will be dereferenced to whatever namespace shortcuts you are using (and those are way easier to refactor for IDE than just plain strings with hardcoded namespaces in string literals)
The use of constant() (or some other method) to ensure the your_constant was defined is particularly important when it is to be defined as either `true` or `false`.
If `BOO` did NOT get defined as a constant, for some reason,
The reason is that PHP ASSUMES you «forgot» quotation marks around `BOO` when it did not see it in its list of defined constants.
So it evaluates: `if (‘BOO’)`.
Since every string, other than the empty string, is «truthy», the expression evaluates to `true` and the do_something() is run, unexpectedly.
then if `BOO` has not been defined, `constant(BOO)` evaluates to `null`,
which is falsey, and `if (null)`. becomes `false`, so do_something() is skipped, as expected.
Note that only the version using `defined()` works without also throwing a PHP Warning «error message.»
(disclosure: I also submitted an answer to the SO question linked to above)
Школа магии PHP
Давайте отбросим установленные рамки правил ООП и сделаем невозможное возможным в школе магии PHP. Главный и первый волшебный преподаватель школы — Александр Лисаченко (NightTiger). Он научит магическому мышлению и, возможно, вы полюбите магические методы, нестандартные способы доступа к свойствам, изменение контекстов, аспектно-ориентированное программирование и потоковые фильтры.
Александр Лисаченко — руководитель отдела веб-разработки и архитектуры в Альпари. Автор и ведущий разработчик аспектно-ориентированного фреймворка Go! AOP. Докладчик на международных конференциях по PHP.
В хорошем фильме «Иллюзия обмана» есть фраза:
«Чем вы ближе, тем меньше вы видите».
Это же можно сказать о PHP, как о магическом трюке, который позволяет проворачивать необычные вещи. Но прежде всего он создан, чтобы вас обмануть: «. an action that is intended to deceive, either as a way of cheating someone, or as a joke or form of entertainment».
Если мы возьмем PHP и вместе попытаемся на нем написать что-то магическое, скорее всего, я вас обману. Я проверну какой-нибудь трюк, и вы будете долго гадать, почему так происходит. Все потому, что PHP — это известный своими необычными штуками язык программирования.
Магическое снаряжение
Что нам потребуется из магического снаряжения? Знакомые до боли методы.
__construct(), __destruct(), __clone(),
__call(), __callStatic(),
__get(), __set(), __isset(), __unset(),
__sleep(), __wakeup(),
__toString(), __invoke(), __set_state(),
__debugInfo()
Последний метод отмечу отдельно — с ним можно проворачивать необычные вещи. Но это не все.
Всегда будь самым умным парнем в комнате.
Трюк #1. Невозможное сравнение
Начнем с первого трюка, который я называю «Невозможное сравнение».
Посмотрите внимательно на код и подумайте, может ли такое произойти в PHP.
Есть переменная, объявляем её значение, а потом она внезапно сама себе не равна.
Not-a-number
Есть такой волшебный артефакт, как NaN — Not-a-number.
Его удивительная особенность в том, что он сам себе не равен. И в этом наш первый трюк: использовать NaN, чтобы озадачить товарища. Но NaN не единственное решение для этой задачи.
Используем константы
Обработчик
Следующий трюк — артиллерия помощнее, чем два предыдущих варианта. Рассмотрим, как он работает.
Трюк #2. Магические выражения
Магические выражения
Посмотрите на код ниже. Есть некоторый класс, который содержит конструктор, и некоторая фабрика, код которой будет далее.
Обратите внимание на последнюю строчку.
Есть несколько вариантов, что может произойти.
Кстати, в этом фреймворке у меня реализована фишка, которая называется «перехват создание новых объектов» — можно «замокать» эту конструкцию.
Лазейка в парсере
Следующий пример касается самого PHP-парсера. Пробовали ли вы когда-нибудь вызывать функции или присваивать переменные внутри фигурных скобок?
Этот трюк мне попался в Twitter, он работает крайне нестандартно.
Что такое магия? Это развлечение, которое позволяет нам почувствовать себя воодушевленно, необычно, сказать: «Что? Так можно было?!»
Трюк #3. Ломаем правила
Мне нравится в PHP то, что можно ломать правила, которые все создают, пытаясь быть суперзащищенными. Есть конструкция под названием «запечатанный класс», у которого приватный конструктор. Ваша задача как ученика мага создать экземпляр этого класса.
Рассмотрим три варианта, как это можно сделать.
Обходной путь
Первый путь самый очевидный. Он должен быть знаком каждому разработчику — это стандартный API, который нам предлагает язык.
Конструкция newInstanceWithoutConstructor позволяет обойти ограничения языка на то, что конструктор приватный, и создать экземпляр класса в обход всех наших объявлений приватного конструктора.
Вариант рабочий, простой, не требует какого-то пояснения.
Замыкание
Второй вариант требует уже больше внимания и умения. Создается анонимная функция, которая затем биндится к скопу того класса.
Здесь мы находимся внутри класса и можем спокойно вызывать приватные методы. Этим и пользуемся, вызывая new static из контекста нашего класса.
Десериализация
Третий вариант самый передовой, на мой взгляд.
Если в определенном формате написать определенную строчку, подставить туда определенные значения — получится наш класс.
doctrine/instantiator package
Магия часто становится документированным фреймворком или библиотекой — например, в doctrine/instantiator все это реализовано. Мы можем создавать любые объекты с любым кодом.
Трюк #4. Intercepting property access
Тучи сгущаются: класс секретный, свойства и конструктор приватные, и еще callback.
Наша задача, как волшебников, как-то вызвать callback.
Добавляем магический… getter
Добавим щепотку магии, чтобы все это заработало.
Теперь надо каким-то образом вызвать callback.
«Unset» внутри замыкания
Чтобы сделать это, создадим замыкание. Внутри замыкания, которое находится в скопе класса, удалим функцией unset() эту переменную.
Вызываем приватный конструктор
Так наш секретный класс рассыпался в пух и прах.
Мы получили сообщение о том, что мы:
leedavis/altr-ego package
Много магии уже задокументировано. Пакет altr-ego как раз притворяется вашим компонентом.
Вы можете создать один свой объект и прицепить к нему второй. Это позволит проводить изменения объекта. Он будет изменяться послушно и выполнять все ваши пожелания.
Трюк #5. Immutable objects в PHP
Существуют ли в PHP Immutable object? Да, причем очень и очень давно.
Единственная незадача — потом достать это свойство оттуда никак нельзя. Оно создается, но недоступно.
Я подумал, что это довольно неудобно — у нас есть Immutable objects, но использовать его нельзя. Поэтому решил, что пора бы запилить свое решение. Я использовал все мои знания и магию, которая имеется в PHP, чтобы создать конструкцию на базе всех наших магических трюков.
Начнем с простого — создадим DTO и попытаемся в ней перехватить все свойства (см. предыдущий трюк).
Вернемся к надежному месту и попробуем поискать.
Безопасное хранение значений
Я спросил на специальном канале Stack Overflow у Никиты Попова и Джея Воткинса об этом.
Это функция, внутри которой объявлена статичная переменная. Можно ли из неё как-то достать, поменять? Ответ — нельзя.
Мы нашли маленькую лазейку в потусторонний мир защищенных переменных и хотим использовать её.
Передача значений по ссылкам
Использовать будем нестандартно, как свойство объекта. Но нельзя передавать свойство, поэтому используем классическую передачу значений по ссылкам.
Сохраняем state
Посмотрим, как сохраняется состояние.
Применяем состояние объекта
Получаем State
Все волшебные методы переопределены и реализуют неизменяемость объекта.
lisachenko/immutable-object
Как положено, все сразу оформляется в библиотеку и готово к использованию.
После этого можно инициировать объект один раз — посмотреть его значение. Оно действительно там сохраняется и даже выглядит, как настоящая DTO.
Но если мы попытаемся ее поменять, например, так…
Если ввязаться в увлекательный челлендж и попытаться её дебажить, то получится следующее.
Это мой подарочек. Как только в PHPStorm вы попытаетесь провалиться внутрь этого класса, он моментально остановит выполнение вашей команды. Не хочу, чтобы вы копались в этом коде — он слишком опасный. Он будет предупреждать, что тут делать нечего.
Трюк #6. Обработка потоков
Заметьте, что регистр разный. Однако, если «заинклюдим» файлик через нашу конструкцию, то получим вот это:
Что произошло в этот момент? Использование конструкции PHP-фильтра в include позволяет подключить любой фильтр, в том числе и ваш, для анализа исходного кода. Вы управление всем, что находится в этом исходном коде. Можно убрать final из классов и методов, сделать свойства публичными — всё, что угодно можно провернуть через эту штуку.
На этом базируется часть моего аспектного фреймворка. Когда подключается ваш класс, он его анализирует и трансформирует в то, что будет выполняться.
Из коробки в PHP есть уже целая пачка готовых фильтров.
Они позволяют «зазиповать» контент, перевести его в верхний или нижний регистр.
Основные трюки, которые я хотел показать, закончились. Теперь перейдем к квинтэссенции всего, что я умею — к аспектно-ориентированному программированию.
Трюк #7. Аспектно-ориентированное программирование
Посмотрите на этот код и подумайте, хороший он или плохой с вашей точки зрения.
Если посмотреть на всю эту лапшу, она повторяется в каждом нашем методе, и ценное здесь только то, что помечено зеленым.
Все остальное: «secondary concerns» или «crosscutting concerns» — сквозная функциональность.
Обычный ООП не дает возможности взять копипастом все эти конструкции, убрать и куда-то вынести. Это плохо, потому что приходится повторяться. Хотелось бы, чтобы код всегда выглядел чисто и аккуратно.
Чтобы это все, включая логирование…
… и проверку безопасности,…
Глоссарий «Aspect»
Есть штука, которая называется «Aspect». Это простой пример, который проверяет права доступа. Благодаря ему вы можете видеть некоторую аннотацию, которую еще и подсвечивает плагин для PhpStorm. «Aspect» объявляет SQL-выражения, к каким точкам в коде применять данное условие. Ниже, например, мы хотим для всех публичных методов из класса UserService применить замыкание.
Дальше в этом callback для каждого метода можно проверить необходимые права доступа, это называется «Advice».
Мы как бы говорим языку: «Уважаемый PHP, пожалуйста, примени этот метод перед каждым вызовом публичного метода из класса UserService ». Всего лишь одна строчка, а много полезного.
Aspect vs Event Listener
Чтобы было понятнее, я сделал сравнение Aspect с Event Listener. Многие работают в Symfony и знают, что такое Event Dispatcher.
Рассмотрим, как это все работает под капотом.
Второй этап. Чтобы это обработать, опять используется предыдущий трюк с PHP-фильтром.
Это специальный компонент, который занимается преобразованием исходного кода. Он это делает скрытно, но работать в продакшн будет хорошо, потому что интегрирован с OPcache.
Дальше начинается довольно сложная матчасть.
PHP-Parser. Чтобы сделать эту сложную работу, провернуть магический трюк, необходимо весь исходный код сперва проанализировать. Хорошо, что есть такая замечательная библиотека Никиты Попова, как PHP-Parser. Она позволяет провести токенизацию и построить абстрактное синтаксическое дерево всего кода.
Четвертый этап. Я создал еще одну библиотеку, которая называется goaop/parser-reflection.
Она работает поверх AST-дерева и позволяет проводить рефлексию исходного кода, не загружая его в память. Для меня это важно, потому что как только класс загружается в память, он оттуда никак не может быть выгружен, а хотелось бы узнать его структуру заранее.
Дальше принимаемся за разбор текущего файла.
На выходе получается очень аккуратная и удобная штука в виде простого класса, которая декорирует ваш оригинальный класс.
Особенность фреймворка в том, что он подменяет ваш класс точно таким же классом с таким же именем, а оригинальный при этом переименовывает в кэше. Меняется не ваш оригинальный код — в кэше создается отдельная версия, в которой чуть-чуть меняется название класса, и потом наследуется от этого класса.
Наследование есть даже в том случае, если класс был финальным. Поэтому можно отлавливать и финальные методы, и финальные классы.
Поверх моего фреймворка работает библиотека Aspect MOCK. Она позволяет «замокать», в том числе и финальные методы, и статические методы. Все это работает под капотом.
Что дальше?
Дальше открываются невероятные возможности.
OPcache preloading for AOP Core. Весь AOP-движок будет прекомпилироваться на этапе загрузки приложения. Это позволит снизить накладные расходы на его исполнение с 10 мс до нуля. Bootstrapping фреймворка будет занимать практически ничего, весь фреймворк будет находиться в памяти PHP.
Modifying PHP engine internal callbacks или модификация PHP-движка со стороны userland. Внутри PHP есть глобальные переменные. Если через FFI подключить PHP сам к себе в userland, то получим доступ к его внутренним свойствам, классам, структурам. Почему бы этим не воспользоваться.
На этом магию остановим, пока не реализуем.
Трюк #8. goaop/framework
Это мой фреймворк, он есть на GitHub и у него там больше тысячи звезд.
Если вы боитесь магии, я создал помощника в виде плагина для PhpStorm.
Плагин удобен тем, что позволяет подсвечивать синтаксис Pointcuts. Мы знаем, какие хотим методы обрабатывать, и как. Также он предлагает навигацию — подсвечивает подсказки у методов, к тем методам, к которым мы хотим перейти.
Trick #9. Отложенные методы
Напоследок сделаем еще один трюк уже с использованием аспектного фреймворка. Посмотрим, как делать отложенные методы.
Что я предлагаю сделать и как это может работать?
В свойство Aspect начинаем накапливать отложенные методы: сохраняем метод, который вызвался, объект, для которого был вызван данный callback, и аргументы, с которыми был вызван callback. Мы не даем выполниться этому коду.
Посмотрим, как всё это будет работать под капотом.
Допустим, какой-то плохой человек сделал его слишком медленным — он спит 2 с.
Мы не хотим, чтобы клиент, который делает запрос в наше приложение, еще 2 секунды ждал ответа.
Код моментально вылетает, клиент сразу получает ответ. Где-то потом в фоновом режиме после завершения запроса, отправляется уведомление, что уже никак не мешает клиенту.
На этом все. Надеюсь, что вам понравился урок магии и натолкнул вас на какие-то размышления. Буду признателен, если оставите комментарии.
Следующая профессиональная конференция PHP Russia 2020 пройдет в мае. Мы готовим новую концепцию и принимаем доклады — подавайте заявки. Подписывайтесь на рассылку и telegram-канал, чтобы раньше других получить приглашение на PHP Russia 2020.
Php обращение к константе внутри класса
Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).
Пример #1 Простое определение класса
Результат выполнения данного примера в PHP 7:
Результат выполнения данного примера в PHP 8:
Если с директивой new используется строка ( string ), содержащая имя класса, то будет создан новый экземпляр этого класса. Если имя находится в пространстве имён, то оно должно быть задано полностью.
В случае отсутствия аргументов в конструктор класса, круглые скобки после названия класса можно опустить.
Пример #3 Создание экземпляра класса
Когда происходит присвоение уже существующего экземпляра класса новой переменной, то эта переменная будет указывать на этот же экземпляр класса. То же самое происходит и при передаче экземпляра класса в функцию. Копию уже созданного объекта можно создать через её клонирование.
Пример #4 Присваивание объекта
Результат выполнения данного примера:
Создавать экземпляры объекта можно двумя способами:
Пример #5 Создание новых объектов
class Test
<
static public function getNew ()
<
return new static;
>
>
class Child extends Test
<>
Результат выполнения данного примера:
Обратиться к свойству или методу только что созданного объекта можно с помощью одного выражения:
Пример #6 Доступ к свойствам/методам только что созданного объекта
Результатом выполнения данного примера будет что-то подобное:
Замечание: До PHP 7.1 аргументы не имели значения, если не определена функция конструктора.
Свойства и методы
Пример #7 Доступ к свойству vs. вызов метода
public function bar () <
return ‘метод’ ;
>
>
Результат выполнения данного примера:
Это означает, что вызвать анонимную функцию, присвоенную переменной, напрямую не получится. Вместо этого свойство должно быть назначено, например, переменной. Можно вызвать такое свойство напрямую, заключив его в скобки.
Пример #8 Вызов анонимной функции, содержащейся в свойстве
Результат выполнения данного примера:
extends
Класс может наследовать константы, методы и свойства другого класса используя ключевое слово extends в его объявлении. Невозможно наследовать несколько классов, один класс может наследовать только один базовый класс.
Наследуемые константы, методы и свойства могут быть переопределены (за исключением случаев, когда метод класса объявлен как final) путём объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределённым методам или статическим свойствам путём обращения к ним через parent::
Пример #9 Простое наследование классов
class ExtendClass extends SimpleClass
<
// Переопределение метода родителя
function displayVar ()
<
echo «Расширенный класс\n» ;
parent :: displayVar ();
>
>
Результат выполнения данного примера:
Правила совместимости сигнатуры
Пример #10 Совместимость дочерних методов
Результат выполнения данного примера:
Следующие примеры демонстрируют, что дочерний метод, который удаляет параметр или делает необязательный параметр обязательным, несовместим с родительским методом.
Пример #11 Фатальная ошибка, когда дочерний метод удаляет параметр
class Extend extends Base
<
function foo ()
<
parent :: foo ( 1 );
>
>
Результат выполнения данного примера в PHP 8 аналогичен:
Пример #12 Фатальная ошибка, когда дочерний метод делает необязательный параметр обязательным.
Результат выполнения данного примера в PHP 8 аналогичен:
Переименование параметра метода в дочернем классе не является несовместимостью сигнатуры. Однако это не рекомендуется, так как приведёт к Error во время выполнения, если используются именованные аргументы.
Пример #13 Ошибка при использовании именованных аргументов и параметров, переименованных в дочернем классе
Результатом выполнения данного примера будет что-то подобное:
::class
Пример #14 Разрешение имени класса
namespace NS <
class ClassName <
>
Результат выполнения данного примера:
Разрешение имён класса с использованием ::class происходит на этапе компиляции. Это означает, что на момент создания строки с именем класса автозагрузки класса не происходит. Как следствие, имена классов раскрываются, даже если класс не существует. Ошибка в этом случае не выдаётся.
Пример #15 Отсутствует разрешение имени класса
Результат выполнения данного примера:
Начиная с PHP 8.0.0, константа ::class также может использоваться для объектов. Это разрешение происходит во время выполнения, а не во время компиляции. То же самое, что и при вызове get_class() для объекта.
Пример #16 Разрешение имени объекта
Результат выполнения данного примера:
Методы и свойства Nullsafe
Пример #17 Оператор Nullsafe
Оператор nullsafe лучше всего использовать, когда null считается допустимым и ожидаемым значением для возвращаемого свойства или метода. Для индикации ошибки предпочтительнее выбрасывать исключение.
User Contributed Notes 11 notes
I was confused at first about object assignment, because it’s not quite the same as normal assignment or assignment by reference. But I think I’ve figured out what’s going on.
First, think of variables in PHP as data slots. Each one is a name that points to a data slot that can hold a value that is one of the basic data types: a number, a string, a boolean, etc. When you create a reference, you are making a second name that points at the same data slot. When you assign one variable to another, you are copying the contents of one data slot to another data slot.
Now, the trick is that object instances are not like the basic data types. They cannot be held in the data slots directly. Instead, an object’s «handle» goes in the data slot. This is an identifier that points at one particular instance of an obect. So, the object handle, although not directly visible to the programmer, is one of the basic datatypes.
What makes this tricky is that when you take a variable which holds an object handle, and you assign it to another variable, that other variable gets a copy of the same object handle. This means that both variables can change the state of the same object instance. But they are not references, so if one of the variables is assigned a new value, it does not affect the other variable.