Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace DrupalCodeGenerator\Command;
|
Chris@0
|
4
|
Chris@0
|
5 use DrupalCodeGenerator\ApplicationFactory;
|
Chris@0
|
6 use DrupalCodeGenerator\Asset;
|
Chris@0
|
7 use DrupalCodeGenerator\Utils;
|
Chris@0
|
8 use Symfony\Component\Console\Command\Command;
|
Chris@0
|
9 use Symfony\Component\Console\Input\InputInterface;
|
Chris@0
|
10 use Symfony\Component\Console\Input\InputOption;
|
Chris@0
|
11 use Symfony\Component\Console\Output\OutputInterface;
|
Chris@4
|
12 use Symfony\Component\Console\Question\Question;
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * Base class for all generators.
|
Chris@0
|
16 */
|
Chris@0
|
17 abstract class BaseGenerator extends Command implements GeneratorInterface {
|
Chris@0
|
18
|
Chris@0
|
19 /**
|
Chris@0
|
20 * The command name.
|
Chris@0
|
21 *
|
Chris@0
|
22 * @var string
|
Chris@0
|
23 */
|
Chris@0
|
24 protected $name;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * The command description.
|
Chris@0
|
28 *
|
Chris@0
|
29 * @var string
|
Chris@0
|
30 */
|
Chris@0
|
31 protected $description;
|
Chris@0
|
32
|
Chris@0
|
33 /**
|
Chris@0
|
34 * The command alias.
|
Chris@0
|
35 *
|
Chris@0
|
36 * @var string
|
Chris@0
|
37 */
|
Chris@0
|
38 protected $alias;
|
Chris@0
|
39
|
Chris@0
|
40 /**
|
Chris@0
|
41 * Command label.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @var string
|
Chris@0
|
44 */
|
Chris@0
|
45 protected $label;
|
Chris@0
|
46
|
Chris@0
|
47 /**
|
Chris@0
|
48 * A path where templates are stored.
|
Chris@0
|
49 *
|
Chris@0
|
50 * @var string
|
Chris@0
|
51 */
|
Chris@0
|
52 protected $templatePath;
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * The working directory.
|
Chris@0
|
56 *
|
Chris@0
|
57 * @var string
|
Chris@0
|
58 */
|
Chris@0
|
59 protected $directory;
|
Chris@0
|
60
|
Chris@0
|
61 /**
|
Chris@0
|
62 * The destination.
|
Chris@0
|
63 *
|
Chris@0
|
64 * @var mixed
|
Chris@0
|
65 */
|
Chris@0
|
66 protected $destination = 'modules/%';
|
Chris@0
|
67
|
Chris@0
|
68 /**
|
Chris@0
|
69 * Files to create.
|
Chris@0
|
70 *
|
Chris@0
|
71 * The key of the each item in the array is the path to the file and
|
Chris@0
|
72 * the value is the generated content of it.
|
Chris@0
|
73 *
|
Chris@0
|
74 * @var array
|
Chris@0
|
75 *
|
Chris@0
|
76 * @deprecated Use self::$assets.
|
Chris@0
|
77 */
|
Chris@0
|
78 protected $files = [];
|
Chris@0
|
79
|
Chris@0
|
80 /**
|
Chris@0
|
81 * Assets to create.
|
Chris@0
|
82 *
|
Chris@0
|
83 * @var \DrupalCodeGenerator\Asset[]
|
Chris@0
|
84 */
|
Chris@0
|
85 protected $assets = [];
|
Chris@0
|
86
|
Chris@0
|
87 /**
|
Chris@0
|
88 * Twig template variables.
|
Chris@0
|
89 *
|
Chris@0
|
90 * @var array
|
Chris@0
|
91 */
|
Chris@0
|
92 protected $vars = [];
|
Chris@0
|
93
|
Chris@0
|
94 /**
|
Chris@0
|
95 * {@inheritdoc}
|
Chris@0
|
96 */
|
Chris@0
|
97 protected function configure() {
|
Chris@0
|
98 $this
|
Chris@0
|
99 ->setName($this->name)
|
Chris@0
|
100 ->setDescription($this->description)
|
Chris@0
|
101 ->addOption(
|
Chris@0
|
102 'directory',
|
Chris@0
|
103 '-d',
|
Chris@0
|
104 InputOption::VALUE_OPTIONAL,
|
Chris@0
|
105 'Working directory'
|
Chris@0
|
106 )
|
Chris@0
|
107 ->addOption(
|
Chris@0
|
108 'answers',
|
Chris@0
|
109 '-a',
|
Chris@0
|
110 InputOption::VALUE_OPTIONAL,
|
Chris@0
|
111 'Default JSON formatted answers'
|
Chris@0
|
112 );
|
Chris@0
|
113
|
Chris@0
|
114 if ($this->alias) {
|
Chris@0
|
115 $this->setAliases([$this->alias]);
|
Chris@0
|
116 }
|
Chris@0
|
117
|
Chris@0
|
118 if (!$this->templatePath) {
|
Chris@0
|
119 $this->templatePath = ApplicationFactory::getRoot() . '/templates';
|
Chris@0
|
120 }
|
Chris@0
|
121 }
|
Chris@0
|
122
|
Chris@0
|
123 /**
|
Chris@0
|
124 * {@inheritdoc}
|
Chris@0
|
125 */
|
Chris@0
|
126 protected function initialize(InputInterface $input, OutputInterface $output) {
|
Chris@0
|
127 $this->getHelperSet()->setCommand($this);
|
Chris@0
|
128 $this->getHelper('dcg_renderer')->addPath($this->templatePath);
|
Chris@0
|
129
|
Chris@0
|
130 $directory = $input->getOption('directory') ?: getcwd();
|
Chris@0
|
131 // No need to look up for extension root when generating an extension.
|
Chris@0
|
132 $extension_destinations = ['modules', 'profiles', 'themes'];
|
Chris@0
|
133 $is_extension = in_array($this->destination, $extension_destinations);
|
Chris@0
|
134 $this->directory = $is_extension
|
Chris@0
|
135 ? $directory : (Utils::getExtensionRoot($directory) ?: $directory);
|
Chris@0
|
136
|
Chris@0
|
137 // Display welcome message.
|
Chris@0
|
138 $header = sprintf(
|
Chris@0
|
139 "\n Welcome to %s generator!",
|
Chris@0
|
140 $this->getName()
|
Chris@0
|
141 );
|
Chris@0
|
142 $output->writeln($header);
|
Chris@0
|
143 $header_length = strlen(trim(strip_tags($header)));
|
Chris@0
|
144 $output->writeln('<fg=cyan;options=bold>–' . str_repeat('–', $header_length) . '–</>');
|
Chris@0
|
145 }
|
Chris@0
|
146
|
Chris@0
|
147 /**
|
Chris@0
|
148 * {@inheritdoc}
|
Chris@0
|
149 */
|
Chris@0
|
150 protected function execute(InputInterface $input, OutputInterface $output) {
|
Chris@0
|
151
|
Chris@0
|
152 // Render all assets.
|
Chris@0
|
153 $renderer = $this->getHelper('dcg_renderer');
|
Chris@0
|
154 foreach ($this->getAssets() as $asset) {
|
Chris@0
|
155 // Supply the asset with all collected variables if it has no local ones.
|
Chris@0
|
156 if (!$asset->getVars()) {
|
Chris@0
|
157 $asset->vars($this->vars);
|
Chris@0
|
158 }
|
Chris@0
|
159 $asset->render($renderer);
|
Chris@0
|
160 }
|
Chris@0
|
161
|
Chris@0
|
162 $dumped_files = $this->getHelper('dcg_dumper')->dump($input, $output);
|
Chris@0
|
163 $this->getHelper('dcg_output_handler')->printSummary($output, $dumped_files);
|
Chris@0
|
164 return 0;
|
Chris@0
|
165 }
|
Chris@0
|
166
|
Chris@0
|
167 /**
|
Chris@0
|
168 * {@inheritdoc}
|
Chris@0
|
169 */
|
Chris@0
|
170 public function getLabel() {
|
Chris@0
|
171 return $this->label;
|
Chris@0
|
172 }
|
Chris@0
|
173
|
Chris@0
|
174 /**
|
Chris@0
|
175 * Returns list of rendered files.
|
Chris@0
|
176 *
|
Chris@0
|
177 * @return array
|
Chris@0
|
178 * An associative array where each key is path to a file and value is
|
Chris@0
|
179 * rendered content.
|
Chris@0
|
180 *
|
Chris@0
|
181 * @deprecated.
|
Chris@0
|
182 */
|
Chris@0
|
183 public function getFiles() {
|
Chris@0
|
184 return $this->files;
|
Chris@0
|
185 }
|
Chris@0
|
186
|
Chris@0
|
187 /**
|
Chris@0
|
188 * {@inheritdoc}
|
Chris@0
|
189 */
|
Chris@0
|
190 public function getAssets() {
|
Chris@0
|
191 if ($this->files) {
|
Chris@0
|
192 // Convert files into assets for legacy commands.
|
Chris@0
|
193 $assets = [];
|
Chris@0
|
194 foreach ($this->getFiles() as $path => $file) {
|
Chris@0
|
195 $asset = new Asset();
|
Chris@0
|
196 $asset->path($path);
|
Chris@0
|
197 if (!is_array($file)) {
|
Chris@0
|
198 $file = ['content' => $file];
|
Chris@0
|
199 }
|
Chris@0
|
200 if (isset($file['content'])) {
|
Chris@0
|
201 $asset->content($file['content']);
|
Chris@0
|
202 }
|
Chris@0
|
203 else {
|
Chris@0
|
204 $asset->type('directory');
|
Chris@0
|
205 }
|
Chris@0
|
206 if (isset($file['action'])) {
|
Chris@0
|
207 $asset->action($file['action']);
|
Chris@0
|
208 }
|
Chris@0
|
209 if (isset($file['header_size'])) {
|
Chris@0
|
210 $asset->headerSize($file['header_size']);
|
Chris@0
|
211 }
|
Chris@0
|
212 if (isset($file['mode'])) {
|
Chris@0
|
213 $asset->mode($file['mode']);
|
Chris@0
|
214 }
|
Chris@0
|
215 $assets[] = $asset;
|
Chris@0
|
216 }
|
Chris@0
|
217 return array_merge($assets, $this->assets);
|
Chris@0
|
218 }
|
Chris@0
|
219
|
Chris@0
|
220 return $this->assets;
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 /**
|
Chris@0
|
224 * {@inheritdoc}
|
Chris@0
|
225 */
|
Chris@0
|
226 public function setDirectory($directory) {
|
Chris@0
|
227 $this->directory = $directory;
|
Chris@0
|
228 }
|
Chris@0
|
229
|
Chris@0
|
230 /**
|
Chris@0
|
231 * {@inheritdoc}
|
Chris@0
|
232 */
|
Chris@0
|
233 public function getDirectory() {
|
Chris@0
|
234 return $this->directory;
|
Chris@0
|
235 }
|
Chris@0
|
236
|
Chris@0
|
237 /**
|
Chris@0
|
238 * {@inheritdoc}
|
Chris@0
|
239 */
|
Chris@0
|
240 public function setDestination($destination) {
|
Chris@0
|
241 $this->destination = $destination;
|
Chris@0
|
242 }
|
Chris@0
|
243
|
Chris@0
|
244 /**
|
Chris@0
|
245 * {@inheritdoc}
|
Chris@0
|
246 */
|
Chris@0
|
247 public function getDestination() {
|
Chris@0
|
248 return $this->destination;
|
Chris@0
|
249 }
|
Chris@0
|
250
|
Chris@0
|
251 /**
|
Chris@0
|
252 * Renders a template.
|
Chris@0
|
253 *
|
Chris@0
|
254 * @param string $template
|
Chris@0
|
255 * Twig template.
|
Chris@0
|
256 * @param array $vars
|
Chris@0
|
257 * Template variables.
|
Chris@0
|
258 *
|
Chris@0
|
259 * @return string
|
Chris@0
|
260 * A string representing the rendered output.
|
Chris@0
|
261 */
|
Chris@0
|
262 protected function render($template, array $vars) {
|
Chris@0
|
263 return $this->getHelper('dcg_renderer')->render($template, $vars);
|
Chris@0
|
264 }
|
Chris@0
|
265
|
Chris@0
|
266 /**
|
Chris@0
|
267 * Asks the user for template variables.
|
Chris@0
|
268 *
|
Chris@0
|
269 * @param \Symfony\Component\Console\Input\InputInterface $input
|
Chris@0
|
270 * Input instance.
|
Chris@0
|
271 * @param \Symfony\Component\Console\Output\OutputInterface $output
|
Chris@0
|
272 * Output instance.
|
Chris@0
|
273 * @param array $questions
|
Chris@0
|
274 * List of questions that the user should answer.
|
Chris@0
|
275 * @param array $vars
|
Chris@0
|
276 * Array of predefined template variables.
|
Chris@0
|
277 *
|
Chris@0
|
278 * @return array
|
Chris@0
|
279 * Template variables.
|
Chris@0
|
280 *
|
Chris@4
|
281 * @see \DrupalCodeGenerator\InputHandler::collectVars()
|
Chris@0
|
282 */
|
Chris@0
|
283 protected function &collectVars(InputInterface $input, OutputInterface $output, array $questions, array $vars = []) {
|
Chris@0
|
284 $this->vars += $this->getHelper('dcg_input_handler')->collectVars($input, $output, $questions, $vars);
|
Chris@0
|
285 return $this->vars;
|
Chris@0
|
286 }
|
Chris@0
|
287
|
Chris@0
|
288 /**
|
Chris@4
|
289 * Asks the user a single question and returns the answer.
|
Chris@4
|
290 *
|
Chris@4
|
291 * @param \Symfony\Component\Console\Input\InputInterface $input
|
Chris@4
|
292 * Input instance.
|
Chris@4
|
293 * @param \Symfony\Component\Console\Output\OutputInterface $output
|
Chris@4
|
294 * Output instance.
|
Chris@4
|
295 * @param \Symfony\Component\Console\Question\Question $question
|
Chris@4
|
296 * A question to ask.
|
Chris@4
|
297 * @param array $vars
|
Chris@4
|
298 * Array of predefined template variables.
|
Chris@4
|
299 *
|
Chris@4
|
300 * @return string
|
Chris@4
|
301 * The answer.
|
Chris@4
|
302 *
|
Chris@4
|
303 * @see \DrupalCodeGenerator\InputHandler::collectVars()
|
Chris@4
|
304 */
|
Chris@4
|
305 protected function ask(InputInterface $input, OutputInterface $output, Question $question, array $vars = []) {
|
Chris@5
|
306 $key = mt_rand();
|
Chris@5
|
307 $answers = $this->getHelper('dcg_input_handler')->collectVars($input, $output, [$key => $question], $vars);
|
Chris@5
|
308 return $answers[$key];
|
Chris@4
|
309 }
|
Chris@4
|
310
|
Chris@4
|
311 /**
|
Chris@0
|
312 * Creates an asset.
|
Chris@0
|
313 *
|
Chris@0
|
314 * @param string $type
|
Chris@0
|
315 * Asset type.
|
Chris@0
|
316 *
|
Chris@0
|
317 * @return \DrupalCodeGenerator\Asset
|
Chris@0
|
318 * The asset.
|
Chris@0
|
319 */
|
Chris@0
|
320 protected function addAsset($type) {
|
Chris@0
|
321 $asset = (new Asset())->type($type);
|
Chris@0
|
322 $this->assets[] = $asset;
|
Chris@0
|
323 return $asset;
|
Chris@0
|
324 }
|
Chris@0
|
325
|
Chris@0
|
326 /**
|
Chris@0
|
327 * Creates file asset.
|
Chris@0
|
328 *
|
Chris@5
|
329 * @param string $path
|
Chris@5
|
330 * (Optional) File path.
|
Chris@5
|
331 *
|
Chris@0
|
332 * @return \DrupalCodeGenerator\Asset
|
Chris@0
|
333 * The asset.
|
Chris@0
|
334 */
|
Chris@5
|
335 protected function addFile($path = NULL) {
|
Chris@5
|
336 return $this->addAsset('file')->path($path);
|
Chris@0
|
337 }
|
Chris@0
|
338
|
Chris@0
|
339 /**
|
Chris@0
|
340 * Creates directory asset.
|
Chris@0
|
341 *
|
Chris@5
|
342 * @param string $path
|
Chris@5
|
343 * (Optional) Directory path.
|
Chris@5
|
344 *
|
Chris@0
|
345 * @return \DrupalCodeGenerator\Asset
|
Chris@0
|
346 * The asset.
|
Chris@0
|
347 */
|
Chris@5
|
348 protected function addDirectory($path = NULL) {
|
Chris@5
|
349 return $this->addAsset('directory')->path($path);
|
Chris@0
|
350 }
|
Chris@0
|
351
|
Chris@0
|
352 /**
|
Chris@0
|
353 * Creates service file asset.
|
Chris@0
|
354 *
|
Chris@5
|
355 * @param string $path
|
Chris@5
|
356 * (Optional) File path.
|
Chris@5
|
357 *
|
Chris@0
|
358 * @return \DrupalCodeGenerator\Asset
|
Chris@0
|
359 * The asset.
|
Chris@0
|
360 */
|
Chris@5
|
361 protected function addServicesFile($path = NULL) {
|
Chris@0
|
362 return $this->addFile()
|
Chris@5
|
363 ->path($path ?: '{machine_name}.services.yml')
|
Chris@0
|
364 ->action('append')
|
Chris@0
|
365 ->headerSize(1);
|
Chris@0
|
366 }
|
Chris@0
|
367
|
Chris@0
|
368 /**
|
Chris@0
|
369 * Creates file asset.
|
Chris@0
|
370 *
|
Chris@0
|
371 * @param string $path
|
Chris@0
|
372 * Path to the file.
|
Chris@0
|
373 * @param string $template
|
Chris@0
|
374 * Twig template to render.
|
Chris@0
|
375 * @param array $vars
|
Chris@0
|
376 * Twig variables.
|
Chris@0
|
377 *
|
Chris@0
|
378 * @deprecated Use self::addFile() or self::addDirectory().
|
Chris@0
|
379 */
|
Chris@0
|
380 protected function setFile($path = NULL, $template = NULL, array $vars = []) {
|
Chris@0
|
381 $this->addFile()
|
Chris@0
|
382 ->path($path)
|
Chris@0
|
383 ->template($template)
|
Chris@0
|
384 ->vars($vars);
|
Chris@0
|
385 }
|
Chris@0
|
386
|
Chris@0
|
387 /**
|
Chris@0
|
388 * Creates service file asset.
|
Chris@0
|
389 *
|
Chris@0
|
390 * @param string $path
|
Chris@0
|
391 * Path to the file.
|
Chris@0
|
392 * @param string $template
|
Chris@0
|
393 * Twig template to render.
|
Chris@0
|
394 * @param array $vars
|
Chris@0
|
395 * Twig variables.
|
Chris@0
|
396 *
|
Chris@0
|
397 * @deprecated Use self::addServiceFile().
|
Chris@0
|
398 */
|
Chris@0
|
399 protected function setServicesFile($path, $template, array $vars) {
|
Chris@0
|
400 $this->addServicesFile()
|
Chris@0
|
401 ->path($path)
|
Chris@0
|
402 ->template($template)
|
Chris@0
|
403 ->vars($vars);
|
Chris@0
|
404 }
|
Chris@0
|
405
|
Chris@4
|
406 /**
|
Chris@4
|
407 * Collects services.
|
Chris@4
|
408 *
|
Chris@4
|
409 * @param \Symfony\Component\Console\Input\InputInterface $input
|
Chris@4
|
410 * Input instance.
|
Chris@4
|
411 * @param \Symfony\Component\Console\Output\OutputInterface $output
|
Chris@4
|
412 * Output instance.
|
Chris@4
|
413 *
|
Chris@4
|
414 * @return array
|
Chris@4
|
415 * List of collected services.
|
Chris@4
|
416 */
|
Chris@4
|
417 protected function collectServices(InputInterface $input, OutputInterface $output) {
|
Chris@4
|
418
|
Chris@4
|
419 $service_definitions = self::getServiceDefinitions();
|
Chris@4
|
420 $service_ids = array_keys($service_definitions);
|
Chris@4
|
421
|
Chris@4
|
422 $services = [];
|
Chris@4
|
423 while (TRUE) {
|
Chris@4
|
424 $question = new Question('Type the service name or use arrows up/down. Press enter to continue');
|
Chris@4
|
425 $question->setValidator([Utils::class, 'validateServiceName']);
|
Chris@4
|
426 $question->setAutocompleterValues($service_ids);
|
Chris@4
|
427 $service = $this->ask($input, $output, $question);
|
Chris@4
|
428 if (!$service) {
|
Chris@4
|
429 break;
|
Chris@4
|
430 }
|
Chris@4
|
431 $services[] = $service;
|
Chris@4
|
432 }
|
Chris@4
|
433
|
Chris@4
|
434 $this->vars['services'] = [];
|
Chris@4
|
435 foreach (array_unique($services) as $service_id) {
|
Chris@4
|
436 if (isset($service_definitions[$service_id])) {
|
Chris@4
|
437 $definition = $service_definitions[$service_id];
|
Chris@4
|
438 }
|
Chris@4
|
439 else {
|
Chris@4
|
440 // Build the definition if the service is unknown.
|
Chris@4
|
441 $definition = [
|
Chris@4
|
442 'type' => 'Drupal\example\ExampleInterface',
|
Chris@4
|
443 'name' => str_replace('.', '_', $service_id),
|
Chris@4
|
444 'description' => "The $service_id service.",
|
Chris@4
|
445 ];
|
Chris@4
|
446 }
|
Chris@4
|
447 $type_parts = explode('\\', $definition['type']);
|
Chris@4
|
448 $definition['short_type'] = end($type_parts);
|
Chris@4
|
449 $this->vars['services'][$service_id] = $definition;
|
Chris@4
|
450 }
|
Chris@4
|
451 return $this->vars['services'];
|
Chris@4
|
452 }
|
Chris@4
|
453
|
Chris@4
|
454 /**
|
Chris@4
|
455 * Gets service definitions.
|
Chris@4
|
456 *
|
Chris@4
|
457 * @return array
|
Chris@4
|
458 * List of service definitions keyed by service ID.
|
Chris@4
|
459 */
|
Chris@4
|
460 protected static function getServiceDefinitions() {
|
Chris@4
|
461 $data_encoded = file_get_contents(ApplicationFactory::getRoot() . '/resources/service-definitions.json');
|
Chris@4
|
462 return json_decode($data_encoded, TRUE);
|
Chris@4
|
463 }
|
Chris@4
|
464
|
Chris@0
|
465 }
|