Конструктор по умолчанию php

Обновлено: 23.04.2024

Для создания объекта необходимо вызвать конструктор класса. По умолчанию компилятор создает конструктор, который не принимает параметров и который мы можем использовать. Но также мы можем определять свои собственные конструкторы. Для определения конструкторов применяется ключевое слово constructor .

Классы в Kotlin могут иметь один первичный конструктор (primary constructor) и один или несколько вторичных конструкторов (secondary constructor).

Первичный конструктор

Первичный конструктор является частью заголовка класса и определяется сразу после имени класса:

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

Если первичный конструктор не имеет никаких аннотаций или модификаторов доступа, как в данном случае, то ключевое слово constructor можно опустить:

Инициализатор

Что делать с полученными через конструктор данными? Мы их можем использовать для инициализации свойств класса. Для этого применяются блоки инициализаторов:

В классе Person определено свойство name, которое хранит имя человека. Чтобы передать эту свойству значение параметра _name из первичного конструктора, применяется блок инициализатора. Блок инициализатора определяется после ключевого слова init .

Цель инициализатора состоит в инициализации объекта при его создании. Стоит отметить, что здесь свойству name не задается начальное значение, потому это свойство в любом случае будет инициализировано в блоке инициализатора, и при создании объекта оно в любом случае получит значение.

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

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

Стоит отметить, что в классе может быть определено одновременно несколько блоков инициализатора.

Также стоит отметить, что в данном случае в инициализаторе нет смысла, так как параметры первичного конструктора можно нарямую передавать свойствам:

Первичный конструктор и свойства

Первичный конструктор также может использоваться для определения свойств:

Свойства определяются как и параметры, при этом их определение начинается с ключевого слова val (если их не планируется изменять) и var (если свойства должны быть изменяемыми). И в этом случае нам уже необязательно явным образом определять эти свойства в теле класса, так как их уже определяет конструктор. И при вызове конструктора этим свойствам автоматически передаются значения: Person("Bob", 23)

Вторичные конструкторы

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

Вторичные конструкторы определяются в теле класса. Если для класса определен первичный конструктор, то вторичный конструктор должен вызывать первичный с помощью ключевого слова this :

Здесь в классе Person определен первичный конструктор, который принимает значение для установки свойства name :

И также добавлен вторичный конструктор. Он принимает два параметра: _name и _age. С помощью ключевого слова this вызывается первичный конструктор, поэтому через этот вызов необходимо передать значения для параметров первичного конструктора. В частности, в первичный конструктор передается значение параметра _name. В самом вторичном конструкторе устанавливается значение свойства age.

Таким образом, при вызове вторичного конструктора вначале вызывается первичный конструктор, срабатывает блок инициализатора, который устанавливает свойство name. Затем выполняются собственно действия вторичного конструктора, который устанавливает свойство age.

Используем данную модификацию класса Person:

В функции main создаются два объекта Person. Для создания объекта tom применяется первичный конструктор, который принимает один параметр. Для создания объекта bob применяется вторичный конструктор с двумя параметрами.

Консольный вывод программы:

При необходимости мы можем определять и больше вторичных конструкторов:

Здесь в класс Person добавлено новое свойство - company , которое описывает компании, в которой работает человек. И также добавлен еще один конструктор, который принимает три параметра:

Чтобы не дублировать код установки свойств name и age, этот вторичный конструктор передает установку этих свойств другому вторичному конструктору, который принимает два параметра, через вызов this(_name, _age) . То есть данный вызов по сути будет вызывать первый вторичный конструктор с двумя параметрами.

Basic class definitions begin with the keyword class , followed by a class name, followed by a pair of curly braces which enclose the definitions of the properties and methods belonging to the class.

The class name can be any valid label, provided it is not a PHP reserved word. A valid class name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. As a regular expression, it would be expressed thus: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$ .

A class may contain its own constants, variables (called "properties"), and functions (called "methods").

class SimpleClass
// property declaration
public $var = 'a default value' ;

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

The pseudo-variable $this is available when a method is called from within an object context. $this is the value of the calling object.

