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

  • Helpers
  • Hooks

Exceptions

  • MethodNotDefined

Functions

  • last_chance_get
  • last_chance_set
  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  * Subclasses of the {@link Object} class are associated with a prototype, which can be used to
 16  * add methods as well as getters and setters to classes.
 17  *
 18  * When using the ICanBoogie framework, methods can be defined using the "hooks" config and the
 19  * "prototypes" namespace:
 20  *
 21  * <pre>
 22  * <?php
 23  *
 24  * return [
 25  *
 26  *     'prototypes' => [
 27  *
 28  *         'Icybee\Modules\Pages\Page::my_additional_method' => 'MyHookClass::my_additional_method',
 29  *         'Icybee\Modules\Pages\Page::lazy_get_my_property' => 'MyHookClass::lazy_get_my_property'
 30  *
 31  *     ]
 32  * ];
 33  * </pre>
 34  */
 35 class Prototype implements \ArrayAccess, \IteratorAggregate
 36 {
 37     /**
 38      * Prototypes built per class.
 39      *
 40      * @var array[string]Prototype
 41      */
 42     static protected $prototypes = [];
 43 
 44     /**
 45      * Pool of prototype methods per class.
 46      *
 47      * @var array[string]callable
 48      */
 49     static protected $pool;
 50 
 51     /**
 52      * Returns the prototype associated with the specified class or object.
 53      *
 54      * @param string|object $class Class name or instance.
 55      *
 56      * @return Prototype
 57      */
 58     static public function from($class)
 59     {
 60         if (is_object($class))
 61         {
 62             $class = get_class($class);
 63         }
 64 
 65         if (empty(self::$prototypes[$class]))
 66         {
 67             self::$prototypes[$class] = new static($class);
 68         }
 69 
 70         return self::$prototypes[$class];
 71     }
 72 
 73     static public function configure(array $config)
 74     {
 75         self::$pool = $config;
 76 
 77         foreach (self::$prototypes as $class => $prototype)
 78         {
 79             $prototype->consolidated_methods = null;
 80 
 81             if (empty($config[$class]))
 82             {
 83                 continue;
 84             }
 85 
 86             $prototype->methods = $config[$class];
 87         }
 88     }
 89 
 90     /**
 91      * Class associated with the prototype.
 92      *
 93      * @var string
 94      */
 95     protected $class;
 96 
 97     /**
 98      * Parent prototype.
 99      *
100      * @var Prototype
101      */
102     protected $parent;
103 
104     /**
105      * Methods defined by the prototype.
106      *
107      * @var array[string]callable
108      */
109     protected $methods = [];
110 
111     /**
112      * Methods defined by the prototypes chain.
113      *
114      * @var array[string]callable
115      */
116     protected $consolidated_methods;
117 
118     /**
119      * Creates a prototype for the specified class.
120      *
121      * @param string $class
122      */
123     protected function __construct($class)
124     {
125         $this->class = $class;
126 
127         $parent_class = get_parent_class($class);
128 
129         if ($parent_class)
130         {
131             $this->parent = static::from($parent_class);
132         }
133 
134         if (isset(self::$pool[$class]))
135         {
136             $this->methods = self::$pool[$class];
137         }
138     }
139 
140     /**
141      * Consolidate the methods of the prototype.
142      *
143      * The method creates a single array from the prototype methods and those of its parents.
144      *
145      * @return array[string]callable
146      */
147     protected function get_consolidated_methods()
148     {
149         if ($this->consolidated_methods !== null)
150         {
151             return $this->consolidated_methods;
152         }
153 
154         $methods = $this->methods;
155 
156         if ($this->parent)
157         {
158             $methods += $this->parent->get_consolidated_methods();
159         }
160 
161         return $this->consolidated_methods = $methods;
162     }
163 
164     /**
165      * Revokes the consolidated methods of the prototype.
166      *
167      * The method must be invoked when prototype methods are modified.
168      */
169     protected function revoke_consolidated_methods()
170     {
171         $class = $this->class;
172 
173         foreach (self::$prototypes as $prototype)
174         {
175             if (!is_subclass_of($prototype->class, $class))
176             {
177                 continue;
178             }
179 
180             $prototype->consolidated_methods = null;
181         }
182 
183         $this->consolidated_methods = null;
184     }
185 
186     /**
187      * Adds or replaces the specified method of the prototype.
188      *
189      * @param string $method The name of the method.
190      *
191      * @param callable $callback
192      */
193     public function offsetSet($method, $callback)
194     {
195         self::$prototypes[$this->class]->methods[$method] = $callback;
196 
197         $this->revoke_consolidated_methods();
198     }
199 
200     /**
201      * Removed the specified method from the prototype.
202      *
203      * @param string $method The name of the method.
204      */
205     public function offsetUnset($method)
206     {
207         unset(self::$prototypes[$this->class]->methods[$method]);
208 
209         $this->revoke_consolidated_methods();
210     }
211 
212     /**
213      * Checks if the prototype defines the specified method.
214      *
215      * @param string $method The name of the method.
216      *
217      * @return bool
218      */
219     public function offsetExists($method)
220     {
221         $methods = $this->get_consolidated_methods();
222 
223         return isset($methods[$method]);
224     }
225 
226     /**
227      * Returns the callback associated with the specified method.
228      *
229      * @param string $method The name of the method.
230      *
231      * @throws Prototype\MethodNotDefined if the method is not defined.
232      *
233      * @return callable
234      */
235     public function offsetGet($method)
236     {
237         $methods = $this->get_consolidated_methods();
238 
239         if (!isset($methods[$method]))
240         {
241             throw new Prototype\MethodNotDefined([ $method, $this->class ]);
242         }
243 
244         return $methods[$method];
245     }
246 
247     /**
248      * Returns an iterator for the prototype methods.
249      *
250      * @see IteratorAggregate::getIterator()
251      */
252     public function getIterator()
253     {
254         $methods = $this->get_consolidated_methods();
255 
256         return new \ArrayIterator($methods);
257     }
258 }
259 
260 namespace ICanBoogie\Prototype;
261 
262 /**
263  * This exception is thrown when one tries to access an undefined prototype method.
264  */
265 class MethodNotDefined extends \BadMethodCallException
266 {
267     public function __construct($message, $code=500, \Exception $previous=null)
268     {
269         if (is_array($message))
270         {
271             $message = sprintf('Method "%s" is not defined by the prototype of class "%s".', $message[0], $message[1]);
272         }
273 
274         parent::__construct($message, $code, $previous);
275     }
276 }
Autodoc API documentation generated by ApiGen 2.8.0