Каждое определение класса начинается с ключевого слова class
, затем
следует имя класса, и далее пара фигурных скобок, которые заключают в себе определение
свойств и методов этого класса.
Именем класса может быть любое слово, при условии, что оно не входит в список
зарезервированных слов PHP, начинается с буквы или
символа подчёркивания и за которым следует любое количество букв, цифр или символов
подчёркивания. Если задать эти правила в виде регулярного выражения, то получится
следующее выражение: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
.
Класс может содержать собственные константы, переменные (называемые свойствами) и функции (называемые методами).
Пример #1 Простое определение класса
<?php
class SimpleClass
{
// объявление свойства
public $var = 'значение по умолчанию';
// объявление метода
public function displayVar() {
echo $this->var;
}
}
?>
Псевдопеременная $this доступна в том случае, если метод был вызван в контексте объекта. $this - значение вызывающего объекта.
Вызов нестатического метода статически вызывает ошибку Error. До PHP 8.0.0 это привело бы к уведомлению об устаревании, и $this не была бы определена.
Пример #2 Некоторые примеры псевдо-переменной $this
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this определена (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this не определена.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
Результат выполнения данного примера в PHP 7:
$this определена (A) Deprecated: Non-static method A::foo() should not be called statically in %s on line 27 $this не определена. Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена. Deprecated: Non-static method B::bar() should not be called statically in %s on line 32 Deprecated: Non-static method A::foo() should not be called statically in %s on line 20 $this не определена.
Результат выполнения данного примера в PHP 8:
$this определена (A) Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27 Stack trace: #0 {main} thrown in %s on line 27
Начиная с PHP 8.2.0, класс может быть помечен модификатором readonly. Пометка класса как readonly добавит модификатор readonly к каждому объявленному свойству и предотвратит создание динамических свойств. Более того, невозможно добавить их поддержку с помощью атрибута AllowDynamicProperties. Попытка это сделать приведёт к ошибке компиляции.
<?php
#[\AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
Поскольку ни нетипизированные, ни статические свойства
не могут быть помечены модификатором readonly
,
классы, доступные только для чтения также не могут их объявлять:
<?php
readonly class Foo
{
public $bar;
}
// Fatal error: Readonly property Foo::$bar must have type
?>
<?php
readonly class Foo
{
public static int $bar;
}
// Fatal error: Readonly class Foo cannot declare static properties
?>
Класс readonly может быть расширен тогда и только тогда, когда дочерний класс также является классом readonly.
Для создания экземпляра класса используется директива new
.
Новый объект всегда будет создан, за исключением случаев, когда он содержит
конструктор, в котором определён вызов
исключения в случае возникновения ошибки.
Рекомендуется определять классы до создания их экземпляров
(в некоторых случаях это обязательно).
Если с директивой new
используется строка (string),
содержащая имя класса, то будет создан новый экземпляр этого класса. Если
имя находится в пространстве имён, то оно должно быть задано полностью.
Замечание:
В случае отсутствия аргументов в конструктор класса, круглые скобки после названия класса можно опустить.
Пример #3 Создание экземпляра класса
<?php
$instance = new SimpleClass();
// Это же можно сделать с помощью переменной:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>
Начиная с PHP 8.0.0, поддерживается использование оператора new
с произвольными выражениями.
Это позволяет создавать более сложные экземпляры, если выражение представлено в виде строки (string).
Выражения должны быть заключены в круглые скобки.
Пример #4 Создание экземпляра с использованием произвольного выражения
В данном примере мы показываем несколько вариантов допустимых произвольных выражений, которые представляют имя класса.
Пример вызова функции, конкатенации строк и константы ::class
.
<?php
class ClassA extends \stdClass {}
class ClassB extends \stdClass {}
class ClassC extends ClassB {}
class ClassD extends ClassA {}
function getSomeClass(): string
{
return 'ClassA';
}
var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>
Результат выполнения данного примера в PHP 8:
object(ClassA)#1 (0) { } object(ClassB)#1 (0) { } object(ClassC)#1 (0) { } object(ClassD)#1 (0) { }
В контексте класса можно создать новый объект через
new self
и new parent
.
Когда происходит присвоение уже существующего экземпляра класса новой переменной, то эта переменная будет указывать на этот же экземпляр класса. То же самое происходит и при передаче экземпляра класса в функцию. Копию уже созданного объекта можно создать через её клонирование.
Пример #5 Присваивание объекта
<?php
$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 будет иметь это значение" }
Создавать экземпляры объекта можно двумя способами:
Пример #6 Создание новых объектов
<?php
class Test
{
static public function getNew()
{
return new static;
}
}
class Child extends Test
{}
$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)
Обратиться к свойству или методу только что созданного объекта можно с помощью одного выражения:
Пример #7 Доступ к свойствам/методам только что созданного объекта
<?php
echo (new DateTime())->format('Y');
?>
Результатом выполнения данного примера будет что-то подобное:
2016
Замечание: До PHP 7.1 аргументы не имели значения, если не определена функция конструктора.
Свойства и методы класса живут в разделённых "пространствах имён", так что возможно иметь свойство и метод с одним и тем же именем. Ссылки как на свойства, так и на методы имеют одинаковую нотацию, и получается, что получите вы доступ к свойству или же вызовете метод - определяется контекстом использования.
Пример #8 Доступ к свойству vs. вызов метода
<?php
class Foo
{
public $bar = 'свойство';
public function bar() {
return 'метод';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
Результат выполнения данного примера:
свойство метод
Это означает, что вызвать анонимную функцию, присвоенную переменной, напрямую не получится. Вместо этого свойство должно быть назначено, например, переменной. Можно вызвать такое свойство напрямую, заключив его в скобки.
Пример #9 Вызов анонимной функции, содержащейся в свойстве
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
Результат выполнения данного примера:
42
Класс может наследовать константы, методы и свойства другого класса используя
ключевое слово extends
в его объявлении. Невозможно наследовать несколько
классов, один класс может наследовать только один базовый класс.
Наследуемые константы, методы и свойства могут быть переопределены (за исключением случаев, когда метод или константа класса объявлены как final) путём объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределённым методам или статическим свойствам путём обращения к ним через parent::
Замечание: Начиная с PHP 8.1.0, константы можно объявлять окончательными (final).
Пример #10 Простое наследование классов
<?php
class ExtendClass extends SimpleClass
{
// Переопределение метода родителя
function displayVar()
{
echo "Расширенный класс\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
Результат выполнения данного примера:
Расширенный класс значение по умолчанию
При переопределении метода его сигнатура должна быть совместима с родительским методом.
В противном случае выдаётся фатальная ошибка или, до PHP 8.0.0,
генерируется ошибка уровня E_WARNING
.
Сигнатура является совместимой, если она соответствует правилам контравариантности, делает
обязательный параметр необязательным, добавляет только необязательные новые параметры и не ограничивает,
а только ослабляет видимость.
Это известно как принцип подстановки Барбары Лисков или сокращённо LSP.
Правила совместимости не распространяются на конструктор и
сигнатуру private
методов, они не будут выдавать фатальную ошибку
в случае несоответствия сигнатуры.
Пример #11 Совместимость дочерних методов
<?php
class Base
{
public function foo(int $a) {
echo "Допустимо\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
Результат выполнения данного примера:
Допустимо Допустимо
Следующие примеры демонстрируют, что дочерний метод, который удаляет параметр или делает необязательный параметр обязательным, несовместим с родительским методом.
Пример #12 Фатальная ошибка, когда дочерний метод удаляет параметр
<?php
class Base
{
public function foo(int $a = 5) {
echo "Допустимо\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
Пример #13 Фатальная ошибка, когда дочерний метод делает необязательный параметр обязательным.
<?php
class Base
{
public function foo(int $a = 5) {
echo "Допустимо\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
Результат выполнения данного примера в PHP 8 аналогичен:
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
Переименование параметра метода в дочернем классе не является несовместимостью сигнатуры. Однако это не рекомендуется, так как приведёт к Error во время выполнения, если используются именованные аргументы.
Пример #14 Ошибка при использовании именованных аргументов и параметров, переименованных в дочернем классе
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Передача параметров согласно контракту A::test()
$obj->test(foo: "foo", bar: "bar"); // ОШИБКА!
Результатом выполнения данного примера будет что-то подобное:
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14 Stack trace: #0 {main} thrown in /in/XaaeN on line 14
Ключевое слово class
используется
для разрешения имени класса.
Чтобы получить полное имя класса ClassName
,
используйте ClassName::class
.
Обычно это довольно полезно при работе с классами, использующими
пространства имён.
Пример #15 Разрешение имени класса
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
Результат выполнения данного примера:
NS\ClassName
Замечание:
Разрешение имён класса с использованием
::class
происходит на этапе компиляции. Это означает, что на момент создания строки с именем класса автозагрузки класса не происходит. Как следствие, имена классов раскрываются, даже если класс не существует. Ошибка в этом случае не выдаётся.Пример #16 Отсутствует разрешение имени класса
<?php
print Does\Not\Exist::class;
?>Результат выполнения данного примера:
Does\Not\Exist
Начиная с PHP 8.0.0, константа ::class
также может использоваться
для объектов. Это разрешение происходит во время выполнения, а не во время компиляции.
То же самое, что и при вызове get_class() для объекта.
Пример #17 Разрешение имени объекта
<?php
namespace NS {
class ClassName {
}
}
$c = new ClassName();
print $c::class;
?>
Результат выполнения данного примера:
NS\ClassName
Начиная с PHP 8.0.0, к свойствам и методам можно также
обращаться с помощью оператора "nullsafe": ?->
.
Оператор nullsafe работает так же, как доступ к свойству или методу, как указано выше,
за исключением того, что если разыменование объекта выдаёт null
, то
будет возвращён null
, а не выброшено исключение. Если разыменование является частью цепочки,
остальная часть цепочки пропускается.
Аналогично заключению каждого обращения в is_null(), но более компактный.
Пример #18 Оператор Nullsafe
<?php
// Начиная с PHP 8.0.0, эта строка:
$result = $repository?->getUser(5)?->name;
// Эквивалентна следующему блоку кода:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
Замечание:
Оператор nullsafe лучше всего использовать, когда null считается допустимым и ожидаемым значением для возвращаемого свойства или метода. Для индикации ошибки предпочтительнее выбрасывать исключение.