Completed
Push — prepare-travis-for-js ( a7ee60 )
by Carsten
08:12 queued 01:33
created

CacheController::confirmFlush()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 6
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 6
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\console\Controller;
12
use yii\caching\Cache;
13
use yii\helpers\Console;
14
use yii\console\Exception;
15
16
/**
17
 * Allows you to flush cache.
18
 *
19
 * see list of available components to flush:
20
 *
21
 *     yii cache
22
 *
23
 * flush particular components specified by their names:
24
 *
25
 *     yii cache/flush first second third
26
 *
27
 * flush all cache components that can be found in the system
28
 *
29
 *     yii cache/flush-all
30
 *
31
 * Note that the command uses cache components defined in your console application configuration file. If components
32
 * configured are different from web application, web application cache won't be cleared. In order to fix it please
33
 * duplicate web application cache components in console config. You can use any component names.
34
 *
35
 * Both APC and OpCache aren't shared between PHP processes so flushing cache from command line has no effect on web.
36
 * Flushing web cache could be either done by:
37
 *
38
 * - Putting a php file under web root and calling it via HTTP
39
 * - Using [Cachetool](http://gordalina.github.io/cachetool/)
40
 *
41
 * @author Alexander Makarov <[email protected]>
42
 * @author Mark Jebri <[email protected]>
43
 * @since 2.0
44
 */
45
class CacheController extends Controller
46
{
47
    /**
48
     * Lists the caches that can be flushed.
49
     */
50
    public function actionIndex()
51
    {
52
        $caches = $this->findCaches();
53
54
        if (!empty($caches)) {
55
            $this->notifyCachesCanBeFlushed($caches);
56
        } else {
57
            $this->notifyNoCachesFound();
58
        }
59
    }
60
61
    /**
62
     * Flushes given cache components.
63
     * For example,
64
     *
65
     * ```
66
     * # flushes caches specified by their id: "first", "second", "third"
67
     * yii cache/flush first second third
68
     * ```
69
     *
70
     */
71
    public function actionFlush()
72
    {
73
        $cachesInput = func_get_args();
74
75
        if (empty($cachesInput)) {
76
            throw new Exception('You should specify cache components names');
77
        }
78
79
        $caches = $this->findCaches($cachesInput);
80
        $cachesInfo = [];
81
82
        $foundCaches = array_keys($caches);
83
        $notFoundCaches = array_diff($cachesInput, array_keys($caches));
84
85
        if ($notFoundCaches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $notFoundCaches 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...
86
            $this->notifyNotFoundCaches($notFoundCaches);
87
        }
88
89
        if (!$foundCaches) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $foundCaches of type array<integer|string> 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...
90
            $this->notifyNoCachesFound();
91
            return static::EXIT_CODE_NORMAL;
92
        }
93
94
        if (!$this->confirmFlush($foundCaches)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirmFlush($foundCaches) 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...
95
            return static::EXIT_CODE_NORMAL;
96
        }
97
98
        foreach ($caches as $name => $class) {
99
            $cachesInfo[] = [
100
                'name' => $name,
101
                'class' => $class,
102
                'is_flushed' => Yii::$app->get($name)->flush(),
103
            ];
104
        }
105
106
        $this->notifyFlushed($cachesInfo);
107
    }
108
109
    /**
110
     * Flushes all caches registered in the system.
111
     */
112
    public function actionFlushAll()
113
    {
114
        $caches = $this->findCaches();
115
        $cachesInfo = [];
116
117
        if (empty($caches)) {
118
            $this->notifyNoCachesFound();
119
            return static::EXIT_CODE_NORMAL;
120
        }
121
122
        foreach ($caches as $name => $class) {
123
            $cachesInfo[] = [
124
                'name' => $name,
125
                'class' => $class,
126
                'is_flushed' => Yii::$app->get($name)->flush(),
127
            ];
128
        }
129
130
        $this->notifyFlushed($cachesInfo);
131
    }
132
133
    /**
134
     * Clears DB schema cache for a given connection component.
135
     *
136
     * ```
137
     * # clears cache schema specified by component id: "db"
138
     * yii cache/flush-schema db
139
     * ```
140
     *
141
     * @param string $db id connection component
142
     * @return int exit code
143
     * @throws Exception
144
     * @throws \yii\base\InvalidConfigException
145
     *
146
     * @since 2.0.1
147
     */
148
    public function actionFlushSchema($db = 'db')
