Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • ActiveRecord
  • Cache
  • Configs
  • Core
  • DateTime
  • Debug
  • DeleteOperation
  • Errors
  • Event
  • EventHook
  • Events
  • FileCache
  • FormattedString
  • Helpers
  • I18n
  • Image
  • Inflections
  • Inflector
  • Models
  • Module
  • Modules
  • Object
  • Operation
  • PingOperation
  • Prototype
  • Route
  • Routes
  • SaveOperation
  • Session
  • TimeZone
  • TimeZoneLocation
  • Uploaded
  • Vars
  • VarsIterator

Interfaces

  • StorageInterface
  • ToArray
  • ToArrayRecursive

Traits

  • PrototypeTrait
  • ToArrayRecursiveTrait

Exceptions

  • AlreadyAuthenticated
  • AuthenticationRequired
  • Exception
  • ModuleConstructorMissing
  • ModuleIsDisabled
  • ModuleNotDefined
  • OffsetError
  • OffsetNotDefined
  • OffsetNotReadable
  • OffsetNotWritable
  • PermissionRequired
  • PropertyError
  • PropertyIsReserved
  • PropertyNotDefined
  • PropertyNotReadable
  • PropertyNotWritable
  • RouteNotDefined
  • SecurityException

Constants

  • TOKEN_ALPHA
  • TOKEN_ALPHA_UPCASE
  • TOKEN_NUMERIC
  • TOKEN_SYMBOL
  • TOKEN_SYMBOL_WIDE

