Для кого написана эта статья
Автор не будет пытаться научить читательа всяческим штучкам. Равно как и показать что-то новое, старое и убедить кого-то что-то использовать. Поводом для написания послужили вынужденные оптимизации скорости работы медленного кода. Оказалось, что медленно он работает именно из-за «Магических» методов в моем контексте. Поискав, автор не нашел четкой информации, как медленно работают магические методы — все, что нашел автор просто «медленнее», поэтому пришлось получать результаты самостоятельно. Так посмотрим же на все это с точки зрения сухой статистики!
Магия
В свое время узнав о «Магических» методах php отсюда а также из ряда других источников, автору это все понравилось, ибо нет ничего лучше, чем интеллектуальный код, который в сочитании с кешем умеет сам понимать, находить, подтягивать и отдавать то, чего от него просят.
Ихнее все
Вооружившись примером магии, автор принялся эксперементировать, да так, что ему это понравилось. Как всегда, простые примеры работают быстро, в пылу страсти азарта автор и не заметил как начал использовать заклинания методы __call, __set и __get чуть ли не в каждом создаваемом классе.
Но вот стала незадача. Казалось бы код, который логически ненагружен, стал выполняться долго не так быстро как хотелось бы. Автора загрызла жаба совесть и началось.
Тестим
Автор не желает перегружать читателя массами кода и всякой всячиной, поэтому в свободное время написал мини-кусочки специально для наглядности.
Вот они два простейших класса, не претендующих на оригинальность:
Обычный себе класс
class A{
public $a;
public function foo($arg) {
return $arg;
}
}
Класс с магическим метидом
class B{
public $params;
public function __call($fName, $args) {
return $args[0];
}
public function __set($name, $value) {
$params[$name] = $value;
}
}
В этих самых классах методы специально сделаны очень простыми — они только возвращают то, что принимают.
Вот и тесты кусочки кода, которые вызывают методы по 1000000 10^6 раз:
__call
Тестируем на прием-отдачу обычный класс А
$a = new A();
for ($i = 0; $i < 1000000; $i++) {
$a->foo($i);
}
Тестируем на прием-отдачу обычный класс B с магической составляющей
$b = new B();
for ($i = 0; $i < 1000000; $i++) {
$b->foo($i);
}
Подоспели результаты, которые слегка удивили
Это занимало (в секундах) ~0.20 для A против ~0.63 для B
Теперь попробуем __set
Заметим, что дополнительные проверки не вводились специально для минимизации погрешностей.
Для А
$a = new A();
for ($i = 0; $i < 1000000; $i++) {
$a->a = 1;
}
И для B
$b = new B();
for ($i = 0; $i < 1000000; $i++) {
$b->b = 1;
}
Вот и инфографика результаты
Немножко модифицируем исходные классы
Т.е. просто совместим первый и второй тесты
class A{
public $a;
public function foo($arg) {
$this->a = $arg;
return $arg;
}
}
class B{
public $params;
public function __call($fName, $args) {
$this->b = $args[0];
return $args[0];
}
public function __set($name, $value) {
$params[$name] = $value;
}
}
И выполним тесты из первого примера — вызов метода.
Результаты говорят сами за себя
Выводы
Автор, порефакторив код, написав нужные методы, все-же оставил в ряде мест эту магию, но впредь будет вести себя значительно осторожнее с заклинаниями такими методами, т.к. известно, что Любая палка о двух концах. Спасибо за внимание!
Автор: aimodify