OSDN Git Service

add method reflection and parameter sorting
[php-libraries/Router.git] / src / Route.php
1 <?php
2
3 namespace PHPRouter;
4
5 class Route
6 {
7     /**
8      * URL of this Route
9      * @var string
10      */
11     private $url;
12
13     /**
14      * Accepted HTTP methods for this route.
15      *
16      * @var string[]
17      */
18     private $methods;
19
20     /**
21      * Target for this route, can be anything.
22      * @var mixed
23      */
24     private $target;
25
26     /**
27      * The name of this route, used for reversed routing
28      * @var string
29      */
30     private $name;
31
32     /**
33      * Custom parameter filters for this route
34      * @var array
35      */
36     private $filters = array();
37
38     /**
39      * Array containing parameters passed through request URL
40      * @var array
41      */
42     private $parameters = array();
43
44     /**
45      * @var null|string
46      */
47     private $action;
48
49     /**
50      * @var array
51      */
52     private $config;
53
54     /**
55      * @var null|string
56      */
57     private $class;
58
59     /**
60      * @param       $resource
61      * @param array $config
62      */
63     public function __construct($resource, array $config)
64     {
65         $this->url        = $resource;
66         $this->config     = $config;
67         $this->methods    = isset($config['methods']) ? (array)$config['methods'] : array();
68         $this->target     = isset($config['target']) ? $config['target'] : null;
69         $this->name       = isset($config['name']) ? $config['name'] : null;
70         $this->parameters = isset($config['parameters']) ? $config['parameters'] : array();
71         $this->filters    = isset($config['filters']) ? $config['filters'] : array();
72         $action           = explode('::', $this->config['_controller']);
73         $this->class      = isset($action[0]) ? $action[0] : null;
74         $this->action     = isset($action[1]) ? $action[1] : null;
75     }
76
77     public function getUrl()
78     {
79         return $this->url;
80     }
81
82     public function setUrl($url)
83     {
84         $url = (string)$url;
85
86         // make sure that the URL is suffixed with a forward slash
87         if (substr($url, -1) !== '/') {
88             $url .= '/';
89         }
90
91         $this->url = $url;
92     }
93
94     public function getTarget()
95     {
96         return $this->target;
97     }
98
99     public function setTarget($target)
100     {
101         $this->target = $target;
102     }
103
104     public function getMethods()
105     {
106         return $this->methods;
107     }
108
109     public function setMethods(array $methods)
110     {
111         $this->methods = $methods;
112     }
113
114     public function getName()
115     {
116         return $this->name;
117     }
118
119     public function setName($name)
120     {
121         $this->name = (string)$name;
122     }
123
124     public function setFilters(array $filters)
125     {
126         $this->filters = $filters;
127     }
128
129     public function getFilters()
130     {
131         return $this->filters;
132     }
133
134     public function getRegex()
135     {
136         return preg_replace_callback('/(:\w+)/', array(&$this, 'substituteFilter'), $this->url);
137     }
138
139     private function substituteFilter($matches)
140     {
141         if (isset($matches[1], $this->filters[$matches[1]])) {
142             return $this->filters[$matches[1]];
143         }
144
145         return '([\w-%]+)';
146     }
147
148     public function getParameters()
149     {
150         return $this->parameters;
151     }
152
153     public function setParameters(array $parameters)
154     {
155         $this->parameters = $parameters;
156     }
157
158     public function getValidController()
159     {
160         $class  = $this->class;
161         $method = $this->action;
162
163         if (!class_exists($class)) {
164             return null;
165         }
166
167         if (empty($method) || trim($method) === '') {
168             $method = "__invoke";
169         }
170
171         try {
172             $classRexl = new \ReflectionClass($class);
173         } catch (\ReflectionException $ex) {
174             return null;
175         }
176
177         try {
178             $classRexl->getMethod($method);
179         } catch (\ReflectionException $ex) {
180             return null;
181         }
182
183         return $this->config['_controller'];
184     }
185
186     /**
187      * sort parameters according the the method's arguments
188      *
189      * @return array
190      *
191      * @throws \ReflectionException
192      */
193     private function sortParameters()
194     {
195         $class      = $this->class;
196         $method     = $this->action;
197         $parameters = $this->parameters;
198         $arguments  = array();
199
200         if (empty($method) || trim($method) === '') {
201             $method = "__invoke";
202         }
203
204         $rexl = new \ReflectionMethod($class, $method);
205
206         foreach ($rexl->getParameters() as $methArgs) {
207             $arg = $methArgs->getName();
208
209             if (array_key_exists($arg, $parameters)) {
210                 $arguments[$arg] = $parameters[$arg];
211
212                 unset($parameters[$arg]);
213             } else {
214                 // argument is not in the parameters
215                 $arguments[$arg] = null;
216             }
217         }
218
219         if (count($parameters) > 0) {
220             // fill the unset arguments
221             foreach ($arguments as $arg => &$v) {
222                 if ($v === null) {
223                     //$key = array_keys($parameters)[0];
224
225                     $v = array_shift($parameters);
226                 }
227
228                 if (count($parameters) <= 0) {
229                     break;
230                 }
231             }
232         }
233
234         // merge the remaining parameters
235         return array_merge($arguments, $parameters);
236     }
237
238     public function dispatch($instance = null)
239     {
240         is_null($instance) and $instance = new $this->class();
241
242         $param = $this->sortParameters();
243
244         ob_start();
245
246         if (empty($this->action) || trim($this->action) === '') {
247             // __invoke on a class
248             call_user_func_array($instance, $param);
249         } else {
250             call_user_func_array(array($instance, $this->action), $param);
251         }
252
253         $result = ob_get_clean();
254
255         return $result;
256     }
257
258     public function getAction()
259     {
260         return $this->action;
261     }
262
263     public function getClass()
264     {
265         return $this->class;
266     }
267 }