Перегрузка в PHP

Перегрузка в PHP

Перегружаемся в PHP

Поговорим о перегрузке методов и свойств в PHP, зачем и нужно ли это вообще

И сразу с ходу удар под дых - хотя в документации официального сайта PHP есть специальный раздел «Перегрузка» , это ни в коем случае не является перегрузкой. Парам-пам!

Потому что, если мы проверим определение перегрузки функции или перегрузки метода, это будет звучать примерно так:

Перегрузка функций или методов - это возможность создавать несколько функций с одним и тем же именем с разными реализациями.

Проще говоря, «перегрузка означает многократное объявление функции с другим набором параметров» . Это будет выглядеть так:


function getValue($a) 
{
return $a;
}

function getValue($a, $b)
{
return $a + $b;
}

echo getValue(10); // вывод "10"
echo getValue(10, 5); // должен вывести "15"

 Ну и на данный момент в PHP нет возможности сделать это. Или можно? Давай-те посмотрим.

Перегрузка в ООП

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

Но разные люди говорят разные вещи, кто-то, что нам не нужно в принципе перегружать в PHP, кто-то, что в PHP есть только частичная поддержка перегрузки и т.д. И каждый отчасти прав, в PHP есть путь, с помощью которого вы все же можете реализовать перегрузку.

Перегрузка в PHP

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

Магические методы начинаются с __ (двойное подчеркивание) и автоматически вызываются PHP. Кроме того, они всегда определяются внутри, а не вне класса. Различные магические методы: __get(), __set(), __ construct(), __destruct(), __call(), __callStatic(), __isset(), __unset(). Большинство магических методов будут запускаться в контексте объекта, за исключением метода __callStatic(), который используется в статическом контексте.

Типы перегрузки PHP

Перегрузку в PHP можно классифицировать как,

  • Перегрузка свойств
  • Перегрузка методов

Перегрузка свойств

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

Мы можем выполнять следующие операции с перегруженными свойствами в PHP.

  • Установка и получение перегруженных свойств.
  • Оценка настройки перегруженных свойств.
  • Отмена настройки таких свойств.


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

  • __set() - срабатывает при инициализации перегруженных свойств.
  • __get() - срабатывает при использовании перегруженных свойств.
  • __isset() - этот магический метод вызывается, когда мы проверяем перегруженные свойства с помощью функции isset()
  • __unset() - аналогично, эта функция будет вызываться при использовании PHP unset() для перегруженных свойств.

Еще проще понять срабатывание этих методов в тот момент, когда свойства, к которым они будут применимы будут недоступны. Т.е. если мы попытаемся присвоить значение недоступному свойству, то как раз сработает __set(), либо наоборот попытка чтение недоступного свойства приведет к срабатыванию __get(). Надеюсь так будет понятнее.

Итак посмотрим на пример, который предназначен для динамического создания элементов массива свойств:


class Toys
{
private $data;

public function __set($name, $value)
{
$this->data[$name] = $value;
}

public function __get($name)
{
echo "Перегруженное свойство name = " . $this->data[$name] . "\r\n";
}

public function __isset($name)
{
if (isset($this->data[$name])) {
echo "Свойство \$$name существует.\r\n>";
} else {
echo "Свойство \$$name не существует.\r\n";
}
}

public function __unset($name)
{
unset($this->data[$name]);
echo "\$$name очищен \r\n";
}
}

$objToys = new Toys;
// сеттеры и геттеры динамических свойств
$objToys->overloaded_property = "new";
echo $objToys->overloaded_property . "\r\n";
// Операции со значениями динамических свойств
isset($objToys->overloaded_property);
unset($objToys->overloaded_property);
isset($objToys->overloaded_property);

//Перегруженное свойство name = new
//Свойство $overloaded_property существует.
//$overloaded_property очищен
//Свойство $overloaded_property не существует.

В приведенном выше примере создается динамическое свойство overloaded_property. При инициализации этого динамического свойства вызывается __set() с парой имя и значение, которая инициализируется как имя и значение элемента массива свойств класса $data.

Вдобавок к этому isset() и unset() с перегруженным свойством будут запускать магические методы __isset() и __unset(). Тут все достаточно просто, перейдем к другой перегрузке. 

Перегрузка методов или функций

Если вы хотите реализовать перегрузку метода или функции в PHP, вы можете добиться этого с помощью магического метода __call() или __callStatic(). В отличие от перегрузки свойств, перегрузка метода PHP позволяет вызывать функцию как в объектном, так и в статическом контексте.

Для начала возьмем пример попроще и с явным поведением:


class Toys
{
public function __call($name, $param)
{
echo "Магический метод, вызываемый при перегрузке метода ссылкой на объект\r\n";
}

public static function __callStatic($name, $param)
{
echo "Магический метод, вызываемый при перегрузке метода со статическим доступом\r\n";
}
}

$objToys = new Toys;
$objToys->overloaded_method(); //Магический метод, вызываемый при перегрузке метода ссылкой на объект

Toys::overloaded_property(); // Магический метод, вызываемый при перегрузке метода со статическим доступом

Как видно из примера доступ к некоторому перегруженному методу с именем класса вызовет статический магический член, определенный внутри класса.

Теперь попробуем реализовать что-нибудь посложнее:


class MyClass 
{
public function __call($member, $arguments)
{
$numberOfArguments = count($arguments);

if (method_exists($this, $function = $member.$numberOfArguments)) {
call_user_func_array(array($this, $function), $arguments);
}
}

private function overloaded_method1 ($argument1)
{
return $argument1;
}

private function overloaded_method2 ($argument1, $argument2)
{
return $argument1 + $argument2;
}
}

$class = new MyClass();

echo $class->overloaded_method(10); // вывод '10'
echo $class->overloaded_method(10, 5); // вывод '15'

Как видите, при вызове метода объекта overloaded_method() класса MyClass он запускает магический метод __call, поскольку вызываемый метод overloaded_method() не существует.

И далее здесь начинается магия. Как вы понимаете, у нас есть два разных метода, вызываемых overloaded_method1() и overloaded_method2() для обработки одного аргумента и двух аргументов соответственно.

Метод __call вызывает эти методы, используя функцию call_user_func_array, основанную на количестве аргументов при вызове метода overloaded_method. И вот так вы можете добиться более сложной перегрузки методов/функций в PHP.

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

Однако, используя данный принцип и, возможно, опираясь на другие варианты использования, возможна такая форма перегрузки, при условии, что у вас есть некоторые строгие соглашения об именах в ваших функциях для их точного вызова. Как у нас  в нашем примере, overloaded_method1 и overloaded_method2. 

Напоследок

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

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


Сергей Мухин

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

Есть вопросы?

Я почти всегда в режиме онлайн

Связаться со мной