025a1662b71bb08f56e6ee21f44200958cce4b91
[yaffs-website] / vendor / drush / drush / src / Commands / core / CacheCommands.php
1 <?php
2 namespace Drush\Commands\core;
3
4 use Consolidation\AnnotatedCommand\CommandData;
5 use Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface;
6 use Consolidation\AnnotatedCommand\Events\CustomEventAwareTrait;
7 use Consolidation\OutputFormatters\StructuredData\PropertyList;
8 use Drush\Boot\AutoloaderAwareInterface;
9 use Drush\Boot\AutoloaderAwareTrait;
10 use Drush\Commands\DrushCommands;
11 use Drupal\Core\DrupalKernel;
12 use Drupal\Core\Site\Settings;
13 use Drupal\Core\Cache\Cache;
14 use Drush\Drush;
15 use Drush\Utils\StringUtils;
16 use Symfony\Component\HttpFoundation\Request;
17
18 /*
19  * Interact with Drupal's Cache API.
20  */
21 class CacheCommands extends DrushCommands implements CustomEventAwareInterface, AutoloaderAwareInterface
22 {
23
24     use CustomEventAwareTrait;
25     use AutoloaderAwareTrait;
26
27     /**
28      * Fetch a cached object and display it.
29      *
30      * @command cache:get
31      * @param $cid The id of the object to fetch.
32      * @param $bin The cache bin to fetch from.
33      * @usage drush cache:get hook_info bootstrap
34      *   Display the data for the cache id "hook_info" from the "bootstrap" bin.
35      * @usage drush cache:get update_available_releases update
36      *   Display the data for the cache id "update_available_releases" from the "update" bin.
37      * @aliases cg,cache-get
38      * @bootstrap full
39      * @field-labels
40      *   cid: Cache ID
41      *   data: Data
42      *   created: Created
43      *   expire: Expire
44      *   tags: Tags
45      *   checksum: Checksum
46      *   valid: Valid
47      * @default-fields cid,data,created,expire,tags
48      * @return \Consolidation\OutputFormatters\StructuredData\PropertyList
49      */
50     public function get($cid, $bin = 'default', $options = ['format' => 'json'])
51     {
52         $result = \Drupal::cache($bin)->get($cid);
53         if (empty($result)) {
54             throw new \Exception(dt('The !cid object in the !bin bin was not found.', ['!cid' => $cid, '!bin' => $bin]));
55         }
56         return new PropertyList($result);
57     }
58
59     /**
60      * Clear a specific cache, or all Drupal caches.
61      *
62      * @command cache:clear
63      * @param string $type The particular cache to clear. Omit this argument to choose from available types.
64      * @param array $args Additional arguments as might be expected (e.g. bin name).
65      * @option cache-clear Set to 0 to suppress normal cache clearing; the caller should then clear if needed.
66      * @hidden-options cache-clear
67      * @aliases cc,cache-clear
68      * @bootstrap max
69      * @notify Caches have been cleared.
70      * @usage drush cc bin entity,bootstrap
71      *   Clear the entity and bootstrap cache bins.
72      */
73     public function clear($type, array $args, $options = ['cache-clear' => true])
74     {
75         $boot_manager = Drush::bootstrapManager();
76
77         if (!$options['cache-clear']) {
78             $this->logger()->info(dt("Skipping cache-clear operation due to --cache-clear=0 option."));
79             return null;
80         }
81
82         $types = $this->getTypes($boot_manager->hasBootstrapped((DRUSH_BOOTSTRAP_DRUPAL_FULL)));
83
84         // Do it.
85         drush_op($types[$type], $args);
86         $this->logger()->success(dt("'!name' cache was cleared.", ['!name' => $type]));
87     }
88
89     /**
90      * @hook interact cache-clear
91      */
92     public function interact($input, $output)
93     {
94         $boot_manager = Drush::bootstrapManager();
95         if (empty($input->getArgument('type'))) {
96             $types = $this->getTypes($boot_manager->hasBootstrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL));
97             $choices = array_combine(array_keys($types), array_keys($types));
98             $type = $this->io()->choice(dt("Choose a cache to clear"), $choices, 'all');
99             $input->setArgument('type', $type);
100         }
101     }
102
103     /**
104      * Cache an object expressed in JSON or var_export() format.
105      *
106      * @command cache:set
107      * @param $cid The id of the object to set.
108      * @param $data The object to set in the cache. Use - to read the object from STDIN.
109      * @param $bin The cache bin to store the object in.
110      * @param $expire 'CACHE_PERMANENT', or a Unix timestamp.
111      * @param $tags A comma delimited list of cache tags.
112      * @option input-format The format of value. Use 'json' for complex values.
113      * @option cache-get If the object is the result a previous fetch from the cache, only store the value in the 'data' property of the object in the cache.
114      * @aliases cs,cache-set
115      * @bootstrap full
116      */
117     public function set($cid, $data, $bin = 'default', $expire = null, $tags = null, $options = ['input-format' => 'string', 'cache-get' => false])
118     {
119         $tags = is_string($tags) ? _convert_csv_to_array($tags) : [];
120
121         // In addition to prepare, this also validates. Can't easily be in own validate callback as
122         // reading once from STDIN empties it.
123         $data = $this->setPrepareData($data, $options);
124         if ($data === false && drush_get_error()) {
125             // An error was logged above.
126             return;
127         }
128
129         if (!isset($expire) || $expire == 'CACHE_PERMANENT') {
130             $expire = Cache::PERMANENT;
131         }
132
133         return \Drupal::cache($bin)->set($cid, $data, $expire, $tags);
134     }
135
136     protected function setPrepareData($data, $options)
137     {
138         if ($data == '-') {
139             $data = file_get_contents("php://stdin");
140         }
141
142         // Now, we parse the object.
143         switch ($options['input-format']) {
144             case 'json':
145                 $data = json_decode($data, true);
146                 break;
147         }
148
149         if ($options['cache-get']) {
150             // $data might be an object.
151             if (is_object($data) && $data->data) {
152                 $data = $data->data;
153             } // But $data returned from `drush cache-get --format=json` will be an array.
154             elseif (is_array($data) && isset($data['data'])) {
155                 $data = $data['data'];
156             } else {
157                 // If $data is neither object nor array and cache-get was specified, then
158                 // there is a problem.
159                 throw new \Exception(dt("'cache-get' was specified as an option, but the data is neither an object or an array."));
160             }
161         }
162
163         return $data;
164     }
165
166     /**
167      * Rebuild a Drupal 8 site.
168      *
169      * This is a copy of core/rebuild.php. Additionally
170      * it also clears Drush cache and Drupal's render cache.
171
172      *
173      * @command cache:rebuild
174      * @option cache-clear Set to 0 to suppress normal cache clearing; the caller should then clear if needed.
175      * @hidden-options cache-clear
176      * @aliases cr,rebuild,cache-rebuild
177      * @bootstrap site
178      */
179     public function rebuild($options = ['cache-clear' => true])
180     {
181         if (!$options['cache-clear']) {
182             $this->logger()->info(dt("Skipping cache-clear operation due to --no-cache-clear option."));
183             return true;
184         }
185         chdir(DRUPAL_ROOT);
186
187         // We no longer clear APC and similar caches as they are useless on CLI.
188         // See https://github.com/drush-ops/drush/pull/2450
189
190         $autoloader = $this->loadDrupalAutoloader(DRUPAL_ROOT);
191         require_once DRUSH_DRUPAL_CORE . '/includes/utility.inc';
192
193         $request = Drush::bootstrap()->getRequest();
194         // Manually resemble early bootstrap of DrupalKernel::boot().
195         require_once DRUSH_DRUPAL_CORE . '/includes/bootstrap.inc';
196         DrupalKernel::bootEnvironment();
197
198         // Avoid 'Only variables should be passed by reference'
199         $root  = DRUPAL_ROOT;
200         $site_path = DrupalKernel::findSitePath($request);
201         Settings::initialize($root, $site_path, $autoloader);
202
203         // Use our error handler since _drupal_log_error() depends on an unavailable theme system (ugh).
204         set_error_handler('drush_error_handler');
205
206         // drupal_rebuild() calls drupal_flush_all_caches() itself, so we don't do it manually.
207         drupal_rebuild($autoloader, $request);
208         $this->logger()->success(dt('Cache rebuild complete.'));
209
210         // As this command replaces `drush cache-clear all` for Drupal 8 users, clear
211         // the Drush cache as well, for consistency with that behavior.
212         CacheCommands::clearDrush();
213     }
214
215     /**
216      * @hook validate cache-clear
217      */
218     public function validate(CommandData $commandData)
219     {
220         $boot_manager = Drush::bootstrapManager();
221         $types = $this->getTypes($boot_manager->hasBootstrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL));
222         $type = $commandData->input()->getArgument('type');
223         // Check if the provided type ($type) is a valid cache type.
224         if ($type && !array_key_exists($type, $types)) {
225             if ($type === 'all') {
226                 throw new \Exception(dt('`cache-clear all` is deprecated for Drupal 8 and later. Please use the `cache-rebuild` command instead.'));
227             }
228             // If we haven't done a full bootstrap, provide a more
229             // specific message with instructions to the user on
230             // bootstrapping a Drupal site for more options.
231             if (!$boot_manager->hasBootstrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
232                 $all_types = $this->getTypes(true);
233                 if (array_key_exists($type, $all_types)) {
234                     throw new \Exception(dt("'!type' cache requires a working Drupal site to operate on. Use the --root and --uri options, or a site @alias, or cd to a directory containing a Drupal settings.php file.", ['!type' => $type]));
235                 } else {
236                     throw new \Exception(dt("'!type' cache is not a valid cache type. There may be more cache types available if you select a working Drupal site.", ['!type' => $type]));
237                 }
238             }
239             throw new \Exception(dt("'!type' cache is not a valid cache type.", ['!type' => $type]));
240         }
241     }
242
243     /**
244      * Types of caches available for clearing. Contrib commands can hook in their own.
245      */
246     public function getTypes($include_bootstrapped_types = false)
247     {
248         $types = [
249             'drush' => [$this, 'clearDrush'],
250         ];
251         if ($include_bootstrapped_types) {
252             $types += [
253                 'theme-registry' => [$this, 'clearThemeRegistry'],
254                 'router' => [$this, 'clearRouter'],
255                 'css-js' => [$this, 'clearCssJs'],
256                 'render' => [$this, 'clearRender'],
257                 'plugin' => [$this, 'clearPlugin'],
258                 'bin' => [$this, 'clearBins'],
259             ];
260         }
261
262         // Command files may customize $types as desired.
263         $handlers = $this->getCustomEventHandlers('cache-clear');
264         foreach ($handlers as $handler) {
265               $handler($types, $include_bootstrapped_types);
266         }
267         return $types;
268     }
269
270     /**
271      * Clear caches internal to Drush core.
272      */
273     public static function clearDrush()
274     {
275         drush_cache_clear_all(null, 'default'); // commandfiles, etc.
276         drush_cache_clear_all(null, 'factory'); // command info from annotated-command library
277     }
278
279     /**
280      * Clear one or more cache bins.
281      */
282     public static function clearBins($args = ['default'])
283     {
284         $bins = StringUtils::csvToArray($args);
285         foreach ($bins as $bin) {
286             \Drupal::service("cache.$bin")->deleteAll();
287         }
288     }
289
290     public static function clearThemeRegistry()
291     {
292         \Drupal::service('theme.registry')->reset();
293     }
294
295     public static function clearRouter()
296     {
297         /** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */
298         $router_builder = \Drupal::service('router.builder');
299         $router_builder->rebuild();
300     }
301
302     public static function clearCssJs()
303     {
304         _drupal_flush_css_js();
305         \Drupal::service('asset.css.collection_optimizer')->deleteAll();
306         \Drupal::service('asset.js.collection_optimizer')->deleteAll();
307     }
308
309     /**
310      * Clears the render cache entries.
311      */
312     public static function clearRender()
313     {
314         Cache::invalidateTags(['rendered']);
315     }
316
317     public static function clearPlugin()
318     {
319         \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions();
320     }
321
322     /**
323      * Loads the Drupal autoloader and returns the instance.
324      */
325     public function loadDrupalAutoloader($drupal_root)
326     {
327         static $autoloader = false;
328
329         $autoloadFilePath = $drupal_root .'/autoload.php';
330         if (!$autoloader && file_exists($autoloadFilePath)) {
331             $autoloader = require $autoloadFilePath;
332         }
333
334         if ($autoloader === true) {
335             // The autoloader was already required. Assume that Drush and Drupal share an autoloader per
336             // "Point autoload.php to the proper vendor directory" - https://www.drupal.org/node/2404989
337             $autoloader = $this->autoloader();
338         }
339
340         return $autoloader;
341     }
342 }