Merge pull request #1634 from bolt/feature/extension-services-and-routes

Copy extension services and routes into Bolt
This commit is contained in:
Ivo Valchev
2020-07-18 09:04:39 +02:00
committed by GitHub
6 changed files with 160 additions and 125 deletions
+2
View File
@@ -135,10 +135,12 @@
"prefer-stable": true,
"scripts": {
"post-install-cmd": [
"php bin/console extensions:configure --with-config",
"@auto-scripts",
"php bin/console bolt:info"
],
"post-update-cmd": [
"php bin/console extensions:configure",
"@auto-scripts",
"php bin/console bolt:info"
],
+18
View File
@@ -0,0 +1,18 @@
# This file is auto-generated by Bolt. Do not modify.
services:
_defaults:
autowire: true
autoconfigure: true
AcmeCorp\ReferenceExtension\:
resource: '../../vendor/acmecorp/reference-extension/src/*'
exclude: '../../vendor/acmecorp/reference-extension/src/{Entity,Exception}'
BobdenOtter\ConfigurationNotices\:
resource: '../../vendor/bobdenotter/configuration-notices/src/*'
exclude: '../../vendor/bobdenotter/configuration-notices/src/{Entity,Exception}'
BobdenOtter\WeatherWidget\:
resource: '../../vendor/bobdenotter/weatherwidget/src/*'
exclude: '../../vendor/bobdenotter/weatherwidget/src/{Entity,Exception}'
Bolt\NewsWidget\:
resource: '../../vendor/bolt/newswidget/src/*'
exclude: '../../vendor/bolt/newswidget/src/{Entity,Exception}'
-18
View File
@@ -1,18 +0,0 @@
# This file is auto-generated by Bolt. Do not modify.
services:
_defaults:
autowire: true
autoconfigure: true
AcmeCorp\ReferenceExtension\:
resource: '../vendor/acmecorp/reference-extension/src/*'
exclude: '../vendor/acmecorp/reference-extension/src/{Entity,Exception}'
BobdenOtter\WeatherWidget\:
resource: '../vendor/bobdenotter/weatherwidget/src/*'
exclude: '../vendor/bobdenotter/weatherwidget/src/{Entity,Exception}'
Bolt\NewsWidget\:
resource: '../vendor/bolt/newswidget/src/*'
exclude: '../vendor/bolt/newswidget/src/{Entity,Exception}'
BobdenOtter\ConfigurationNotices\:
resource: '../vendor/bobdenotter/configuration-notices/src/*'
exclude: '../vendor/bobdenotter/configuration-notices/src/{Entity,Exception}'
+140
View File
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
namespace Bolt\Command;
use Bolt\Common\Str;
use Bolt\Extension\ExtensionRegistry;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Webmozart\PathUtil\Path;
class ExtensionsConfigureCommand extends Command
{
protected static $defaultName = 'extensions:configure';
/** @var ExtensionRegistry */
private $extensionRegistry;
/** @var mixed */
private $projectDir;
public function __construct(ExtensionRegistry $extensionRegistry, ContainerInterface $container)
{
$this->extensionRegistry = $extensionRegistry;
$this->projectDir = $container->getParameter('kernel.project_dir');
parent::__construct();
}
protected function configure(): void
{
$this
->setDescription('Copy the config/config.yaml, config/services.yaml and config/routes.yaml files from extensions.')
->addOption('with-config', null, InputOption::VALUE_NONE, 'If set, Bolt will copy the default extension config.yaml file');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$extensions = $this->extensionRegistry->getExtensions();
$this->copyExtensionRoutesAndServices($extensions);
if ($input->getOption('with-config')) {
$this->copyExtensionConfig($extensions);
}
return 0;
}
public function copyExtensionConfig(array $packages): void
{
// @todo: Combine this with Bolt\Extension\ConfigTrait.php
foreach ($packages as $package) {
$path = $this->getPackagePath($package);
$configPath = $this->getRelativePath($path) . '/config/config.yaml';
if (file_exists($configPath)) {
[$namespace, $name] = explode('\\', mb_strtolower($this->getNamespace($package)));
$destination = $this->getExtensionConfigPath($namespace, $name);
file_put_contents($destination, file_get_contents($configPath));
}
}
}
public function copyExtensionRoutesAndServices(array $packages): void
{
$oldExtensionsRoutes = glob($this->getExtensionRoutesPath());
$oldExtensionsServices = glob($this->getExtensionServicesPath());
foreach ($packages as $package) {
$path = $this->getPackagePath($package);
$extensionRoutesPath = $this->getRelativePath($path) . '/config/routes.yaml';
if (file_exists($extensionRoutesPath)) {
$destination = $this->getExtensionRoutesPath($path);
$oldExtensionsRoutes = array_diff($oldExtensionsRoutes, [$destination]);
file_put_contents($destination, file_get_contents($extensionRoutesPath));
}
$extensionServicesPath = $this->getRelativePath($path) . '/../config.services.yaml';
if (file_exists($extensionServicesPath)) {
$destination = $this->getExtensionServicesPath($path);
$oldExtensionsServices = array_diff($oldExtensionsServices, [$destination]);
file_put_contents($destination, file_get_contents($extensionRoutesPath));
}
}
// Remove routes.yaml files for old (uninstalled) extensions
array_map('unlink', $oldExtensionsRoutes);
// Remove services.yaml files for old (uninstalled) extensions
array_map('unlink', $oldExtensionsServices);
}
private function getRelativePath(string $path): string
{
return Path::makeRelative($path, $this->projectDir);
}
/**
* Helper function that returns the path of the extension routes.yaml file
* inside Bolt.
*/
private function getExtensionRoutesPath(string $path = '*'): string
{
return $this->projectDir . '/config/routes/extension_' . Str::splitLast($path, '/') . '.yaml';
}
/**
* Helper function that returns the path of the extension services.yaml file
* inside Bolt.
*/
private function getExtensionServicesPath(string $path = '*'): string
{
return $this->projectDir . '/config/packages/services_extension_' . Str::splitLast($path, '/') . '.yaml';
}
private function getExtensionConfigPath(string $namespace, string $name): string
{
return $this->projectDir . '/config/extensions/' . $namespace . '-' . $name . '.yaml';
}
private function getPackagePath($package): string
{
$reflection = new \ReflectionClass($package);
return dirname(dirname($reflection->getFilename()));
}
private function getNamespace($package): string
{
$reflection = new \ReflectionClass($package);
return $reflection->getNamespaceName();
}
}
-96
View File
@@ -1,96 +0,0 @@
<?php
declare(strict_types=1);
namespace Bolt\Extension;
use Bolt\Common\Str;
use Composer\Package\PackageInterface;
use ComposerPackages\Types;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Yaml\Yaml;
use Webmozart\PathUtil\Path;
class ExtensionCompilerPass implements CompilerPassInterface
{
/** string */
private $projectDir;
public function process(ContainerBuilder $container): void
{
if ($container->has(ExtensionRegistry::class) === false) {
return;
}
$registry = $container->findDefinition(ExtensionRegistry::class);
$packages = array_keys($container->findTaggedServiceIds(ExtensionInterface::CONTAINER_TAG));
$this->projectDir = $container->getParameter('kernel.project_dir');
/* @see ExtensionRegistry::addCompilerPass() */
$registry->addMethodCall('addCompilerPass', [$packages]);
// Rebuild our own `services_bolt.yml` file.
$this->buildServices($packages);
}
public function buildServices(array $packages): void
{
$services = [
'services' => [
'_defaults' => [
'autowire' => true,
'autoconfigure' => true,
],
],
];
$packages = $this->addComposerPackages($packages);
foreach ($packages as $package) {
[$name, $service] = $this->createService($package);
if ($name) {
$services['services'][$name] = $service;
}
}
$yaml = "# This file is auto-generated by Bolt. Do not modify.\n\n";
$yaml .= Yaml::dump($services, 3);
$filename = $this->projectDir . '/config/services_bolt.yaml';
file_put_contents($filename, $yaml);
}
private function createService(string $package): array
{
if (! class_exists($package)) {
return [null, null];
}
$reflection = new \ReflectionClass($package);
$namespace = Str::removeLast($reflection->getName(), Str::splitLast($reflection->getName(), '\\'));
$path = Path::makeRelative(dirname($reflection->getFileName()), $this->projectDir . '/foo');
return [$namespace, [
'resource' => $path . '/*',
'exclude' => $path . '/{Entity,Exception,Exclude}',
]];
}
private function addComposerPackages(array $packages): array
{
$composerPackages = Types::get('bolt-extension');
/** @var PackageInterface $package */
foreach ($composerPackages as $package) {
$extra = $package->getExtra();
if (array_key_exists('entrypoint', $extra)) {
$packages[] = $extra['entrypoint'];
}
}
return array_unique($packages);
}
}
-11
View File
@@ -6,7 +6,6 @@ namespace Bolt;
use Bolt\Configuration\Parser\ContentTypesParser;
use Bolt\Configuration\Parser\TaxonomyParser;
use Bolt\Extension\ExtensionCompilerPass;
use Bolt\Extension\ExtensionInterface;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\FileLocator;
@@ -44,9 +43,6 @@ class Kernel extends BaseKernel
$container
->registerForAutoconfiguration(ExtensionInterface::class)
->addTag(ExtensionInterface::CONTAINER_TAG);
// Process all our implementors through our CompilerPass
$container->addCompilerPass(new ExtensionCompilerPass());
}
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
@@ -64,13 +60,6 @@ class Kernel extends BaseKernel
$this->setBoltParameters($container, $confDir);
$this->setContentTypeRequirements($container);
$this->setTaxonomyRequirements($container);
try {
$loader->load($confDir . '/{services}_bolt' . self::CONFIG_EXTS, 'glob');
} catch (\Throwable $e) {
// Ignore errors. The file will be updated on next `cache:clear` or whenever
// the container gets refreshed
}
}
protected function configureRoutes(RouteCollectionBuilder $routes): void