OSDN Git Service

Merge branch 'feature/routing'
[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 string $pathInfo
61      * @param array  $config
62      */
63     public function __construct($pathInfo, array $config)
64     {
65         $this->url        = $pathInfo;
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           = isset ($this->config['_controller']) ? explode('::', $this->config['_controller']) : array();
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         $url = preg_quote($this->url);
137
138         return preg_replace_callback('/(\\\\(:\w+))/', array(&$this, 'substituteFilter'), $url);
139     }
140
141     private function substituteFilter($matches)
142     {
143         if (isset($matches[1], $this->filters[$matches[2]])) {
144             return $this->filters[$matches[2]];
145         }
146
147         return '([\w-%]+)';
148     }
149
150     public function getParameters()
151     {
152         return $this->parameters;
153     }
154
155     public function setParameters(array $parameters)
156     {
157         $this->parameters = $parameters;
158     }
159
160     public function getValidController()
161     {
162         $class  = $this->class;
163         $method = $this->action;
164
165         if (!class_exists($class)) {
166             return null;
167         }
168
169         if (empty($method) || trim($method) === '') {
170             $method = "__invoke";
171         }
172
173         try {
174             $classRexl = new \ReflectionClass($class);
175         } catch (\ReflectionException $ex) {
176             return null;
177         }
178
179         try {
180             $methRexl = $classRexl->getMethod($method);
181         } catch (\ReflectionException $ex) {
182             return null;
183         }
184
185         // use only public methods
186         // to avoid calling inherited protected methods
187         if(!$methRexl->isPublic()) {
188             return null;
189         }
190
191         return $this->config['_controller'];
192     }
193
194     /**
195      * sort parameters according the the method's arguments
196      *
197      * @return array
198      *
199      * @throws \ReflectionException
200      */
201     private function sortParameters()
202     {
203         $class      = $this->class;
204         $method     = $this->action;
205         $parameters = $this->parameters;
206         $arguments  = array();
207
208         if (empty($method) || trim($method) === '') {
209             $method = "__invoke";
210         }
211
212         $rexl = new \ReflectionMethod($class, $method);
213
214         foreach ($rexl->getParameters() as $methArgs) {
215             $arg = $methArgs->getName();
216
217             if (array_key_exists($arg, $parameters)) {
218                 $arguments[$arg] = $parameters[$arg];
219
220                 unset($parameters[$arg]);
221             } else {
222                 // argument is not in the parameters
223                 $arguments[$arg] = null;
224             }
225         }
226
227         if (count($parameters) > 0) {
228             // fill the unset arguments
229             foreach ($arguments as $arg => &$v) {
230                 if ($v === null) {
231                     //$key = array_keys($parameters)[0];
232
233                     $v = array_shift($parameters);
234                 }
235
236                 if (count($parameters) <= 0) {
237                     break;
238                 }
239             }
240         }
241
242         // merge the remaining parameters
243         return array_merge($arguments, $parameters);
244     }
245
246     private function canBeEchoed($var)
247     {
248         return method_exists($var, '__toString') || (is_scalar($var) && !is_null($var));
249     }
250
251     public function dispatch($instance = null)
252     {
253         is_null($instance) and $instance = new $this->class();
254
255         $param = $this->sortParameters();
256
257         ob_start();
258
259         if (empty($this->action) || trim($this->action) === '') {
260             // __invoke on a class
261             $result = call_user_func_array($instance, $param);
262         } else {
263             $result = call_user_func_array(array($instance, $this->action), $param);
264         }
265
266         if ($this->canBeEchoed($result)) {
267             echo $result;
268         }
269
270         $buffer = ob_get_clean();
271
272         return $buffer;
273     }
274
275     public function getAction()
276     {
277         return $this->action;
278     }
279
280     public function getClass()
281     {
282         return $this->class;
283     }
284 }