Passed
Push — master ( 3bddf7...37f0e3 )
by Max
07:30
created

AbstractDictionary::setDefaultView()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
0 ignored issues
show
Coding Style introduced by
Missing file doc comment
Loading history...
3
declare(strict_types=1);
4
5
namespace EasyDictionary;
6
7
use EasyDictionary\Interfaces\DataProviderInterface;
8
use EasyDictionary\Interfaces\DictionaryInterface;
9
use Psr\SimpleCache\CacheInterface;
10
11
/**
12
 * Class AbstractDictionary
13
 * @package EasyDictionary
0 ignored issues
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
14
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @author tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
15
abstract class AbstractDictionary implements DictionaryInterface
16
{
17
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
18
     * @var string
19
     */
20
    protected $name = '';
21
22
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
23
     * @var DataProviderInterface
24
     */
25
    protected $dataProvider = null;
26
27
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
28
     * @var callable
29
     */
30
    protected $view = null;
31
32
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
33
     * @var \Psr\SimpleCache\CacheInterface
34
     */
35
    protected $cache = null;
36
37
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
38
     * @var int
39
     */
40
    protected $cacheTTL = 0;
41
42
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
43
     * @var iterable
44
     */
45
    protected $data = [];
46
47
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
48
     * @var array
49
     */
50
    protected $searchFields = [];
51
52
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
53
     * @var bool
54
     */
55
    protected $dataLoaded = false;
56
57
    /**
58
     * AbstractDictionary constructor.
59
     * @param string $name
0 ignored issues
show
Coding Style introduced by
There must be exactly one blank line before the tags in a doc comment
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
60
     */
61
    public function __construct(string $name = '')
62
    {
63
        $this->setName($name);
64
    }
65
66
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
67
     * @param callable|null $view
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
68
     * @return $this
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
69
     */
70
    public function setDefaultView(callable $view = null)
71
    {
72
        $this->view = $view;
73
74
        return $this;
75
    }
76
77
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
78
     * @return null|DataProviderInterface
79
     */
80
    public function getDataProvider(): ?DataProviderInterface
81
    {
82
        return $this->dataProvider;
83
    }
84
85
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
86
     * @param DataProviderInterface $provider
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
87
     * @return $this
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
88
     */
89
    public function setDataProvider(DataProviderInterface $provider)
90
    {
91
        $this->dataProvider = $provider;
92
93
        return $this;
94
    }
95
96
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
97
     * @return \Iterator
98
     */
99
    public function getIterator(): iterable
100
    {
101
        $view = $this->getDefaultView();
102
103
        if (is_null($view)) {
104
            foreach ($this->getData() as $key => $item) {
105
                yield $key => $item;
106
            }
107
        } else {
108
            yield from $this->withView($view);
109
        }
110
    }
111
112
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
113
     * @return callable|null
114
     */
115
    public function getDefaultView(): ?callable
116
    {
117
        return $this->view;
118
    }
119
120
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
121
     * @return mixed
122
     */
123
    public function getData(): iterable
124
    {
125
        if (false === $this->dataLoaded) {
126
            $cache = $this->getCache();
127
128
            if (is_null($cache)) {
129
                $this->data = $this->loadData();
130
            } else {
131
                $key = static::class . '_' . $this->getName();
132
133
                try {
134
                    if (!($this->data = $cache->get($key, []))) {
135
                        $this->data = $this->loadData();
136
                        $cache->set($key, $this->data, $this->cacheTTL);
137
                    }
138
                } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
139
                    $this->data = [];
0 ignored issues
show
Documentation Bug introduced by
It seems like array() of type array is incompatible with the declared type object<EasyDictionary\iterable> of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
140
                }
141
            }
142
143
            $this->dataLoaded = true;
144
        }
145
146
        return $this->data;
147
    }
148
149
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
150
     * @return CacheInterface
151
     */
152
    public function getCache(): ?CacheInterface
153
    {
154
        return $this->cache;
155
    }
156
157
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
158
     * @param CacheInterface $cache
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
159
     * @param int $ttl
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 12 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
160
     * @return $this
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
161
     */
162
    public function setCache(CacheInterface $cache, int $ttl = 3600)
163
    {
164
        $this->cache = $cache;
165
        $this->cacheTTL = $ttl;
166
167
        return $this;
168
    }
169
170
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
171
     * @return iterable
172
     */
173
    abstract protected function loadData(): iterable;
174
175
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
176
     * @return string
177
     */
178
    public function getName(): string
179
    {
180
        return $this->name;
181
    }
182
183
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
184
     * @param string $name
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
185
     * @return self
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
186
     */
187
    public function setName(string $name)
188
    {
189
        $this->name = $name;
190
191
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (EasyDictionary\AbstractDictionary) is incompatible with the return type declared by the interface EasyDictionary\Interface...onaryInterface::setName of type string.

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...
192
    }
193
194
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
195
     * @param callable $callable
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
196
     * @return \Generator
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
197
     */
198
    public function withView(callable $callable = null): iterable
199
    {
200
        if (is_callable($callable)) {
201
            yield from call_user_func($callable, $this->getData());
202
        } else {
203
            yield from $this->getIterator();
204
        }
205
    }
206
207
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
208
     * @return int
209
     */
210
    public function count(): int
211
    {
212
        return count($this->getData());
213
    }
214
215
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
216
     * @param string $pattern
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
217
     * @param bool $strict
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
218
     * @return iterable
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
219
     */
220
    public function search(string $pattern, bool $strict = false): iterable
221
    {
222
        $data = [];
223
        $searchData = [];
224
        $searchFields = array_filter($this->getSearchFields($strict));
225
        foreach ($this as $key => $value) {
226
            $data[$key] = $value;
227
228
            if (is_array($value)) {
229
                $searchData[$key] = (string)$key . ' ' .
230
                    join(' ', empty($searchFields) ? $value : array_intersect_key($value, $searchFields));
231
            } else {
232
                $searchData[$key] = (string)$key . ' ' . (string)$value;
233
            }
234
        }
235
236
        return array_intersect_key($data, preg_grep($pattern, $searchData));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array_intersect_k...pattern, $searchData)); (array) is incompatible with the return type declared by the interface EasyDictionary\Interface...ionaryInterface::search of type EasyDictionary\Interfaces\iterable.

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...
237
    }
238
239
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
240
     * @param bool $strict
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
241
     * @return array
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
242
     */
243
    public function getSearchFields(bool $strict = false): array
244
    {
245
        return true === $strict ? array_filter($this->searchFields) : $this->searchFields;
246
    }
247
248
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
249
     * @param array $searchFields
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value indented incorrectly; expected 2 spaces but found 1
Loading history...
250
     * @return $this
0 ignored issues
show
Coding Style introduced by
Tag cannot be grouped with parameter tags in a doc comment
Loading history...
251
     */
252
    public function setSearchFields(array $searchFields)
253
    {
254
        $this->searchFields = $searchFields;
255
256
        return $this;
257
    }
258
}
259