Functions

  • array_flatten
  • array_insert
  • array_merge_recursive
  • camelize
  • capitalize
  • downcase
  • dump
  • escape
  • escape_all
  • exact_array_merge_recursive
  • excerpt
  • format
  • generate_token
  • generate_token_wide
  • generate_v4_uuid
  • get_autoconfig
  • humanize
  • hyphenate
  • log
  • log_error
  • log_info
  • log_success
  • log_time
  • normalize
  • normalize_namespace_part
  • normalize_url_path
  • pbkdf2
  • pluralize
  • remove_accents
  • shorten
  • singularize
  • sort_by_weight
  • stable_sort
  • strip_root
  • titleize
  • unaccent_compare
  • unaccent_compare_ci
  • underscore
  • upcase
  1 <?php
  2 
  3 /*
  4  * This file is part of the ICanBoogie package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace ICanBoogie;
 13 
 14 /**
 15  * Events collected from the "hooks" config or attached by the user.
 16  */
 17 class Events implements \IteratorAggregate
 18 {
 19     static private $jumptable = array
 20     (
 21         'get' => array(__CLASS__, 'get')
 22     );
 23 
 24     /**
 25      * Calls the callback of a patchable function.
 26      *
 27      * @param string $name Name of the function.
 28      * @param array $arguments Arguments.
 29      *
 30      * @return mixed
 31      */
 32     static public function __callstatic($name, array $arguments)
 33     {
 34         return call_user_func_array(self::$jumptable[$name], $arguments);
 35     }
 36 
 37     /**
 38      * Patches a patchable function.
 39      *
 40      * @param string $name Name of the function.
 41      * @param collable $callback Callback.
 42      *
 43      * @throws \RuntimeException is attempt to patch an undefined function.
 44      */
 45     static public function patch($name, $callback)
 46     {
 47         if (empty(self::$jumptable[$name]))
 48         {
 49             throw new \RuntimeException("Undefined patchable: $name.");
 50         }
 51 
 52         self::$jumptable[$name] = $callback;
 53     }
 54 
 55     /**
 56      * Returns the singleton instance of the class.
 57      *
 58      * @return Events
 59      */
 60     static private function get()
 61     {
 62         static $events;
 63 
 64         if (!$events)
 65         {
 66             $events = new static;
 67         }
 68 
 69         return $events;
 70     }
 71 
 72     /**
 73      * Event collection.
 74      *
 75      * @var array[string]array
 76      */
 77     protected $hooks = array();
 78 
 79     /**
 80      * Event hooks consolidated by class and type.
 81      *
 82      * @var array[string]array
 83      */
 84     protected $consolidated_hooks = array();
 85 
 86     /**
 87      * Lists of skippable events.
 88      *
 89      * @var array[string]bool
 90      */
 91     protected $skippable = array();
 92 
 93     public function __construct(array $hooks=array())
 94     {
 95         $this->hooks = $hooks;
 96     }
 97 
 98     /**
 99      * Returns an iterator for event hooks.
100      */
101     public function getIterator()
102     {
103         return new \ArrayIterator($this->hooks);
104     }
105 
106     /**
107      * Attaches an event hook.
108      *
109      * The name of the event is resolved from the parameters of the event hook. Consider the
110      * following code:
111      *
112      * <pre>
113      * <?php
114      *
115      * $events->attach(function(ICanBoogie\Operation\BeforeProcessEvent $event, ICanBoogie\SaveOperation $target) {
116      *
117      *     // …
118      *
119      * });
120      * </pre>
121      *
122      * The hook will be attached to the `ICanBoogie\SaveOperation::process:before` event.
123      *
124      * @param callable $hook The event hook.
125      *
126      * @return EventHook An {@link EventHook} instance that can be used to easily detach the event
127      * hook.
128      *
129      * @throws \InvalidArgumentException when `$hook` is not a callable.
130      */
131     public function attach($name, $hook=null)
132     {
133         if ($hook === null)
134         {
135             $hook = $name;
136             $name = null;
137         }
138 
139         if (!is_callable($hook))
140         {
141             throw new \InvalidArgumentException(format
142             (
143                 'The event hook must be a callable, %type given: :hook', array
144                 (
145                     'type' => gettype($hook),
146                     'hook' => $hook
147                 )
148             ));
149         }
150 
151         if ($name === null)
152         {
153             $name = self::resolve_event_type_from_hook($hook);
154         }
155 
156         if (!isset($this->hooks[$name]))
157         {
158             $this->hooks[$name] = array();
159         }
160 
161         array_unshift($this->hooks[$name], $hook);
162 
163         #
164         # If the event is a targeted event, we reset the skippable and consolidated hooks arrays.
165         #
166 
167         $this->skippable = array();
168 
169         if (strpos($name, '::') !== false)
170         {
171             $this->consolidated_hooks = array();
172         }
173 
174         return new EventHook($this, $name, $hook);
175     }
176 
177     /**
178      * Resolve an event type using the parameters of the specified hook.
179      *
180      * @param callable $hook
181      *
182      * @return string
183      */
184     static private function resolve_event_type_from_hook($hook)
185     {
186         if (is_array($hook))
187         {
188             $reflection = new \ReflectionMethod($hook[0], $hook[1]);
189         }
190         else if (is_string($hook) && strpos($hook, '::'))
191         {
192             list($class, $method) = explode('::', $hook);
193 
194             $reflection = new \ReflectionMethod($class, $method);
195         }
196         else
197         {
198             $reflection = new \ReflectionFunction($hook);
199         }
200 
201         list($event, $target) = $reflection->getParameters();
202 
203         $event_class = self::get_parameter_class($event);
204         $target_class = self::get_parameter_class($target);
205 
206         $event_class_base = basename('/' . strtr($event_class, '\\', '/'));
207         $type = substr($event_class_base, 0, -5);
208 
209         if (strpos($event_class_base, 'Before') === 0)
210         {
211             $type = hyphenate(substr($type, 6)) . ':before';
212         }
213         else
214         {
215             $type = hyphenate($type);
216         }
217 
218         $type = strtr($type, '-', '_');
219 
220         return $target_class . '::' . $type;
221     }
222 
223     /**
224      * Returns the class of a parameter reflection.
225      *
226      * Contrary of the {@link ReflectionParameter::getClass()} method, the class does not need to
227      * be available to be successfully retrieved.
228      *
229      * @param \ReflectionParameter $param
230      *
231      * @return string|null
232      */
233     static private function get_parameter_class(\ReflectionParameter $param)
234     {
235         if (!preg_match('#\[\s*(<[^>]+>)?\s*([^\s]+)#', $param, $matches))
236         {
237             return;
238         }
239 
240         return $matches[2];
241     }
242 
243     public function batch_attach(array $definitions)
244     {
245         $this->hooks = \array_merge_recursive($this->hooks, $definitions);
246         $this->skippable = array();
247         $this->consolidated_hooks = array();
248     }
249 
250     /**
251      * Detaches an event hook.
252      *
253      * @param string $name The name of the event.
254      * @param callable $hook The event hook.
255      *
256      * @return void
257      *
258      * @throws Exception when the event hook is not attached to the event name.
259      */
260     public function detach($name, $hook)
261     {
262         $hooks = &$this->hooks;
263 
264         if (isset($hooks[$name]))
265         {
266             foreach ($hooks[$name] as $key => $h)
267             {
268                 if ($h != $hook)
269                 {
270                     continue;
271                 }
272 
273                 unset($hooks[$name][$key]);
274 
275                 if (!count($hooks[$name]))
276                 {
277                     unset($hooks[$name]);
278                 }
279 
280                 if (strpos($name, '::') !== false)
281                 {
282                     $this->consolidated_hooks = array();
283                 }
284 
285                 return;
286             }
287         }
288 
289         throw new \Exception("The specified event hook is not attached to `{$name}`.");
290     }
291 
292     /**
293      * Marks an event as skippable.
294      *
295      * @param string $name The event name.
296      */
297     public function skip($name)
298     {
299         $this->skippable[$name] = true;
300     }
301 
302     /**
303      * Returns whether or not an event has been marked as skippable.
304      *
305      * @param string $name The event name.
306      *
307      * @return boolean `true` if the event can be skipped, `false` otherwise.
308      */
309     public function is_skippable($name)
310     {
311         return isset($this->skippable[$name]);
312     }
313 
314     /**
315      * Returns the event hooks attached to the specified event name.
316      *
317      * If the class of the event's target is provided, event hooks are filtered according to
318      * the class and its hierarchy.
319      *
320      * @param string $name The event name.
321      *
322      * @return array
323      */
324     public function get_hooks($name)
325     {
326         if (!strpos($name, '::'))
327         {
328             return isset($this->hooks[$name]) ? $this->hooks[$name] : array();
329         }
330 
331         if (isset($this->consolidated_hooks[$name]))
332         {
333             return $this->consolidated_hooks[$name];
334         }
335 
336         list($class, $type) = explode('::', $name);
337 
338         $hooks = array();
339         $c = $class;
340 
341         while ($c)
342         {
343             if (isset($this->hooks[$c . '::' . $type]))
344             {
345                 $hooks = array_merge($hooks, $this->hooks[$c . '::' . $type]);
346             }
347 
348             $c = get_parent_class($c);
349         }
350 
351         $this->consolidated_hooks[$name] = $hooks;
352 
353         return $hooks;
354     }
355 }
356 
357 /**
358  * An event hook.
359  *
360  * An {@link EventHook} instance is created when an event hook is attached. The purpose of this
361  * instance is to ease its detaching:
362  *
363  * <pre>
364  * <?php
365  *
366  * use ICanBoogie\HTTP\Dispatcher;
367  *
368  * $eh = $events->attach(function(Dispatcher\CollectEvent $event, Dispatcher $target) {
369  *
370  *     // …
371  *
372  * });
373  *
374  * $eh->detach();
375  * </pre>
376  *
377  * @property-read Events $events Events collection.
378  * @property-read string $type Event type
379  * @property-read callable $hook Event hook.
380  */
381 class EventHook
382 {
383     private $type;
384     private $hook;
385     private $events;
386 
387     public function __construct(Events $events, $type, $hook)
388     {
389         $this->events = $events;
390         $this->type = $type;
391         $this->hook = $hook;
392     }
393 
394     public function __get($property)
395     {
396         static $readers = array('events', 'type', 'hook');
397 
398         if (in_array($property, $readers))
399         {
400             return $this->$property;
401         }
402 
403         throw new PropertyNotDefined(array($property, $this));
404     }
405 
406     /**
407      * Detaches the event hook from the events.
408      */
409     public function detach()
410     {
411         $this->events->detach($this->type, $this->hook);
412     }
413 }
Autodoc API documentation generated by ApiGen 2.8.0