149
    {
150
        $connection = Yii::$app->get($db, false);
151
        if ($connection === null) {
152
            $this->stdout("Unknown component \"$db\".\n", Console::FG_RED);
153
            return self::EXIT_CODE_ERROR;
154
        }
155
156
        if (!$connection instanceof \yii\db\Connection) {
157
            $this->stdout("\"$db\" component doesn't inherit \\yii\\db\\Connection.\n", Console::FG_RED);
158
            return self::EXIT_CODE_ERROR;
159
        } elseif (!$this->confirm("Flush cache schema for \"$db\" connection?")) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->confirm("Flush ca...\"{$db}\" connection?") 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...
160
            return static::EXIT_CODE_NORMAL;
161
        }
162
163
        try {
164
            $schema = $connection->getSchema();
165
            $schema->refresh();
166
            $this->stdout("Schema cache for component \"$db\", was flushed.\n\n", Console::FG_GREEN);
167
        } catch (\Exception $e) {
168
            $this->stdout($e->getMessage() . "\n\n", Console::FG_RED);
169
        }
170
    }
171
172
    /**
173
     * Notifies user that given caches are found and can be flushed.
174
     * @param array $caches array of cache component classes
175
     */
176
    private function notifyCachesCanBeFlushed($caches)
177
    {
178
        $this->stdout("The following caches were found in the system:\n\n", Console::FG_YELLOW);
179
180
        foreach ($caches as $name => $class) {
181
            $this->stdout("\t* $name ($class)\n", Console::FG_GREEN);
182
        }
183
184
        $this->stdout("\n");
185
    }
186
187
    /**
188
     * Notifies user that there was not found any cache in the system.
189
     */
190
    private function notifyNoCachesFound()
191
    {
192
        $this->stdout("No cache components were found in the system.\n", Console::FG_RED);
193
    }
194
195
    /**
196
     * Notifies user that given cache components were not found in the system.
197
     * @param array $cachesNames
198
     */
199
    private function notifyNotFoundCaches($cachesNames)
200
    {
201
        $this->stdout("The following cache components were NOT found:\n\n", Console::FG_RED);
202
203
        foreach ($cachesNames as $name) {
204
            $this->stdout("\t* $name \n", Console::FG_GREEN);
205
        }
206
207
        $this->stdout("\n");
208
    }
209
210
    /**
211
     *
212
     * @param array $caches
213
     */
214
    private function notifyFlushed($caches)
215
    {
216
        $this->stdout("The following cache components were processed:\n\n", Console::FG_YELLOW);
217
218
        foreach ($caches as $cache) {
219
            $this->stdout("\t* " . $cache['name'] .' (' . $cache['class'] . ')', Console::FG_GREEN);
220
221
            if (!$cache['is_flushed']) {
222
                $this->stdout(" - not flushed\n", Console::FG_RED);
223
            } else {
224
                $this->stdout("\n");
225
            }
226
        }
227
228
        $this->stdout("\n");
229
    }
230
231
    /**
232
     * Prompts user with confirmation if caches should be flushed.
233
     * @param array $cachesNames
234
     * @return bool
235
     */
236
    private function confirmFlush($cachesNames)
237
    {
238
        $this->stdout("The following cache components will be flushed:\n\n", Console::FG_YELLOW);
239
240
        foreach ($cachesNames as $name) {
241
            $this->stdout("\t* $name \n", Console::FG_GREEN);
242
        }
243
244
        return $this->confirm("\nFlush above cache components?");
245
    }
246
247
    /**
248
     * Returns array of caches in the system, keys are cache components names, values are class names.
249
     * @param array $cachesNames caches to be found
250
     * @return array
251
     */
252
    private function findCaches(array $cachesNames = [])
253
    {
254
        $caches = [];
255
        $components = Yii::$app->getComponents();
256
        $findAll = ($cachesNames === []);
257
258
        foreach ($components as $name => $component) {
259
            if (!$findAll && !in_array($name, $cachesNames)) {
260
                continue;
261
            }
262
263
            if ($component instanceof Cache) {
264
                $caches[$name] = get_class($component);
265
            } elseif (is_array($component) && isset($component['class']) && $this->isCacheClass($component['class'])) {
266
                $caches[$name] = $component['class'];
267
            } elseif (is_string($component) && $this->isCacheClass($component)) {
268
                $caches[$name] = $component;
269
            }
270
        }
271
272
        return $caches;
273
    }
274
275
    /**
276
     * Checks if given class is a Cache class.
277
     * @param string $className class name.
278
     * @return bool
279
     */
280
    private function isCacheClass($className)
281
    {
282
        return is_subclass_of($className, Cache::className());
283
    }
284
}
285