GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 7c9800...6d277d )
by Robert
25:06
created

FixtureController::actionUnload()   C

Complexity

Conditions 7
Paths 15

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 7.016

Importance

Changes 0
Metric Value
dl 0
loc 45
rs 6.7272
c 0
b 0
f 0
ccs 27
cts 29
cp 0.931
cc 7
eloc 26
nc 15
nop 1
crap 7.016
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\console\controllers;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
use yii\base\InvalidParamException;
13
use yii\console\Controller;
14
use yii\console\Exception;
15
use yii\helpers\Console;
16
use yii\helpers\FileHelper;
17
use yii\test\FixtureTrait;
18
19
/**
20
 * Manages fixture data loading and unloading.
21
 *
22
 * ```
23
 * #load fixtures from UsersFixture class with default namespace "tests\unit\fixtures"
24
 * yii fixture/load User
25
 *
26
 * #also a short version of this command (generate action is default)
27
 * yii fixture User
28
 *
29
 * #load all fixtures
30
 * yii fixture "*"
31
 *
32
 * #load all fixtures except User
33
 * yii fixture "*, -User"
34
 *
35
 * #load fixtures with different namespace.
36
 * yii fixture/load User --namespace=alias\my\custom\namespace\goes\here
37
 * ```
38
 *
39
 * The `unload` sub-command can be used similarly to unload fixtures.
40
 *
41
 * @author Mark Jebri <[email protected]>
42
 * @since 2.0
43
 */
44
class FixtureController extends Controller
45
{
46
    use FixtureTrait;
47
48
    /**
49
     * @var string controller default action ID.
50
     */
51
    public $defaultAction = 'load';
52
    /**
53
     * @var string default namespace to search fixtures in
54
     */
55
    public $namespace = 'tests\unit\fixtures';
56
    /**
57
     * @var array global fixtures that should be applied when loading and unloading. By default it is set to `InitDbFixture`
58
     * that disables and enables integrity check, so your data can be safely loaded.
59
     */
60
    public $globalFixtures = [
61
        'yii\test\InitDb',
62
    ];
63
64
65
    /**
66
     * @inheritdoc
67
     */
68
    public function options($actionID)
69
    {
70
        return array_merge(parent::options($actionID), [
71
            'namespace', 'globalFixtures'
72
        ]);
73
    }
74
75
    /**
76
     * @inheritdoc
77
     * @since 2.0.8
78
     */
79
    public function optionAliases()
80
    {
81
        return array_merge(parent::optionAliases(), [
82
            'g' => 'globalFixtures',
83
            'n' => 'namespace',
84
        ]);
85
    }
86
87
    /**
88
     * Loads the specified fixture data.
89
     * For example,
90
     *
91
     * ```
92
     * # load the fixture data specified by User and UserProfile.
93
     * # any existing fixture data will be removed first
94
     * yii fixture/load "User, UserProfile"
95
     *
96
     * # load all available fixtures found under 'tests\unit\fixtures'
97
     * yii fixture/load "*"
98
     *
99
     * # load all fixtures except User and UserProfile
100
     * yii fixture/load "*, -User, -UserProfile"
101
     * ```
102
     *
103
     * @param array $fixturesInput
104
     * @return int return code
105
     * @throws Exception if the specified fixture does not exist.
106
     */
107 6
    public function actionLoad(array $fixturesInput = [])
108
    {
109 6
        if ($fixturesInput === []) {
110
            $this->stdout($this->getHelpSummary() . "\n");
111
112
            $helpCommand = Console::ansiFormat('yii help fixture', [Console::FG_CYAN]);
113
            $this->stdout("Use $helpCommand to get usage info.\n");
114
115
            return self::EXIT_CODE_NORMAL;
116
        }
117
118 6
        $filtered = $this->filterFixtures($fixturesInput);
119 6
        $except = $filtered['except'];
120
121 6
        if (!$this->needToApplyAll($fixturesInput[0])) {
122 4
            $fixtures = $filtered['apply'];
123
124 4
            $foundFixtures = $this->findFixtures($fixtures);
125 4
            $notFoundFixtures = array_diff($fixtures, $foundFixtures);
126
127 4
            if ($notFoundFixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $notFoundFixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
128 1
                $this->notifyNotFound($notFoundFixtures);
129 1
            }
130 4
        } else {
131 2
            $foundFixtures = $this->findFixtures();
132
        }
133
134 6
        $fixturesToLoad = array_diff($foundFixtures, $except);
135
136 6
        if (!$foundFixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foundFixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
137 1
            throw new Exception(
138 1
                "No files were found for: \"" . implode(', ', $fixturesInput) . "\".\n" .
139 1
                "Check that files exist under fixtures path: \n\"" . $this->getFixturePath() . "\"."
140 1
            );
141
        }
142
143 5
        if (!$fixturesToLoad) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fixturesToLoad of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
144 1
            $this->notifyNothingToLoad($foundFixtures, $except);
145 1
            return static::EXIT_CODE_NORMAL;
146
        }
147
148 4
        if (!$this->confirmLoad($fixturesToLoad, $except)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirmLoad($fixturesToLoad, $except) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
149
            return static::EXIT_CODE_NORMAL;
150
        }
151
152 4
        $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToLoad));
