Failed Conditions
Push — master ( 67837d...90c23c )
by Florian
03:47
created

Dispatcher::setClassParts()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
/**
4
 * Copyright (c) Florian Krämer (https://florian-kraemer.net)
5
 * Licensed under The MIT License
6
 * For full copyright and license information, please see the LICENSE.txt
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright Copyright (c) Florian Krämer (https://florian-kraemer.net)
10
 * @author    Florian Krämer
11
 * @link      https://github.com/Phauthentic
12
 * @license   https://opensource.org/licenses/MIT MIT License
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phauthentic\Infrastructure\Http\Dispatcher;
18
19
use Psr\Container\ContainerInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\RequestHandlerInterface;
23
24
/**
25
 * A simple controller dispatcher middleware
26
 *
27
 * It will check if there is route in the request attributes. If there is a
28
 * route it will take the handler and check if it is a string and tries to
29
 * resolve it against the DI container.
30
 */
31
class Dispatcher implements DispatcherInterface
32
{
33
    /**
34
     * @var string
35
     */
36
    protected $defaultNamespace = '';
37
38
    /**
39
     * @var string
40
     */
41
    protected $methodSeparator = '@';
42
43
    /**
44
     * @var string
45
     */
46
    protected $namespaceSeparator = '.';
47
48
    /**
49
     * @var string
50
     */
51
    protected $classTemplate = '{namespace}{className}';
52
53
    /**
54
     * @var array
55
     */
56
    protected $classParts = [];
57
58
    /**
59
     * @var \Psr\Container\ContainerInterface;
60
     */
61
    protected $container;
62
63
    /**
64
     * @param \Psr\Container\ContainerInterface $container PSR Container
65
     */
66 2
    public function __construct(
67
        ContainerInterface $container
68
    ) {
69 2
        $this->container = $container;
70 2
    }
71
72
    /**
73
     * @param string $namespace Namespace
74
     * @return $this
75
     */
76
    public function setDefaultNamespace(string $namespace): DispatcherInterface
77
    {
78
        $this->defaultNamespace = $namespace;
79
80
        return $this;
81
    }
82
83
    /**
84
     * @param array $classParts Class Template Vars
85
     * @return $this
86
     */
87
    public function setClassParts(array $classParts): DispatcherInterface
88
    {
89
        $this->classParts = $classParts;
90
91
        return $this;
92
    }
93
94
    /**
95
     * @param string $template Template String
96
     * @return $this
97
     */
98
    public function setClassTemplate(string $template): DispatcherInterface
99
    {
100
        $this->classTemplate = $template;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Phauthentic\Infrastructu...her\DispatcherInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
101
    }
102
103
    /**
104
     * @param string $separator Separator
105
     * @return $this
106
     */
107
    public function setMethodSeparator(string $separator): DispatcherInterface
108
    {
109
        $this->methodSeparator = $separator;
110
111
        return $this;
112
    }
113
114
    /**
115
     * @param string $separator Separator
116
     * @return $this
117
     */
118
    public function setNamespaceSeparator(string $separator): DispatcherInterface
119
    {
120
        $this->namespaceSeparator = $separator;
121
122
        return $this;
123
    }
124
125
    /**
126
     * @inheritDoc
127
     */
128 2
    public function dispatch(
129
        ServerRequestInterface $request,
130
        $handler
131
    ): ?ResponseInterface {
132 2
        if (is_string($handler)) {
133
            $handler = $this->stringHandler($handler, $request);
134
        }
135
136 2
        if ($handler instanceof RequestHandlerInterface) {
137
            return $handler->handle($request);
138
        }
139
140 2
        if (is_callable($handler)) {
141 2
            return $handler($request);
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * @param string $handler Handler String
149
     * @param \Psr\Http\Message\ServerRequestInterface $request
150
     * @return mixed
151
     */
152
    protected function stringHandler(
153
        string $handler,
154
        ServerRequestInterface $request
155
    ) {
156
        $result = $this->parseString($handler);
157
158
        if (!$this->container->has($result['className'])) {
159
            return null;
160
        }
161
162
        $handler = $this->container->get($result['className']);
163
164
        if ($result['method'] === null) {
165
            return $handler;
166
        }
167
168
        return $handler->{$result['method']}($request);
169
    }
170
171
    /**
172
     * @param string $handler Handler String
173
     * @return array
174
     */
175
    protected function parseString(string $handler): array
176
    {
177
        $namespace = $this->defaultNamespace;
178
        $method = null;
179
180
        // Determine the namespace
181
        $position = strpos($handler, $this->namespaceSeparator);
182
        if ($position) {
183
            $namespace = strstr($handler, '.', true);
184
            $handler = substr($handler, $position + 1);
185
        }
186
187
        // Determine if there is an action besides a class
188
        $position = strpos($handler, $this->methodSeparator);
189
        if ($position) {
190
            $method = substr($handler, $position + 1);
191
            $handler = substr($handler, 0, $position);
192
        }
193
194
        $fqcn = $this->classTemplate;
195
        $this->classParts['className'] = $handler;
196
        $this->classParts['namespace'] = $namespace;
197
198
        foreach ($this->classParts as $placeholder => $var) {
199
            $fqcn = str_replace(
200
                '{' . (string)$placeholder . '}',
201
                (string)$var,
202
                $fqcn
203
            );
204
        }
205
206
        return [
207
            'className' => $fqcn,
208
            'method' => $method
209
        ];
210
    }
211
}
212