Как стать автором
Обновить

C#-like cобытия для PHP. Reflection, closures…

PHP
Задача — сделать на PHP эвенты а-ля C# т.е. произвольный объект может генерировать события. Другие объекты могут на эти события подписываться непосредственно у экземпляра генерирующего объекта.

Основное отличие от того что видел ранее — строгая проверка навешиваемого хука. Проверяется наличие метода, количество его аргументов, etc…

  1. <?php
  2. abstract class EventClass {
  3. /**
  4. * Хранятся колбэки для событий
  5. *
  6. * @var array
  7. */
  8. protected $eventRecievers = array();
  9. /**
  10. * События - массив строк
  11. *
  12. * @var array
  13. */
  14. protected $myEvents = array();
  15. /**
  16. * Сеттер для событий
  17. *
  18. * @param string $prop Событие
  19. * @param callback $val Колбэк
  20. */
  21. function __set($prop, $val)
  22. {
  23. if(in_array($prop, $this->myEvents))
  24. $this->attachEvent($prop, $val);
  25. else
  26. throw new Exception("Property {$prop} is not found or read only");
  27. }
  28. /**
  29. * Caller overloader
  30. *
  31. * @param string $name Имя вызываемого метода
  32. * @param array $args Массив аргументов
  33. */
  34. function __call($name, $args)
  35. {
  36. if(in_array($name, $this->myEvents))
  37. $this->fireEvent($name, $args);
  38. else
  39. throw new Exception("No such event or bad arguments given");
  40. }
  41. /**
  42. * Присоединяет хэндлер события к выбранному событию
  43. *
  44. * @param srting $event Событие
  45. * @param callback $callback Колбэк
  46. */
  47. protected function attachEvent($event, $callback)
  48. {
  49. if(in_array($event, $this->myEvents))
  50. {
  51. if(is_array($callback) && is_object($callback[0]) && is_string($callback[1]))
  52. {
  53. $reflection = new ReflectionClass(get_class($callback[0]));
  54. try {
  55. $method = $reflection->getMethod($callback[1]);
  56. if($method->getNumberOfParameters() == 1)
  57. $this->eventRecievers[$event][]=$callback;
  58. else
  59. throw new Exception(get_class($callback[0])."::{$callback[1]} have more than 1 arguments");
  60. }
  61. catch(Exception $e) {
  62. throw new Exception("Class ".get_class($callback[0])." doesn't have method {$callback[1]}");
  63. }
  64. }
  65. else
  66. throw new Exception("Wrong callback format");
  67. }
  68. else
  69. throw new Exception(__CLASS__." has no event {$event}");
  70. }
  71. /**
  72. * "Зажигает" событие
  73. *
  74. * @param string $event
  75. * @param EventArgument $argument
  76. */
  77. protected function fireEvent($event, $argument)
  78. {
  79. if(in_array($event, $this->myEvents))
  80. {
  81. if(is_array($this->eventRecievers[$event]) && count($this->eventRecievers[$event]))
  82. foreach($this->eventRecievers[$event] as $callback)
  83. call_user_func_array($callback, array(new EventArgument($this, $argument)));
  84. }
  85. else
  86. throw new Exception(__CLASS__." have no event {$event}");
  87. }
  88. }
  89. class CanGenerateEvents extends EventClass {
  90. protected $myEvents = array('onSomething', 'onSomethingElse');
  91. // Code goes here
  92. function makeSomething()
  93. {
  94. // Doing something useful...
  95. $this->onSomething("someText");
  96. }
  97. }
  98. class WantSomething {
  99. function recieveHere(EventArgument $arg)
  100. {
  101. echo $arg->argument;
  102. }
  103. }
  104. $w = new WantSomething();
  105. $g = new CanGenerateEvents();
  106. $g->onSomething = array($w, 'recieveHere');
  107. ?>
* This source code was highlighted with Source Code Highlighter.

Когда в PHP повится метод ReflectionMethod::getClosure() (который, судя по всему, не войдет в 5.3 хотя ранее присутствовал в документации) можно будет отказаться от использования call_user_func_array и использовать замыкания, заменив строку 63 на следующий код
$this->eventRecievers[$event][]=$method->getClosure();

* This source code was highlighted with Source Code Highlighter.

А строку в функции fireEvent на следующее…
$callback(new EventArgument($this, $argument));

* This source code was highlighted with Source Code Highlighter.


UPD: Забытый класс EventArgument
class EventArgument {

public $caller = null;

public $argument = null;

function __construct($caller, $argument = null)
{
$this->caller = $caller;
$this->argument = $argument;
}

}


* This source code was highlighted with Source Code Highlighter.

Теги:eventsсобытияreflectionclosureзамыканияphp5
Хабы: PHP
Всего голосов 19: ↑10 и ↓9+1
Просмотры1.1K
Комментарии Комментарии 17

Похожие публикации

Лучшие публикации за сутки