153
154 4
        if (!$fixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
155
            throw new Exception('No fixtures were found in namespace: "' . $this->namespace . '"' . '');
156
        }
157
158 4
        $fixturesObjects = $this->createFixtures($fixtures);
159
160 4
        $this->unloadFixtures($fixturesObjects);
161 4
        $this->loadFixtures($fixturesObjects);
162 4
        $this->notifyLoaded($fixtures);
163
164 4
        return static::EXIT_CODE_NORMAL;
165
    }
166
167
    /**
168
     * Unloads the specified fixtures.
169
     * For example,
170
     *
171
     * ```
172
     * # unload the fixture data specified by User and UserProfile.
173
     * yii fixture/unload "User, UserProfile"
174
     *
175
     * # unload all fixtures found under 'tests\unit\fixtures'
176
     * yii fixture/unload "*"
177
     *
178
     * # unload all fixtures except User and UserProfile
179
     * yii fixture/unload "*, -User, -UserProfile"
180
     * ```
181
     *
182
     * @param array $fixturesInput
183
     * @return int return code
184
     * @throws Exception if the specified fixture does not exist.
185
     */
186 6
    public function actionUnload(array $fixturesInput = [])
187
    {
188 6
        $filtered = $this->filterFixtures($fixturesInput);
189 6
        $except = $filtered['except'];
190
191 6
        if (!$this->needToApplyAll($fixturesInput[0])) {
192 4
            $fixtures = $filtered['apply'];
193
194 4
            $foundFixtures = $this->findFixtures($fixtures);
195 4
            $notFoundFixtures = array_diff($fixtures, $foundFixtures);
196
197 4
            if ($notFoundFixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $notFoundFixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
198 1
                $this->notifyNotFound($notFoundFixtures);
199 1
            }
200 4
        } else {
201 2
            $foundFixtures = $this->findFixtures();
202
        }
203
204 6
        $fixturesToUnload = array_diff($foundFixtures, $except);
205
206 6
        if (!$foundFixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foundFixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
207 1
            throw new Exception(
208 1
                "No files were found for: \"" . implode(', ', $fixturesInput) . "\".\n" .
209 1
                "Check that files exist under fixtures path: \n\"" . $this->getFixturePath() . "\"."
210 1
            );
211
        }
212
213 5
        if (!$fixturesToUnload) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fixturesToUnload of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
214 1
            $this->notifyNothingToUnload($foundFixtures, $except);
215 1
            return static::EXIT_CODE_NORMAL;
216
        }
217
218 4
        if (!$this->confirmUnload($fixturesToUnload, $except)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirmUnload($fixturesToUnload, $except) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
219
            return static::EXIT_CODE_NORMAL;
220
        }
221
222 4
        $fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $fixturesToUnload));
223
224 4
        if (!$fixtures) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fixtures of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
225
            throw new Exception('No fixtures were found in namespace: ' . $this->namespace . '".');
226
        }
227
228 4
        $this->unloadFixtures($this->createFixtures($fixtures));
229 4
        $this->notifyUnloaded($fixtures);
230 4
    }
231
232
    /**
233
     * Notifies user that fixtures were successfully loaded.
234
     * @param array $fixtures
235
     */
236 4
    private function notifyLoaded($fixtures)
237
    {
238 4
        $this->stdout("Fixtures were successfully loaded from namespace:\n", Console::FG_YELLOW);
239 4
        $this->stdout("\t\"" . Yii::getAlias($this->namespace) . "\"\n\n", Console::FG_GREEN);
240 4
        $this->outputList($fixtures);
241 4
    }
242
243
    /**
244
     * Notifies user that there are no fixtures to load according input conditions
245
     * @param array $foundFixtures array of found fixtures
246
     * @param array $except array of names of fixtures that should not be loaded
247
     */
248 1
    public function notifyNothingToLoad($foundFixtures, $except)
249
    {
250 1
        $this->stdout("Fixtures to load could not be found according given conditions:\n\n", Console::FG_RED);
251 1
        $this->stdout("Fixtures namespace is: \n", Console::FG_YELLOW);
252 1
        $this->stdout("\t" . $this->namespace . "\n", Console::FG_GREEN);
253
254 1
        if (count($foundFixtures)) {
255 1
            $this->stdout("\nFixtures founded under the namespace:\n\n", Console::FG_YELLOW);
256 1
            $this->outputList($foundFixtures);
257 1
        }
258
259 1
        if (count($except)) {
260 1
            $this->stdout("\nFixtures that will NOT be loaded: \n\n", Console::FG_YELLOW);
261 1
            $this->outputList($except);
262 1
        }
263 1
    }
