Что такое self php

PHP: Разница между self. static:: и parent::

В этой статье мы обсудим различия между self:: , static:: и parent:: в PHP. Также расскажем, когда и почему вы можете использовать каждый из них в своём коде.

Вступление

При работе с PHP-кодом часто встречаются parent:: , static:: и self:: . Но если вы начинающий разработчик, то можете не знать, что они делают и как отличаются.

Я признаюсь, что когда был начинающим разработчиком, то долго считал, что static:: и self:: это одно и тоже.

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

Что такое parent:: ?

Начнём с разговора о parent:: .

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

Давайте представим, что у нас есть класс BaseTestCase с методом setUp :

class BaseTestCase 

public function setUp(): void

echo 'Run base test case set up here. ';
>
>

(new BaseTestCase())->setUp();

// Output is: "Run base test case set up here. ';

Как мы видим, когда мы вызываем метод setUp , он работает, как и ожидалось, и выводит текст.

Теперь давайте представим, что мы хотим создать новый класс FeatureTest , который наследует класс BaseTestCase . Если бы мы хотели запустить метод setUp класса FeatureTest , мы могли бы сделать это следующим образом:

class FeatureTest extends BaseTestCase 

//
>

(new FeatureTest())->setUp();

// Output is: "Run base test case set up here. ";

Как видите, мы не определили метод setUp в нашем FeatureTest , поэтому вместо него будет запущен метод, определённый в BaseTestCase .

Теперь предположим, что мы хотим запустить дополнительную логику при запуске метода setUp в нашем FeatureTest . Например, если бы эти классы были тестовыми примерами, использующимися как часть PHPUnit теста, мы могли бы захотеть сделать такие вещи, как создание моделей в базе данных или установка тестовых значений.

Сначала вы можете (ошибочно) подумать, что можно просто определить метод setUp в классе FeatureTest и вызвать $this->setUp() . Честно говоря, я всегда попадал в эту ловушку, когда начинал изучать программирование!

Таким образом, наш код может выглядеть так:

class FeatureTest extends BaseTestCase 

public function setUp(): void

$this->setUp();

echo 'Run extra feature test set up here. ';
>
>

(new FeatureTest())->setUp();

Но вы обнаружите, что если бы мы запустили этот код, он бы зациклился, что привело бы к сбою вашего приложения. Это связано с тем, что мы просим SetUp снова и снова рекурсивно вызывать себя. Скорее всего вы получите вывод, подобный этому:

Fatal error: Out of memory (allocated 31457280 bytes) (tried to allocate 262144 bytes) in /in/1MXtt on line 15 

mmap() failed: [12] Cannot allocate memory

mmap() failed: [12] Cannot allocate memory

Process exited with code 255.

Вместо использования $this->setUp() нам нужно указать PHP использовать метод setUp из BaseTestCase . Для этого мы должны заменить $this->setUp() на parent::setUp() :

class FeatureTest extends BaseTestCase 

public function setUp(): void

parent::setUp();

echo 'Run extra feature test set up here. ';
>
>

(new FeatureTest())->setUp();

// Output is: "Run base test case set up here. Run extra feature test set up here. ";

Теперь, при вызове метода setUp в классе FeatureTest , сначала выполняется код из BaseTestCase , а затем продолжается работа остального кода определённого в дочернем классе.

Стоит отметить, что не всегда нужно размещать вызов parent:: в начале метода. На самом деле его можно разместить там, где хотите, чтобы он лучше соответствовал назначению кода. Например, если хотите сначала запустить свой код в классе FeatureTest , а затем выполнить код из BaseTestCase , вы можете переместить вызов parent::setUp() в конец метода:

class FeatureTest extends BaseTestCase 

public function setUp(): void

echo 'Run extra feature test set up here. ';

parent::setUp();
>
>

(new FeatureTest())->setUp();

// Output is: "Run extra feature test set up here. Run base test case set up here. ";

Что такое self:: ?

Теперь давайте взглянем на self:: .

Представим, что у нас есть класс Model со статическим свойством connection и методом makeConnection . Мы так же представим, что у нас есть класс User наследующий класс Model и переопределяющий свойство connection .

Эти классы могут выглядеть так:

class Model 

public static string $connection = 'mysql';

public function makeConnection(): void

echo 'Making connection to: '.self::$connection;
>
>

class User extends Model

public static string $connection = 'postgres';
>

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

