Основы
Каждое определение класса начинается с ключевого слова class, затем следует имя класса, и далее пара фигурных скобок, которые заключают в себе определение свойств и методов этого класса.
Именем класса может быть любое слово, при условии, что оно не входит в список зарезервированных слов PHP, начинается с буквы или символа подчеркивания и за которым следует любое количество букв, цифр или символов подчеркивания. Если задать эти правила в виде регулярного выражения, то получится следующее выражение: ^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$.
Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).
Пример #1 Простое определение класса
class SimpleClass
// объявление свойства
public $var = ‘значение по умолчанию’ ;
?php
// объявление метода
public function displayVar () echo $this -> var ;
>
>
?>
Псевдо-переменная $this доступна в том случае, если метод был вызван в контексте объекта. $this является ссылкой на вызываемый объект. Обычно это тот объект, которому принадлежит вызванный метод, но может быть и другой объект, если метод был вызван статически из контекста другого объекта. Это показано на следующих примерах:
Пример #2 Переменная $this
class A
function foo ()
if (isset( $this )) echo ‘$this определена (‘ ;
echo get_class ( $this );
echo «)\n» ;
> else echo «\$this не определена.\n» ;
>
>
>
?php
class B
function bar ()
// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
A :: foo ();
>
>
// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
A :: foo ();
$b = new B ();
$b -> bar ();
// Замечание: следующая строка вызовет предупреждение, если включен параметр E_STRICT.
B :: bar ();
?>
Результат выполнения данного примера:
$this определена (A) $this не определена. $this определена (B) $this не определена.
new
Для создания экземпляра класса используется директива new. Новый объект всегда будет создан, за исключением случаев, когда он содержит конструктор, в котором определен вызов исключения в случае ошибки. Рекомендуется определять классы до создания их экземпляров (в некоторых случаях это обязательно).
Если с директивой new используется строка ( string ), содержащая имя класса, то будет создан новый экземпляр этого класса. Если имя находится в пространстве имен, то оно должно быть задано полностью.
Пример #3 Создание экземпляра класса
// Это же можно сделать с помощью переменной:
$className = ‘Foo’ ;
$instance = new $className (); // Foo()
?>
В контексте класса можно создать новый объект через new self и new parent.
Когда происходит присвоение уже существующего экземпляра класса новой переменной, то эта переменная будет указывать на этот же экземпляр класса. Тоже самое происходит и при передаче экземпляра класса в функцию. Копию уже созданного объекта можно создать через ее клонирование.
Пример #4 Присваивание объекта
$instance = new SimpleClass ();
$assigned = $instance ;
$reference =& $instance ;
$instance -> var = ‘$assigned будет иметь это значение’ ;
$instance = null ; // $instance и $reference становятся null
var_dump ( $instance );
var_dump ( $reference );
var_dump ( $assigned );
?>
Результат выполнения данного примера:
NULL NULL object(SimpleClass)#1 (1) < ["var"]=>string(30) "$assigned будет иметь это значение" >
В PHP 5.3.0 введены несколько новых методов создания экземпляров объекта:
Пример #5 Создание новых объектов
class Test
static public function getNew ()
return new static;
>
>
?php
$obj1 = new Test ();
$obj2 = new $obj1 ;
var_dump ( $obj1 !== $obj2 );
$obj3 = Test :: getNew ();
var_dump ( $obj3 instanceof Test );
$obj4 = Child :: getNew ();
var_dump ( $obj4 instanceof Child );
?>
Результат выполнения данного примера:
bool(true) bool(true) bool(true)
extends
Класс может наследовать методы и свойства другого класса используя ключевое слово extends при его описании. Невозможно наследовать несколько классов, один класс может наследовать только один базовый класс.
Наследуемые методы и свойства могут быть переопределены (за исключением случаев, когда метод класса объявлен как final) путем объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределенным методам или статическим методам путем обращения к ним через parent::
Когда переопределяются методы число и типы аргументов должны оставаться такими же как и были, иначе PHP вызовет ошибку уровня E_STRICT . Это не относится к конструктору, который можно переопределить с другими параметрами.
Пример #6 Простое наследование классов
class ExtendClass extends SimpleClass
// Переопределение метода родителя
function displayVar ()
echo «Расширенный класс\n» ;
parent :: displayVar ();
>
>
?php
$extended = new ExtendClass ();
$extended -> displayVar ();
?>
Результат выполнения данного примера:
Расширенный класс значение по умолчанию
::class
Начиная с версии PHP 5.5 можно использовать ключевое слово class для разрешения имени класса. С помощью конструкции ClassName::class можно получить строку с абсолютным именем класса ClassName. Обычно это довольно полезно при работе с классами, использующими пространства имен.
Пример #7 Разрешение имени класса
Результат выполнения данного примера:
PHP 8.1: оператор new в инициализаторах
В PHP 8.1 добавлена интересная функция, которая на первый взгляд может показаться мелочью, но я думаю, что она будет оказывать значительное повседневное влияние на многих людей.
Так в чем же польза от оператора new в инициализаторах? Давайте посмотрим на пример, раньше мы все писали такой код:
class MyStateMachine
public function __construct(
private ?State $state = null,
) $this->state ??= new InitialState();
>
>
В этом примере с конечным автоматом мы хотели бы построить наш класс двумя способами: с начальным состоянием и без него. Если мы построим его без начального состояния, мы хотим, чтобы было установлено состояние по умолчанию. PHP, конечно, поддерживает установку начальных значений непосредственно в списке параметров, но только для примитивных типов. Например, если бы наш конечный автомат использовал строки вместо объектов внутри, мы могли бы написать его конструктор следующим образом:
class MyStateMachine
public function __construct(
private string $state = 'initial',
) >
>
Таким образом, с PHP 8.1 мы можем использовать тот же синтаксис «значения по умолчанию» и для объектов. Другими словами: вы можете использовать new для аргументов по умолчанию (которые являются одним из примеров «инициализаторов»):
class MyStateMachine
public function __construct(
private State $state = new InitialState(),
) >
>
«Инициализаторы» — это больше, чем значения параметров по умолчанию, вот простое объяснение из RFC:
Этот RFC предлагает разрешить использование новых выражений внутри значений параметров по умолчанию, аргументов атрибутов, инициализаторов статических переменных и инициализаторов глобальных констант.
Да, вы правильно прочитали: атрибуты тоже есть в этом списке! Представьте себе простую библиотеку проверки, которая использует атрибуты для проверки ввода свойств. Возможно, она должна иметь возможность проверять элементы массива, примерно так:
class CreateEmailsRequest extends FormRequestData
#[ValidArray(
email: [new Required, new ValidEmail],
name: [new Required, new ValidString],
)]
public array $people;
>
До PHP 8.1 вы не могли писать такой код, потому что вам не разрешалось использовать new в атрибутах из-за способа их оценки, но теперь вы можете!
Давайте взглянем на некоторые важные детали, о которых стоит упомянуть.
Создание только при необходимости
Такого рода «новые значения» будут создаваться только тогда, когда это действительно необходимо. Это означает, что в нашем первом примере PHP создаст только новый объект, если InitialState не содержит аргументов:
class MyStateMachine
public function __construct(
private State $state = new InitialState(),
) >
>
new MyStateMachine(new DraftState()); // InitialState не создается
new MyStateMachine(); // Сейчас создается
В случае атрибутов, например, объекты будут созданы только при newInstance вызове атрибута отражения.
Не в свойствах класса
Также вы должны знать, что не можете использовать new значение по умолчанию в свойствах класса. Поддержка этой функции привела бы к множеству непредвиденных побочных эффектов, например, при сериализации и десериализации объектов:
class MyStateMachine
private State $state = new InitialState();
>
Сначала кажется, что это большое упущение, но к счастью, это решается с помощью объявления свойства в конструкторе, которые допускают значение по умолчанию, поскольку PHP будет переносить синтаксис объявления свойств, сохраняя значение по умолчанию в аргументе конструктора, но не фактически в свойстве:
class MyStateMachine
private State $state;
public function __construct(
State $state = new InitialState(),
) $this->state = $state;
>
>
Ограниченный ввод
Возможно, вы уже догадались, но вы можете передать только ограниченный набор входных данных при создании новых объектов в инициализаторах. Например, вы не можете использовать переменные, оператор распаковки, анонимные классы и т.д.