264
265
    /**
266
     * Notifies user that there are no fixtures to unload according input conditions
267
     * @param array $foundFixtures array of found fixtures
268
     * @param array $except array of names of fixtures that should not be loaded
269
     */
270 1
    public function notifyNothingToUnload($foundFixtures, $except)
271
    {
272 1
        $this->stdout("Fixtures to unload could not be found according to given conditions:\n\n", Console::FG_RED);
273 1
        $this->stdout("Fixtures namespace is: \n", Console::FG_YELLOW);
274 1
        $this->stdout("\t" . $this->namespace . "\n", Console::FG_GREEN);
275
276 1
        if (count($foundFixtures)) {
277 1
            $this->stdout("\nFixtures found under the namespace:\n\n", Console::FG_YELLOW);
278 1
            $this->outputList($foundFixtures);
279 1
        }
280
281 1
        if (count($except)) {
282 1
            $this->stdout("\nFixtures that will NOT be unloaded: \n\n", Console::FG_YELLOW);
283 1
            $this->outputList($except);
284 1
        }
285 1
    }
286
287
    /**
288
     * Notifies user that fixtures were successfully unloaded.
289
     * @param array $fixtures
290
     */
291 4
    private function notifyUnloaded($fixtures)
292
    {
293 4
        $this->stdout("\nFixtures were successfully unloaded from namespace: ", Console::FG_YELLOW);
294 4
        $this->stdout(Yii::getAlias($this->namespace) . "\"\n\n", Console::FG_GREEN);
295 4
        $this->outputList($fixtures);
296 4
    }
297
298
    /**
299
     * Notifies user that fixtures were not found under fixtures path.
300
     * @param array $fixtures
301
     */
302 2
    private function notifyNotFound($fixtures)
303
    {
304 2
        $this->stdout("Some fixtures were not found under path:\n", Console::BG_RED);
305 2
        $this->stdout("\t" . $this->getFixturePath() . "\n\n", Console::FG_GREEN);
306 2
        $this->stdout("Check that they have correct namespace \"{$this->namespace}\" \n", Console::BG_RED);
307 2
        $this->outputList($fixtures);
308 2
        $this->stdout("\n");
309 2
    }
310
311
    /**
312
     * Prompts user with confirmation if fixtures should be loaded.
313
     * @param array $fixtures
314
     * @param array $except
315
     * @return bool
316
     */
317 4
    private function confirmLoad($fixtures, $except)
318
    {
319 4
        $this->stdout("Fixtures namespace is: \n", Console::FG_YELLOW);
320 4
        $this->stdout("\t" . $this->namespace . "\n\n", Console::FG_GREEN);
321
322 4
        if (count($this->globalFixtures)) {
323 1
            $this->stdout("Global fixtures will be used:\n\n", Console::FG_YELLOW);
324 1
            $this->outputList($this->globalFixtures);
325 1
        }
326
327 4
        if (count($fixtures)) {
328 4
            $this->stdout("\nFixtures below will be loaded:\n\n", Console::FG_YELLOW);
329 4
            $this->outputList($fixtures);
330 4
        }
331
332 4
        if (count($except)) {
333 2
            $this->stdout("\nFixtures that will NOT be loaded: \n\n", Console::FG_YELLOW);
334 2
            $this->outputList($except);
335 2
        }
336
337 4
        $this->stdout("\nBe aware that:\n", Console::BOLD);
338 4
        $this->stdout("Applying leads to purging of certain data in the database!\n", Console::FG_RED);
339
340 4
        return $this->confirm("\nLoad above fixtures?");
341
    }
342
343
    /**
344
     * Prompts user with confirmation for fixtures that should be unloaded.
345
     * @param array $fixtures
346
     * @param array $except
347
     * @return bool
348
     */
349 4
    private function confirmUnload($fixtures, $except)
350
    {
351 4
        $this->stdout("Fixtures namespace is: \n", Console::FG_YELLOW);
352 4
        $this->stdout("\t" . $this->namespace . "\n\n", Console::FG_GREEN);
353
354 4
        if (count($this->globalFixtures)) {
355 1
            $this->stdout("Global fixtures will be used:\n\n", Console::FG_YELLOW);
356 1
            $this->outputList($this->globalFixtures);
357 1
        }
358
359 4
        if (count($fixtures)) {
360 4
            $this->stdout("\nFixtures below will be unloaded:\n\n", Console::FG_YELLOW);
361 4
            $this->outputList($fixtures);
362 4
        }
363
364 4
        if (count($except)) {
365 2
            $this->stdout("\nFixtures that will NOT be unloaded:\n\n", Console::FG_YELLOW);
366 2
            $this->outputList($except);
367 2
        }
368
369 4
        return $this->confirm("\nUnload fixtures?");
370
    }
