2013-10-30 php-apply

Материал из YourcmcWiki
Перейти к: навигация, поиск

Вот пишут люди про PHP ерунду всякую — фрактал плохого дизайна… сравнение нетранзитивное… ещё что-то там… Так вот, это всё фигня и вообще не расстраивает ни разу (разве что соглашусь, что синтаксис и реализация неймспейсов дебильны).

А вот что меня реально всегда расстраивало — так это то, что в PHP классы после загрузки нельзя модифицировать извне. Ну то есть можно — есть runkit, и говорят, что он в последнее время даже работает (когда я его пробовал 2-3 года назад — не работал, крашился). Но он довольно нетривиален, делает всякую магию — копирует байткод функции, меняет переходы… Не факт, что это быстро, и также не факт, что не отвалится в будущем.

Особенно «модификация класса извне» становится актуальна с появлением trait’ов (примесей), которые авторы реализовали, но реализовали как-то косо и по моему ощущению они там ни к селу, ни к городу. Вроде хочется, чтобы trait был таким себе плагинчиком, который можно подоткнуть в класс в любой момент, ан нет — его надо use внутри самого определения класса, да ещё применить довольно странный синтаксис разрешения конфликтов имён:
class X { use Trait1, Trait2 { Trait1::a insteadof Trait2; } }

Если взглянуть немного шире, становится ясно, что зачастую сам класс-то менять, в общем, и не обязательно, достаточно сделать ему apply(), как в js — то есть вызвать внешнюю функцию в контексте произвольного объекта. Но и этого в php сделать нельзя, потому что $this — это не просто первый аргумент функции, как в перле или питоне, а некое поле внутренних структур движка, которое просто так не поменяешь. Ну, и ещё в отличие от js есть protected/private методы.

Так вот — сегодня меня вштырило и я этот apply родил в виде тривиального экстенжна с одной функцией — apply_user_func(object $object, callable $callback, array $args).

С ним можно делать вот так:

class A
{
    var $b = "abc\n";
    private function f_a()
    {
        print "Опа!\n";
    }
}
 
function f()
{
    print $this->b; // нам будет доступно поле класса A
    $this->f_a(); // и его private метод тоже будет доступен
}
 
$a = new A();
apply_user_func($a, 'f', array()); 

Экстенжн реально тривиальный, всего сотня с лишним строчек, из которых НЕ-копипасты — на самом деле вообще строчек 8. Ну, тест ещё 76 строчек, ок. По скорости работает чууть-чуть помедленнее, чем call_user_func_array с аналогичной семантикой, и где-то на 80% медленнее, чем обычный вызов функции.

В общем, можно юзать :) под 5.4 и 5.5 работает.

[ Хронологический вид ]Комментарии

(нет элементов)

Войдите, чтобы комментировать.