(new Model())->makeConnection(); 

// Output is: "Making connection to mysql"

(new User())->makeConnection();

// Output is: "Making connection to mysql";

Как видим, оба вызова в результате использовали свойство connection класса Model . Это связано с тем, что self:: использует свойство, определённое в классе, в котором существует метод. В обоих случаях метод вызывается makeConnection класса Model , поскольку он не определён в классе User .

Чтобы нагляднее продемонстрировать это, мы продублируем метод makeConnection в классе User :

class Model 

public static string $connection = 'mysql';

public function makeConnection(): void

echo 'Making connection to: '.self::$connection;
>
>

class User extends Model

public static string $connection = 'postgres';

public function makeConnection(): void

echo 'Making connection to: '.self::$connection;
>
>

Теперь, если снова вызвать оба этих метода, мы получим следующий результат:

(new Model())->makeConnection(); 

// Output is: "Making connection to mysql"

(new User())->makeConnection();

// Output is: "Making connection to postgres";

Как видите, вызов метода makeConnection в классе User будет использовать свойство connection класса User , потому что в теперь в нём существует этот метод.

Что такое static:: ?

Теперь, когда у нас есть представление, что делает метод self:: , давайте взглянем на static:: .

Чтобы лучше понять, что он делает, давайте обновим код, используя static:: вместо self:: :

class Model 

public static $connection = 'mysql';

public function makeConnection()

echo 'Making connection to: '.static::$connection;
>
>

class User extends Model

public static $connection = 'postgres';
>

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

new Model())->makeConnection(); 

// Output is: "Making connection to mysql"

(new User())->makeConnection();

// Output is: "Making connection to postgres";

Как видите этот результат отличается от того, когда использовался self::$connection ранее. Вызов метода makeConnection в классе User использует свойство connection класса User , а не класса Model (где этот метод фактически размещён). Это связано с функцией PHP называемой позднее статическое связывание .

Если вам интересно узнать больше о позднем статическом связывании, вы можете почитать о ней в документации по PHP.

Согласно документации PHP:

Само название «позднее статическое связывание» отражает в себе внутреннюю реализацию этой особенности. «Позднее связывание» отражает тот факт, что обращения через static:: не будут вычисляться по отношению к классу, в котором вызываемый метод определён, а будут вычисляться на основе информации в ходе исполнения. Также эта особенность была названа «статическое связывание» потому, что она может быть использована (но не обязательно) в статических методах.

Таким образом, в нашем примере используется свойство connection класса User , потому, что мы вызываем метод makeConnection в том же самом классе.

Следует отметить, что если бы свойство connection не существовало в классе User , вместо этого было бы использовано свойство класса Model .

Что использовать self:: или static::

Теперь, когда у нас есть общее представление о разнице между self:: и static:: , давайте быстро рассмотрим, как решить, что из них использовать в вашем собственном коде.

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

В общем, я бы использовал static:: вместо self:: , потому что хотел бы, чтобы мои классы были расширяемыми и обеспечивали поддержку, если они унаследованы.

Предположим я хочу написать класс, от которого я намерен полностью наследовать дочерний класс (например, класс BaseTestCase из ранее приведённого примера). Если бы я действительно не хотел предотвратить, чтобы дочерний класс переопределял свойство или метод, я бы использовал static:: .

Это означало бы, что можно быть уверенным, что если я переопределю любой из статических методов или свойств, мой дочерний класс будет использовать мои переопределения. Я не могу сказать, сколько раз сталкивался с ошибками в своём коде, когда использовал self:: в родительском классе, а затем не мог понять, почему мой дочерний класс не использует мой переопределение!

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

Значит, если бы вы следовали этому принципу, вам не нужно было бы использовать static:: , потом что вы никогда не будете расширять родительский класс. Если вы хотите убедиться, что класс нельзя расширить, вы можете сделать ещё один шаг в этом направлении и использовать ключевое final при определении класса. Использование ключевого слова final предотвращает наследование класса, потому это может снизить ваше беспокойство, что класс может быть случайно расширен и это приведёт к потенциальным ошибкам.

В общем, лучше всего в каждом конкретном случае во время написания кода решать, следует использовать static:: или self:: .

Заключение

Надеюсь эта статья дала вам представление о разнице между static:: , self:: и parent:: .

Источник

Читайте также:  Открылась wp login php
Оцените статью