PHP включает полную объектную модель. Некоторые из его особенностей: видимость , абстрактные и окончательные классы и методы, дополнительные магические методы , интерфейсы и клонирование .

PHP обрабатывает объекты так же, как ссылки или дескрипторы, что означает, что каждая переменная содержит ссылку на объект, а не копию всего объекта.

Основные определения классов начинаются с ключевого слова class, за которым следует имя класса, за которым следует пара фигурных скобок, заключающих в себе определения свойств и методов, принадлежащих классу.

Имя класса может быть любой допустимой меткой, при условии, что это не зарезервированное слово PHP . Допустимое имя класса начинается с буквы или знака подчеркивания, за которым следует любое количество букв, цифр или знаков подчеркивания. В качестве регулярного выражения это будет выглядеть так: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$.

Класс может содержать свои собственные константы , переменные (называемые «свойствами») и функции (называемые «методами»).

Пример #1 Простое определение класса

<?php
class SimpleClass
{
    // property declaration
    public $var = 'a default value';

    // method declaration
    public function displayVar() {
        echo $this->var;
    }
}
?>

Псевдопеременная $this доступна, когда метод вызывается из контекста объекта. $это значение вызывающего объекта.

Предупреждение

Вызов нестатического метода статически вызывает ошибку . До PHP 8.0.0 это генерировало уведомление об устаревании, а $this было неопределенным.

Пример #2 Некоторые примеры использования псевдопеременной $this

<?php
class A
{
    function foo()
    {
        if (isset($this)) {
            echo '$this is defined (';
            echo get_class($this);
            echo ")\n";
        } else {
            echo "\$this is not defined.\n";
        }
    }
}

class B
{
    function bar()
    {
        A::foo();
    }
}

$a = new A();
$a->foo();

A::foo();

$b = new B();
$b->bar();

B::bar();
?>

New

Чтобы создать экземпляр класса, newнеобходимо использовать ключевое слово. Объект всегда будет создаваться, если для объекта не определен конструктор , который выдает исключение при ошибке. Классы должны быть определены до создания экземпляра (и в некоторых случаях это требование).

Если строка , содержащая имя класса, используется с new, будет создан новый экземпляр этого класса. Если класс находится в пространстве имен, при этом должно использоваться его полное имя.

Примечание :

Если нет аргументов для передачи конструктору класса, круглые скобки после имени класса могут быть опущены.

Пример #3 Создание экземпляра

<?php
$instance = new SimpleClass();

// This can also be done with a variable:
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
?>

Начиная с PHP 8.0.0 поддерживается использование newс произвольными выражениями. Это позволяет создавать более сложные экземпляры, если выражение создает строку . Выражения должны быть заключены в круглые скобки.

Пример #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:

объект (класс A) № 1 (0 ) { } объект (класс B) № 1 (0) { } объект (класс C) № 1 (0) { } объект (класс D) № 1 (0) { }

В контексте класса можно создать новый объект с помощью new selfи new parent.

При назначении уже созданного экземпляра класса новой переменной новая переменная будет обращаться к тому же экземпляру, что и назначенный объект. Такое же поведение происходит при передаче экземпляров в функцию. Копию уже созданного объекта можно сделать, клонировав его.

Пример #5 Назначение объекта

<?php

$instance = new SimpleClass();

$assigned   =  $instance;
$reference  =& $instance;

$instance->var = '$assigned will have this value';

$instance = null; // $instance and $reference become 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);
?>

Приведенный выше пример выведет:

логическое (истинное) 
логическое (истинное) 
логическое (истинное)

Можно получить доступ к члену вновь созданного объекта в одном выражении:

Пример #7 Доступ к члену только что созданного объекта

<?php
echo (new DateTime())->format('Y');
?>

Приведенный выше пример выведет что-то похожее на: 2016

Примечание . До версии PHP 7.1 аргументы не оценивались, если функция-конструктор не определена.

Свойства и методы

Свойства и методы класса находятся в отдельных «пространствах имен», поэтому свойство и метод могут иметь одно и то же имя. Ссылка как на свойство, так и на метод имеет одинаковую нотацию, и будет ли осуществляться доступ к свойству или будет вызываться метод, зависит исключительно от контекста, т. е. от того, является ли использование доступом к переменной или вызовом функции.

Пример #8 Доступ к свойству и вызов метода

<?php
class Foo
{
    public $bar = 'property';
    
    public function bar() {
        return 'method';
    }
}

$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;

Приведенный выше пример выведет: method property

Это означает, что вызов анонимной функции , назначенной свойству, напрямую невозможен. Вместо этого свойство должно быть сначала присвоено, например, переменной. Такое свойство можно вызвать напрямую, заключив его в круглые скобки.

Пример #9 Вызов анонимной функции, хранящейся в свойстве

<?php
class Foo
{
    public $bar;
    
    public function __construct() {
        $this->bar = function() {
            return 42;
        };
    }
}

