Yaffs site version 1.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                 null,
107                 InputOption::VALUE_REQUIRED,
108                 $this->trans('commands.common.options.module')
109             )
110             ->addOption(
111                 'plugin-id',
112                 null,
113                 InputOption::VALUE_REQUIRED,
114                 $this->trans('commands.generate.plugin.options.plugin-id')
115             )
116             ->addOption(
117                 'class',
118                 null,
119                 InputOption::VALUE_OPTIONAL,
120                 $this->trans('commands.generate.plugin.block.options.class')
121             )
122             ->addOption(
123                 'services',
124                 null,
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 1;
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         return 0;
180     }
181
182     protected function interact(InputInterface $input, OutputInterface $output)
183     {
184         $io = new DrupalStyle($input, $output);
185
186         $module = $input->getOption('module');
187         if (!$module) {
188             // @see Drupal\Console\Command\ModuleTrait::moduleQuestion
189             $module = $this->moduleQuestion($io);
190             $input->setOption('module', $module);
191         }
192
193         $pluginId = $input->getOption('plugin-id');
194         if (!$pluginId) {
195             $plugins = $this->getPlugins();
196             $pluginId = $io->choiceNoList(
197                 $this->trans('commands.generate.plugin.skeleton.questions.plugin'),
198                 $plugins
199             );
200             $input->setOption('plugin-id', $pluginId);
201         }
202
203         if (array_key_exists($pluginId, $this->pluginGeneratorsImplemented)) {
204             $io->warning(
205                 sprintf(
206                     $this->trans('commands.generate.plugin.skeleton.messages.plugin-dont-exist'),
207                     $pluginId,
208                     $this->pluginGeneratorsImplemented[$pluginId]
209                 )
210             );
211         }
212
213         // --class option
214         $class = $input->getOption('class');
215         if (!$class) {
216             $class = $io->ask(
217                 $this->trans('commands.generate.plugin.skeleton.options.class'),
218                 sprintf('%s%s', 'Default', ucfirst($this->stringConverter->underscoreToCamelCase($pluginId))),
219                 function ($class) {
220                     return $this->validator->validateClassName($class);
221                 }
222             );
223             $input->setOption('class', $class);
224         }
225
226         // --services option
227         // @see Drupal\Console\Command\Shared\ServicesTrait::servicesQuestion
228         $services = $input->getOption('services');
229         if (!$services) {
230             $services = $this->servicesQuestion($io);
231             $input->setOption('services', $services);
232         }
233     }
234
235     protected function getPluginMetadata($pluginId)
236     {
237         $pluginMetaData = [
238             'serviceId' => 'plugin.manager.' . $pluginId,
239         ];
240
241         // Load service and create reflection
242         $service = \Drupal::service($pluginMetaData['serviceId']);
243
244         $reflectionClass = new \ReflectionClass($service);
245
246         // Get list of properties with $reflectionClass->getProperties();
247         $pluginManagerProperties = [
248             'subdir' => 'subdir',
249             'pluginInterface' => 'pluginInterface',
250             'pluginDefinitionAnnotationName' => 'pluginAnnotation',
251         ];
252
253         foreach ($pluginManagerProperties as $propertyName => $key) {
254             if (!$reflectionClass->hasProperty($propertyName)) {
255                 $pluginMetaData[$key] = '';
256                 continue;
257             }
258
259             $property = $reflectionClass->getProperty($propertyName);
260             $property->setAccessible(true);
261             $pluginMetaData[$key] = $property->getValue($service);
262         }
263
264         if (empty($pluginMetaData['pluginInterface'])) {
265             $pluginMetaData['pluginInterfaceMethods'] = [];
266         } else {
267             $pluginMetaData['pluginInterfaceMethods'] = $this->getClassMethods($pluginMetaData['pluginInterface']);
268         }
269
270         if (isset($pluginMetaData['pluginAnnotation']) && class_exists($pluginMetaData['pluginAnnotation'])) {
271             $pluginMetaData['pluginAnnotationProperties'] = $this->getPluginAnnotationProperties($pluginMetaData['pluginAnnotation']);
272         } else {
273             $pluginMetaData['pluginAnnotationProperties'] = [];
274         }
275
276         return $pluginMetaData;
277     }
278
279     /**
280      * Get data for the methods of a class.
281      *
282      * @param $class
283      *  The fully-qualified name of class.
284      *
285      * @return
286      *  An array keyed by method name, where each value is an array containing:
287      *  - 'name: The name of the method.
288      *  - 'declaration': The function declaration line.
289      *  - 'description': The description from the method's docblock first line.
290      */
291     protected function getClassMethods($class)
292     {
293         // Get a reflection class.
294         $classReflection = new \ReflectionClass($class);
295         $methods = $classReflection->getMethods();
296
297         $metaData = [];
298         $methodData = [];
299
300         foreach ($methods as $method) {
301             $methodData['name'] = $method->getName();
302
303             $filename = $method->getFileName();
304             $source = file($filename);
305             $startLine = $method->getStartLine();
306
307             $methodData['declaration'] = substr(trim($source[$startLine - 1]), 0, -1);
308
309             $methodDocComment = explode("\n", $method->getDocComment());
310             foreach ($methodDocComment as $line) {
311                 if (substr($line, 0, 5) == '   * ') {
312                     $methodData['description'] = substr($line, 5);
313                     break;
314                 }
315             }
316
317             $metaData[$method->getName()] = $methodData;
318         }
319
320         return $metaData;
321     }
322
323     /**
324      * Get the list of properties from an annotation class.
325      *
326      * @param $pluginAnnotationClass
327      *  The fully-qualified name of the plugin annotation class.
328      *
329      * @return
330      *  An array keyed by property name, where each value is an array containing:
331      *  - 'name: The name of the property.
332      *  - 'description': The description from the property's docblock first line.
333      */
334     protected function getPluginAnnotationProperties($pluginAnnotationClass)
335     {
336         // Get a reflection class for the annotation class.
337         // Each property of the annotation class describes a property for the
338         // plugin annotation.
339         $annotationReflection = new \ReflectionClass($pluginAnnotationClass);
340         $propertiesReflection = $annotationReflection->getProperties(\ReflectionProperty::IS_PUBLIC);
341
342         $pluginProperties = [];
343         $annotationPropertyMetadata = [];
344
345         foreach ($propertiesReflection as $propertyReflection) {
346             $annotationPropertyMetadata['name'] = $propertyReflection->name;
347
348             $propertyDocblock = $propertyReflection->getDocComment();
349             $propertyDocblockLines = explode("\n", $propertyDocblock);
350             foreach ($propertyDocblockLines as $line) {
351                 if (substr($line, 0, 3) == '/**') {
352                     continue;
353                 }
354
355                 // Take the first actual docblock line to be the description.
356                 if (!isset($annotationPropertyMetadata['description']) && substr($line, 0, 5) == '   * ') {
357                     $annotationPropertyMetadata['description'] = substr($line, 5);
358                 }
359
360                 // Look for a @var token, to tell us the type of the property.
361                 if (substr($line, 0, 10) == '   * @var ') {
362                     $annotationPropertyMetadata['type'] = substr($line, 10);
363                 }
364             }
365
366             $pluginProperties[$propertyReflection->name] = $annotationPropertyMetadata;
367         }
368
369         return $pluginProperties;
370     }
371
372     protected function getPlugins()
373     {
374         $plugins = [];
375
376         foreach ($this->container->getServiceIds() as $serviceId) {
377             if (strpos($serviceId, 'plugin.manager.') === 0) {
378                 $plugins[] = substr($serviceId, 15);
379             }
380         }
381
382         return $plugins;
383     }
384 }