Mixin в PHP
27 апреля 2010
В Ruby есть довольно занимательная штука, называемая mixin, позволяющая «подмешивать» в класс методы модуля. Чем-то это напоминает множественное наследование, но более точным будет назвать это делегированием методов.
В PHP достичь того же можно при помощи магического метода __call, а использовав __set, __get, __isset и __unset можно реализовать делегирование свойств.
Реализация довольно проста:
class Mixin { private $mixed = array(); public function __get($name){ foreach($this->mixed as $object){ if(property_exists($object,$name)) return $object->$name; } throw new Exception("Property $name is not defined."); } public function __set($name,$value){ foreach($this->mixed as $object){ if(property_exists($object,$name)) return $object->$name=$value; } throw new Exception("Property $name is not defined."); } public function __isset($name){ foreach($this->mixed as $object){ if(property_exists($object,$name) && isset($this->$name)) return true; } return false; } public function __unset($name){ foreach($this->mixed as $object){ if(property_exists($object,$name)) $object->$name = null; } } public function __call($name,$parameters){ foreach($this->mixed as $object){ if(method_exists($object,$name)) return call_user_func_array(array($object,$name),$parameters); } throw new Exception("Method $name is not defined."); } public function mix($name, $class){ return $this->mixed[$name]=new $class(); } }
Всё. Можно пользоваться:
class A extends Mixin { } class B { public $foo = "barn"; function test(){ echo "Success!\n"; } } $a = new A(); $a->mix('b', 'B'); $a->test(); echo $a->foo;
Кстати, во многих популярных фреймворках это уже реализовано. Точно могу сказать, что есть реализации в Yii, CakePhp, Symfony, Propel и Doctrine. Называется это чаще всего behavior.
Единственный минус, который приходит в голову — отсутствие поддержки в IDE. То есть дополнения таких «подмешанных» методов мы не получим. Хотя, в PhpStorm, возможно, это когда-нибудь будет.
Комментарии RSS по email OK
В следующей major версии PHP будут либо Mixins, либо Traits. Сейчас в internals идёт бурная разработка и обсуждение этих вещей.
А приведите, пожалуйста примеры, когда было бы оправдано применение миксинов. Я так понимаю, можно в какой-то класс подмешать несколько других классов. И после этого подмешанные классы могут обращаться к публичным методам и свойствам друг друга. И где это можно применить?
ничего нового. Вот если б как-то сделать множественное наследование да так, что бы протектед-методы вызывались из потомка. А то паблик-методы я и сам в состоянии вызывать.
Psih
Был бы благодарен за ссылку.
Neyronius
Например, для модели данных можно сделать такие штуки как Taggable (для тегирования модели), SoftDelete (для мягкого удаления) или Versionable (для сохранения версий модели). При этом их можно будет подключить к любой модели.
gildenburg
Тут надо два прокси-метода: один в mixin-е, второй в том, к чему его подмешали. Ну и вызывать соответственно из одного другой. Не очень красиво, но работать будет.
Не люблю я такие штуки... Поведение становится непредсказуемым, код - менее читаемым.
SAM
Mixin rfc
Судя по всему частично функионал mixin попадёт в Traits. Вобщем-то смотрите Traits, это то, что активно обсуждается и даже вроде патчи делаются.
Traits RFC
Можно в классе описать @property и @method.
Тогда и среда прекрасно отрабатывает автодополнение для таких классов (причем с подтягиванием типа переменной).
Самое интересное, что для ORM систем можно в итоге получать всю иеархию базы (вернее все, куда по внешним ключам можно дотянуться), просто описав поля таким образом:[url=http://docs.google.com/File?id=dchkhbfc_17fk2ft3fm_напрмер[/url].
Работает как минимум в Eclipse PDT и NetBeans.
Что-то URL криво вставился, прошу прощения.
Вот нормальный:
http://docs.google.com/File?id=dchkhbfc_17fk2ft3fm_b
Makc
Да, но описывать так не очень удобно.
Psih
Спасибо, почитаю.
Sam, а можете накидать пример такой эмуляции можественного наследования с вызовом протекед методов? Поприкольней миксин же будет)
Похоже на поведения в yii
Один большой минус этого искусственного паттерна и не совсем подходящего для PHP это что класс B подмешавшись к А не имеет полноценного доступа к свойствам А... Что в Ruby например имеет место быть с легкостью.
В Yii проблема решена ссылкой из B на A:
Написал небольшую библиотеку для реализации примесей в PHP с помощью либо включения, либо наследования. В принципе, все довольно неплохо работает
call_user_func_array(array($object,$name),$parameters); можно просто вызвать $object->$name($parameters);
Извините, а разве в одиночных кавычках
переменная $name конкатенируется ? ( sorry for my English )))
Нет, конечно :) Это опечатка
Кажется, я понял. Здесь в исключение нужно "бросать" не значение переменной $name, а её Имя, т.е.
Если я не прав, то прошу меня поправить. Спасибо.
User, я поправил. Просто кавычки были не те.
Извините, но может быть в коде
просто пропущен слеш "\" ? И нужно писАть так ?
Мы программо-кодеры, народ педантичный. Извините )))
Тоже поправил :) Спасибо.