Passed
Push — master ( b1935f...050f56 )
by Alain
02:40
created

ViewBuilder   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 68.08%

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 20
c 8
b 0
f 1
lcom 1
cbo 6
dl 0
loc 228
ccs 32
cts 47
cp 0.6808
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A create() 0 9 2
A getEngine() 0 4 1
A getView() 0 8 2
A getViewFinder() 0 4 1
A getEngineFinder() 0 4 1
A addLocation() 0 4 1
A scanLocations() 0 11 3
A getFinder() 0 9 2
B resolveType() 0 28 6
1
<?php
2
/**
3
 * Bright Nucleus View Component.
4
 *
5
 * @package   BrightNucleus\View
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   MIT
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\View;
13
14
use BrightNucleus\Config\ConfigInterface;
15
use BrightNucleus\Config\ConfigTrait;
16
use BrightNucleus\Config\Exception\FailedToProcessConfigException;
17
use BrightNucleus\View\Engine\EngineFinderInterface;
18
use BrightNucleus\View\Engine\EngineInterface;
19
use BrightNucleus\View\Engine\ViewFinderInterface;
20
use BrightNucleus\View\Exception\FailedToInstantiateViewException;
21
use BrightNucleus\View\Location\LocationInterface;
22
use BrightNucleus\View\Support\FinderInterface;
23
use BrightNucleus\View\View\ViewInterface;
24
25
/**
26
 * Class ViewBuilder.
27
 *
28
 * @since   0.1.0
29
 *
30
 * @package BrightNucleus\View
31
 * @author  Alain Schlesser <[email protected]>
32
 */
