Completed
Push — master ( 55b06d...9f2a87 )
by Alexander
35:57
created

framework/console/controllers/CacheController.php (2 issues)

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