Passed
Pull Request — master (#7)
by Chauncey
02:26
created

DynamicTemplateRegistry::get()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8977
c 0
b 0
f 0
cc 6
nc 6
nop 1
1
<?php
2
3
namespace Charcoal\View;
4
5
use InvalidArgumentException;
6
7
/**
8
 * Dynamic Template Registry
9
 */
10
class DynamicTemplateRegistry extends AbstractTemplateRegistry
11
{
12
    /**
13
     * Store of registered template keys.
14
     *
15
     * @var boolean[]
16
     */
17
    private $registered = [];
18
19
    /**
20
     * Store of template views.
21
     *
22
     * @var boolean[]
23
     */
24
    private $templates = [];
25
26
    /**
27
     * Store of protected template keys.
28
     *
29
     * @var boolean[]
30
     */
31
    private $protected = [];
32
33
    /**
34
     * Store of single-use templates.
35
     *
36
     * @var {string|boolean}[]
37
     */
38
    private $once = [];
39
40
    /**
41
     * Create new dynamic template collection
42
     *
43
     * @param array $templates Pre-populate collection with these templates.
44
     */
45
    public function __construct(array $templates = [])
46
    {
47
        $this->replace($templates);
48
    }
49
50
    /**
51
     * Set the template for the given key.
52
     *
53
     * @param  string $key      The template key.
54
     * @param  string $template The template view.
55
     * @throws InvalidArgumentException If the template is protected.
56
     * @return void
57
     */
58
    public function set($key, $template)
59
    {
60 View Code Duplication
        if (isset($this->protected[$key])) {
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...
61
            if (isset($this->templates[$key]) || isset($this->once[$key])) {
62
                throw new InvalidArgumentException(
63
                    sprintf('Cannot remove protected dynamic template "%s".', $key)
64
                );
65
            }
66
        }
67
68
        $this->templates[$key]  = $template;
69
        $this->registered[$key] = true;
70
    }
71
72
    /**
73
     * Retrieve the template by key.
74
     *
75
     * @param  string $key The template key.
76
     * @return string|null The view assigned to the $key or NULL if template is missing.
77
     */
78
    public function get($key)
79
    {
80
        if (!isset($this->registered[$key])) {
81
            return null;
82
        }
83
84
        if (isset($this->once[$key])) {
85
            $template = $this->once[$key];
86
            if ($template !== true) {
87
                if (isset($this->protected[$key])) {
88
                    $this->once[$key] = true;
89
                } elseif (isset($this->templates[$key])) {
90
                    unset($this->once[$key]);
91
                } else {
92
                    unset($this->registered[$key], $this->once[$key]);
93
                }
94
95
                return $template;
96
            } else {
97
                return null;
98
            }
99
        }
100
101
        return $this->templates[$key];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->templates[$key]; (boolean) is incompatible with the return type declared by the interface Charcoal\View\LoaderRegistryInterface::get of type string|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
102
    }
103
104
    /**
105
     * Determine if a template exists in the collection by key.
106
     *
107
     * @param  string $key The template key.
108
     * @return boolean
109
     */
110
    public function has($key)
111
    {
112
        return isset($this->registered[$key]);
113
    }
114
115
    /**
116
     * Remove template from collection by key.
117
     *
118
     * @param  string  $key   The template key.
119
     * @param  boolean $force To remove protected templates.
120
     * @throws InvalidArgumentException If the template is protected.
121
     * @return void
122
     */
123
    public function remove($key, $force = false)
124
    {
125
        if (!isset($this->registered[$key])) {
126
            return;
127
        }
128
129
        if ($force === true) {
130
            unset($this->registered[$key], $this->templates[$key], $this->once[$key], $this->protected[$key]);
131
            return;
132
        }
133
134
        if (isset($this->protected[$key])) {
135
            throw new InvalidArgumentException(
136
                sprintf('Cannot remove protected dynamic template "%s".', $key)
137
            );
138
        }
139
140
        unset($this->registered[$key], $this->templates[$key], $this->once[$key]);
141
    }
142
143
    /**
144
     * Protects the template from being replaced or removed.
145
     *
146
     * Note: The template view can be assigned after enabling protection.
147
     *
148
     * @param  string      $key      The template key to protect from being changed.
149
     * @param  string|null $template Optional. The template view to protect.
150
     * @return void
151
     */
152
    public function protect($key, $template = null)
153
    {
154
        $this->protected[$key] = true;
155
156
        if ($template !== null) {
157
            $this->set($key, $template);
158
        }
159
    }
160
161
    /**
162
     * Set the template for the given key to be is used at most once.
163
     *
164
     * The `once()` method is identical to `set()`, except that the template
165
     * is unbound after its first retrieval.
166
     *
167
     * Note: If the template is protected, the template cannot be rebound.
168
     *
169
     * @param  string      $key      The template key to limit.
170
     * @param  string|null $template Optional. The template view to limit.
171
     * @throws InvalidArgumentException If the template is protected.
172
     * @return void
173
     */
174
    public function once($key, $template = null)
175
    {
176 View Code Duplication
        if (isset($this->protected[$key]) && isset($this->once[$key])) {
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...
177
            throw new InvalidArgumentException(
178
                sprintf('Cannot remove protected dynamic template "%s".', $key)
179
            );
180
        }
181
182
        $this->once[$key]       = $template;
183
        $this->registered[$key] = true;
184
    }
185
186
    /**
187
     * Add template(s) to collection, replacing existing templates with the same key.
188
     *
189
     * @param  array $templates Key-value array of templates to replace to this collection.
190
     * @return void
191
     */
192
    public function replace(array $templates)
193
    {
194
        foreach ($templates as $key => $template) {
195
            $this->set($key, $template);
196
        }
197
    }
198
199
    /**
200
     * Retrieve all registered template keys.
201
     *
202
     * @return string[]
203
     */
204
    public function registeredTemplates()
205
    {
206
        return array_keys($this->templates);
207
    }
208
209
    /**
210
     * Remove all templates from collection.
211
     *
212
     * @param  boolean $force If TRUE, protected templates are also removed.
213
     * @return void
214
     */
215
    public function clear($force = false)
216
    {
217
        if ($force === true) {
218
            $this->registered = [];
219
            $this->protected  = [];
220
            $this->templates  = [];
221
            $this->once       = [];
222
        } else {
223
            $this->registered = array_intersect_key($this->registered, $this->protected);
224
            $this->templates  = array_intersect_key($this->templates, $this->protected);
225
            $this->once       = array_intersect_key($this->once, $this->protected);
226
        }
227
    }
228
}
229