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 use ICanBoogie\HTTP\RedirectResponse;
 15 use ICanBoogie\HTTP\Request;
 16 use ICanBoogie\HTTP\Response;
 17 use ICanBoogie\Prototype\MethodNotDefined;
 18 use ICanBoogie\Routing\Pattern;
 19 
 20 /**
 21  * The route collection.
 22  *
 23  * Initial routes are collected from the "routes" config.
 24  *
 25  *
 26  *
 27  * Event: ICanBoogie\Routes::collect:before
 28  * ----------------------------------------
 29  *
 30  * Third parties may use the event {@link Routes\BeforeCollectEvent} to alter the configuration
 31  * fragments before they are synthesized. The event is fired during {@link __construct()}.
 32  *
 33  *
 34  *
 35  * Event: ICanBoogie\Routes::collect
 36  * ---------------------------------
 37  *
 38  * Third parties may use the event {@link Routes\CollectEvent} to alter the routes read from
 39  * the configuration. The event is fired during {@link __construct()}.
 40  */
 41 class Routes implements \IteratorAggregate, \ArrayAccess
 42 {
 43     static protected $instance;
 44 
 45     /**
 46      * Returns the singleton instance of the class.
 47      *
 48      * @return \ICanBoogie\Routes
 49      */
 50     static public function get()
 51     {
 52         if (!self::$instance)
 53         {
 54             self::$instance = new static();
 55         }
 56 
 57         # patch to support $routes->get('/', ... )
 58 
 59         if (func_num_args() > 1)
 60         {
 61             return self::$instance->__call('get', func_get_args());
 62         }
 63 
 64         return self::$instance;
 65     }
 66 
 67     protected $routes = array();
 68 
 69     protected $instances = array();
 70 
 71     protected $default_route_class = 'ICanBoogie\Route';
 72 
 73     /**
 74      * Collects routes definitions from the "routes" config.
 75      */
 76     protected function __construct()
 77     {
 78         $this->routes = $this->collect();
 79     }
 80 
 81     public function __call($method, array $arguments)
 82     {
 83         $method = strtoupper($method);
 84 
 85         if ($method === Request::METHOD_ANY || in_array($method, Request::$methods))
 86         {
 87             list($pattern, $controller) = $arguments;
 88 
 89             $definition = array
 90             (
 91                 'pattern' => $pattern,
 92                 'via' => $method,
 93                 'controller' => $controller
 94             );
 95 
 96             $id = $method . ' ' . $pattern;
 97             $this[$id] = $definition;
 98 
 99             if ($method === Request::METHOD_GET)
100             {
101                 $this[Request::METHOD_HEAD . ' ' . $pattern] = array_merge($definition, array('via' => Request::METHOD_HEAD));
102             }
103 
104             return $this[$id];
105         }
106 
107         throw new MethodNotDefined(array($method, $this));
108     }
109 
110     public function getIterator()
111     {
112         return new \ArrayIterator($this->routes);
113     }
114 
115     public function offsetExists($offset)
116     {
117         return isset($this->routes[$offset]);
118     }
119 
120     public function offsetGet($id)
121     {
122         if (isset($this->instances[$id]))
123         {
124             return $this->instances[$id];
125         }
126 
127         if (!$this->offsetExists($id))
128         {
129             throw new RouteNotDefined($id);
130         }
131 
132         $properties = $this->routes[$id];
133 
134         $class = $this->default_route_class;
135 
136         if (isset($properties['class']))
137         {
138             $class = $properties['class'];
139         }
140 
141         return $this->instances[$id] = new $class($properties['pattern'], $properties);
142     }
143 
144     /**
145      * Adds or replaces a route.
146      *
147      * @param mixed $offset The identifier of the route.
148      * @param array $route The route definition.
149      *
150      * @throws \LogicException if the route definition is invalid.
151      *
152      * @see ArrayAccess::offsetSet()
153      */
154     public function offsetSet($id, $route)
155     {
156         if (empty($route['pattern']))
157         {
158             throw new \LogicException(format
159             (
160                 "Route %id has no pattern. !route", array
161                 (
162                     'id' => $id,
163                     'route' => $route
164                 )
165             ));
166         }
167 
168         $this->routes[$id] = $route + array
169         (
170             'id' => $id,
171             'via' => Request::METHOD_ANY
172         );
173     }
174 
175     static public function add($id, $definition)
176     {
177         $routes = static::get();
178         $routes[$id] = $definition;
179     }
180 
181     /**
182      * Removes a route.
183      *
184      * @param string $offset The identifier of the route.
185      *
186      * @see ArrayAccess::offsetUnset()
187      */
188     public function offsetUnset($offset)
189     {
190         unset($this->routes[$offset]);
191     }
192 
193     /**
194      * Returns route collection.
195      *
196      * The collection is built in 4 steps:
197      *
198      * 1. Routes are traversed to add the `module` and `via` properties. If the route is defined
199      * by a module the `module` property is set to the id of the module, otherwise it is set
200      * to `null`. The `via` property is set to {@link Request::METHOD_ANY} if it is not defined.
201      *
202      * 2. The {@link Routes\BeforeCollectEvent} event is fired.
203      *
204      * @return array
205      */
206     protected function collect()
207     {
208         global $core;
209 
210         // TODO-20121119: all of this should be outside the class, in a configurator
211 
212         if (!isset($core))
213         {
214             return array();
215         }
216 
217         $collection = $this;
218 
219         return $core->configs->synthesize
220         (
221             'routes', function($fragments) use($collection)
222             {
223                 global $core;
224 
225                 $module_roots = array();
226 
227                 foreach ($core->modules->descriptors as $module_id => $descriptor)
228                 {
229                     $module_roots[$descriptor[Module::T_PATH]] = $module_id;
230                 }
231 
232                 foreach ($fragments as $module_root => &$fragment)
233                 {
234                     $module_root = dirname(dirname($module_root)) . DIRECTORY_SEPARATOR;
235                     $module_id = isset($module_roots[$module_root]) ? $module_roots[$module_root] : null;
236 
237                     foreach ($fragment as $route_id => &$route)
238                     {
239                         $route += array
240                         (
241                             'via' => Request::METHOD_ANY,
242                             'module' => $module_id
243                         );
244                     }
245                 }
246 
247                 unset($fragment);
248                 unset($route);
249 
250                 new Routes\BeforeCollectEvent($collection, array('fragments' => &$fragments));
251 
252                 $routes = array();
253 
254                 foreach ($fragments as $fragment)
255                 {
256                     foreach ($fragment as $id => $route)
257                     {
258                         $routes[$id] = $route + array
259                         (
260                             'pattern' => null
261                         );
262                     }
263                 }
264 
265                 new Routes\CollectEvent($collection, array('routes' => &$routes));
266 
267                 return $routes;
268             }
269         );
270     }
271 
272     /**
273      * Search for a route matching the specified pathname and method.
274      *
275      * @param string $uri The URI to match. If the URI includes a query string it is removed
276      * before searching for a matching route.
277      * @param array|null $captured The parameters captured from the URI. If the URI included a
278      * query string, its parsed params are stored under the `__query__` key.
279      * @param string $method One of HTTP\Request::METHOD_* methods.
280      * @param string $namespace Namespace restriction.
281      *
282      * @return Route
283      */
284     public function find($uri, &$captured=null, $method=Request::METHOD_ANY, $namespace=null)
285     {
286         $captured = array();
287 
288         if ($namespace)
289         {
290             $namespace = '/' . $namespace . '/';
291         }
292 
293         $found = null;
294         $pattern = null;
295 
296         $qs = null;
297         $qs_pos = strpos($uri, '?');
298 
299         if ($qs_pos !== false)
300         {
301             $qs = substr($uri, $qs_pos + 1);
302             $uri = substr($uri, 0, $qs_pos);
303         }
304 
305         foreach ($this->routes as $id => $route)
306         {
307             $pattern = $route['pattern'];
308 
309             if ($namespace && strpos($pattern, $namespace) !== 0)
310             {
311                 continue;
312             }
313 
314             $pattern = Pattern::from($pattern);
315 
316             if (!$pattern->match($uri, $captured))
317             {
318                 continue;
319             }
320 
321             if ($method == Request::METHOD_ANY)
322             {
323                 $found = true;
324                 break;
325             }
326 
327             $route_method = $route['via'];
328 
329             if (is_array($route_method))
330             {
331                 if (in_array($method, $route_method))
332                 {
333                     $found = true;
334                     break;
335                 }
336             }
337             else
338             {
339                 if ($route_method === Request::METHOD_ANY || $route_method === $method)
340                 {
341                     $found = true;
342                     break;
343                 }
344             }
345         }
346 
347         if (!$found)
348         {
349             return;
350         }
351 
352         if ($qs)
353         {
354             parse_str($qs, $parsed_query_string);
355 
356             $captured['__query__'] = $parsed_query_string;
357         }
358 
359         return new Route
360         (
361             $pattern, $route + array
362             (
363                 'id' => $id
364             )
365         );
366     }
367 }
368 
369 /*
370  * EXCEPTIONS
371  */
372 
373 /**
374  * Exception thrown when a route does not exists.
375  *
376  * @property-read string $id The identifier of the route.
377  */
378 class RouteNotDefined extends \Exception
379 {
380     private $id;
381 
382     /**
383      * @param string $id Identifier of the route.
384      * @param int $code
385      * @param \Exception $previous
386      */
387     public function __construct($id, $code=404, \Exception $previous=null)
388     {
389         $this->id = $id;
390 
391         parent::__construct("The route <q>$id</q> is not defined.", $code, $previous);
392     }
393 
394     public function __get($property)
395     {
396         if ($property == 'id')
397         {
398             return $this->id;
399         }
400 
401         throw new PropertyNotDefined(array($property, $this));
402     }
403 }
404 
405 /*
406  * EVENTS
407  */
408 
409 namespace ICanBoogie\Routes;
410 
411 /**
412  * Event class for the `ICanBoogie\Events::collect:before` event.
413  *
414  * Third parties may use this event to alter the configuration fragments before they are
415  * synthesized.
416  */
417 class BeforeCollectEvent extends \ICanBoogie\Event
418 {
419     /**
420      * Reference to the configuration fragments.
421      *
422      * @var array
423      */
424     public $fragments;
425 
426     /**
427      * The event is constructed with the type `alter:before`.
428      *
429      * @param \ICanBoogie\Routes $target The routes collection.
430      * @param array $payload
431      */
432     public function __construct(\ICanBoogie\Routes $target, array $payload)
433     {
434         parent::__construct($target, 'collect:before', $payload);
435     }
436 }
437 
438 /**
439  * Event class for the `ICanBoogie\Events::collect` event.
440  *
441  * Third parties may use this event to alter the routes read from the configuration.
442  */
443 class CollectEvent extends \ICanBoogie\Event
444 {
445     /**
446      * Reference to the routes.
447      *
448      * @var array[string]array
449      */
450     public $routes;
451 
452     /**
453      * The event is constructed with the type `collect`.
454      *
455      * @param \ICanboogie\Routes $target The routes collection.
456      * @param array $payload
457      */
458     public function __construct(\ICanboogie\Routes $target, array $payload)
459     {
460         parent::__construct($target, 'collect', $payload);
461     }
462 }
Autodoc API documentation generated by ApiGen 2.8.0