Test Failed
Push — master ( 7ec6d8...dcbebe )
by Alain
02:23
created

AbstractView::addToContext()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 41

Duplication

Lines 18
Ratio 43.9 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 18
loc 41
c 0
b 0
f 0
ccs 0
cts 0
cp 0
rs 7.6666
cc 10
nc 10
nop 3
crap 110

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
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-2017 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\View\View;
13
14
use BrightNucleus\Config\Exception\FailedToProcessConfigException;
15
use BrightNucleus\View\Exception\FailedToInstantiateView;
16
use BrightNucleus\View\Exception\InvalidContextAddingBehavior;
17
use BrightNucleus\View\View;
18
use BrightNucleus\View\Engine\Engine;
19
use BrightNucleus\View\ViewBuilder;
20
use BrightNucleus\Views;
21
use Closure;
22
23
/**
24
 * Abstract class AbstractView.
25
 *
26
 * @since   0.1.0
27
 *
28
 * @package BrightNucleus\View\View
29
 * @author  Alain Schlesser <[email protected]>
30
 */
31
abstract class AbstractView implements View
32
{
33
34
    /**
35
     * URI of the view.
36
     *
37
     * The underscores are used to prevent accidental use of these properties from within the rendering closure.
38
     *
39
     * @since 0.1.0
40
     *
41
     * @var string
42
     */
43
    protected $_uri_;
44
45
    /**
46
     * Engine to use for the view.
47
     *
48
     * The underscores are used to prevent accidental use of these properties from within the rendering closure.
49
     *
50
     * @since 0.1.0
51
     *
52
     * @var Engine
53
     */
54
    protected $_engine_;
55
56
    /**
57
     * ViewBuilder instance.
58
     *
59
     * The underscores are used to prevent accidental use of these properties from within the rendering closure.
60
     *
61
     * @since 0.2.0
62
     *
63
     * @var ViewBuilder
64
     */
65
    protected $_builder_;
66 29
67
    /**
68 29
     * The context with which the view will be rendered.
69 29
     *
70 29
     * The underscores are used to prevent accidental use of these properties from within the rendering closure.
71
     *
72
     * @since 0.4.0
73
     *
74
     * @var array
75
     */
76
    protected $_context_;
77
78
    /**
79
     * Instantiate an AbstractView object.
80
     *
81
     * @since 0.1.0
82 29
     *
83
     * @param string      $uri         URI for the view.
84 29
     * @param Engine      $engine      Engine to use for the view.
85 29
     * @param ViewBuilder $viewBuilder View builder instance to use.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $viewBuilder not be null|ViewBuilder?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
86
     * @param array       $context     Initial context to use.
87 29
     */
88 29
    public function __construct(string $uri, Engine $engine, ViewBuilder $viewBuilder = null, array $context = [])
89
    {
90 29
        $this->_uri_     = $uri;
91
        $this->_engine_  = $engine;
92
        $this->_builder_ = $viewBuilder ?? Views::getViewBuilder();
93 29
        $this->_context_ = $context;
94 29
    }
95
96
    /**
97
     * Render the view.
98
     *
99
     * @since 0.1.0
100
     *
101
     * @param array $context Optional. The context in which to render the view.
102
     * @param bool  $echo    Optional. Whether to echo the output immediately. Defaults to false.
103
     *
104
     * @return string Rendered HTML.
105
     * @throws FailedToProcessConfigException If the Config could not be processed.
106
     */
107
    public function render(array $context = [], bool $echo = false): string
108
    {
109
        $this->assimilateContext($context);
110
111 1
        $closure = Closure::bind(
112
            $this->_engine_->getRenderCallback($this->_uri_, $context),
113 1
            $this,
114 1
            static::class
115
        );
116
117 1
        $output = $closure();
118 1
119
        if ($echo) {
120 1
            echo $output;
121
        }
122
123
        return $output;
124
    }
125
126
    /**
127
     * Render a partial view (or section) for a given URI.
128
     *
129
     * @since 0.2.0
130
     *
131
     * @param string      $view    View identifier to create a view for.
132 29
     * @param array       $context Optional. The context in which to render the view.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $context not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
133
     * @param string|null $type    Type of view to create.
134 29
     *
135
     * @return string Rendered HTML content.
136 29
     * @throws FailedToProcessConfigException If the Config could not be processed.
137
     * @throws FailedToInstantiateView If the View could not be instantiated.
138
     */
139
    public function section(string $view, array $context = null, $type = null): string
140
    {
141
        if (null === $context) {
142
            $context = $this->_context_;
143
        }
144 29
145
        $viewObject = $this->_builder_->create($view, $type);
146 29
147
        return $viewObject->render($context);
148
    }
149 29
150
    /**
151
     * Get the entire array of contextual data.
152
     *
153
     * @since 0.4.0
154
     *
155
     * @return array Array of contextual data.
156
     */
157
    public function getContext(): array
158 29
    {
159
        return $this->_context_;
160 29
    }
161 29
162 29
    /**
163
     * Add information to the context.
164 29
     *
165
     * @param string $key      Context key to add.
166
     * @param mixed  $value    Value to add under the given key.
167
     * @param string $behavior Behavior to use for adapting the context.
168
     * @return View
169
     */
170
    public function addToContext( string $key, $value, string $behavior ): View
171
    {
172
        switch ($behavior) {
173
            case View::REPLACE:
174
                $this->_context_[$key] = $value;
175
                return $this;
176
            case View::MERGE:
177
                if(array_key_exists($key, $this->_context_)) {
178
                    $this->_context_ = array_merge_recursive($this->_context_, [$key => $value]);
179
                    return $this;
180
                }
181
                $this->_context_[$key] = $value;
182
                return $this;
183 View Code Duplication
            case View::ADD_ONLY:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
184
                if (array_key_exists($key, $this->_context_)) {
185
                    return $this;
186
                }
187
                $this->_context_[$key] = $value;
188
                return $this;
189 View Code Duplication
            case View::REPLACE_ONLY:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
190
                if (! array_key_exists($key, $this->_context_)) {
191
                    return $this;
192
                }
193
                $this->_context_[$key] = $value;
194
                return $this;
195 View Code Duplication
            case View::MERGE_ONLY:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
196
                if (! array_key_exists($key, $this->_context_)) {
197
                    return $this;
198
                }
199
                $this->_context_ = array_merge_recursive($this->_context_, [$key => $value]);
200
                return $this;
201
            default:
202
                throw new InvalidContextAddingBehavior(
203
                    sprintf(
204
                        _('Invalid behavior "%s" for adding to the context of view "%s".'),
205
                        $key,
206
                        $this->_uri_
207
                    )
208
                );
209
        }
210
    }
211
212
    /**
213
     * Associate a view builder with this view.
214
     *
215
     * @since 0.2.0
216
     *
217
     * @param ViewBuilder $builder
218
     *
219
     * @return View
220
     */
221
    public function setBuilder(ViewBuilder $builder): View
222
    {
223
        $this->_builder_ = $builder;
224
225
        return $this;
226
    }
227
228
    /**
229
     * Assimilate the context to make it available as properties.
230
     *
231
     * @since 0.2.0
232
     *
233
     * @param array $context Context to assimilate.
234
     */
235
    protected function assimilateContext(array $context = [])
236
    {
237
        $this->_context_ = $context;
238
        foreach ($context as $key => $value) {
239
            $this->$key = $value;
240
        }
241
    }
242
243
    /**
244
     * Turn invokable objects as properties into methods of the view.
245
     *
246
     * @param string $method    Method that was called on the view.
247
     * @param array  $arguments Array of arguments that were used.
248
     * @return mixed Return value of the invokable object.
249
     */
250
    public function __call($method, $arguments) {
251
        if ( ! property_exists($this, $method)
252
             || ! is_callable($this->$method)) {
253
            trigger_error(
254
                "Call to undefined method {$method} on a view.",
255
                E_USER_ERROR
256
            );
257
        }
258
259
        $callable = $this->$method;
260
261
        return $callable(...$arguments);
262
    }
263
}
264