Completed
Push — master ( 7beb93...2f17d3 )
by Changwan
03:48
created

Dispatcher::getCompiledRoutes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 4
nop 0
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php
2
namespace Wandu\Router;
3
4
use Closure;
5
use FastRoute\Dispatcher as FastDispatcher;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Wandu\Router\Contracts\LoaderInterface;
8
use Wandu\Router\Contracts\ResponsifierInterface;
9
10
class Dispatcher
11
{
12
    /** @var \Wandu\Router\Contracts\LoaderInterface */
13
    protected $loader;
14
15
    /** @var \Wandu\Router\Responsifier\NullResponsifier */
16
    protected $responsifier;
17
    
18
    /** @var \Wandu\Router\Configuration */
19
    protected $config;
20
    
21
    /** @var \Closure */
22
    protected $handler;
23
    
24
    /** @var \Wandu\Router\CompiledRoutes */
25
    protected $compiledRoutes;
26
    
27 24
    public function __construct(
28
        LoaderInterface $loader = null,
29
        ResponsifierInterface $responsifier = null,
30
        Configuration $config = null
31
    ) {
32 24
        $this->loader = $loader;
33 24
        $this->responsifier = $responsifier;
0 ignored issues
show
Documentation Bug introduced by
It seems like $responsifier can also be of type object<Wandu\Router\Cont...\ResponsifierInterface>. However, the property $responsifier is declared as type object<Wandu\Router\Resp...ifier\NullResponsifier>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
34 24
        $this->config = $config ?: new Configuration([]);
35 24
    }
36
37 1
    public function flush()
38
    {
39 1
        if ($this->config->isCacheEnabled()) {
40 1
            @unlink($this->config->getCacheFile());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
41
        }
42 1
    }
43
44
    /**
45
     * @deprecated use setRoutes
46
     * 
47
     * @param \Closure $handler
48
     * @return \Wandu\Router\Dispatcher
49
     */
50
    public function withRoutes(Closure $handler)
51
    {
52
        $inst = clone $this;
53
        $inst->setRoutes($handler);
54
        return $inst;
55
    }
56
57
    /**
58
     * @param \Closure $handler
59
     */
60 24
    public function setRoutes(Closure $handler)
61
    {
62 24
        $this->compiledRoutes = null;
63 24
        $this->handler = $handler;
64 24
    }
65
    
66
    /**
67
     * @param string $name
68
     * @param array $attributes
69
     * @return string
70
     */
71 1
    public function getPath($name, array $attributes = [])
72
    {
73 1
        return $this->getCompiledRoutes()->getPattern($name)->path($attributes);
74
    }
75
76
    /**
77
     * @param \Psr\Http\Message\ServerRequestInterface $request
78
     * @return \Psr\Http\Message\ResponseInterface
79
     */
80 23
    public function dispatch(ServerRequestInterface $request)
81
    {
82 23
        $compiledRoutes = $this->getCompiledRoutes();
83 23
        $request = $this->applyVirtualMethod($request);
84 23
        return $compiledRoutes->dispatch($request, $this->loader, $this->responsifier);
85
    }
86
    
87 24
    protected function getCompiledRoutes(): CompiledRoutes
88
    {
89 24
        if (!$this->compiledRoutes) {
90 24
            $cacheEnabled = $this->config->isCacheEnabled();
91 24
            if ($this->isCached()) {
92 2
                $this->compiledRoutes = $this->restoreCache();
93
            } else {
94 24
                $this->compiledRoutes = CompiledRoutes::compile($this->handler, $this->config);
95 24
                if ($cacheEnabled) {
96 2
                    $this->storeCache($this->compiledRoutes);
97
                }
98
            }
99
        }
100 24
        return $this->compiledRoutes;
101
    }
102
103
    /**
104
     * @param \Psr\Http\Message\ServerRequestInterface $request
105
     * @return \Psr\Http\Message\ServerRequestInterface
106
     */
107 23
    protected function applyVirtualMethod(ServerRequestInterface $request)
108
    {
109 23
        if (!$this->config->isVirtualMethodEnabled()) {
110 20
            return $request;
111
        }
112 3
        $parsedBody = $request->getParsedBody();
113 3
        if (isset($parsedBody['_method'])) {
114 1
            return $request->withMethod(strtoupper($parsedBody['_method']));
115
        }
116 2
        if ($request->hasHeader('X-Http-Method-Override')) {
117 1
            return $request->withMethod(strtoupper($request->getHeaderLine('X-Http-Method-Override')));
118
        }
119 1
        return $request;
120
    }
121
122
    /**
123
     * @return bool
124
     */
125 24
    public function isCached(): bool
126
    {
127 24
        $cacheEnabled = $this->config->isCacheEnabled();
128 24
        $cacheFile = $this->config->getCacheFile();
129 24
        return $cacheEnabled && file_exists($cacheFile);
130
    }
131
    
132 2
    private function storeCache(CompiledRoutes $routes)
133
    {
134 2
        file_put_contents($this->config->getCacheFile(), serialize($routes));
135 2
    }
136
137 2
    private function restoreCache(): CompiledRoutes
138
    {
139 2
        return unserialize(file_get_contents($this->config->getCacheFile()));
140
    }
141
}
142