371
372
    /**
373
     * Outputs data to the console as a list.
374
     * @param array $data
375
     */
376 12
    private function outputList($data)
377
    {
378 12
        foreach ($data as $index => $item) {
379 12
            $this->stdout("\t" . ($index + 1) . ". {$item}\n", Console::FG_GREEN);
380 12
        }
381 12
    }
382
383
    /**
384
     * Checks if needed to apply all fixtures.
385
     * @param string $fixture
386
     * @return bool
387
     */
388 12
    public function needToApplyAll($fixture)
389
    {
390 12
        return $fixture === '*';
391
    }
392
393
    /**
394
     * Finds fixtures to be loaded, for example "User", if no fixtures were specified then all of them
395
     * will be searching by suffix "Fixture.php".
396
     * @param array $fixtures fixtures to be loaded
397
     * @return array Array of found fixtures. These may differ from input parameter as not all fixtures may exists.
398
     */
399 12
    private function findFixtures(array $fixtures = [])
400
    {
401 12
        $fixturesPath = $this->getFixturePath();
402
403 12
        $filesToSearch = ['*Fixture.php'];
404 12
        $findAll = ($fixtures === []);
405
406 12
        if (!$findAll) {
407 8
            $filesToSearch = [];
408
409 8
            foreach ($fixtures as $fileName) {
410 8
                $filesToSearch[] = $fileName . 'Fixture.php';
411 8
            }
412 8
        }
413
414 12
        $files = FileHelper::findFiles($fixturesPath, ['only' => $filesToSearch]);
0 ignored issues
show
Bug introduced by
It seems like $fixturesPath defined by $this->getFixturePath() on line 401 can also be of type boolean; however, yii\helpers\BaseFileHelper::findFiles() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
415 12
        $foundFixtures = [];
416
417 12
        foreach ($files as $fixture) {
418 10
            $foundFixtures[] = basename($fixture, 'Fixture.php');
419 12
        }
420
421 12
        return $foundFixtures;
422
    }
423
424
    /**
425
     * Returns valid fixtures config that can be used to load them.
426
     * @param array $fixtures fixtures to configure
427
     * @return array
428
     */
429 8
    private function getFixturesConfig($fixtures)
430
    {
431 8
        $config = [];
432
433 8
        foreach ($fixtures as $fixture) {
434 8
            $isNamespaced = (strpos($fixture, '\\') !== false);
435 8
            $fullClassName = $isNamespaced ? $fixture . 'Fixture' : $this->namespace . '\\' . $fixture . 'Fixture';
436
437 8
            if (class_exists($fullClassName)) {
438 8
                $config[] = $fullClassName;
439 8
            }
440 8
        }
441
442 8
        return $config;
443
    }
444
445
    /**
446
     * Filters fixtures by splitting them in two categories: one that should be applied and not.
447
     * If fixture is prefixed with "-", for example "-User", that means that fixture should not be loaded,
448
     * if it is not prefixed it is considered as one to be loaded. Returns array:
449
     *
450
     * ```php
451
     * [
452
     *     'apply' => [
453
     *         'User',
454
     *         ...
455
     *     ],
456
     *     'except' => [
457
     *         'Custom',
458
     *         ...
459
     *     ],
460
     * ]
461
     * ```
462
     * @param array $fixtures
463
     * @return array fixtures array with 'apply' and 'except' elements.
464
     */
465 12
    private function filterFixtures($fixtures)
466
    {
467
        $filtered = [
468 12
            'apply' => [],
469 12
            'except' => [],
470 12
        ];
471
472 12
        foreach ($fixtures as $fixture) {
473 12
            if (mb_strpos($fixture, '-') !== false) {
474 6
                $filtered['except'][] = str_replace('-', '', $fixture);
475 6
            } else {
476 12
                $filtered['apply'][] = $fixture;
477
            }
478 12
        }
479
480 12
        return $filtered;
481
    }
482
483
    /**
484
     * Returns fixture path that determined on fixtures namespace.
485
     * @throws InvalidConfigException if fixture namespace is invalid
486
     * @return string fixture path
487
     */
488 12
    private function getFixturePath()
489
    {
490
        try {
491 12
            return Yii::getAlias('@' . str_replace('\\', '/', $this->namespace));
492
        } catch (InvalidParamException $e) {
493
            throw new InvalidConfigException('Invalid fixture namespace: "' . $this->namespace . '". Please, check your FixtureController::namespace parameter');
494
        }
495
    }
496
}
497