Test Failed
Push — master ( 6cb9f0...7deac3 )
by Antonio Carlos
07:23 queued 02:51
created

src/Support/ResourceChecker.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace PragmaRX\Health\Support;
4
5
use Exception;
6
use Illuminate\Support\Collection;
7
use PragmaRX\Health\Support\Traits\HandleExceptions;
8
9
class ResourceChecker
10
{
11
    use HandleExceptions;
12
13
    /**
14
     * Unknown error.
15
     */
16
    const UNKNOWN_ERROR = 'Unknown error.';
17
18
    /**
19
     * The current action.
20
     *
21
     * @var
22
     */
23
    protected $currentAction = 'check';
24
25
    /**
26
     * All resources.
27
     *
28
     * @var
29
     */
30
    protected $resources;
31
32
    /**
33
     * Was checked?
34
     *
35
     * @var
36
     */
37
    protected $checked;
38
39
    /**
40
     * Services already notified of error.
41
     *
42
     * @var
43
     */
44
    protected $notified = [];
45
46
    /**
47
     * The cache service.
48
     *
49
     * @var Cache
50
     */
51
    protected $cache;
52
53
    /**
54
     * Resource loader.
55
     *
56
     * @var ResourceLoader
57
     */
58
    protected $resourceLoader;
59
60
    /**
61
     * ResourceChecker constructor.
62
     *
63
     * @param ResourceLoader $resourceLoader
64
     * @param Cache $cache
65
     */
66 2
    public function __construct(ResourceLoader $resourceLoader, Cache $cache)
67
    {
68 2
        $this->cache = $cache;
69
70 2
        $this->resourceLoader = $resourceLoader;
71 2
    }
72
73
    /**
74
     * Check all resources.
75
     *
76
     * @param bool $force
77
     * @return \Illuminate\Support\Collection
78
     * @throws Exception
79
     */
80
    public function checkResources($force = false)
81
    {
82
        if (!($resources = $this->getCachedResources($force))->isEmpty()) {
83
            return $resources;
84
        }
85
86
        if (!$this->allResourcesAreGood()) {
87
            return $this->resources = collect();
88
        }
89
90
        $resources = $this->sortResources(
91
            $this->getNonGlobalResources()
92
                ->each(function ($resource) {
93
                    $this->checkResource($resource);
94
                })
95
                ->merge(
96
                    $this->getGlobalResources()->each(function ($resource) {
97
                        return $resource->checkGlobal($this->getResources());
98
                    })
99
                )
100
        );
101
102
        $this->checked = true;
103
104
        return $this->resources = $this->cache->cacheResources($resources);
105
    }
106
107
    /**
108
     * Check a resource.
109
     *
110
     * @param $resource
111
     * @return array
112
     * @throws Exception
113
     */
114
    public function checkResource($resource)
115
    {
116
        $resource =
117
            $resource instanceof Resource
118
                ? $resource
119
                : $this->getResourceBySlug($resource);
120
121
        $checked = $this->cache->remember($resource->slug, function () use (
122
            $resource
123
        ) {
124
            return $resource->check();
125
        });
126
127
        $resource->targets = $checked->targets;
128
129
        return $resource;
130
    }
131
132
    /**
133
     * Get cached resources.
134
     *
135
     * @param bool $force
136
     * @return \Illuminate\Support\Collection
137
     * @throws Exception
138
     */
139
    protected function getCachedResources($force = false)
140
    {
141
        if ($force) {
142
            return collect();
143
        }
144
145
        if ($this->checked) {
146
            return $this->getResources();
147
        }
148
149
        return $this->resources = $this->cache->getCachedResources();
150
    }
151
152
    /**
153
     * Get current action.
154
     *
155
     * @return mixed
156
     */
157
    public function getCurrentAction()
158
    {
159
        return $this->currentAction;
160
    }
161
162
    /**
163
     * Get all non global resources.
164
     *
165
     * @return bool
166
     * @throws Exception
167
     */
168
    protected function allResourcesAreGood()
169
    {
170
        return !$this->getResources()
171
            ->reject(function ($resource) {
172
                return !$resource instanceof Resource;
173
            })
174
            ->isEmpty();
175
    }
176
177
    /**
178
     * Get all non global resources.
179
     *
180
     * @return Collection
181
     * @throws Exception
182
     */
183
    protected function getNonGlobalResources()
184
    {
185
        return $this->getResources()->filter(function (Resource $resource) {
186
            return !$resource->isGlobal;
187
        });
188
    }
189
190
    /**
191
     * Get all global resources.
192
     *
193
     * @return Collection
194
     * @throws Exception
195
     */
196
    protected function getGlobalResources()
197
    {
198
        return $this->getResources()->filter(function (Resource $resource) {
199
            return $resource->isGlobal;
200
        });
201
    }
202
203
    /**
204
     * Get a resource by slug.
205
     *
206
     * @param $slug
207
     * @return mixed
208
     * @throws Exception
209
     */
210
    public function getResourceBySlug($slug)
211
    {
212
        return $this->getResources()
213
            ->where('slug', $slug)
214
            ->first();
215
    }
216
217
    /**
218
     * Make the result array.
219
     *
220
     * @param $exception
221
     * @param $resourceChecker
222
     * @return array
223
     */
224
    protected function makeResult($exception, $resourceChecker)
225
    {
226
        $message = $exception->getMessage()
227
            ? $exception->getMessage()
228
            : static::UNKNOWN_ERROR;
229
230
        if (!isset($resourceChecker)) {
231
            return [
232
                null,
233
                [
234
                    'healthy' => false,
235
                    'message' => $message,
236
                ],
237
            ];
238
        }
239
240
        $resourceChecker->makeResult(false, $message);
241
242
        return [$resourceChecker, null];
243
    }
244
245
    /**
246
     * Get all resources.
247
     *
248
     * @return \Illuminate\Support\Collection
249
     * @throws Exception
250
     */
251
    public function getResources()
252
    {
253
        return $this->sortResources($this->loadResources());
254
    }
255
256
    /**
257
     * Set the current action.
258
     *
259
     * @param mixed $currentAction
260
     */
261
    public function setCurrentAction($currentAction)
262
    {
263
        $this->currentAction = $currentAction;
264
    }
265
266
    /**
267
     * Resources setter.
268
     *
269
     * @param mixed $resources
270
     */
271
    public function setResources($resources)
272
    {
273
        $this->resources = $resources;
274
    }
275
276
    /**
277
     * Load all resources.
278
     *
279
     * @return \Illuminate\Support\Collection
280
     * @throws Exception
281
     */
282
    public function loadResources()
283
    {
284
        if (is_null($this->resources) || $this->resources->isEmpty()) {
285
            $this->resources = $this->resourceLoader->loadResources()->map(
286
                function ($resource) {
287
                    return $this->handleExceptions(function () use ($resource) {
288
                        return $this->makeResource($resource);
289
                    });
290
                }
291
            );
292
        }
293
294
        return $this->resources;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->resources; (object|integer|double|string|array|boolean) is incompatible with the return type documented by PragmaRX\Health\Support\...eChecker::loadResources of type Illuminate\Support\Collection.

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...
295
    }
296
297
    /**
298
     * Get one resource.
299
     *
300
     * @param resource|Collection $resource
301
     * @return \PragmaRX\Health\Support\Resource
302
     */
303
    public function makeResource($resource)
304
    {
305
        if ($resource instanceof Resource) {
306
            return $resource;
307
        }
308
309
        return Resource::factory($resource);
0 ignored issues
show
It seems like $resource defined by parameter $resource on line 303 can also be of type resource; however, PragmaRX\Health\Support\Resource::factory() does only seem to accept object<Illuminate\Support\Collection>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
310
    }
311
312
    /**
313
     * Sort resources.
314
     *
315
     * @param $resources
316
     * @return \Illuminate\Support\Collection
317
     */
318
    protected function sortResources($resources)
319
    {
320
        if ($sortBy = config('health.sort_by')) {
321
            return $resources->sortBy(function ($resource) use ($sortBy) {
322
                return $this->handleExceptions(function () use (
323
                    $resource,
324
                    $sortBy
325
                ) {
326
                    return (
327
                        ($resource->isGlobal ? 'a-' : 'z-') . $resource->$sortBy
328
                    );
329
                });
330
            });
331
        }
332
333
        return $resources;
334
    }
335
}
336