Calling a non-static method statically throws an Error . Prior to PHP 8.0.0, this would generate a deprecation notice, and $this would be undefined.

class A
function foo ()
if (isset( $this )) echo '$this is defined (' ;
echo get_class ( $this );
echo ")\n" ;
> else echo "\$this is not defined.\n" ;
>
>
>

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

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

Output of the above example in PHP 7:

Output of the above example in PHP 8:

To create an instance of a class, the new keyword must be used. An object will always be created unless the object has a constructor defined that throws an exception on error. Classes should be defined before instantiation (and in some cases this is a requirement).

If a string containing the name of a class is used with new , a new instance of that class will be created. If the class is in a namespace, its fully qualified name must be used when doing this.

Note:

If there are no arguments to be passed to the class's constructor, parentheses after the class name may be omitted.

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

As of PHP 8.0.0, using new with arbitrary expressions is supported. This allows more complex instantiation if the expression produces a string . The expressions must be wrapped in parentheses.

In the given example we show multiple examples of valid arbitrary expressions that produce a class name. This shows a call to a function, string concatenation, and the ::class constant.

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));
?>

Output of the above example in PHP 8:

In the class context, it is possible to create a new object by new self and new parent .

When assigning an already created instance of a class to a new variable, the new variable will access the same instance as the object that was assigned. This behaviour is the same when passing instances to a function. A copy of an already created object can be made by cloning it.

$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 );
?>

The above example will output:

It's possible to create instances of an object in a couple of ways:

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 );
?>

The above example will output:

It is possible to access a member of a newly created object in a single expression:

The above example will output something similar to:

Note: Prior to PHP 7.1, the arguments are not evaluated if there is no constructor function defined.

Properties and methods

Class properties and methods live in separate "namespaces", so it is possible to have a property and a method with the same name. Referring to both a property and a method has the same notation, and whether a property will be accessed or a method will be called, solely depends on the context, i.e. whether the usage is a variable access or a function call.

public function bar () return 'method' ;
>
>

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

The above example will output:

That means that calling an anonymous function which has been assigned to a property is not directly possible. Instead the property has to be assigned to a variable first, for instance. It is possible to call such a property directly by enclosing it in parentheses.

public function __construct () $this -> bar = function() return 42 ;
>;
>
>

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

The above example will output:

extends

A class can inherit the constants, methods, and properties of another class by using the keyword extends in the class declaration. It is not possible to extend multiple classes; a class can only inherit from one base class.

The inherited constants, methods, and properties can be overridden by redeclaring them with the same name defined in the parent class. However, if the parent class has defined a method or constant as final, they may not be overridden. It is possible to access the overridden methods or static properties by referencing them with parent::.

Note: As of PHP 8.1.0, constants may be declared as final.

class ExtendClass extends SimpleClass
// Redefine the parent method
function displayVar ()
echo "Extending class\n" ;
parent :: displayVar ();
>
>

$extended = new ExtendClass ();
$extended -> displayVar ();
?>

The above example will output:

Signature compatibility rules

When overriding a method, its signature must be compatible with the parent method. Otherwise, a fatal error is emitted, or, prior to PHP 8.0.0, an E_WARNING level error is generated. A signature is compatible if it respects the variance rules, makes a mandatory parameter optional, and if any new parameters are optional. This is known as the Liskov Substitution Principle, or LSP for short. The constructor, and private methods are exempt from these signature compatibility rules, and thus won't emit a fatal error in case of a signature mismatch.

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 );

The above example will output:

The following examples demonstrate that a child method which removes a parameter, or makes an optional parameter mandatory, is not compatible with the parent method.

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

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

Output of the above example in PHP 8 is similar to:

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

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

Output of the above example in PHP 8 is similar to:

Renaming a method's parameter in a child class is not a signature incompatibility. However, this is discouraged as it will result in a runtime Error if named arguments are used.

class A public function test ( $foo , $bar ) <>
>

class B extends A public function test ( $a , $b ) <>
>

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

The above example will output something similar to:

::class

The class keyword is also used for class name resolution. To obtain the fully qualified name of a class ClassName use ClassName::class . This is particularly useful with namespaced classes.

