Completed
Push — master ( 3752f9...b4e647 )
by Oscar
03:55
created

App::get()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
namespace Fol;
4
5
use Fol\NotFoundException;
6
use Fol\ContainerException;
7
use Psr\Container\ContainerInterface;
8
use Interop\Container\ServiceProviderInterface;
9
use Psr\Http\Message\UriInterface;
10
use Throwable;
11
12
/**
13
 * Manages an app.
14
 */
15
class App implements ContainerInterface
16
{
17
    private $services = [];
18
    private $items = [];
19
    private $path;
20
    private $uri;
21
22
    public function __construct(string $path, UriInterface $uri)
23
    {
24
        $this->path = rtrim($path, '/') ?: '/';
25
        $this->uri = $uri;
26
    }
27
28
    public function addServiceProvider(ServiceProviderInterface $provider): self
29
    {
30
        foreach ($provider->getFactories() as $id => $factory) {
31
            $this->addFactory($id, $factory);
32
        }
33
34
        foreach ($provider->getExtensions() as $id => $extension) {
35
            $this->addExtension($id, $extension);
36
        }
37
38
        return $this;
39
    }
40
41 View Code Duplication
    public function addFactory(string $id, callable $factory): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
42
    {
43
        $service = $this->services[$id] ?? [];
44
        $service[0] = $factory;
45
        $this->services[$id] = $service;
46
47
        return $this;
48
    }
49
50 View Code Duplication
    public function addExtension($id, callable $extension): self
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
51
    {
52
        $service = $this->services[$id] ?? [null];
53
        $service[] = $extension;
54
        $this->services[$id] = $service;
55
56
        return $this;
57
    }
58
59
    /**
60
     * @see ContainerInterface
61
     */
62
    public function has($id)
63
    {
64
        if (isset($this->items[$id])) {
65
            return true;
66
        }
67
68
        if (isset($this->services[$id][0])) {
69
            return true;
70
        }
71
72
        return false;
73
    }
74
75
    /**
76
     * @see ContainerInterface
77
     */
78
    public function get($id)
79
    {
80
        if (isset($this->items[$id])) {
81
            return $this->items[$id];
82
        }
83
84
        if (isset($this->services[$id][0])) {
85
            try {
86
                return $this->items[$id] = array_reduce(
87
                    $this->services[$id],
88
                    function ($item, callable $callback) {
89
                        return $callback($this, $item);
90
                    }
91
                );
92
            } catch (Throwable $exception) {
93
                throw new ContainerException("Error retrieving {$id}", 0, $exception);
94
            }
95
        }
96
97
        throw new NotFoundException("Identifier {$id} is not found");
98
    }
99
100
    public function set(string $id, $value): self
101
    {
102
        $this->items[$id] = $value;
103
104
        return $this;
105
    }
106
107
    /**
108
     * Returns the absolute path of the app.
109
     */
110
    public function getPath(string ...$dirs): string
111
    {
112
        if (empty($dirs)) {
113
            return $this->path;
114
        }
115
116
        return self::canonicalize($this->path, ...$dirs);
117
    }
118
119
    /*
120
     * Returns the base uri of the app.
121
     */
122
    public function getUri(string ...$dirs): UriInterface
123
    {
124
        if (empty($dirs)) {
125
            return $this->uri;
126
        }
127
128
        return $this->uri->withPath(self::canonicalize($this->uri->getPath(), ...$dirs));
129
    }
130
131
    /**
132
     * helper function to fix paths '//' or '/./' or '/foo/../' in a path.
133
     */
134
    private static function canonicalize(string $base, string ...$dirs): string
135
    {
136
        $path = $base.'/'.implode('/', $dirs);
137
        $replace = ['#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'];
138
139
        do {
140
            $path = preg_replace($replace, '/', $path, -1, $n);
141
        } while ($n > 0);
142
143
        return $path;
144
    }
145
}
146