annotate vendor/chi-teck/drupal-code-generator/src/Command/BaseGenerator.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
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 }