The above example will output:

Note:

The class name resolution using ::class is a compile time transformation. That means at the time the class name string is created no autoloading has happened yet. As a consequence, class names are expanded even if the class does not exist. No error is issued in that case.

The above example will output:

As of PHP 8.0.0, the ::class constant may also be used on objects. This resolution happens at runtime, not compile time. Its effect is the same as calling get_class() on the object.

The above example will output:

Nullsafe methods and properties

As of PHP 8.0.0, properties and methods may also be accessed with the "nullsafe" operator instead: ?-> . The nullsafe operator works the same as property or method access as above, except that if the object being dereferenced is null then null will be returned rather than an exception thrown. If the dereference is part of a chain, the rest of the chain is skipped.

The effect is similar to wrapping each access in an is_null() check first, but more compact.

// 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 ;
>
>
?>

Note:

The nullsafe operator is best used when null is considered a valid and expected possible value for a property or method return. For indicating an error, a thrown exception is preferable.

User Contributed Notes 13 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.

// Assignment of an object
Class Object public $foo = "bar" ;
>;

$objectVar = new Object ();
$reference =& $objectVar ;
$assignment = $objectVar

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="bar"
//
?>

$assignment has a different data slot from $objectVar, but its data slot holds a handle to the same object. This makes it behave in some ways like a reference. If you use the variable $objectVar to change the state of the Object instance, those changes also show up under $assignment, because it is pointing at that same Object instance.

$objectVar -> foo = "qux" ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
//
?>

But it is not exactly the same as a reference. If you null out $objectVar, you replace the handle in its data slot with NULL. This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.

$objectVar = null ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

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

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

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

class SimpleClass
// объявление свойства
public $var = 'значение по умолчанию' ;

// объявление метода
public function displayVar () echo $this -> var ;
>
>
?>

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

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

class A
function foo ()
if (isset( $this )) echo '$this определена (' ;
echo get_class ( $this );
echo ")\n" ;
> else echo "\$this не определена.\n" ;
>
>
>

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

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

Результат выполнения данного примера в PHP 7:

Результат выполнения данного примера в PHP 8:

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

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

Замечание:

В случае отсутствия аргументов в конструктор класса, круглые скобки после названия класса можно опустить.

// Это же можно сделать с помощью переменной:
$className = 'SimpleClass' ;
$instance = new $className (); // new SimpleClass()
?>

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

В данном примере мы показываем несколько вариантов допустимых произвольных выражений, которые представляют имя класса. Пример вызова функции, конкатенации строк и константы ::class .

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:

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

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

$instance = new SimpleClass ();

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

$instance -> var = '$assigned будет иметь это значение' ;

$instance = null ; // $instance и $reference становятся null

var_dump ( $instance );
var_dump ( $reference );
var_dump ( $assigned );
?>

Результат выполнения данного примера:

Создавать экземпляры объекта можно двумя способами:

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 );
?>

Результат выполнения данного примера:

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

Результатом выполнения данного примера будет что-то подобное:

Замечание: До PHP 7.1 аргументы не имели значения, если не определена функция конструктора.

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

Свойства и методы класса живут в разделённых "пространствах имён", так что возможно иметь свойство и метод с одним и тем же именем. Ссылки как на свойства, так и на методы имеют одинаковую нотацию, и получается, что получите вы доступ к свойству или же вызовете метод - определяется контекстом использования.

public function bar () return 'метод' ;
>
>

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

Результат выполнения данного примера:

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

public function __construct () $this -> bar = function() return 42 ;
>;
>
>

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

Результат выполнения данного примера:

extends

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

Наследуемые константы, методы и свойства могут быть переопределены (за исключением случаев, когда метод или константа класса объявлены как final) путём объявления их с теми же именами, как и в родительском классе. Существует возможность доступа к переопределённым методам или статическим свойствам путём обращения к ним через parent::

Замечание: Начиная с PHP 8.1.0, константы можно объявлять окончательными (final).

class ExtendClass extends SimpleClass
// Переопределение метода родителя
function displayVar ()
echo "Расширенный класс\n" ;
parent :: displayVar ();
>
>