33
class ViewBuilder
34
{
35
36
    use ConfigTrait;
37
38
    const ENGINE_FINDER_KEY = 'EngineFinder';
39
    const VIEW_FINDER_KEY   = 'ViewFinder';
40
41
    /**
42
     * ViewFinder instance.
43
     *
44
     * @since 0.1.0
45
     *
46
     * @var ViewFinderInterface
47
     */
48
    protected $viewFinder;
49
50
    /**
51
     * EngineFinder instance.
52
     *
53
     * @since 0.1.0
54
     *
55
     * @var EngineFinderInterface
56
     */
57
    protected $engineFinder;
58
59
    /**
60
     * Locations to scan for views.
61
     *
62
     * @since 0.1.0
63
     *
64
     * @var LocationInterface[]
65
     */
66
    protected $locations;
67
68
    /**
69
     * Instantiate a ViewBuilder object.
70
     *
71
     * @since 0.1.0
72
     *
73
     * @param ConfigInterface            $config       Configuration settings.
74
     * @param ViewFinderInterface|null   $viewFinder   ViewFinder instance.
75
     * @param EngineFinderInterface|null $engineFinder EngineFinder instance.
76
     *
77
     * @throws FailedToProcessConfigException If the config could not be processed.
78
     */
79 7
    public function __construct(
80
        ConfigInterface $config,
81
        ViewFinderInterface $viewFinder = null,
82
        EngineFinderInterface $engineFinder = null
83
    ) {
84 7
        $this->processConfig($config);
85 7
        $this->viewFinder   = $viewFinder;
86 7
        $this->engineFinder = $engineFinder;
87 7
    }
88
89
    /**
90
     * Create a new view for a given URI.
91
     *
92
     * @since 0.1.0
93
     *
94
     * @param string $view View identifier to create a view for.
95
     * @param mixed  $type Type of view to create.
96
     *
97
     * @return ViewInterface Instance of the requested view.
98
     */
99 17
    public function create($view, $type = null)
100
    {
101 17
        $uri    = $this->scanLocations([$view]);
102 17
        $engine = $this->getEngine($uri);
103
104 17
        return $uri
105 17
            ? $this->getView($uri, $engine, $type)
106 17
            : $this->getViewFinder()->getNullObject();
107
    }
108
109
    /**
110
     * Get an Engine that can deal with the given URI.
111
     *
112
     * @since 0.1.0
113
     *
114
     * @param string|false $uri URI to get an engine for.
115
     *
116
     * @return EngineInterface Instance of an engine that can deal with the given URI.
117
     */
118 17
    public function getEngine($uri)
119
    {
120 17
        return $this->getEngineFinder()->find([$uri]);
121
    }
122
123
    /**
124
     * Get a view for a given URI, engine and type.
125
     *
126
     * @since 0.1.0
127
     *
128
     * @param string          $uri    URI to get a view for.
129
     * @param EngineInterface $engine Engine to use for the view.
130
     * @param mixed           $type   Type of view to get.
131
     *
132
     * @return ViewInterface View that matches the given requirements.
133
     */
134 17
    public function getView($uri, EngineInterface $engine, $type = null)
135
    {
136 17
        if (null === $type) {
137 17
            return $this->getViewFinder()->find([$uri], $engine);
138
        }
139
140
        return $this->resolveType($type, $uri, $engine);
141
    }
142
143
    /**
144
     * Get the ViewFinder instance.
145
     *
146
     * @since 0.1.0
147
     *
148
     * @return ViewFinderInterface Instance of a ViewFinder.
149
     */
150 17
    public function getViewFinder()
151
    {
152 17
        return $this->getFinder($this->viewFinder, ViewBuilder::VIEW_FINDER_KEY);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
153
    }
154
155
    /**
156
     * Get the EngineFinder instance.
157
     *
158
     * @since 0.1.0
159
     *
160
     * @return EngineFinderInterface Instance of a EngineFinder.
161
     */
162 17
    public function getEngineFinder()
163
    {
164 17
        return $this->getFinder($this->engineFinder, ViewBuilder::ENGINE_FINDER_KEY);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
165
    }
166
167
    /**
168
     * Add a location to scan with the ViewFinder.
169
     *
170
     * @since 0.1.0
171
     *
172
     * @param LocationInterface $location Location to scan with the ViewFinder.
173
     */
174 17
    public function addLocation(LocationInterface $location)
175
    {
176 17
        $this->locations[] = $location;
177 17
    }
178
179
    /**
180
     * Scan Locations for an URI that matches the specified criteria.
181
     *
182
     * @since 0.1.0
183
     *
184
     * @param array $criteria Criteria to match.
185
     *
186
     * @return string|false URI of the requested view, or false if not found.
187
     */
188 17
    public function scanLocations(array $criteria)
189
    {
190
        /** @var LocationInterface $location */
191 17
        foreach ($this->locations as $location) {
192 17
            if ($uri = $location->getURI($criteria)) {
193 17
                return $uri;
194
            }
195
        }
196
197
        return false;
198
    }
199
200
    /**
201
     * Get a finder instance.
202
     *
203
     * @since 0.1.1
204
     *
205
     * @param mixed  $property Property to use.
206
     * @param string $key      Configuration key to use.
207
     *
208
     * @return FinderInterface The requested finder instance.
209
     */
210 17
    protected function getFinder(&$property, $key)
211
    {
212 17
        if (null === $property) {
213 7
            $engineFinderClass = $this->config->getKey($key, 'ClassName');
1 ignored issue
show
Unused Code introduced by
The call to ConfigInterface::getKey() has too many arguments starting with 'ClassName'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
214 7
            $property          = new $engineFinderClass($this->config->getSubConfig($key));
215
        }
216
217 17
        return $property;
218
    }
219
220
    /**
221
     * Resolve the view type.
222
     *
223
     * @since 0.1.0
224
     *
225
     * @param mixed                $type   Type of view that was requested.
226
     * @param string               $uri    URI to get a view for.
227
     * @param EngineInterface|null $engine Engine to use for the view.
228
     *
229
     * @return ViewInterface Resolved View object.
230
     * @throws FailedToInstantiateViewException If the view type could not be resolved.
231
     */
232
    protected function resolveType($type, $uri, EngineInterface $engine = null)
233
    {
234
        $configKey = [static::VIEW_FINDER_KEY, 'Views', $type];
235
236
        if (is_string($type) && $this->config->hasKey($configKey)) {
1 ignored issue
show
Documentation introduced by
$configKey is of type array<integer,*,{"0":"st...,"1":"string","2":"*"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
237
            $className = $this->config->getKey($configKey);
1 ignored issue
show
Documentation introduced by
$configKey is of type array<integer,*,{"0":"st...,"1":"string","2":"*"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
238
            $type      = new $className($uri, $engine);
239
        }
240
241
        if (is_string($type)) {
242
            $type = new $type($uri, $engine);
243
        }
244
245
        if (is_callable($type)) {
246
            $type = $type($uri, $engine);
247
        }
248
249
        if (! $type instanceof ViewInterface) {
250
            throw new FailedToInstantiateViewException(
251
                sprintf(
252
                    _('Could not instantiate view "%s".'),
253
                    serialize($type)
254
                )
255
            );
256
        }
257
258
        return $type;
259
    }
260
}
261