Overview

Packages

  • Pinoco
    • PAL

Classes

  • Pinoco
  • Pinoco_Delegate
  • Pinoco_DynamicVars
  • Pinoco_HttpRequestVars
  • Pinoco_List
  • Pinoco_MIMEType
  • Pinoco_NativeRenderer
  • Pinoco_NothingVars
  • Pinoco_NullRenderer
  • Pinoco_OptionalParam
  • Pinoco_Pagination
  • Pinoco_PDOStatementWrapper
  • Pinoco_PDOWrapper
  • Pinoco_Renderer
  • Pinoco_Router
  • Pinoco_TALRenderer
  • Pinoco_TestEnvironment
  • Pinoco_Validator
  • Pinoco_ValidatorContext
  • Pinoco_Vars

Interfaces

  • Pinoco_ArrayConvertible

Functions

  • __pinoco_autoload_impl
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Pinoco: makes existing static web site dynamic transparently.
  4:  * Copyright 2010-2012, Hisateru Tanaka <tanakahisateru@gmail.com>
  5:  *
  6:  * Licensed under The MIT License
  7:  * Redistributions of files must retain the above copyright notice.
  8:  *
  9:  * PHP Version 5
 10:  *
 11:  * @author     Hisateru Tanaka <tanakahisateru@gmail.com>
 12:  * @copyright  Copyright 2010-2012, Hisateru Tanaka <tanakahisateru@gmail.com>
 13:  * @license    MIT License (http://www.opensource.org/licenses/mit-license.php)
 14:  * @package    Pinoco
 15:  */
 16: 
 17: /**
 18:  * Pattern matching router.
 19:  * This utility can be used on any phase of Pinoco runtime process.
 20:  *
 21:  * <code>
 22:  * $router = Pinoco::instance()->route();
 23:  * $router->pass(array('', 'index.html',))
 24:  *     ->on('list', function() use($self, $data) {
 25:  *         // ...
 26:  *     })
 27:  *     ->on('show/{id}', function($id) use($self, $data) {
 28:  *         // ...
 29:  *     })
 30:  *     ->on('POST:post', function() use($self, $data) {
 31:  *         // ...
 32:  *     })
 33:  *     ->on('GET:post', array($this, 'forbidden'))
 34:  *     ->on('*', array($this, 'notfound'));
 35:  * </code>
 36:  *
 37:  * Pattern rules:
 38:  *
 39:  * <pre>
 40:  * '/index'            : Fixed route.
 41:  * 'index'             : Fixed route in relative path from current (_enter) hook.
 42:  * '/show/{id}'        : Parametrized one. Such path elements are passed to handler.
 43:  * 'GET: /edit/{id}' or
 44:  * 'POST: /edit/{id}'  : Different HTTP methods can be different routes.
 45:  * '*:*'               : Matches any patterns. Useful to be bound to Pinoco::notfound()  or forbidden().
 46:  * </pre>
 47:  *
 48:  * @package Pinoco
 49:  */
 50: class Pinoco_Router
 51: {
 52:     /** @var bool */
 53:     protected $matched;
 54: 
 55:     private $_tmp_params;
 56: 
 57:     /**
 58:      * Constructor
 59:      *
 60:      * @param Pinoco $pinoco
 61:      */
 62:     public function __construct($pinoco)
 63:     {
 64:         $this->pinoco = $pinoco;
 65:         $this->matched = false;
 66:     }
 67: 
 68:     /**
 69:      * Routing rules which binds URI path to any callable.
 70:      *
 71:      * Specified handler is called immediately if the route matches. Please note that
 72:      * this method is not a definition but invoker.
 73:      *
 74:      * @param string|array $route
 75:      * @param callback $handler
 76:      * @return $this
 77:      */
 78:     public function on($route, $handler)
 79:     {
 80:         if ($this->matched) {
 81:             return $this;
 82:         }
 83: 
 84:         if (is_array($route)) {
 85:             foreach ($route as $r) {
 86:                 $this->on($r, $handler);
 87:             }
 88:             return $this;
 89:         }
 90: 
 91:         list($method, $path) = $this->extractRoute($route);
 92: 
 93:         if ($this->matchesWithMethod($method)) {
 94:             return $this;
 95:         }
 96: 
 97:         $matchParams = $this->matchesWithPath($path);
 98:         if (!is_null($matchParams)) {
 99:             call_user_func_array($handler, $matchParams);
100:             $this->matched = true;
101:         }
102: 
103:         return $this;
104:     }
105: 
106:     /**
107:      * Specified route is ignored and delegated to the next script step.
108:      * This method is useful to ignore matching patterns below. (e.g. '*:*')
109:      *
110:      * @param string|array $route
111:      * @return $this
112:      */
113:     public function pass($route)
114:     {
115:         if ($this->matched) {
116:             return $this;
117:         }
118: 
119:         if (is_array($route)) {
120:             array_map(array($this, 'pass'), $route);
121:             return $this;
122:         }
123: 
124:         list($method, $path) = $this->extractRoute($route);
125: 
126:         if ($this->matchesWithMethod($method)) {
127:             return $this;
128:         }
129: 
130:         $matchParams = $this->matchesWithPath($path);
131:         if (!is_null($matchParams)) {
132:             $this->matched = true;
133:         }
134: 
135:         return $this;
136:     }
137: 
138:     /**
139:      * Returns which a matching router was found and handled or not.
140:      *
141:      * @return bool
142:      */
143:     public function wasMatched()
144:     {
145:         return $this->matched;
146:     }
147: 
148:     /**
149:      * @param string $route
150:      * @return array
151:      */
152:     private function extractRoute($route)
153:     {
154:         $delimpos = strpos($route, ':');
155:         if ($delimpos !== false) {
156:             $method = strtoupper(trim(substr($route, 0, $delimpos)));
157:             $path = trim(substr($route, $delimpos + 1));
158:             return array($method, $path);
159:         } else {
160:             $method = '*';
161:             $path = trim($route);
162:             return array($method, $path);
163:         }
164:     }
165: 
166:     /**
167:      * @param $path
168:      * @return array
169:      */
170:     private function matchesWithPath($path)
171:     {
172:         if (preg_match('|^/|', $path)) {
173:             $uri = $this->pinoco->path;
174:         } else {
175:             // Pinoco::subpath is set when running hook script.
176:             // If not, it means taht current flow is out of hook, then
177:             // Pinoco::path should be default instead.
178:             $uri = !is_null($this->pinoco->subpath) ? $this->pinoco->subpath : $this->pinoco->path;
179:         }
180: 
181:         $this->_tmp_params = array();
182:         $pathregexp = $path;
183:         $pathregexp = preg_replace('/\|/', '\|', $pathregexp);
184:         $pathregexp = preg_replace('/\+/', '\+', $pathregexp);
185:         $pathregexp = preg_replace('/\*+/', '.+?', $pathregexp);
186:         $pathregexp = preg_replace_callback('/\{(.*?)\}/', array($this, '__each_path_args'), $pathregexp);
187: 
188:         if (preg_match('|^' . $pathregexp . '$|', $uri, $matches)) {
189:             array_shift($matches);
190:             return $matches;
191:         }
192:         else {
193:             return null;
194:         }
195:     }
196: 
197:     /**
198:      * @param $matches
199:      * @return string
200:      * @internal
201:      */
202:     public function __each_path_args($matches)
203:     {
204:         $name = $matches[1];
205:         $this->_tmp_params[] = $name;
206:         return '([^/]+?)';
207:     }
208: 
209:     /**
210:      * @param $method
211:      * @return bool
212:      */
213:     private function matchesWithMethod($method)
214:     {
215:         return $method != '*' && $method != $this->pinoco->request->method;
216:     }
217: }
Pinoco 0.8.0 Documentation API documentation generated by ApiGen 2.8.0