$extended = new ExtendClass ();
$extended -> displayVar ();
?>

Результат выполнения данного примера:

Правила совместимости сигнатуры

При переопределении метода его сигнатура должна быть совместима с родительским методом. В противном случае выдаётся фатальная ошибка или, до PHP 8.0.0, генерируется ошибка уровня E_WARNING . Сигнатура является совместимой, если она соответствует правилам контравариантности, делает обязательный параметр необязательным и если какие-либо новые параметры являются необязательными. Это известно как принцип подстановки Барбары Лисков или сокращённо LSP. Правила совместимости не распространяются на конструктор и сигнатуру private методов, они не будут выдавать фатальную ошибку в случае несоответствия сигнатуры.

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 );

Результат выполнения данного примера:

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

class Base
public function foo ( int $a = 5 ) echo "Допустимо\n" ;
>
>

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

Результат выполнения данного примера в PHP 8 аналогичен:

class Base
public function foo ( int $a = 5 ) echo "Допустимо\n" ;
>
>

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

Результат выполнения данного примера в PHP 8 аналогичен:

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

class A public function test ( $foo , $bar ) <>
>

class B extends A public function test ( $a , $b ) <>
>

// Передача параметров согласно контракту A::test()
$obj -> test ( foo : "foo" , bar : "bar" ); // ОШИБКА!

Результатом выполнения данного примера будет что-то подобное:

::class

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

Результат выполнения данного примера:

Замечание:

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

Результат выполнения данного примера:

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

Результат выполнения данного примера:

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

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

Аналогично заключению каждого обращения в is_null() , но более компактный.

// Начиная с 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 считается допустимым и ожидаемым значением для возвращаемого свойства или метода. Для индикации ошибки предпочтительнее выбрасывать исключение.

User Contributed Notes 13 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.

// Assignment of an object
Class Object public $foo = "bar" ;
>;

$objectVar = new Object ();
$reference =& $objectVar ;
$assignment = $objectVar

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="bar"
//
?>

$assignment has a different data slot from $objectVar, but its data slot holds a handle to the same object. This makes it behave in some ways like a reference. If you use the variable $objectVar to change the state of the Object instance, those changes also show up under $assignment, because it is pointing at that same Object instance.

$objectVar -> foo = "qux" ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

//
// $objectVar --->+---------+
// |(handle1)----+
// $reference --->+---------+ |
// |
// +---------+ |
// $assignment -->|(handle1)----+
// +---------+ |
// |
// v
// Object(1):foo="qux"
//
?>

But it is not exactly the same as a reference. If you null out $objectVar, you replace the handle in its data slot with NULL. This means that $reference, which points at the same data slot, will also be NULL. But $assignment, which is a different data slot, will still hold its copy of the handle to the Object instance, so it will not be NULL.

$objectVar = null ;
print_r ( $objectVar );
print_r ( $reference );
print_r ( $assignment );

Конструкторы представляют специальные методы, которые выполняются при создании объекта и служат для начальной инициализации его свойств. Для создания конструктора надо объявить функцию с именем __construct (с двумя подчеркиваниями впереди):

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

Параметры по умолчанию

Чтобы сделать конструктор более гибким, мы можем обозначить один или несколько параметров в качестве необязательных. Тогда при создании объекта необязательно указывать все параметры. Например, изменим конструктор следующим образом:

Таким образом, если не будут заданы параметры, вместо них будут использоваться значения "Том" и 36. И теперь мы можем создать объект Person несколькими способами:

Объявление свойств через конструктор

Начиная с версии PHP 8 в языке появилась возможность определить свойства через список параметров конструктора. Любой параметр конструктора, который имеет модификатор доступа, например, public , будет автоматически представлять новое свойство.

Например, определим свойства name и age напрямую через параметры конструктора:

Хотя в данном случае в классе явным образом не определены свойства name и age, но поскольку в списке параметров конструктора заданы параметры с такими именами и с модификатором доступа (в данном случае public ), то у класса автоматически создаются подобные переменные.

Можно сочетать оба способа объявления переменных:

