mirror of
https://github.com/jbcr/core.git
synced 2026-04-28 03:03:10 +02:00
Merge pull request #1634 from bolt/feature/extension-services-and-routes
Copy extension services and routes into Bolt
This commit is contained in:
@@ -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"
|
||||
],
|
||||
|
||||
@@ -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}'
|
||||
@@ -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}'
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user