Version 1
[yaffs-website] / vendor / drupal / console / src / Command / Generate / PluginSkeletonCommand.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Console\Command\Generate\PluginSkeletonCommand.
6  */
7
8 namespace Drupal\Console\Command\Generate;
9
10 use Drupal\Console\Generator\PluginSkeletonGenerator;
11 use Symfony\Component\Console\Input\InputInterface;
12 use Symfony\Component\Console\Input\InputOption;
13 use Symfony\Component\Console\Output\OutputInterface;
14 use Symfony\Component\Console\Command\Command;
15 use Drupal\Console\Command\Shared\ModuleTrait;
16 use Drupal\Console\Command\Shared\ConfirmationTrait;
17 use Drupal\Console\Command\Shared\ServicesTrait;
18 use Drupal\Console\Core\Style\DrupalStyle;
19 use Drupal\Console\Extension\Manager;
20 use Drupal\Console\Core\Command\Shared\ContainerAwareCommandTrait;
21 use Drupal\Console\Core\Utils\StringConverter;
22 use Drupal\Console\Core\Utils\ChainQueue;
23 use Drupal\Console\Utils\Validator;
24
25 /**
26  * Class PluginSkeletonCommand
27  *
28  * @package Drupal\Console\Command\Generate
29  */
30 class PluginSkeletonCommand extends Command
31 {
32     use ModuleTrait;
33     use ConfirmationTrait;
34     use ServicesTrait;
35     use ContainerAwareCommandTrait;
36
37     /**
38  * @var Manager
39 */
40     protected $extensionManager;
41
42     /**
43  * @var PluginSkeletonGenerator
44 */
45     protected $generator;
46
47     /**
48      * @var StringConverter
49      */
50     protected $stringConverter;
51
52     /**
53  * @var Validator
54 */
55     protected $validator;
56
57     /**
58      * @var ChainQueue
59      */
60     protected $chainQueue;
61
62
63     /**
64      * PluginSkeletonCommand constructor.
65      *
66      * @param Manager                 $extensionManager
67      * @param PluginSkeletonGenerator $generator
68      * @param StringConverter         $stringConverter
69      * @param Validator               $validator
70      * @param ChainQueue              $chainQueue
71      */
72     public function __construct(
73         Manager $extensionManager,
74         PluginSkeletonGenerator $generator,
75         StringConverter $stringConverter,
76         Validator $validator,
77         ChainQueue $chainQueue
78     ) {
79         $this->extensionManager = $extensionManager;
80         $this->generator = $generator;
81         $this->stringConverter = $stringConverter;
82         $this->validator = $validator;
83         $this->chainQueue = $chainQueue;
84         parent::__construct();
85     }
86
87     protected $pluginGeneratorsImplemented = [
88         'block' => 'generate:plugin:block',
89         'ckeditor.plugin' => 'generate:plugin:ckeditorbutton',
90         'condition' => 'generate:plugin:condition',
91         'field.formatter' => 'generate:plugin:fieldformatter',
92         'field.field_type' => 'generate:plugin:fieldtype',
93         'field.widget' =>'generate:plugin:fieldwidget',
94         'image.effect' => 'generate:plugin:imageeffect',
95         'mail' => 'generate:plugin:mail'
96     ];
97
98     protected function configure()
99     {
100         $this
101             ->setName('generate:plugin:skeleton')
102             ->setDescription($this->trans('commands.generate.plugin.skeleton.description'))
103             ->setHelp($this->trans('commands.generate.plugin.skeleton.help'))
104             ->addOption(
105                 'module',
106                 '',
107                 InputOption::VALUE_REQUIRED,
108                 $this->trans('commands.common.options.module')
109             )
110             ->addOption(
111                 'plugin-id',
112                 '',
113                 InputOption::VALUE_REQUIRED,
114                 $this->trans('commands.generate.plugin.options.plugin-id')
115             )
116             ->addOption(
117                 'class',
118                 '',
119                 InputOption::VALUE_OPTIONAL,
120                 $this->trans('commands.generate.plugin.block.options.class')
121             )
122             ->addOption(
123                 'services',
124                 '',
125                 InputOption::VALUE_OPTIONAL| InputOption::VALUE_IS_ARRAY,
126                 $this->trans('commands.common.options.services')
127             );
128     }
129
130     /**
131      * {@inheritdoc}
132      */
133     protected function execute(InputInterface $input, OutputInterface $output)
134     {
135         $io = new DrupalStyle($input, $output);
136         $plugins = $this->getPlugins();
137
138         // @see use Drupal\Console\Command\ConfirmationTrait::confirmGeneration
139         if (!$this->confirmGeneration($io)) {
140             return;
141         }
142
143         $module = $input->getOption('module');
144
145         $pluginId = $input->getOption('plugin-id');
146         $plugin = ucfirst($this->stringConverter->underscoreToCamelCase($pluginId));
147
148         // Confirm that plugin.manager is available
149         if (!$this->validator->validatePluginManagerServiceExist($pluginId, $plugins)) {
150             throw new \Exception(
151                 sprintf(
152                     $this->trans('commands.generate.plugin.skeleton.messages.plugin-dont-exist'),
153                     $pluginId
154                 )
155             );
156         }
157
158         if (array_key_exists($pluginId, $this->pluginGeneratorsImplemented)) {
159             $io->warning(
160                 sprintf(
161                     $this->trans('commands.generate.plugin.skeleton.messages.plugin-generator-implemented'),
162                     $pluginId,
163                     $this->pluginGeneratorsImplemented[$pluginId]
164                 )
165             );
166         }
167
168         $className = $input->getOption('class');
169         $services = $input->getOption('services');
170
171         // @see use Drupal\Console\Command\Shared\ServicesTrait::buildServices
172         $buildServices = $this->buildServices($services);
173         $pluginMetaData = $this->getPluginMetadata($pluginId);
174
175         $this->generator->generate($module, $pluginId, $plugin, $className, $pluginMetaData, $buildServices);
176
177         $this->chainQueue->addCommand('cache:rebuild', ['cache' => 'discovery']);
178     }
179
180     protected function interact(InputInterface $input, OutputInterface $output)
181     {
182         $io = new DrupalStyle($input, $output);
183
184         $module = $input->getOption('module');
185         if (!$module) {
186             // @see Drupal\Console\Command\ModuleTrait::moduleQuestion
187             $module = $this->moduleQuestion($io);
188             $input->setOption('module', $module);
189         }
190
191         $pluginId = $input->getOption('plugin-id');
192         if (!$pluginId) {
193             $plugins = $this->getPlugins();
194             $pluginId = $io->choiceNoList(
195                 $this->trans('commands.generate.plugin.skeleton.questions.plugin'),
196                 $plugins
197             );
198             $input->setOption('plugin-id', $pluginId);
199         }
200
201         if (array_key_exists($pluginId, $this->pluginGeneratorsImplemented)) {
202             $io->warning(
203                 sprintf(
204                     $this->trans('commands.generate.plugin.skeleton.messages.plugin-dont-exist'),
205                     $pluginId,
206                     $this->pluginGeneratorsImplemented[$pluginId]
207                 )
208             );
209         }
210
211         // --class option
212         $class = $input->getOption('class');
213         if (!$class) {
214             $class = $io->ask(
215                 $this->trans('commands.generate.plugin.skeleton.options.class'),
216                 sprintf('%s%s', 'Default', ucfirst($this->stringConverter->underscoreToCamelCase($pluginId))),
217                 function ($class) {
218                     return $this->validator->validateClassName($class);
219                 }
220             );
221             $input->setOption('class', $class);
222         }
223
224         // --services option
225         // @see Drupal\Console\Command\Shared\ServicesTrait::servicesQuestion
226         $services = $input->getOption('services');
227         if (!$services) {
228             $services = $this->servicesQuestion($io);
229             $input->setOption('services', $services);
230         }
231     }
232
233     protected function getPluginMetadata($pluginId)
234     {
235         $pluginMetaData = [
236             'serviceId' => 'plugin.manager.' . $pluginId,
237         ];
238
239         // Load service and create reflection
240         $service = \Drupal::service($pluginMetaData['serviceId']);
241
242         $reflectionClass = new \ReflectionClass($service);
243
244         // Get list of properties with $reflectionClass->getProperties();
245         $pluginManagerProperties = [
246             'subdir' => 'subdir',
247             'pluginInterface' => 'pluginInterface',
248             'pluginDefinitionAnnotationName' => 'pluginAnnotation',
249         ];
250
251         foreach ($pluginManagerProperties as $propertyName => $key) {
252             if (!$reflectionClass->hasProperty($propertyName)) {
253                 $pluginMetaData[$key] = '';
254                 continue;
255             }
256
257             $property = $reflectionClass->getProperty($propertyName);
258             $property->setAccessible(true);
259             $pluginMetaData[$key] = $property->getValue($service);
260         }
261
262         if (empty($pluginMetaData['pluginInterface'])) {
263             $pluginMetaData['pluginInterfaceMethods'] = [];
264         } else {
265             $pluginMetaData['pluginInterfaceMethods'] = $this->getClassMethods($pluginMetaData['pluginInterface']);
266         }
267
268         if (isset($pluginMetaData['pluginAnnotation']) && class_exists($pluginMetaData['pluginAnnotation'])) {
269             $pluginMetaData['pluginAnnotationProperties'] = $this->getPluginAnnotationProperties($pluginMetaData['pluginAnnotation']);
270         } else {
271             $pluginMetaData['pluginAnnotationProperties'] = [];
272         }
273
274         return $pluginMetaData;
275     }
276
277     /**
278      * Get data for the methods of a class.
279      *
280      * @param $class
281      *  The fully-qualified name of class.
282      *
283      * @return
284      *  An array keyed by method name, where each value is an array containing:
285      *  - 'name: The name of the method.
286      *  - 'declaration': The function declaration line.
287      *  - 'description': The description from the method's docblock first line.
288      */
289     protected function getClassMethods($class)
290     {
291         // Get a reflection class.
292         $classReflection = new \ReflectionClass($class);
293         $methods = $classReflection->getMethods();
294
295         $metaData = [];
296         $methodData = [];
297
298         foreach ($methods as $method) {
299             $methodData['name'] = $method->getName();
300
301             $filename = $method->getFileName();
302             $source = file($filename);
303             $startLine = $method->getStartLine();
304
305             $methodData['declaration'] = substr(trim($source[$startLine - 1]), 0, -1);
306
307             $methodDocComment = explode("\n", $method->getDocComment());
308             foreach ($methodDocComment as $line) {
309                 if (substr($line, 0, 5) == '   * ') {
310                     $methodData['description'] = substr($line, 5);
311                     break;
312                 }
313             }
314
315             $metaData[$method->getName()] = $methodData;
316         }
317
318         return $metaData;
319     }
320
321     /**
322      * Get the list of properties from an annotation class.
323      *
324      * @param $pluginAnnotationClass
325      *  The fully-qualified name of the plugin annotation class.
326      *
327      * @return
328      *  An array keyed by property name, where each value is an array containing:
329      *  - 'name: The name of the property.
330      *  - 'description': The description from the property's docblock first line.
331      */
332     protected function getPluginAnnotationProperties($pluginAnnotationClass)
333     {
334         // Get a reflection class for the annotation class.
335         // Each property of the annotation class describes a property for the
336         // plugin annotation.
337         $annotationReflection = new \ReflectionClass($pluginAnnotationClass);
338         $propertiesReflection = $annotationReflection->getProperties(\ReflectionProperty::IS_PUBLIC);
339
340         $pluginProperties = [];
341         $annotationPropertyMetadata = [];
342
343         foreach ($propertiesReflection as $propertyReflection) {
344             $annotationPropertyMetadata['name'] = $propertyReflection->name;
345
346             $propertyDocblock = $propertyReflection->getDocComment();
347             $propertyDocblockLines = explode("\n", $propertyDocblock);
348             foreach ($propertyDocblockLines as $line) {
349                 if (substr($line, 0, 3) == '/**') {
350                     continue;
351                 }
352
353                 // Take the first actual docblock line to be the description.
354                 if (!isset($annotationPropertyMetadata['description']) && substr($line, 0, 5) == '   * ') {
355                     $annotationPropertyMetadata['description'] = substr($line, 5);
356                 }
357
358                 // Look for a @var token, to tell us the type of the property.
359                 if (substr($line, 0, 10) == '   * @var ') {
360                     $annotationPropertyMetadata['type'] = substr($line, 10);
361                 }
362             }
363
364             $pluginProperties[$propertyReflection->name] = $annotationPropertyMetadata;
365         }
366
367         return $pluginProperties;
368     }
369
370     protected function getPlugins()
371     {
372         $plugins = [];
373
374         foreach ($this->container->getServiceIds() as $serviceId) {
375             if (strpos($serviceId, 'plugin.manager.') === 0) {
376                 $plugins[] = substr($serviceId, 15);
377             }
378         }
379
380         return $plugins;
381     }
382 }