|
@@ -1,4 +1,5 @@
|
|
<?php
|
|
<?php
|
|
|
|
+
|
|
declare(strict_types = 1);
|
|
declare(strict_types = 1);
|
|
|
|
|
|
namespace Fgm\Drupal\Composer;
|
|
namespace Fgm\Drupal\Composer;
|
|
@@ -6,30 +7,43 @@ namespace Fgm\Drupal\Composer;
|
|
use Composer\Command\BaseCommand;
|
|
use Composer\Command\BaseCommand;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
+use Symfony\Component\Yaml\Exception\ParseException;
|
|
|
|
+use Symfony\Component\Yaml\Yaml;
|
|
|
|
+use Twig\Environment;
|
|
|
|
+use Twig\Extension\DebugExtension;
|
|
|
|
+use Twig\Loader\FilesystemLoader;
|
|
|
|
+use Twig\TemplateWrapper;
|
|
|
|
|
|
-abstract class BaseBuilderCommand extends BaseCommand
|
|
|
|
-{
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * The name of the argument defining the file to generate.
|
|
|
|
- */
|
|
|
|
- const ARG_FILE = 'file';
|
|
|
|
|
|
+/**
|
|
|
|
+ * Provides command primitives useful for most commands, focused on templating.
|
|
|
|
+ *
|
|
|
|
+ * @package Fgm\Drupal\Composer
|
|
|
|
+ */
|
|
|
|
+abstract class BaseBuilderCommand extends BaseCommand {
|
|
|
|
|
|
- protected function getBuilderConfig(): array
|
|
|
|
- {
|
|
|
|
- $conf = $this->getComposer()->getPackage()->getExtra()[Builder::NAME] ?? [];
|
|
|
|
- return $conf;
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * The name of the argument defining the file to generate.
|
|
|
|
+ */
|
|
|
|
+ const ARG_FILE = 'file';
|
|
|
|
|
|
- protected function getSettingsPath(): string
|
|
|
|
- {
|
|
|
|
- $settingsPath = getcwd() . "/settings";
|
|
|
|
- return $settingsPath;
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Provide the path to the project settings directory.
|
|
|
|
+ *
|
|
|
|
+ * @return string
|
|
|
|
+ * The absolute path to the directory.
|
|
|
|
+ */
|
|
|
|
+ protected function getSettingsPath(): string {
|
|
|
|
+ $settingsPath = getcwd() . "/settings";
|
|
|
|
+ return $settingsPath;
|
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ * Validate template definition for command, returning its name on success.
|
|
|
|
+ *
|
|
* @param \Symfony\Component\Console\Input\InputInterface $input
|
|
* @param \Symfony\Component\Console\Input\InputInterface $input
|
|
|
|
+ * Command input.
|
|
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
|
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
|
|
|
+ * Command output.
|
|
*
|
|
*
|
|
* @return array
|
|
* @return array
|
|
* - string Template name
|
|
* - string Template name
|
|
@@ -37,15 +51,117 @@ abstract class BaseBuilderCommand extends BaseCommand
|
|
*/
|
|
*/
|
|
public function validateTemplateName(InputInterface $input, OutputInterface $output): array {
|
|
public function validateTemplateName(InputInterface $input, OutputInterface $output): array {
|
|
$file = $input->getArgument(static::ARG_FILE);
|
|
$file = $input->getArgument(static::ARG_FILE);
|
|
- $conf = $this->getBuilderConfig();
|
|
|
|
|
|
+ $conf = $this->getComposer()->getPackage()->getExtra()[Builder::NAME] ?? [];
|
|
$templateName = $conf['templates'][$file] ?? '';
|
|
$templateName = $conf['templates'][$file] ?? '';
|
|
if (empty($templateName)) {
|
|
if (empty($templateName)) {
|
|
- $output->writeln(sprintf(
|
|
|
|
- 'Could not build file %s: no such template in composer.json extra section',
|
|
|
|
- $file
|
|
|
|
- ));
|
|
|
|
|
|
+ $output->writeln(sprintf(
|
|
|
|
+ 'Could not build file %s: no such template in composer.json extra section',
|
|
|
|
+ $file
|
|
|
|
+ ));
|
|
return ["", 1];
|
|
return ["", 1];
|
|
}
|
|
}
|
|
return [$templateName, 0];
|
|
return [$templateName, 0];
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Perform template rendering.
|
|
|
|
+ *
|
|
|
|
+ * @param \Twig\TemplateWrapper $wrapper
|
|
|
|
+ * A Twig user-space template wrapper.
|
|
|
|
+ * @param array $context
|
|
|
|
+ * The data context with which to perform the rendering.
|
|
|
|
+ * @param string $destination
|
|
|
|
+ * The path where to write the rendering result.
|
|
|
|
+ *
|
|
|
|
+ * @return array
|
|
|
|
+ * - string message
|
|
|
|
+ * - int status: 0 on success, other values on errors.
|
|
|
|
+ */
|
|
|
|
+ protected function render(
|
|
|
|
+ TemplateWrapper $wrapper,
|
|
|
|
+ array $context,
|
|
|
|
+ string $destination
|
|
|
|
+ ): array {
|
|
|
|
+ if (file_exists($destination)) {
|
|
|
|
+ $ok = unlink($destination);
|
|
|
|
+ if (!$ok) {
|
|
|
|
+ return [sprintf("Could not remove old %s file", $destination), 1];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $rendered = $wrapper->render($context);
|
|
|
|
+ $ok = file_put_contents($destination, $rendered, LOCK_EX);
|
|
|
|
+ if (!$ok) {
|
|
|
|
+ return [sprintf('Could not write new %s file', $destination), 2];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ["", 0];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Prepare the template and its parameters as obtained from configuration.
|
|
|
|
+ *
|
|
|
|
+ * @param \Symfony\Component\Console\Input\InputInterface $input
|
|
|
|
+ * Command input.
|
|
|
|
+ * @param \Symfony\Component\Console\Output\OutputInterface $output
|
|
|
|
+ * Command output.
|
|
|
|
+ *
|
|
|
|
+ * @return array
|
|
|
|
+ * - TemplateWrapper|NULL: Twig template wrapper
|
|
|
|
+ * - array: template parameters
|
|
|
|
+ * - string: error message
|
|
|
|
+ * - int: error, 0 if OK. If non-zero, only the error message is reliable.
|
|
|
|
+ *
|
|
|
|
+ * @throws \Twig\Error\LoaderError
|
|
|
|
+ * @throws \Twig\Error\RuntimeError
|
|
|
|
+ * @throws \Twig\Error\SyntaxError
|
|
|
|
+ */
|
|
|
|
+ protected function prepare(InputInterface $input, OutputInterface $output): array {
|
|
|
|
+ [$templateName, $err] = $this->validateTemplateName($input, $output);
|
|
|
|
+ if ($err) {
|
|
|
|
+ return [NULL, [], "Could not validate template name", 1];
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ $settingsPath = $this->getSettingsPath();
|
|
|
|
+ $templatePath = "${settingsPath}/${templateName}";
|
|
|
|
+ $realTemplatePath = realpath($templatePath);
|
|
|
|
+ if (empty($realTemplatePath)) {
|
|
|
|
+ return [NULL, [],
|
|
|
|
+ sprintf("Could not load template %s: no such file", $templateName),
|
|
|
|
+ 2,
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $paramsPath = "${settingsPath}/merged.params.local.yml";
|
|
|
|
+ $realParamsPath = realpath($paramsPath);
|
|
|
|
+ if (empty($realParamsPath)) {
|
|
|
|
+ return [NULL, [],
|
|
|
|
+ sprintf("Could not load parameters %s: no such file", $paramsPath),
|
|
|
|
+ 3,
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $yaml = new Yaml();
|
|
|
|
+ try {
|
|
|
|
+ $params = $yaml->parseFile($realParamsPath);
|
|
|
|
+ }
|
|
|
|
+ catch (ParseException $e) {
|
|
|
|
+ return [NULL, [],
|
|
|
|
+ sprintf("Could not parse %s: %s", $realParamsPath, $e->getMessage()),
|
|
|
|
+ 4,
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $loader = new FilesystemLoader($settingsPath, $settingsPath);
|
|
|
|
+ $twig = new Environment($loader, [
|
|
|
|
+ 'auto_reload' => TRUE,
|
|
|
|
+ 'cache' => FALSE,
|
|
|
|
+ 'debug' => TRUE,
|
|
|
|
+ 'strict_variables' => TRUE,
|
|
|
|
+ ]);
|
|
|
|
+ $twig->addExtension(new DebugExtension());
|
|
|
|
+ $wrapper = $twig->load($templateName);
|
|
|
|
+ return [$wrapper, $params, "", 0];
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|