ControllerCollectorTrait::set()
last analyzed

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 1
ccs 0
cts 0
cp 0
nc 1
1
<?php
2
3
/**
4
 * Codeburner Framework.
5
 *
6
 * @author Alex Rohleder <[email protected]>
7
 * @copyright 2016 Alex Rohleder
8
 * @license http://opensource.org/licenses/MIT
9
 */
10
11
namespace Codeburner\Router\Collectors;
12
13
use Codeburner\Router\Collector;
14
use Codeburner\Router\Group;
15
use Codeburner\Router\Parser;
16
use ReflectionClass;
17
use ReflectionMethod;
18
use ReflectionParameter;
19
20
/**
21
 * Methods for enable the collector to make routes from a controller.
22
 *
23
 * @author Alex Rohleder <[email protected]>
24
 */
25
26
trait ControllerCollectorTrait
27
{
28
29
    /**
30
     * @return Parser
31
     */
32
33
    abstract public function getParser();
34
35
    /**
36
     * @param string $method
37
     * @param string $pattern
38
     * @param callable $action
39
     *
40
     * @return Group
41
     */
42
43
    abstract public function set($method, $pattern, $action);
44
45
    /**
46
     * Define how controller actions names will be joined to form the route pattern.
47
     *
48
     * @var string
49
     */
50
51
    protected $controllerActionJoin = "/";
52
53
    /**
54
     * Maps all the controller methods that begins with a HTTP method, and maps the rest of
55
     * name as a path. The path will be the method name with slashes before every camelcased 
56
     * word and without the HTTP method prefix, and the controller name will be used to prefix
57
     * the route pattern. e.g. ArticlesController::getCreate will generate a route to: GET articles/create
58
     *
59
     * @param string $controller The controller name
60
     * @param string $prefix
61
     *
62
     * @throws \ReflectionException
63
     * @return Group
64
     */
65
66 5
    public function controller($controller, $prefix = null)
67
    {
68 5
        $controller = new ReflectionClass($controller);
69 5
        $prefix     = $prefix === null ? $this->getControllerPrefix($controller) : $prefix;
70 5
        $methods    = $controller->getMethods(ReflectionMethod::IS_PUBLIC);
71 5
        return $this->collectControllerRoutes($controller, $methods, "/$prefix/");
72
    }
73
74
    /**
75
     * Maps several controllers at same time.
76
     *
77
     * @param string[] $controllers Controllers name.
78
     * @throws \ReflectionException
79
     * @return Group
80
     */
81
82 1
    public function controllers(array $controllers)
83
    {
84 1
        $group = new Group;
85 1
        foreach ($controllers as $controller)
86 1
            $group->set($this->controller($controller));
87 1
        return $group;
88
    }
89
90
    /**
91
     * Alias for Collector::controller but maps a controller without using the controller name as prefix.
92
     *
93
     * @param string $controller The controller name
94
     * @throws \ReflectionException
95
     * @return Group
96
     */
97
98 2
    public function controllerWithoutPrefix($controller)
99
    {
100 2
        $controller = new ReflectionClass($controller);
101 2
        $methods = $controller->getMethods(ReflectionMethod::IS_PUBLIC);
102 2
        return $this->collectControllerRoutes($controller, $methods, "/");
103
    }
104
105
    /**
106
     * Alias for Collector::controllers but maps a controller without using the controller name as prefix.
107
     *
108
     * @param string[] $controllers
109
     * @throws \ReflectionException
110
     * @return Group
111
     */
112
113 1
    public function controllersWithoutPrefix(array $controllers)
114
    {
115 1
        $group = new Group;
116 1
        foreach ($controllers as $controller)
117 1
            $group->set($this->controllerWithoutPrefix($controller));
118 1
        return $group;
119
    }
120
121
    /**
122
     * @param ReflectionClass $controller
123
     * @param ReflectionMethod[] $methods
124
     * @param string $prefix
125
     *
126
     * @return Group
127
     */
128
129 7
    protected function collectControllerRoutes(ReflectionClass $controller, array $methods, $prefix)
130
    {
131 7
        $group = new Group;
132 7
        $controllerDefaultStrategy = $this->getAnnotatedStrategy($controller);
133
134 7
        foreach ($methods as $method) {
135 7
            $name = preg_split("~(?=[A-Z])~", $method->name);
136 7
            $http = $name[0];
137 7
            unset($name[0]);
138
 
139 7
            if (strpos(Collector::HTTP_METHODS, $http) !== false) {
140 7
                $action   = $prefix . strtolower(implode($this->controllerActionJoin, $name));
141 7
                $dynamic  = $this->getMethodConstraints($method);
142 7
                $strategy = $this->getAnnotatedStrategy($method);
143
144 7
                $route = $this->set($http, "$action$dynamic", [$controller->name, $method->name]);
145
146 7
                if ($strategy !== null) {
147
                       $route->setStrategy($strategy);
148 7
                } else $route->setStrategy($controllerDefaultStrategy);
149
150 7
                $group->set($route);
151 7
            }
152 7
        }
153
154 7
        return $group;
155
    }
156
157
    /**
158
     * @param ReflectionClass $controller
159
     *
160
     * @return string
161
     */
162
163 4
    protected function getControllerPrefix(ReflectionClass $controller)
164
    {
165 4
        preg_match("~\@prefix\s([a-zA-Z\\\_]+)~i", (string) $controller->getDocComment(), $prefix);
166 4
        return isset($prefix[1]) ? $prefix[1] : str_replace("controller", "", strtolower($controller->getShortName()));
167
    }
168
169
    /**
170
     * @param \ReflectionMethod
171
     * @return string
172
     */
173
174 7
    protected function getMethodConstraints(ReflectionMethod $method)
175
    {
176 7
        $beginPath = "";
177 7
        $endPath = "";
178
179 7
        if ($parameters = $method->getParameters()) {
180 7
            $types = $this->getParamsConstraint($method);
181
182 7
            foreach ($parameters as $parameter) {
183 7
                if ($parameter->isOptional()) {
184 6
                    $beginPath .= "[";
185 6
                    $endPath .= "]";
186 6
                }
187
188 7
                $beginPath .= $this->getPathConstraint($parameter, $types);
189 7
            }
190 7
        }
191
192 7
        return $beginPath . $endPath;
193
    }
194
195
    /**
196
     * @param ReflectionParameter $parameter
197
     * @param string[] $types
198
     * @return string
199
     */
200
201 7
    protected function getPathConstraint(ReflectionParameter $parameter, $types)
202
    {
203 7
        $name = $parameter->name;
204 7
        $path = "/{" . $name;
205 7
        return isset($types[$name]) ? "$path:{$types[$name]}}" : "$path}";
206
    }
207
208
    /**
209
     * @param ReflectionMethod $method
210
     * @return string[]
211
     */
212
213 7
    protected function getParamsConstraint(ReflectionMethod $method)
214
    {
215 7
        $params = [];
216 7
        preg_match_all("~\@param\s(" . implode("|", array_keys($this->getParser()->getWildcards())) . "|\(.+\))\s\\$([a-zA-Z0-1_]+)~i",
217 7
            $method->getDocComment(), $types, PREG_SET_ORDER);
218
219 7
        foreach ((array) $types as $type) {
220
            // if a pattern is defined on Match take it otherwise take the param type by PHPDoc.
221 1
            $params[$type[2]] = isset($type[4]) ? $type[4] : $type[1];
222 7
        }
223
224 7
        return $params;
225
    }
226
227
    /**
228
     * @param ReflectionClass|ReflectionMethod $reflector
229
     * @return string|null
230
     */
231
232 7
    protected function getAnnotatedStrategy($reflector)
233
    {
234 7
        preg_match("~\@strategy\s([a-zA-Z\\\_]+)~i", (string) $reflector->getDocComment(), $strategy);
235 7
        return isset($strategy[1]) ? $strategy[1] : null;
236
    }
237
238
    /**
239
     * Define how controller actions names will be joined to form the route pattern.
240
     * Defaults to "/" so actions like "getMyAction" will be "/my/action". If changed to
241
     * "-" the new pattern will be "/my-action".
242
     *
243
     * @param string $join
244
     */
245
246 1
    public function setControllerActionJoin($join)
247
    {
248 1
        $this->controllerActionJoin = $join;
249 1
    }
250
251
    /**
252
     * @return string
253
     */
254
255 1
    public function getControllerActionJoin()
256
    {
257 1
        return $this->controllerActionJoin;
258
    }
259
260
}
261