devel_wizard-2.x-dev/src/Plugin/DevelWizard/Spell/PhpstormIdeaSpell.php
src/Plugin/DevelWizard/Spell/PhpstormIdeaSpell.php
<?php
declare(strict_types=1);
namespace Drupal\devel_wizard\Plugin\DevelWizard\Spell;
use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\devel_wizard\Attribute\DevelWizardSpell;
use Drupal\devel_wizard\Utils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;
/**
* @todo Enable and configure Symfony plugin.
* @todo Enable and configure PHPCS.
* @todo Configure directory <---> PHP namespace mapping.
* @todo Configure Task server.
* @todo Configure Git repository.
* @todo Configure GitLink plugin.
*
* @see \Drupal\devel_wizard\Plugin\DevelWizard\Spell\ProjectDrupalDrushSpell::doItPhpstorm
*/
#[DevelWizardSpell(
id: 'devel_wizard_phpstorm_idea',
category: new TranslatableMarkup('Code'),
label: new TranslatableMarkup('PHPStorm .idea'),
description: new TranslatableMarkup('Generates a basic PHPStorm project.'),
tags: [
'code' => new TranslatableMarkup('Code'),
'ide' => new TranslatableMarkup('IDE'),
'phpstorm' => new TranslatableMarkup('PHPStorm'),
],
)]
class PhpstormIdeaSpell extends SpellBase implements
PluginFormInterface,
ContainerFactoryPluginInterface,
ConfigurableInterface {
protected Filesystem $fs;
protected TwigEnvironment $twig;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger'),
$container->get('logger.channel.devel_wizard_spell'),
$container->get('string_translation'),
$container->get('devel_wizard.utils'),
$container->get('config.factory'),
$container->get('twig'),
new Filesystem(),
);
}
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
MessengerInterface $messenger,
LoggerInterface $logger,
TranslationInterface $stringTranslation,
Utils $utils,
ConfigFactoryInterface $configFactory,
TwigEnvironment $twig,
Filesystem $fs,
) {
$this->fs = $fs;
$this->twig = $twig;
parent::__construct(
$configuration,
$plugin_id,
$plugin_definition,
$messenger,
$logger,
$stringTranslation,
$utils,
$configFactory,
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'project_root' => '',
'project_name' => [],
'version_major' => 0,
];
}
protected function populateCalculatedConfigurationValues(): static {
parent::populateCalculatedConfigurationValues();
if (empty($this->configuration['project_root'])) {
throw new \InvalidArgumentException('project_root is required');
}
$composerFilePath = "{$this->configuration['project_root']}/composer.json";
if (!$this->fs->exists($composerFilePath)) {
throw new \InvalidArgumentException("$composerFilePath not exists");
}
$composer = json_decode(file_get_contents($composerFilePath) ?: '{}', TRUE);
$parts = explode('/', $composer['name'] ?? '/');
if (empty($parts[0]) || empty($parts[1])) {
throw new \InvalidArgumentException("$composerFilePath is invalid");
}
$this->configuration['project_name'] = [
'vendor' => $parts[0],
'project' => $parts[1],
];
$dir = pathinfo($this->configuration['project_root'], \PATHINFO_BASENAME);
$parts = explode('-', $dir);
$branch = end($parts);
if (!$branch) {
$branch = '1.x';
}
$matches = [];
preg_match('/^(?P<major>\d+)\./', $branch, $matches);
$this->configuration['version_major'] = $matches['major'] ?? '1';
$this->configuration['project_name_full'] = sprintf(
'%s.%s-%d',
$this->configuration['project_name']['vendor'],
$this->configuration['project_name']['project'],
$this->configuration['version_major'],
);
return $this;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['project_root'] = [
'#type' => 'textfield',
'#required' => TRUE,
'#title' => $this->t('Project root'),
'#description' => $this->t('The directory where the <code>composer.json</code> is.'),
'#default_value' => $this->configuration['project_root'] ?? '',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// @todo Implement validateConfigurationForm() method.
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValue($form['#parents'], []);
$this->setConfiguration($values);
}
/**
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\LoaderError
*/
protected function doIt(): static {
$this
->doItName()
->doItIml()
->doItModules();
$this->batchContext['sandbox']['finished'] = 1.0;
return $this;
}
protected function doItName(): static {
$fileName = Path::join($this->configuration['project_root'], '.idea', '.name');
$this->fs->mkdir(Path::getDirectory($fileName));
$this->fs->dumpFile($fileName, $this->configuration['project_name_full']);
$this->messageFilesystemEntryCreate($fileName);
return $this;
}
/**
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\LoaderError
*/
protected function doItIml(): static {
// - Find *.iml
$fileNames = glob("{$this->configuration['project_root']}/.idea/*.iml");
$fileName = $fileNames ? reset($fileNames) : NULL;
if (!$fileName) {
$fileName = Path::join(
$this->configuration['project_root'],
'.idea',
"{$this->configuration['project_name_full']}.iml",
);
$this->fs->mkdir(Path::getDirectory($fileName));
$this->fs->dumpFile(
$fileName,
$this->twig->render('@devel_wizard/phpstorm_idea/iml.xml.twig', $this->configuration),
);
$this->messageFilesystemEntryCreate($fileName);
return $this;
}
$dir = Path::getDirectory($fileName);
$fileName = pathinfo($fileName, \PATHINFO_BASENAME);
$fileNameExpected = "{$this->configuration['project_name_full']}.iml";
if ($fileName !== $fileNameExpected) {
$this->fs->rename(
"$dir/$fileName",
"$dir/$fileNameExpected",
);
// @todo Message.
}
return $this;
}
/**
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \Twig\Error\LoaderError
*/
protected function doItModules(): static {
$filePath = Path::join($this->configuration['project_root'], '.idea', 'modules.xml');
if ($this->fs->exists($filePath)) {
// @todo Update.
return $this;
}
$this->configuration['idea_modules'] = [
'root' => [
'fileurl' => "file://\$PROJECT_DIR\$/.idea/{$this->configuration['project_name_full']}.iml",
'filepath' => "\$PROJECT_DIR\$/.idea/{$this->configuration['project_name_full']}.iml",
],
];
$filePath = Path::join(
$this->configuration['project_root'],
'.idea',
'modules.xml',
);
$this->fs->mkdir(Path::getDirectory($filePath));
$this->fs->dumpFile(
$filePath,
$this->twig->render('@devel_wizard/phpstorm_idea/modules.xml.twig', $this->configuration),
);
$this->messageFilesystemEntryCreate($filePath);
return $this;
}
}