$obj = new Foo();

echo ($obj->bar)(), PHP_EOL;

Приведенный выше пример выведет: 42

Extend

Класс может наследовать константы, методы и свойства другого класса, используя ключевое слово extendsв объявлении класса. Невозможно расширить несколько классов; класс может наследовать только от одного базового класса.

Унаследованные константы, методы и свойства можно переопределить, повторно объявив их с тем же именем, которое определено в родительском классе. Однако, если родительский класс определил метод или константу как final , они не могут быть переопределены. Доступ к переопределенным методам или статическим свойствам можно получить, сославшись на них с помощью parent:: .

Примечание . Начиная с PHP 8.1.0, константы могут быть объявлены как final.

Пример #10 Простое наследование классов

<?php
class ExtendClass extends SimpleClass
{
    // Redefine the parent method
    function displayVar()
    {
        echo "Extending class\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 "Valid\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);

Приведенный выше пример выведет:

Valid Valid

В следующих примерах показано, что дочерний метод, который удаляет параметр или делает необязательный параметр обязательным, несовместим с родительским методом.

Пример #12 Фатальная ошибка, когда дочерний метод удаляет параметр

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Valid\n";
    }
}

class Extend extends Base
{
    function foo()
    {
        parent::foo(1);
    }
}

Вывод приведенного выше примера в PHP 8 аналогичен:

Неустранимая ошибка: объявление Extend::foo() должно быть совместимо с Base::foo(int $a = 5) в /in/evtlq в строке 13.

Пример #13 Фатальная ошибка, когда дочерний метод делает необязательный параметр обязательным

<?php

class Base
{
    public function foo(int $a = 5) {
        echo "Valid\n";
    }
}

class Extend extends Base
{
    function foo(int $a)
    {
        parent::foo($a);
    }
}

Вывод приведенного выше примера в PHP 8 аналогичен:

Неустранимая ошибка: объявление Extend::foo(int $a) должно быть совместимо с Base::foo(int $a = 5) в /in/qJXVC в строке 13.

Предупреждение

Переименование параметра метода в дочернем классе не является несовместимостью сигнатур. Однако это не рекомендуется, так как это приведет к ошибке времени выполнения , если используются именованные аргументы .

Пример #14 Ошибка при использовании именованных аргументов и переименовании параметров в дочернем классе

<?php

class A {
    public function test($foo, $bar) {}
}

class B extends A {
    public function test($a, $b) {}
}

$obj = new B;

// Pass parameters according to A::test() contract
$obj->test(foo: "foo", bar: "bar"); // ERROR!

Приведенный выше пример выведет что-то похожее на:

Неустранимая ошибка: Uncaught Error: Неизвестный именованный параметр $foo в /in/XaaeN:14 
Трассировка стека: 
#0 {main} 
  добавлено в /in/XaaeN в строке 14

::class

Ключевое classслово также используется для разрешения имени класса. Чтобы получить полное имя класса, ClassName используйте ClassName::class. Это особенно полезно для классов с пространством имен .

 

Пример #15 Разрешение имени класса

<?php
namespace NS {
    class ClassName {
    }
    
    echo ClassName::class;
}
?>

Приведенный выше пример выведет:

NS\ClassName

Примечание :

Использование разрешения имени класса ::class— это преобразование времени компиляции. Это означает, что на момент создания строки имени класса автозагрузка еще не произошла. Как следствие, имена классов расширяются, даже если класс не существует. В этом случае ошибка не выдается.

Пример #16 Отсутствует разрешение имени класса

<?php
print Does\Not\Exist::class;
?>

Приведенный выше пример выведет:

Не существует

Начиная с PHP 8.0.0, эту ::classконстанту также можно использовать для объектов. Это разрешение происходит во время выполнения, а не во время компиляции. Его действие такое же, как при вызове get_class() для объекта.

Пример #17 Разрешение имени объекта

<?php
namespace NS {
    class ClassName {
    }
}
$c = new ClassName();
print $c::class;
?>

Приведенный выше пример выведет:

NS\ClassName

Nullsafe методы и свойства

Начиная с PHP 8.0.0, свойства и методы также могут быть доступны с помощью оператора «nullsafe»: ?->. Оператор nullsafe работает так же, как доступ к свойству или методу, как описано выше, за исключением того, что если разыменованный объект nullбудет null возвращен, а не будет выдано исключение. Если разыменование является частью цепочки, остальная часть цепочки пропускается.

Эффект похож на то, как если бы сначала каждый доступ был заключен в проверку is_null() , но более компактный.

 

Пример #18 Оператор Nullsafe

<?php

// As of PHP 8.0.0, this line:
$result = $repository?->getUser(5)?->name;

// Is equivalent to the following code block:
if (is_null($repository)) {
    $result = null;
} else {
    $user = $repository->getUser(5);
    if (is_null($user)) {
        $result = null;
    } else {
        $result = $user->name;
    }
}
?>