При подобном объявлении свойств также можно передавать им значения по умолчанию:

Деструкторы

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

Деструктор определяется с помощью функции __destruct (два подчеркивания впереди):

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

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

На уровне кода конструктор представляет метод, который называется по имени класса, который может иметь параметры, но для него не надо определять возвращаемый тип. Например, определим в классе Person простейший конструктор:

Конструкторы могут иметь модификаторы, которые указываются перед именем конструктора. Так, в данном случае, чтобы конструктор был доступен вне класса Person, он определен с модификатором public .

Определив конструктор, мы можем вызвать его для создания объекта Person:

В данном случае выражение Person() как раз представляет вызов определенного в классе конструктора (это больше не автоматический конструктор по умолчанию, которого у класса теперь нет). Соответственно при его выполнении на консоли будет выводиться строка "Создание объекта Person"

Подобным образом мы можем определять и другие конструкторы в классе. Например, изменим класс Person следующим образом:

Теперь в классе определено три конструктора, каждый из которых принимает различное количество параметров и устанавливает значения полей класса. И мы можем вызвать один из этих конструкторов для создания объекта класса.

Консольный вывод данной программы:

Ключевое слово this

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

В примере выше во втором и третьем конструкторе параметры называются также, как и поля класса. И чтобы разграничить параметры и поля класса, к полям класса обращение идет через ключевое слово this . Так, в выражении

первая часть - this.name означает, что name - это поле текущего класса, а не название параметра name. Если бы у нас параметры и поля назывались по-разному, то использовать слово this было бы необязательно. Также через ключевое слово this можно обращаться к любому полю или методу.

Цепочка вызова конструкторов

В примере выше определены три конструктора. Все три конструктора выполняют однотипные действия - устанавливают значения полей name и age. Но этих повторяющихся действий могло быть больше. И мы можем не дублировать функциональность конструкторов, а просто обращаться из одного конструктора к другому также через ключевое слово this , передавая нужные значения для параметров:

В данном случае первый конструктор вызывает второй, а второй конструктор вызывает третий. По количеству и типу параметров компилятор узнает, какой именно конструктор вызывается. Например, во втором конструкторе:

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

Стоит отметить, что в примере выше фактически все конструкторы не определяют каких-то других действий, кроме как передают третьему конструктору некоторые значения. Поэтому в реальности в данном случае проще оставить один конструктор, определив для его параметров значения по умолчанию:

И если при вызове конструктора мы не передаем значение для какого-то параметра, то применяется значение по умолчанию.

Инициализаторы объектов

Для инициализации объектов классов можно применять инициализаторы . Инициализаторы представляют передачу в фигурных скобках значений доступным полям и свойствам объекта:

С помощью инициализатора объектов можно присваивать значения всем доступным полям и свойствам объекта в момент создания. При использовании инициализаторов следует учитывать следующие моменты:

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

Инициализатор выполняется после конструктора, поэтому если и в конструкторе, и в инициализаторе устанавливаются значения одних и тех же полей и свойств, то значения, устанавливаемые в конструкторе, заменяются значениями из инициализатора.

Инициализаторы удобно применять, когда поле или свойство класса представляет другой класс:

Обратите внимание, как устанавливается поле company :

Деконструкторы

Деконструкторы (не путать с деструкторами) позволяют выполнить декомпозицию объекта на отдельные части.

Например, пусть у нас есть следующий класс Person:

В этом случае мы могли бы выполнить декомпозицию объекта Person так:

Значения переменным из деконструктора передаюся по позиции. То есть первое возвращаемое значение в виде параметра personName передается первой переменной - name, второе возващаемое значение - переменной age.

По сути деконструкторы это не более,чем синтаксический сахар. Это все равно, что если бы мы написали:

При получении значений из декоструктора нам необходимо предоставить столько переменных, сколько деконструктор возвращает значений. Однако бывает, что не все эти значения нужны. И вместо возвращаемых значений мы можм использовать прочерк _ . Например, нам надо получить только возраст пользователя:

Поскольку первое возвращаемое значение - это имя пользователя, которое не нужно, в в данном случае вместо переменной прочерк.

Читайте также: