Форумы портала PHP.SU » PHP » Пользовательские функции » Класс, реализующий примеси в PHP

Страниц (1): [1]
 

1. EuGen - 27 Марта, 2012 - 14:55:09 - перейти к сообщению
Обсуждалось в теме:
http://forum.php.su/topic.php?fo...2&topic=1661
- вопрос об осуществлении множественного наследования в PHP.
Представляю вашему вниманию класс Inheritance, который позволяет наследовать от произвольных классов без их модификации, а так же сохраняющий области видимости при вызове методов.
PHP:
скопировать код в буфер обмена
  1. class Exception_Callback_Absent extends Exception
  2. {
  3.     function __construct()
  4.     {
  5.         parent::__construct();
  6.     }
  7. }
  8.  
  9. class Exception_Callback_Deny extends Exception
  10. {
  11.     function __construct()
  12.     {
  13.         parent::__construct();
  14.     }
  15. }
  16.  
  17. class Inheritance
  18. {
  19.     protected $_rgInheritance=array();
  20.     protected function createInheritance($rParent, $bAllowInstances=false)
  21.     {
  22.         $bReject=false;
  23.         if(get_class($this)!=get_class($rParent))
  24.         {
  25.             foreach($this->_rgInheritance as $rClass)
  26.             {
  27.                 if(get_class($rClass)==get_class($rParent))
  28.                 {
  29.                     $bReject=true;
  30.                     break;
  31.                 }
  32.             }
  33.         }
  34.         else
  35.         {
  36.             $bReject=true;
  37.         }
  38.         if($bAllowInstances || !$bReject)
  39.         {
  40.             $this->_rgInheritance[]=$rParent;
  41.         }
  42.     }
  43.     public function __call($sMethod, $rgParameters)
  44.     {
  45.         $fnBind = function($sMethod, $rgParameters)
  46.         {            
  47.             if(method_exists($this, $sMethod))
  48.             {
  49.                 $rReflect = new ReflectionMethod($this, $sMethod);
  50.                 if($rReflect->isPrivate())
  51.                 {
  52.                     throw new Exception_Callback_Deny();
  53.                 }
  54.                 return $this->$sMethod($rgParams);
  55.             }
  56.             else
  57.             {
  58.                 throw new Exception_Callback_Absent();
  59.             }
  60.         };
  61.         $bExecuted=false;
  62.         foreach($this->_rgInheritance as $rParent)
  63.         {
  64.             try
  65.             {
  66.                 $fnBind = $fnBind->bindTo($rParent, $rParent);
  67.                 $fnBind($sMethod, $rgParameters);
  68.                 $bExecuted=true;
  69.                 break;
  70.             }
  71.             catch(Exception_Callback_Deny $e)
  72.             {
  73.                 throw $e;
  74.             }
  75.             catch(Exception_Callback_Absent $e)
  76.             {
  77.                 continue;
  78.             }
  79.         }
  80.         if(!$bExecuted)
  81.         {
  82.             throw new Exception_Callback_Absent();
  83.         }
  84.     }
  85. }

Если предположить, что у нас есть:
PHP:
скопировать код в буфер обмена
  1. class Foo
  2. {
  3.    public function doFoo()
  4.    {
  5.       echo('called doFoo()'.PHP_EOL);
  6.    }
  7.  
  8.    protected function protectedFoo()
  9.    {
  10.       echo('called protectedFoo()'.PHP_EOL);
  11.    }
  12. }
  13.  
  14. class Bar
  15. {
  16.    public function doBar()
  17.    {
  18.       echo('called doBar()'.PHP_EOL);
  19.    }
  20.  
  21.    private function privateBar()
  22.    {
  23.       echo('called privateBar()'.PHP_EOL);
  24.    }
  25. }

То можно создать несложный пример использования:
PHP:
скопировать код в буфер обмена
  1. class FooBar extends Inheritance
  2. {
  3.    public function __construct()
  4.    {
  5.       $this->createInheritance(new Foo);
  6.       $this->createInheritance(new Bar);
  7.    }
  8. }
  9.  
  10. $rTest = new FooBar();
  11. $rTest->doFoo();
  12. $rTest->doBar();
  13. $rTest->protectedFoo();
  14. $rTest->privateBar();
  15. $rTest->notExists();

(сохранил в "Пользовательских функциях", чтобы легче было в дальнейшем отыскать).
2. DeepVarvar - 28 Июня, 2012 - 09:41:09 - перейти к сообщению
EuGen, а в строке 82 случаем не UNCAUGHT Exception ? Если нет, то почему? Я не нашел обертки try в данном контексте.
3. EuGen - 28 Июня, 2012 - 09:51:36 - перейти к сообщению
DeepVarvar
В случае, если была сделана попытка вызвать несуществующий метод, произойдет исключение, обрабатывать которое оставлено на усмотрение пользователя данного класса. Класс будет искать первого псевдородителя, у которого существует вызываемый метод, но если не найдет ни одного, то произойдет вызов исключения. Сделано это по той причине, что неясно, __call какого из классов-псевдородителей вызывать в этом случае, поэтому и оставлено в таком виде.
4. DeepVarvar - 29 Июня, 2012 - 00:14:12 - перейти к сообщению
А, вот теперь понятно - с умыслом и про запас.
5. DlTA - 29 Июня, 2012 - 17:11:53 - перейти к сообщению
а мне вот интересно а это действительно настолько больная проблема, отсутствие множественного наследования?
6. EuGen - 29 Июня, 2012 - 17:30:17 - перейти к сообщению
DlTA
Ну, класс выше был написан исключительно из академического интереса, так как с появлением php 5.4 и traits (примесей) в нем всякого рода реализации множественного наследования уже не актуальны.
Само множественное наследование, строго говоря, не является необходимым приемом, так как любая система может быть спроектирована без его использования. Однако иногда оно может быть полезным и изящным решением некоторых проблем.
В моей практике не было случая, когда была большая нужда в такого рода построениях, так что, думаю, то, что множественного наследования нет в php - не большая беда.
7. Crate - 29 Июня, 2012 - 17:33:57 - перейти к сообщению
EuGen
А почему разрабы PHP не добавили такой возможности в сам язык? Просто очень интересно.
8. DeepVarvar - 29 Июня, 2012 - 17:52:36 - перейти к сообщению
Ну например для ACL с перекрестными правами ролей.
Хотя я не юзаю это, во всяком случае пока.
Как только понадобится - буду использовать.
9. caballero - 29 Июня, 2012 - 18:04:51 - перейти к сообщению
Crate пишет:
А почему разрабы PHP не добавили такой возможности в сам язык? Просто очень интересно.

потому что это сильно осложнит написание компилятора.
10. EuGen - 29 Июня, 2012 - 18:34:58 - перейти к сообщению
Что ж, настоящего компилятора никто и не писал, но тем не менее - нет такой нужды. Без множественного наследования можно обойтись и в примере DeepVarvar - ну да это не так важно. И все же я не могу ответить однозначно на вопрос
Crate пишет:
А почему разрабы PHP не добавили такой возможности в сам язык? Просто очень интересно.

- ведь я не являюсь разработчиком PHP.
Однако я не удивлюсь, если в следующих версиях это будет сделано (взять хотя бы реализацию тех же traits - вещь полезная но явно не необходимая).
11. caballero - 29 Июня, 2012 - 18:59:28 - перейти к сообщению
Цитата:
Однако я не удивлюсь, если в следующих версиях это будет сделано

надеюсь что не будет иначе PHP превратится в неповоротливого монстра. Если такие языки как java и C# обходятся без множественного наследования то PHP и подавно обойдется.
Те же трейты я лично даже не представляю куда можно приткнуть так чтобы по уму а не лишь бы было. Замыкания тоже сделаны через заднее место.

Кстати раз уж в твоем примере используется интроспекция (рефлексия в смысле) то можно сделать более элегантное решение - указывать типа наследуемые классы в типа аннотациях
12. DeepVarvar - 29 Июня, 2012 - 19:12:43 - перейти к сообщению
EuGen пишет:
Без множественного наследования можно обойтись и в примере DeepVarvar
Я именно так и обхожусь - без )))
Да, верно, все это просто еще один путь решения задачи по структуре приложения.
13. Stierus - 11 Июля, 2012 - 09:03:33 - перейти к сообщению
Жень, ты б написал про паттерн decorator, все такое Улыбка

Насчет traits - это великое зло, не хотел бы я получить в наследство проект, написанный с их спользованием. Сами разработчики говорят, что это довольно сомнительное удовольствие, дебажить приложение с использованием трэйтов, что при их использовании невозможно отслеживать баги этих трейтов (в пыхе нет возможности понять, юзает класс тот или иной трейт или нет - а значит, используя метод, описаный в трейте из объекта - мы можем лишь надеятся, что он там есть Улыбка) На данный момент это один из самых вероятных источников огромного количества плавающих багов, я не вижу серьезных причин их использования.

Множественное наследование вообще ломает концепцию ООП и так же вызывает огромные сомнения по полезности Улыбка

ps
Я читал, что ты это делал из академического интереса, это я для новичков, которые задумываются над использованием подобных "хаков"
14. EuGen - 11 Июля, 2012 - 09:16:59 - перейти к сообщению
Stierus пишет:
Я читал, что ты это делал из академического интереса

Ну конечно. Практическая "ценность" этого кода - это посмотреть на использование Reflection плюс bindTo. Наверное, и все. Ну, может, показать "иллюзию" множественного наследования (хотя, какое же это наследование - если мы оперируем объектами, а не классами)
Насчет декоратора - посчитал, что это описание здесь не требуется.
Про traits - да, согласен. Использую php 5.4 отнюдь не из-за них, и, если честно, не возникло такой ситуации, где я бы не мог без них обойтись, при этом сохраняя стройность архитектуры и кода.
15. Stierus - 11 Июля, 2012 - 09:43:58 - перейти к сообщению
zzz002, не впадлу было региться что бы гадость написать? Улыбка

 

Powered by ExBB FM 1.0 RC1