first commit
This commit is contained in:
132
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AboutCommand.php
vendored
Normal file
132
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AboutCommand.php
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
use Symfony\Component\Console\Helper\TableSeparator;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* A console command to display information about the current installation.
|
||||
*
|
||||
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'about', description: 'Display information about the current project')]
|
||||
class AboutCommand extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command displays information about the current Symfony project.
|
||||
|
||||
The <info>PHP</info> section displays important configuration that could affect your application. The values might
|
||||
be different between web and CLI.
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
if (method_exists($kernel, 'getBuildDir')) {
|
||||
$buildDir = $kernel->getBuildDir();
|
||||
} else {
|
||||
$buildDir = $kernel->getCacheDir();
|
||||
}
|
||||
|
||||
$rows = [
|
||||
['<info>Symfony</>'],
|
||||
new TableSeparator(),
|
||||
['Version', Kernel::VERSION],
|
||||
['Long-Term Support', 4 === Kernel::MINOR_VERSION ? 'Yes' : 'No'],
|
||||
['End of maintenance', Kernel::END_OF_MAINTENANCE.(self::isExpired(Kernel::END_OF_MAINTENANCE) ? ' <error>Expired</>' : ' (<comment>'.self::daysBeforeExpiration(Kernel::END_OF_MAINTENANCE).'</>)')],
|
||||
['End of life', Kernel::END_OF_LIFE.(self::isExpired(Kernel::END_OF_LIFE) ? ' <error>Expired</>' : ' (<comment>'.self::daysBeforeExpiration(Kernel::END_OF_LIFE).'</>)')],
|
||||
new TableSeparator(),
|
||||
['<info>Kernel</>'],
|
||||
new TableSeparator(),
|
||||
['Type', $kernel::class],
|
||||
['Environment', $kernel->getEnvironment()],
|
||||
['Debug', $kernel->isDebug() ? 'true' : 'false'],
|
||||
['Charset', $kernel->getCharset()],
|
||||
['Cache directory', self::formatPath($kernel->getCacheDir(), $kernel->getProjectDir()).' (<comment>'.self::formatFileSize($kernel->getCacheDir()).'</>)'],
|
||||
['Build directory', self::formatPath($buildDir, $kernel->getProjectDir()).' (<comment>'.self::formatFileSize($buildDir).'</>)'],
|
||||
['Log directory', self::formatPath($kernel->getLogDir(), $kernel->getProjectDir()).' (<comment>'.self::formatFileSize($kernel->getLogDir()).'</>)'],
|
||||
new TableSeparator(),
|
||||
['<info>PHP</>'],
|
||||
new TableSeparator(),
|
||||
['Version', \PHP_VERSION],
|
||||
['Architecture', (\PHP_INT_SIZE * 8).' bits'],
|
||||
['Intl locale', class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a'],
|
||||
['Timezone', date_default_timezone_get().' (<comment>'.(new \DateTimeImmutable())->format(\DateTimeInterface::W3C).'</>)'],
|
||||
['OPcache', \extension_loaded('Zend OPcache') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'],
|
||||
['APCu', \extension_loaded('apcu') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'],
|
||||
['Xdebug', \extension_loaded('xdebug') ? 'true' : 'false'],
|
||||
];
|
||||
|
||||
$io->table([], $rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static function formatPath(string $path, string $baseDir): string
|
||||
{
|
||||
return preg_replace('~^'.preg_quote($baseDir, '~').'~', '.', $path);
|
||||
}
|
||||
|
||||
private static function formatFileSize(string $path): string
|
||||
{
|
||||
if (is_file($path)) {
|
||||
$size = filesize($path) ?: 0;
|
||||
} else {
|
||||
if (!is_dir($path)) {
|
||||
return 'n/a';
|
||||
}
|
||||
|
||||
$size = 0;
|
||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) {
|
||||
if ($file->isReadable()) {
|
||||
$size += $file->getSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Helper::formatMemory($size);
|
||||
}
|
||||
|
||||
private static function isExpired(string $date): bool
|
||||
{
|
||||
$date = \DateTimeImmutable::createFromFormat('d/m/Y', '01/'.$date);
|
||||
|
||||
return false !== $date && new \DateTimeImmutable() > $date->modify('last day of this month 23:59:59');
|
||||
}
|
||||
|
||||
private static function daysBeforeExpiration(string $date): string
|
||||
{
|
||||
$date = \DateTimeImmutable::createFromFormat('d/m/Y', '01/'.$date);
|
||||
|
||||
return (new \DateTimeImmutable())->diff($date->modify('last day of this month 23:59:59'))->format('in %R%a days');
|
||||
}
|
||||
}
|
195
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AbstractConfigCommand.php
vendored
Normal file
195
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AbstractConfigCommand.php
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\Console\Exception\LogicException;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\StyleInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
|
||||
/**
|
||||
* A console command for dumping available configuration reference.
|
||||
*
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
* @author Wouter J <waldio.webdesign@gmail.com>
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
abstract class AbstractConfigCommand extends ContainerDebugCommand
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function listBundles(OutputInterface|StyleInterface $output)
|
||||
{
|
||||
$title = 'Available registered bundles with their extension alias if available';
|
||||
$headers = ['Bundle name', 'Extension alias'];
|
||||
$rows = [];
|
||||
|
||||
$bundles = $this->getApplication()->getKernel()->getBundles();
|
||||
usort($bundles, fn ($bundleA, $bundleB) => strcmp($bundleA->getName(), $bundleB->getName()));
|
||||
|
||||
foreach ($bundles as $bundle) {
|
||||
$extension = $bundle->getContainerExtension();
|
||||
$rows[] = [$bundle->getName(), $extension ? $extension->getAlias() : ''];
|
||||
}
|
||||
|
||||
if ($output instanceof StyleInterface) {
|
||||
$output->title($title);
|
||||
$output->table($headers, $rows);
|
||||
} else {
|
||||
$output->writeln($title);
|
||||
$table = new Table($output);
|
||||
$table->setHeaders($headers)->setRows($rows)->render();
|
||||
}
|
||||
}
|
||||
|
||||
protected function listNonBundleExtensions(OutputInterface|StyleInterface $output): void
|
||||
{
|
||||
$title = 'Available registered non-bundle extension aliases';
|
||||
$headers = ['Extension alias'];
|
||||
$rows = [];
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
$bundleExtensions = [];
|
||||
foreach ($kernel->getBundles() as $bundle) {
|
||||
if ($extension = $bundle->getContainerExtension()) {
|
||||
$bundleExtensions[$extension::class] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$extensions = $this->getContainerBuilder($kernel)->getExtensions();
|
||||
|
||||
foreach ($extensions as $alias => $extension) {
|
||||
if (isset($bundleExtensions[$extension::class])) {
|
||||
continue;
|
||||
}
|
||||
$rows[] = [$alias];
|
||||
}
|
||||
|
||||
if (!$rows) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($output instanceof StyleInterface) {
|
||||
$output->title($title);
|
||||
$output->table($headers, $rows);
|
||||
} else {
|
||||
$output->writeln($title);
|
||||
$table = new Table($output);
|
||||
$table->setHeaders($headers)->setRows($rows)->render();
|
||||
}
|
||||
}
|
||||
|
||||
protected function findExtension(string $name): ExtensionInterface
|
||||
{
|
||||
$bundles = $this->initializeBundles();
|
||||
$minScore = \INF;
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
if ($kernel instanceof ExtensionInterface && ($kernel instanceof ConfigurationInterface || $kernel instanceof ConfigurationExtensionInterface)) {
|
||||
if ($name === $kernel->getAlias()) {
|
||||
return $kernel;
|
||||
}
|
||||
|
||||
if ($kernel->getAlias()) {
|
||||
$distance = levenshtein($name, $kernel->getAlias());
|
||||
|
||||
if ($distance < $minScore) {
|
||||
$guess = $kernel->getAlias();
|
||||
$minScore = $distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bundles as $bundle) {
|
||||
if ($name === $bundle->getName()) {
|
||||
if (!$bundle->getContainerExtension()) {
|
||||
throw new \LogicException(sprintf('Bundle "%s" does not have a container extension.', $name));
|
||||
}
|
||||
|
||||
return $bundle->getContainerExtension();
|
||||
}
|
||||
|
||||
$distance = levenshtein($name, $bundle->getName());
|
||||
|
||||
if ($distance < $minScore) {
|
||||
$guess = $bundle->getName();
|
||||
$minScore = $distance;
|
||||
}
|
||||
}
|
||||
|
||||
$container = $this->getContainerBuilder($kernel);
|
||||
|
||||
if ($container->hasExtension($name)) {
|
||||
return $container->getExtension($name);
|
||||
}
|
||||
|
||||
foreach ($container->getExtensions() as $extension) {
|
||||
$distance = levenshtein($name, $extension->getAlias());
|
||||
|
||||
if ($distance < $minScore) {
|
||||
$guess = $extension->getAlias();
|
||||
$minScore = $distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (!str_ends_with($name, 'Bundle')) {
|
||||
$message = sprintf('No extensions with configuration available for "%s".', $name);
|
||||
} else {
|
||||
$message = sprintf('No extension with alias "%s" is enabled.', $name);
|
||||
}
|
||||
|
||||
if (isset($guess) && $minScore < 3) {
|
||||
$message .= sprintf("\n\nDid you mean \"%s\"?", $guess);
|
||||
}
|
||||
|
||||
throw new LogicException($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function validateConfiguration(ExtensionInterface $extension, mixed $configuration)
|
||||
{
|
||||
if (!$configuration) {
|
||||
throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup.', $extension->getAlias()));
|
||||
}
|
||||
|
||||
if (!$configuration instanceof ConfigurationInterface) {
|
||||
throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable.', get_debug_type($configuration)));
|
||||
}
|
||||
}
|
||||
|
||||
private function initializeBundles(): array
|
||||
{
|
||||
// Re-build bundle manually to initialize DI extensions that can be extended by other bundles in their build() method
|
||||
// as this method is not called when the container is loaded from the cache.
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$container = $this->getContainerBuilder($kernel);
|
||||
$bundles = $kernel->getBundles();
|
||||
foreach ($bundles as $bundle) {
|
||||
if ($extension = $bundle->getContainerExtension()) {
|
||||
$container->registerExtension($extension);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($bundles as $bundle) {
|
||||
$bundle->build($container);
|
||||
}
|
||||
|
||||
return $bundles;
|
||||
}
|
||||
}
|
271
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php
vendored
Normal file
271
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* Command that places bundle web assets into a given directory.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Gábor Egyed <gabor.egyed@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'assets:install', description: 'Install bundle\'s web assets under a public directory')]
|
||||
class AssetsInstallCommand extends Command
|
||||
{
|
||||
public const METHOD_COPY = 'copy';
|
||||
public const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink';
|
||||
public const METHOD_RELATIVE_SYMLINK = 'relative symlink';
|
||||
|
||||
private Filesystem $filesystem;
|
||||
private string $projectDir;
|
||||
|
||||
public function __construct(Filesystem $filesystem, string $projectDir)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->filesystem = $filesystem;
|
||||
$this->projectDir = $projectDir;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', null),
|
||||
])
|
||||
->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlink the assets instead of copying them')
|
||||
->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks')
|
||||
->addOption('no-cleanup', null, InputOption::VALUE_NONE, 'Do not remove the assets of the bundles that no longer exist')
|
||||
->setHelp(<<<'EOT'
|
||||
The <info>%command.name%</info> command installs bundle assets into a given
|
||||
directory (e.g. the <comment>public</comment> directory).
|
||||
|
||||
<info>php %command.full_name% public</info>
|
||||
|
||||
A "bundles" directory will be created inside the target directory and the
|
||||
"Resources/public" directory of each bundle will be copied into it.
|
||||
|
||||
To create a symlink to each bundle instead of copying its assets, use the
|
||||
<info>--symlink</info> option (will fall back to hard copies when symbolic links aren't possible:
|
||||
|
||||
<info>php %command.full_name% public --symlink</info>
|
||||
|
||||
To make symlink relative, add the <info>--relative</info> option:
|
||||
|
||||
<info>php %command.full_name% public --symlink --relative</info>
|
||||
|
||||
EOT
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$targetArg = rtrim($input->getArgument('target') ?? '', '/');
|
||||
if (!$targetArg) {
|
||||
$targetArg = $this->getPublicDirectory($kernel->getContainer());
|
||||
}
|
||||
|
||||
if (!is_dir($targetArg)) {
|
||||
$targetArg = $kernel->getProjectDir().'/'.$targetArg;
|
||||
|
||||
if (!is_dir($targetArg)) {
|
||||
throw new InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $targetArg));
|
||||
}
|
||||
}
|
||||
|
||||
$bundlesDir = $targetArg.'/bundles/';
|
||||
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->newLine();
|
||||
|
||||
if ($input->getOption('relative')) {
|
||||
$expectedMethod = self::METHOD_RELATIVE_SYMLINK;
|
||||
$io->text('Trying to install assets as <info>relative symbolic links</info>.');
|
||||
} elseif ($input->getOption('symlink')) {
|
||||
$expectedMethod = self::METHOD_ABSOLUTE_SYMLINK;
|
||||
$io->text('Trying to install assets as <info>absolute symbolic links</info>.');
|
||||
} else {
|
||||
$expectedMethod = self::METHOD_COPY;
|
||||
$io->text('Installing assets as <info>hard copies</info>.');
|
||||
}
|
||||
|
||||
$io->newLine();
|
||||
|
||||
$rows = [];
|
||||
$copyUsed = false;
|
||||
$exitCode = 0;
|
||||
$validAssetDirs = [];
|
||||
/** @var BundleInterface $bundle */
|
||||
foreach ($kernel->getBundles() as $bundle) {
|
||||
if (!is_dir($originDir = $bundle->getPath().'/Resources/public') && !is_dir($originDir = $bundle->getPath().'/public')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$assetDir = preg_replace('/bundle$/', '', strtolower($bundle->getName()));
|
||||
$targetDir = $bundlesDir.$assetDir;
|
||||
$validAssetDirs[] = $assetDir;
|
||||
|
||||
if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) {
|
||||
$message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir);
|
||||
} else {
|
||||
$message = $bundle->getName();
|
||||
}
|
||||
|
||||
try {
|
||||
$this->filesystem->remove($targetDir);
|
||||
|
||||
if (self::METHOD_RELATIVE_SYMLINK === $expectedMethod) {
|
||||
$method = $this->relativeSymlinkWithFallback($originDir, $targetDir);
|
||||
} elseif (self::METHOD_ABSOLUTE_SYMLINK === $expectedMethod) {
|
||||
$method = $this->absoluteSymlinkWithFallback($originDir, $targetDir);
|
||||
} else {
|
||||
$method = $this->hardCopy($originDir, $targetDir);
|
||||
}
|
||||
|
||||
if (self::METHOD_COPY === $method) {
|
||||
$copyUsed = true;
|
||||
}
|
||||
|
||||
if ($method === $expectedMethod) {
|
||||
$rows[] = [sprintf('<fg=green;options=bold>%s</>', '\\' === \DIRECTORY_SEPARATOR ? 'OK' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */), $message, $method];
|
||||
} else {
|
||||
$rows[] = [sprintf('<fg=yellow;options=bold>%s</>', '\\' === \DIRECTORY_SEPARATOR ? 'WARNING' : '!'), $message, $method];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$exitCode = 1;
|
||||
$rows[] = [sprintf('<fg=red;options=bold>%s</>', '\\' === \DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage()];
|
||||
}
|
||||
}
|
||||
// remove the assets of the bundles that no longer exist
|
||||
if (!$input->getOption('no-cleanup') && is_dir($bundlesDir)) {
|
||||
$dirsToRemove = Finder::create()->depth(0)->directories()->exclude($validAssetDirs)->in($bundlesDir);
|
||||
$this->filesystem->remove($dirsToRemove);
|
||||
}
|
||||
|
||||
if ($rows) {
|
||||
$io->table(['', 'Bundle', 'Method / Error'], $rows);
|
||||
}
|
||||
|
||||
if (0 !== $exitCode) {
|
||||
$io->error('Some errors occurred while installing assets.');
|
||||
} else {
|
||||
if ($copyUsed) {
|
||||
$io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.');
|
||||
}
|
||||
$io->success($rows ? 'All assets were successfully installed.' : 'No assets were provided by any bundle.');
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to create relative symlink.
|
||||
*
|
||||
* Falling back to absolute symlink and finally hard copy.
|
||||
*/
|
||||
private function relativeSymlinkWithFallback(string $originDir, string $targetDir): string
|
||||
{
|
||||
try {
|
||||
$this->symlink($originDir, $targetDir, true);
|
||||
$method = self::METHOD_RELATIVE_SYMLINK;
|
||||
} catch (IOException) {
|
||||
$method = $this->absoluteSymlinkWithFallback($originDir, $targetDir);
|
||||
}
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to create absolute symlink.
|
||||
*
|
||||
* Falling back to hard copy.
|
||||
*/
|
||||
private function absoluteSymlinkWithFallback(string $originDir, string $targetDir): string
|
||||
{
|
||||
try {
|
||||
$this->symlink($originDir, $targetDir);
|
||||
$method = self::METHOD_ABSOLUTE_SYMLINK;
|
||||
} catch (IOException) {
|
||||
// fall back to copy
|
||||
$method = $this->hardCopy($originDir, $targetDir);
|
||||
}
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates symbolic link.
|
||||
*
|
||||
* @throws IOException if link cannot be created
|
||||
*/
|
||||
private function symlink(string $originDir, string $targetDir, bool $relative = false): void
|
||||
{
|
||||
if ($relative) {
|
||||
$this->filesystem->mkdir(\dirname($targetDir));
|
||||
$originDir = $this->filesystem->makePathRelative($originDir, realpath(\dirname($targetDir)));
|
||||
}
|
||||
$this->filesystem->symlink($originDir, $targetDir);
|
||||
if (!file_exists($targetDir)) {
|
||||
throw new IOException(sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies origin to target.
|
||||
*/
|
||||
private function hardCopy(string $originDir, string $targetDir): string
|
||||
{
|
||||
$this->filesystem->mkdir($targetDir, 0777);
|
||||
// We use a custom iterator to ignore VCS files
|
||||
$this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir));
|
||||
|
||||
return self::METHOD_COPY;
|
||||
}
|
||||
|
||||
private function getPublicDirectory(ContainerInterface $container): string
|
||||
{
|
||||
$defaultPublicDir = 'public';
|
||||
|
||||
if (null === $this->projectDir && !$container->hasParameter('kernel.project_dir')) {
|
||||
return $defaultPublicDir;
|
||||
}
|
||||
|
||||
$composerFilePath = ($this->projectDir ?? $container->getParameter('kernel.project_dir')).'/composer.json';
|
||||
|
||||
if (!file_exists($composerFilePath)) {
|
||||
return $defaultPublicDir;
|
||||
}
|
||||
|
||||
$composerConfig = json_decode(file_get_contents($composerFilePath), true);
|
||||
|
||||
return $composerConfig['extra']['public-dir'] ?? $defaultPublicDir;
|
||||
}
|
||||
}
|
71
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/BuildDebugContainerTrait.php
vendored
Normal file
71
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/BuildDebugContainerTrait.php
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Config\ConfigCache;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait BuildDebugContainerTrait
|
||||
{
|
||||
protected ContainerBuilder $container;
|
||||
|
||||
/**
|
||||
* Loads the ContainerBuilder from the cache.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilder
|
||||
{
|
||||
if (isset($this->container)) {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
if (!$kernel->isDebug() || !$kernel->getContainer()->getParameter('debug.container.dump') || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) {
|
||||
$buildContainer = \Closure::bind(function () {
|
||||
$this->initializeBundles();
|
||||
|
||||
return $this->buildContainer();
|
||||
}, $kernel, $kernel::class);
|
||||
$container = $buildContainer();
|
||||
$container->getCompilerPassConfig()->setRemovingPasses([]);
|
||||
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
|
||||
$container->compile();
|
||||
} else {
|
||||
$buildContainer = \Closure::bind(function () {
|
||||
$containerBuilder = $this->getContainerBuilder();
|
||||
$this->prepareContainer($containerBuilder);
|
||||
|
||||
return $containerBuilder;
|
||||
}, $kernel, $kernel::class);
|
||||
$container = $buildContainer();
|
||||
(new XmlFileLoader($container, new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump'));
|
||||
$locatorPass = new ServiceLocatorTagPass();
|
||||
$locatorPass->process($container);
|
||||
|
||||
$container->getCompilerPassConfig()->setBeforeOptimizationPasses([]);
|
||||
$container->getCompilerPassConfig()->setOptimizationPasses([]);
|
||||
$container->getCompilerPassConfig()->setBeforeRemovingPasses([]);
|
||||
}
|
||||
|
||||
return $this->container = $container;
|
||||
}
|
||||
}
|
254
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CacheClearCommand.php
vendored
Normal file
254
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CacheClearCommand.php
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Dumper\Preloader;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
|
||||
use Symfony\Component\HttpKernel\RebootableInterface;
|
||||
|
||||
/**
|
||||
* Clear and Warmup the cache.
|
||||
*
|
||||
* @author Francis Besset <francis.besset@gmail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'cache:clear', description: 'Clear the cache')]
|
||||
class CacheClearCommand extends Command
|
||||
{
|
||||
private CacheClearerInterface $cacheClearer;
|
||||
private Filesystem $filesystem;
|
||||
|
||||
public function __construct(CacheClearerInterface $cacheClearer, ?Filesystem $filesystem = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->cacheClearer = $cacheClearer;
|
||||
$this->filesystem = $filesystem ?? new Filesystem();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputOption('no-warmup', '', InputOption::VALUE_NONE, 'Do not warm up the cache'),
|
||||
new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command clears and warms up the application cache for a given environment
|
||||
and debug mode:
|
||||
|
||||
<info>php %command.full_name% --env=dev</info>
|
||||
<info>php %command.full_name% --env=prod --no-debug</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$fs = $this->filesystem;
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$realCacheDir = $kernel->getContainer()->getParameter('kernel.cache_dir');
|
||||
$realBuildDir = $kernel->getContainer()->hasParameter('kernel.build_dir') ? $kernel->getContainer()->getParameter('kernel.build_dir') : $realCacheDir;
|
||||
// the old cache dir name must not be longer than the real one to avoid exceeding
|
||||
// the maximum length of a directory or file path within it (esp. Windows MAX_PATH)
|
||||
$oldCacheDir = substr($realCacheDir, 0, -1).(str_ends_with($realCacheDir, '~') ? '+' : '~');
|
||||
$fs->remove($oldCacheDir);
|
||||
|
||||
if (!is_writable($realCacheDir)) {
|
||||
throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realCacheDir));
|
||||
}
|
||||
|
||||
$useBuildDir = $realBuildDir !== $realCacheDir;
|
||||
$oldBuildDir = substr($realBuildDir, 0, -1).(str_ends_with($realBuildDir, '~') ? '+' : '~');
|
||||
if ($useBuildDir) {
|
||||
$fs->remove($oldBuildDir);
|
||||
|
||||
if (!is_writable($realBuildDir)) {
|
||||
throw new RuntimeException(sprintf('Unable to write in the "%s" directory.', $realBuildDir));
|
||||
}
|
||||
|
||||
if ($this->isNfs($realCacheDir)) {
|
||||
$fs->remove($realCacheDir);
|
||||
} else {
|
||||
$fs->rename($realCacheDir, $oldCacheDir);
|
||||
}
|
||||
$fs->mkdir($realCacheDir);
|
||||
}
|
||||
|
||||
$io->comment(sprintf('Clearing the cache for the <info>%s</info> environment with debug <info>%s</info>', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
if ($useBuildDir) {
|
||||
$this->cacheClearer->clear($realBuildDir);
|
||||
}
|
||||
$this->cacheClearer->clear($realCacheDir);
|
||||
|
||||
// The current event dispatcher is stale, let's not use it anymore
|
||||
$this->getApplication()->setDispatcher(new EventDispatcher());
|
||||
|
||||
$containerFile = (new \ReflectionObject($kernel->getContainer()))->getFileName();
|
||||
$containerDir = basename(\dirname($containerFile));
|
||||
|
||||
// the warmup cache dir name must have the same length as the real one
|
||||
// to avoid the many problems in serialized resources files
|
||||
$warmupDir = substr($realBuildDir, 0, -1).(str_ends_with($realBuildDir, '_') ? '-' : '_');
|
||||
|
||||
if ($output->isVerbose() && $fs->exists($warmupDir)) {
|
||||
$io->comment('Clearing outdated warmup directory...');
|
||||
}
|
||||
$fs->remove($warmupDir);
|
||||
|
||||
if ($_SERVER['REQUEST_TIME'] <= filemtime($containerFile) && filemtime($containerFile) <= time()) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Cache is fresh.');
|
||||
}
|
||||
if (!$input->getOption('no-warmup') && !$input->getOption('no-optional-warmers')) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Warming up optional cache...');
|
||||
}
|
||||
$this->warmupOptionals($realCacheDir, $realBuildDir, $io);
|
||||
}
|
||||
} else {
|
||||
$fs->mkdir($warmupDir);
|
||||
|
||||
if (!$input->getOption('no-warmup')) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Warming up cache...');
|
||||
}
|
||||
$this->warmup($warmupDir, $realBuildDir);
|
||||
|
||||
if (!$input->getOption('no-optional-warmers')) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Warming up optional cache...');
|
||||
}
|
||||
$this->warmupOptionals($useBuildDir ? $realCacheDir : $warmupDir, $warmupDir, $io);
|
||||
}
|
||||
|
||||
// fix references to cached files with the real cache directory name
|
||||
$search = [$warmupDir, str_replace('/', '\\/', $warmupDir), str_replace('\\', '\\\\', $warmupDir)];
|
||||
$replace = str_replace('\\', '/', $realBuildDir);
|
||||
foreach (Finder::create()->files()->in($warmupDir) as $file) {
|
||||
$content = str_replace($search, $replace, file_get_contents($file), $count);
|
||||
if ($count) {
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fs->exists($warmupDir.'/'.$containerDir)) {
|
||||
$fs->rename($realBuildDir.'/'.$containerDir, $warmupDir.'/'.$containerDir);
|
||||
touch($warmupDir.'/'.$containerDir.'.legacy');
|
||||
}
|
||||
|
||||
if ($this->isNfs($realBuildDir)) {
|
||||
$io->note('For better performance, you should move the cache and log directories to a non-shared folder of the VM.');
|
||||
$fs->remove($realBuildDir);
|
||||
} else {
|
||||
$fs->rename($realBuildDir, $oldBuildDir);
|
||||
}
|
||||
|
||||
$fs->rename($warmupDir, $realBuildDir);
|
||||
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Removing old build and cache directory...');
|
||||
}
|
||||
|
||||
if ($useBuildDir) {
|
||||
try {
|
||||
$fs->remove($oldBuildDir);
|
||||
} catch (IOException $e) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->warning($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$fs->remove($oldCacheDir);
|
||||
} catch (IOException $e) {
|
||||
if ($output->isVerbose()) {
|
||||
$io->warning($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($output->isVerbose()) {
|
||||
$io->comment('Finished');
|
||||
}
|
||||
|
||||
$io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function isNfs(string $dir): bool
|
||||
{
|
||||
static $mounts = null;
|
||||
|
||||
if (null === $mounts) {
|
||||
$mounts = [];
|
||||
if ('/' === \DIRECTORY_SEPARATOR && @is_readable('/proc/mounts') && $files = @file('/proc/mounts')) {
|
||||
foreach ($files as $mount) {
|
||||
$mount = \array_slice(explode(' ', $mount), 1, -3);
|
||||
if (!\in_array(array_pop($mount), ['vboxsf', 'nfs'])) {
|
||||
continue;
|
||||
}
|
||||
$mounts[] = implode(' ', $mount).'/';
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($mounts as $mount) {
|
||||
if (str_starts_with($dir, $mount)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function warmup(string $warmupDir, string $realBuildDir): void
|
||||
{
|
||||
// create a temporary kernel
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
if (!$kernel instanceof RebootableInterface) {
|
||||
throw new \LogicException('Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is not supported.');
|
||||
}
|
||||
$kernel->reboot($warmupDir);
|
||||
}
|
||||
|
||||
private function warmupOptionals(string $cacheDir, string $warmupDir, SymfonyStyle $io): void
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$warmer = $kernel->getContainer()->get('cache_warmer');
|
||||
// non optional warmers already ran during container compilation
|
||||
$warmer->enableOnlyOptionalWarmers();
|
||||
$preload = (array) $warmer->warmUp($cacheDir, $warmupDir, $io);
|
||||
|
||||
if ($preload && file_exists($preloadFile = $warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) {
|
||||
Preloader::append($preloadFile, $preload);
|
||||
}
|
||||
}
|
||||
}
|
144
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php
vendored
Normal file
144
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer;
|
||||
|
||||
/**
|
||||
* Clear cache pools.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
#[AsCommand(name: 'cache:pool:clear', description: 'Clear cache pools')]
|
||||
final class CachePoolClearCommand extends Command
|
||||
{
|
||||
private Psr6CacheClearer $poolClearer;
|
||||
private ?array $poolNames;
|
||||
|
||||
/**
|
||||
* @param string[]|null $poolNames
|
||||
*/
|
||||
public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->poolClearer = $poolClearer;
|
||||
$this->poolNames = $poolNames;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'A list of cache pools or cache pool clearers'),
|
||||
])
|
||||
->addOption('all', null, InputOption::VALUE_NONE, 'Clear all cache pools')
|
||||
->addOption('exclude', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'A list of cache pools or cache pool clearers to exclude')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command clears the given cache pools or cache pool clearers.
|
||||
|
||||
%command.full_name% <cache pool or clearer 1> [...<cache pool or clearer N>]
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$pools = [];
|
||||
$clearers = [];
|
||||
|
||||
$poolNames = $input->getArgument('pools');
|
||||
$excludedPoolNames = $input->getOption('exclude');
|
||||
if ($clearAll = $input->getOption('all')) {
|
||||
if (!$this->poolNames) {
|
||||
throw new InvalidArgumentException('Could not clear all cache pools, try specifying a specific pool or cache clearer.');
|
||||
}
|
||||
|
||||
if (!$excludedPoolNames) {
|
||||
$io->comment('Clearing all cache pools...');
|
||||
}
|
||||
|
||||
$poolNames = $this->poolNames;
|
||||
} elseif (!$poolNames) {
|
||||
throw new InvalidArgumentException('Either specify at least one pool name, or provide the --all option to clear all pools.');
|
||||
}
|
||||
|
||||
$poolNames = array_diff($poolNames, $excludedPoolNames);
|
||||
|
||||
foreach ($poolNames as $id) {
|
||||
if ($this->poolClearer->hasPool($id)) {
|
||||
$pools[$id] = $id;
|
||||
} elseif (!$clearAll || $kernel->getContainer()->has($id)) {
|
||||
$pool = $kernel->getContainer()->get($id);
|
||||
|
||||
if ($pool instanceof CacheItemPoolInterface) {
|
||||
$pools[$id] = $pool;
|
||||
} elseif ($pool instanceof Psr6CacheClearer) {
|
||||
$clearers[$id] = $pool;
|
||||
} else {
|
||||
throw new InvalidArgumentException(sprintf('"%s" is not a cache pool nor a cache clearer.', $id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($clearers as $id => $clearer) {
|
||||
$io->comment(sprintf('Calling cache clearer: <info>%s</info>', $id));
|
||||
$clearer->clear($kernel->getContainer()->getParameter('kernel.cache_dir'));
|
||||
}
|
||||
|
||||
$failure = false;
|
||||
foreach ($pools as $id => $pool) {
|
||||
$io->comment(sprintf('Clearing cache pool: <info>%s</info>', $id));
|
||||
|
||||
if ($pool instanceof CacheItemPoolInterface) {
|
||||
if (!$pool->clear()) {
|
||||
$io->warning(sprintf('Cache pool "%s" could not be cleared.', $pool));
|
||||
$failure = true;
|
||||
}
|
||||
} else {
|
||||
if (false === $this->poolClearer->clearPool($id)) {
|
||||
$io->warning(sprintf('Cache pool "%s" could not be cleared.', $pool));
|
||||
$failure = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($failure) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->success('Cache was successfully cleared.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if (\is_array($this->poolNames) && $input->mustSuggestArgumentValuesFor('pools')) {
|
||||
$suggestions->suggestValues($this->poolNames);
|
||||
}
|
||||
}
|
||||
}
|
90
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolDeleteCommand.php
vendored
Normal file
90
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolDeleteCommand.php
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer;
|
||||
|
||||
/**
|
||||
* Delete an item from a cache pool.
|
||||
*
|
||||
* @author Pierre du Plessis <pdples@gmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'cache:pool:delete', description: 'Delete an item from a cache pool')]
|
||||
final class CachePoolDeleteCommand extends Command
|
||||
{
|
||||
private Psr6CacheClearer $poolClearer;
|
||||
private ?array $poolNames;
|
||||
|
||||
/**
|
||||
* @param string[]|null $poolNames
|
||||
*/
|
||||
public function __construct(Psr6CacheClearer $poolClearer, ?array $poolNames = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->poolClearer = $poolClearer;
|
||||
$this->poolNames = $poolNames;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('pool', InputArgument::REQUIRED, 'The cache pool from which to delete an item'),
|
||||
new InputArgument('key', InputArgument::REQUIRED, 'The cache key to delete from the pool'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> deletes an item from a given cache pool.
|
||||
|
||||
%command.full_name% <pool> <key>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$pool = $input->getArgument('pool');
|
||||
$key = $input->getArgument('key');
|
||||
$cachePool = $this->poolClearer->getPool($pool);
|
||||
|
||||
if (!$cachePool->hasItem($key)) {
|
||||
$io->note(sprintf('Cache item "%s" does not exist in cache pool "%s".', $key, $pool));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$cachePool->deleteItem($key)) {
|
||||
throw new \Exception(sprintf('Cache item "%s" could not be deleted.', $key));
|
||||
}
|
||||
|
||||
$io->success(sprintf('Cache item "%s" was successfully deleted.', $key));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if (\is_array($this->poolNames) && $input->mustSuggestArgumentValuesFor('pool')) {
|
||||
$suggestions->suggestValues($this->poolNames);
|
||||
}
|
||||
}
|
||||
}
|
109
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolInvalidateTagsCommand.php
vendored
Normal file
109
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolInvalidateTagsCommand.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
use Symfony\Contracts\Service\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'cache:pool:invalidate-tags', description: 'Invalidate cache tags for all or a specific pool')]
|
||||
final class CachePoolInvalidateTagsCommand extends Command
|
||||
{
|
||||
private ServiceProviderInterface $pools;
|
||||
private array $poolNames;
|
||||
|
||||
public function __construct(ServiceProviderInterface $pools)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->pools = $pools;
|
||||
$this->poolNames = array_keys($pools->getProvidedServices());
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('tags', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'The tags to invalidate')
|
||||
->addOption('pool', 'p', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The pools to invalidate on')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command invalidates tags from taggable pools. By default, all pools
|
||||
have the passed tags invalidated. Pass <info>--pool=my_pool</info> to invalidate tags on a specific pool.
|
||||
|
||||
php %command.full_name% tag1 tag2
|
||||
php %command.full_name% tag1 tag2 --pool=cache2 --pool=cache1
|
||||
EOF)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$pools = $input->getOption('pool') ?: $this->poolNames;
|
||||
$tags = $input->getArgument('tags');
|
||||
$tagList = implode(', ', $tags);
|
||||
$errors = false;
|
||||
|
||||
foreach ($pools as $name) {
|
||||
$io->comment(sprintf('Invalidating tag(s): <info>%s</info> from pool <comment>%s</comment>.', $tagList, $name));
|
||||
|
||||
try {
|
||||
$pool = $this->pools->get($name);
|
||||
} catch (ServiceNotFoundException) {
|
||||
$io->error(sprintf('Pool "%s" not found.', $name));
|
||||
$errors = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$pool instanceof TagAwareCacheInterface) {
|
||||
$io->error(sprintf('Pool "%s" is not taggable.', $name));
|
||||
$errors = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$pool->invalidateTags($tags)) {
|
||||
$io->error(sprintf('Cache tag(s) "%s" could not be invalidated for pool "%s".', $tagList, $name));
|
||||
$errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
$io->error('Done but with errors.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->success('Successfully invalidated cache tags.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestOptionValuesFor('pool')) {
|
||||
$suggestions->suggestValues($this->poolNames);
|
||||
}
|
||||
}
|
||||
}
|
58
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolListCommand.php
vendored
Normal file
58
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolListCommand.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* List available cache pools.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
#[AsCommand(name: 'cache:pool:list', description: 'List available cache pools')]
|
||||
final class CachePoolListCommand extends Command
|
||||
{
|
||||
private array $poolNames;
|
||||
|
||||
/**
|
||||
* @param string[] $poolNames
|
||||
*/
|
||||
public function __construct(array $poolNames)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->poolNames = $poolNames;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command lists all available cache pools.
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->table(['Pool name'], array_map(fn ($pool) => [$pool], $this->poolNames));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
66
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.php
vendored
Normal file
66
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Cache pool pruner command.
|
||||
*
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*/
|
||||
#[AsCommand(name: 'cache:pool:prune', description: 'Prune cache pools')]
|
||||
final class CachePoolPruneCommand extends Command
|
||||
{
|
||||
private iterable $pools;
|
||||
|
||||
/**
|
||||
* @param iterable<mixed, PruneableInterface> $pools
|
||||
*/
|
||||
public function __construct(iterable $pools)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->pools = $pools;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command deletes all expired items from all pruneable pools.
|
||||
|
||||
%command.full_name%
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
foreach ($this->pools as $name => $pool) {
|
||||
$io->comment(sprintf('Pruning cache pool: <info>%s</info>', $name));
|
||||
$pool->prune();
|
||||
}
|
||||
|
||||
$io->success('Successfully pruned cache pool(s).');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
86
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php
vendored
Normal file
86
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
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\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Dumper\Preloader;
|
||||
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate;
|
||||
use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface;
|
||||
|
||||
/**
|
||||
* Warmup the cache.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'cache:warmup', description: 'Warm up an empty cache')]
|
||||
class CacheWarmupCommand extends Command
|
||||
{
|
||||
private CacheWarmerAggregate $cacheWarmer;
|
||||
|
||||
public function __construct(CacheWarmerAggregate $cacheWarmer)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->cacheWarmer = $cacheWarmer;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command warms up the cache.
|
||||
|
||||
Before running this command, the cache must be empty.
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$io->comment(sprintf('Warming up the cache for the <info>%s</info> environment with debug <info>%s</info>', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
|
||||
if (!$input->getOption('no-optional-warmers')) {
|
||||
$this->cacheWarmer->enableOptionalWarmers();
|
||||
}
|
||||
$cacheDir = $kernel->getContainer()->getParameter('kernel.cache_dir');
|
||||
|
||||
if ($kernel instanceof WarmableInterface) {
|
||||
$kernel->warmUp($cacheDir);
|
||||
}
|
||||
|
||||
$preload = $this->cacheWarmer->warmUp($cacheDir);
|
||||
|
||||
$buildDir = $kernel->getContainer()->getParameter('kernel.build_dir');
|
||||
if ($preload && $cacheDir === $buildDir && file_exists($preloadFile = $buildDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) {
|
||||
Preloader::append($preloadFile, $preload);
|
||||
}
|
||||
|
||||
$io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
275
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ConfigDebugCommand.php
vendored
Normal file
275
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ConfigDebugCommand.php
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\Config\Definition\Processor;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Exception\LogicException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ValidateEnvPlaceholdersPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* A console command for dumping available configuration reference.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'debug:config', description: 'Dump the current configuration for an extension')]
|
||||
class ConfigDebugCommand extends AbstractConfigCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$commentedHelpFormats = array_map(fn ($format) => sprintf('<comment>%s</comment>', $format), $this->getAvailableFormatOptions());
|
||||
$helpFormats = implode('", "', $commentedHelpFormats);
|
||||
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'),
|
||||
new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'),
|
||||
new InputOption('resolve-env', null, InputOption::VALUE_NONE, 'Display resolved environment variable values instead of placeholders'),
|
||||
new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), class_exists(Yaml::class) ? 'txt' : 'json'),
|
||||
])
|
||||
->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command dumps the current configuration for an
|
||||
extension/bundle.
|
||||
|
||||
Either the extension alias or bundle name can be used:
|
||||
|
||||
<info>php %command.full_name% framework</info>
|
||||
<info>php %command.full_name% FrameworkBundle</info>
|
||||
|
||||
The <info>--format</info> option specifies the format of the configuration,
|
||||
these are "{$helpFormats}".
|
||||
|
||||
<info>php %command.full_name% framework --format=json</info>
|
||||
|
||||
For dumping a specific option, add its path as second argument:
|
||||
|
||||
<info>php %command.full_name% framework serializer.enabled</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
if (null === $name = $input->getArgument('name')) {
|
||||
$this->listBundles($errorIo);
|
||||
$this->listNonBundleExtensions($errorIo);
|
||||
|
||||
$errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. <comment>debug:config FrameworkBundle</comment>)');
|
||||
$errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. <comment>debug:config FrameworkBundle serializer</comment> to dump the <comment>framework.serializer</comment> configuration)');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$extension = $this->findExtension($name);
|
||||
$extensionAlias = $extension->getAlias();
|
||||
$container = $this->compileContainer();
|
||||
|
||||
$config = $this->getConfig($extension, $container, $input->getOption('resolve-env'));
|
||||
|
||||
$format = $input->getOption('format');
|
||||
|
||||
if (\in_array($format, ['txt', 'yml'], true) && !class_exists(Yaml::class)) {
|
||||
$errorIo->error('Setting the "format" option to "txt" or "yaml" requires the Symfony Yaml component. Try running "composer install symfony/yaml" or use "--format=json" instead.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (null === $path = $input->getArgument('path')) {
|
||||
if ('txt' === $input->getOption('format')) {
|
||||
$io->title(
|
||||
sprintf('Current configuration for %s', $name === $extensionAlias ? sprintf('extension with alias "%s"', $extensionAlias) : sprintf('"%s"', $name))
|
||||
);
|
||||
}
|
||||
|
||||
$io->writeln($this->convertToFormat([$extensionAlias => $config], $format));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
$config = $this->getConfigForPath($config, $path, $extensionAlias);
|
||||
} catch (LogicException $e) {
|
||||
$errorIo->error($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path));
|
||||
|
||||
$io->writeln($this->convertToFormat($config, $format));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function convertToFormat(mixed $config, string $format): string
|
||||
{
|
||||
return match ($format) {
|
||||
'txt', 'yaml' => Yaml::dump($config, 10),
|
||||
'json' => json_encode($config, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE),
|
||||
default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))),
|
||||
};
|
||||
}
|
||||
|
||||
private function compileContainer(): ContainerBuilder
|
||||
{
|
||||
$kernel = clone $this->getApplication()->getKernel();
|
||||
$kernel->boot();
|
||||
|
||||
$method = new \ReflectionMethod($kernel, 'buildContainer');
|
||||
$container = $method->invoke($kernel);
|
||||
$container->getCompiler()->compile($container);
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over configuration until the last step of the given path.
|
||||
*
|
||||
* @throws LogicException If the configuration does not exist
|
||||
*/
|
||||
private function getConfigForPath(array $config, string $path, string $alias): mixed
|
||||
{
|
||||
$steps = explode('.', $path);
|
||||
|
||||
foreach ($steps as $step) {
|
||||
if (!\array_key_exists($step, $config)) {
|
||||
throw new LogicException(sprintf('Unable to find configuration for "%s.%s".', $alias, $path));
|
||||
}
|
||||
|
||||
$config = $config[$step];
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function getConfigForExtension(ExtensionInterface $extension, ContainerBuilder $container): array
|
||||
{
|
||||
$extensionAlias = $extension->getAlias();
|
||||
|
||||
$extensionConfig = [];
|
||||
foreach ($container->getCompilerPassConfig()->getPasses() as $pass) {
|
||||
if ($pass instanceof ValidateEnvPlaceholdersPass) {
|
||||
$extensionConfig = $pass->getExtensionConfig();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($extensionConfig[$extensionAlias])) {
|
||||
return $extensionConfig[$extensionAlias];
|
||||
}
|
||||
|
||||
// Fall back to default config if the extension has one
|
||||
|
||||
if (!$extension instanceof ConfigurationExtensionInterface && !$extension instanceof ConfigurationInterface) {
|
||||
throw new \LogicException(sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias));
|
||||
}
|
||||
|
||||
$configs = $container->getExtensionConfig($extensionAlias);
|
||||
$configuration = $extension instanceof ConfigurationInterface ? $extension : $extension->getConfiguration($configs, $container);
|
||||
$this->validateConfiguration($extension, $configuration);
|
||||
|
||||
return (new Processor())->processConfiguration($configuration, $configs);
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||||
$suggestions->suggestValues($this->getAvailableExtensions());
|
||||
$suggestions->suggestValues($this->getAvailableBundles());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestArgumentValuesFor('path') && null !== $name = $input->getArgument('name')) {
|
||||
try {
|
||||
$config = $this->getConfig($this->findExtension($name), $this->compileContainer());
|
||||
$paths = array_keys(self::buildPathsCompletion($config));
|
||||
$suggestions->suggestValues($paths);
|
||||
} catch (LogicException) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
}
|
||||
}
|
||||
|
||||
private function getAvailableExtensions(): array
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
$extensions = [];
|
||||
foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) {
|
||||
$extensions[] = $alias;
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
private function getAvailableBundles(): array
|
||||
{
|
||||
$availableBundles = [];
|
||||
foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
|
||||
$availableBundles[] = $bundle->getName();
|
||||
}
|
||||
|
||||
return $availableBundles;
|
||||
}
|
||||
|
||||
private function getConfig(ExtensionInterface $extension, ContainerBuilder $container, bool $resolveEnvs = false): mixed
|
||||
{
|
||||
return $container->resolveEnvPlaceholders(
|
||||
$container->getParameterBag()->resolveValue(
|
||||
$this->getConfigForExtension($extension, $container)
|
||||
), $resolveEnvs ?: null
|
||||
);
|
||||
}
|
||||
|
||||
private static function buildPathsCompletion(array $paths, string $prefix = ''): array
|
||||
{
|
||||
$completionPaths = [];
|
||||
foreach ($paths as $key => $values) {
|
||||
if (\is_array($values)) {
|
||||
$completionPaths += self::buildPathsCompletion($values, $prefix.$key.'.');
|
||||
} else {
|
||||
$completionPaths[$prefix.$key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $completionPaths;
|
||||
}
|
||||
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return ['txt', 'yaml', 'json'];
|
||||
}
|
||||
}
|
188
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php
vendored
Normal file
188
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper;
|
||||
use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* A console command for dumping available configuration reference.
|
||||
*
|
||||
* @author Kevin Bond <kevinbond@gmail.com>
|
||||
* @author Wouter J <waldio.webdesign@gmail.com>
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'config:dump-reference', description: 'Dump the default configuration for an extension')]
|
||||
class ConfigDumpReferenceCommand extends AbstractConfigCommand
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$commentedHelpFormats = array_map(fn ($format) => sprintf('<comment>%s</comment>', $format), $this->getAvailableFormatOptions());
|
||||
$helpFormats = implode('", "', $commentedHelpFormats);
|
||||
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'),
|
||||
new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'),
|
||||
new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'yaml'),
|
||||
])
|
||||
->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> command dumps the default configuration for an
|
||||
extension/bundle.
|
||||
|
||||
Either the extension alias or bundle name can be used:
|
||||
|
||||
<info>php %command.full_name% framework</info>
|
||||
<info>php %command.full_name% FrameworkBundle</info>
|
||||
|
||||
The <info>--format</info> option specifies the format of the configuration,
|
||||
these are "{$helpFormats}".
|
||||
|
||||
<info>php %command.full_name% FrameworkBundle --format=xml</info>
|
||||
|
||||
For dumping a specific option, add its path as second argument (only available for the yaml format):
|
||||
|
||||
<info>php %command.full_name% framework http_client.default_options</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
if (null === $name = $input->getArgument('name')) {
|
||||
$this->listBundles($errorIo);
|
||||
$this->listNonBundleExtensions($errorIo);
|
||||
|
||||
$errorIo->comment([
|
||||
'Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. <comment>config:dump-reference FrameworkBundle</comment>)',
|
||||
'For dumping a specific option, add its path as the second argument of this command. (e.g. <comment>config:dump-reference FrameworkBundle http_client.default_options</comment> to dump the <comment>framework.http_client.default_options</comment> configuration)',
|
||||
]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$extension = $this->findExtension($name);
|
||||
|
||||
if ($extension instanceof ConfigurationInterface) {
|
||||
$configuration = $extension;
|
||||
} else {
|
||||
$configuration = $extension->getConfiguration([], $this->getContainerBuilder($this->getApplication()->getKernel()));
|
||||
}
|
||||
|
||||
$this->validateConfiguration($extension, $configuration);
|
||||
|
||||
$format = $input->getOption('format');
|
||||
|
||||
if ('yaml' === $format && !class_exists(Yaml::class)) {
|
||||
$errorIo->error('Setting the "format" option to "yaml" requires the Symfony Yaml component. Try running "composer install symfony/yaml" or use "--format=xml" instead.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$path = $input->getArgument('path');
|
||||
|
||||
if (null !== $path && 'yaml' !== $format) {
|
||||
$errorIo->error('The "path" option is only available for the "yaml" format.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($name === $extension->getAlias()) {
|
||||
$message = sprintf('Default configuration for extension with alias: "%s"', $name);
|
||||
} else {
|
||||
$message = sprintf('Default configuration for "%s"', $name);
|
||||
}
|
||||
|
||||
if (null !== $path) {
|
||||
$message .= sprintf(' at path "%s"', $path);
|
||||
}
|
||||
|
||||
switch ($format) {
|
||||
case 'yaml':
|
||||
$io->writeln(sprintf('# %s', $message));
|
||||
$dumper = new YamlReferenceDumper();
|
||||
break;
|
||||
case 'xml':
|
||||
$io->writeln(sprintf('<!-- %s -->', $message));
|
||||
$dumper = new XmlReferenceDumper();
|
||||
break;
|
||||
default:
|
||||
$io->writeln($message);
|
||||
throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions())));
|
||||
}
|
||||
|
||||
$io->writeln(null === $path ? $dumper->dump($configuration, $extension->getNamespace()) : $dumper->dumpAtPath($configuration, $path));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||||
$suggestions->suggestValues($this->getAvailableExtensions());
|
||||
$suggestions->suggestValues($this->getAvailableBundles());
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
}
|
||||
}
|
||||
|
||||
private function getAvailableExtensions(): array
|
||||
{
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
$extensions = [];
|
||||
foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) {
|
||||
$extensions[] = $alias;
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
private function getAvailableBundles(): array
|
||||
{
|
||||
$bundles = [];
|
||||
|
||||
foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) {
|
||||
$bundles[] = $bundle->getName();
|
||||
}
|
||||
|
||||
return $bundles;
|
||||
}
|
||||
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return ['yaml', 'xml'];
|
||||
}
|
||||
}
|
369
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ContainerDebugCommand.php
vendored
Normal file
369
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ContainerDebugCommand.php
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
|
||||
/**
|
||||
* A console command for retrieving information about services.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@thatsquality.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'debug:container', description: 'Display current services for an application')]
|
||||
class ContainerDebugCommand extends Command
|
||||
{
|
||||
use BuildDebugContainerTrait;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'),
|
||||
new InputOption('show-arguments', null, InputOption::VALUE_NONE, 'Show arguments in services'),
|
||||
new InputOption('show-hidden', null, InputOption::VALUE_NONE, 'Show hidden (internal) services'),
|
||||
new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Show all services with a specific tag'),
|
||||
new InputOption('tags', null, InputOption::VALUE_NONE, 'Display tagged services for an application'),
|
||||
new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Display a specific parameter for an application'),
|
||||
new InputOption('parameters', null, InputOption::VALUE_NONE, 'Display parameters for an application'),
|
||||
new InputOption('types', null, InputOption::VALUE_NONE, 'Display types (classes/interfaces) available in the container'),
|
||||
new InputOption('env-var', null, InputOption::VALUE_REQUIRED, 'Display a specific environment variable used in the container'),
|
||||
new InputOption('env-vars', null, InputOption::VALUE_NONE, 'Display environment variables used in the container'),
|
||||
new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
|
||||
new InputOption('deprecations', null, InputOption::VALUE_NONE, 'Display deprecations generated when compiling and warming up the container'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command displays all configured <comment>public</comment> services:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
To see deprecations generated during container compilation and cache warmup, use the <info>--deprecations</info> option:
|
||||
|
||||
<info>php %command.full_name% --deprecations</info>
|
||||
|
||||
To get specific information about a service, specify its name:
|
||||
|
||||
<info>php %command.full_name% validator</info>
|
||||
|
||||
To get specific information about a service including all its arguments, use the <info>--show-arguments</info> flag:
|
||||
|
||||
<info>php %command.full_name% validator --show-arguments</info>
|
||||
|
||||
To see available types that can be used for autowiring, use the <info>--types</info> flag:
|
||||
|
||||
<info>php %command.full_name% --types</info>
|
||||
|
||||
To see environment variables used by the container, use the <info>--env-vars</info> flag:
|
||||
|
||||
<info>php %command.full_name% --env-vars</info>
|
||||
|
||||
Display a specific environment variable by specifying its name with the <info>--env-var</info> option:
|
||||
|
||||
<info>php %command.full_name% --env-var=APP_ENV</info>
|
||||
|
||||
Use the --tags option to display tagged <comment>public</comment> services grouped by tag:
|
||||
|
||||
<info>php %command.full_name% --tags</info>
|
||||
|
||||
Find all services with a specific tag by specifying the tag name with the <info>--tag</info> option:
|
||||
|
||||
<info>php %command.full_name% --tag=form.type</info>
|
||||
|
||||
Use the <info>--parameters</info> option to display all parameters:
|
||||
|
||||
<info>php %command.full_name% --parameters</info>
|
||||
|
||||
Display a specific parameter by specifying its name with the <info>--parameter</info> option:
|
||||
|
||||
<info>php %command.full_name% --parameter=kernel.debug</info>
|
||||
|
||||
By default, internal services are hidden. You can display them
|
||||
using the <info>--show-hidden</info> flag:
|
||||
|
||||
<info>php %command.full_name% --show-hidden</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
$this->validateInput($input);
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$object = $this->getContainerBuilder($kernel);
|
||||
|
||||
if ($input->getOption('env-vars')) {
|
||||
$options = ['env-vars' => true];
|
||||
} elseif ($envVar = $input->getOption('env-var')) {
|
||||
$options = ['env-vars' => true, 'name' => $envVar];
|
||||
} elseif ($input->getOption('types')) {
|
||||
$options = [];
|
||||
$options['filter'] = $this->filterToServiceTypes(...);
|
||||
} elseif ($input->getOption('parameters')) {
|
||||
$parameters = [];
|
||||
$parameterBag = $object->getParameterBag();
|
||||
foreach ($parameterBag->all() as $k => $v) {
|
||||
$parameters[$k] = $object->resolveEnvPlaceholders($v);
|
||||
}
|
||||
$object = new ParameterBag($parameters);
|
||||
if ($parameterBag instanceof ParameterBag) {
|
||||
foreach ($parameterBag->allDeprecated() as $k => $deprecation) {
|
||||
$object->deprecate($k, ...$deprecation);
|
||||
}
|
||||
}
|
||||
$options = [];
|
||||
} elseif ($parameter = $input->getOption('parameter')) {
|
||||
$options = ['parameter' => $parameter];
|
||||
} elseif ($input->getOption('tags')) {
|
||||
$options = ['group_by' => 'tags'];
|
||||
} elseif ($tag = $input->getOption('tag')) {
|
||||
$tag = $this->findProperTagName($input, $errorIo, $object, $tag);
|
||||
$options = ['tag' => $tag];
|
||||
} elseif ($name = $input->getArgument('name')) {
|
||||
$name = $this->findProperServiceName($input, $errorIo, $object, $name, $input->getOption('show-hidden'));
|
||||
$options = ['id' => $name];
|
||||
} elseif ($input->getOption('deprecations')) {
|
||||
$options = ['deprecations' => true];
|
||||
} else {
|
||||
$options = [];
|
||||
}
|
||||
|
||||
$helper = new DescriptorHelper();
|
||||
$options['format'] = $input->getOption('format');
|
||||
$options['show_arguments'] = $input->getOption('show-arguments');
|
||||
$options['show_hidden'] = $input->getOption('show-hidden');
|
||||
$options['raw_text'] = $input->getOption('raw');
|
||||
$options['output'] = $io;
|
||||
$options['is_debug'] = $kernel->isDebug();
|
||||
|
||||
try {
|
||||
$helper->describe($io, $object, $options);
|
||||
|
||||
if ('txt' === $options['format'] && isset($options['id'])) {
|
||||
if ($object->hasDefinition($options['id'])) {
|
||||
$definition = $object->getDefinition($options['id']);
|
||||
if ($definition->isDeprecated()) {
|
||||
$errorIo->warning($definition->getDeprecation($options['id'])['message'] ?? sprintf('The "%s" service is deprecated.', $options['id']));
|
||||
}
|
||||
}
|
||||
if ($object->hasAlias($options['id'])) {
|
||||
$alias = $object->getAlias($options['id']);
|
||||
if ($alias->isDeprecated()) {
|
||||
$errorIo->warning($alias->getDeprecation($options['id'])['message'] ?? sprintf('The "%s" alias is deprecated.', $options['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($options['id']) && isset($kernel->getContainer()->getRemovedIds()[$options['id']])) {
|
||||
$errorIo->note(sprintf('The "%s" service or alias has been removed or inlined when the container was compiled.', $options['id']));
|
||||
}
|
||||
} catch (ServiceNotFoundException $e) {
|
||||
if ('' !== $e->getId() && '@' === $e->getId()[0]) {
|
||||
throw new ServiceNotFoundException($e->getId(), $e->getSourceId(), null, [substr($e->getId(), 1)]);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && !$input->getOption('env-vars') && !$input->getOption('env-var') && $input->isInteractive()) {
|
||||
if ($input->getOption('tags')) {
|
||||
$errorIo->comment('To search for a specific tag, re-run this command with a search term. (e.g. <comment>debug:container --tag=form.type</comment>)');
|
||||
} elseif ($input->getOption('parameters')) {
|
||||
$errorIo->comment('To search for a specific parameter, re-run this command with a search term. (e.g. <comment>debug:container --parameter=kernel.debug</comment>)');
|
||||
} elseif (!$input->getOption('deprecations')) {
|
||||
$errorIo->comment('To search for a specific service, re-run this command with a search term. (e.g. <comment>debug:container log</comment>)');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$object = $this->getContainerBuilder($kernel);
|
||||
|
||||
if ($input->mustSuggestArgumentValuesFor('name')
|
||||
&& !$input->getOption('tag') && !$input->getOption('tags')
|
||||
&& !$input->getOption('parameter') && !$input->getOption('parameters')
|
||||
&& !$input->getOption('env-var') && !$input->getOption('env-vars')
|
||||
&& !$input->getOption('types') && !$input->getOption('deprecations')
|
||||
) {
|
||||
$suggestions->suggestValues($this->findServiceIdsContaining(
|
||||
$object,
|
||||
$input->getCompletionValue(),
|
||||
(bool) $input->getOption('show-hidden')
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('tag')) {
|
||||
$suggestions->suggestValues($object->findTags());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('parameter')) {
|
||||
$suggestions->suggestValues(array_keys($object->getParameterBag()->all()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates input arguments and options.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function validateInput(InputInterface $input): void
|
||||
{
|
||||
$options = ['tags', 'tag', 'parameters', 'parameter'];
|
||||
|
||||
$optionsCount = 0;
|
||||
foreach ($options as $option) {
|
||||
if ($input->getOption($option)) {
|
||||
++$optionsCount;
|
||||
}
|
||||
}
|
||||
|
||||
$name = $input->getArgument('name');
|
||||
if ((null !== $name) && ($optionsCount > 0)) {
|
||||
throw new InvalidArgumentException('The options tags, tag, parameters & parameter cannot be combined with the service name argument.');
|
||||
} elseif ((null === $name) && $optionsCount > 1) {
|
||||
throw new InvalidArgumentException('The options tags, tag, parameters & parameter cannot be combined together.');
|
||||
}
|
||||
}
|
||||
|
||||
private function findProperServiceName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $container, string $name, bool $showHidden): string
|
||||
{
|
||||
$name = ltrim($name, '\\');
|
||||
|
||||
if ($container->has($name) || !$input->isInteractive()) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
$matchingServices = $this->findServiceIdsContaining($container, $name, $showHidden);
|
||||
if (!$matchingServices) {
|
||||
throw new InvalidArgumentException(sprintf('No services found that match "%s".', $name));
|
||||
}
|
||||
|
||||
if (1 === \count($matchingServices)) {
|
||||
return $matchingServices[0];
|
||||
}
|
||||
|
||||
natsort($matchingServices);
|
||||
|
||||
return $io->choice('Select one of the following services to display its information', array_values($matchingServices));
|
||||
}
|
||||
|
||||
private function findProperTagName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $container, string $tagName): string
|
||||
{
|
||||
if (\in_array($tagName, $container->findTags(), true) || !$input->isInteractive()) {
|
||||
return $tagName;
|
||||
}
|
||||
|
||||
$matchingTags = $this->findTagsContaining($container, $tagName);
|
||||
if (!$matchingTags) {
|
||||
throw new InvalidArgumentException(sprintf('No tags found that match "%s".', $tagName));
|
||||
}
|
||||
|
||||
if (1 === \count($matchingTags)) {
|
||||
return $matchingTags[0];
|
||||
}
|
||||
|
||||
natsort($matchingTags);
|
||||
|
||||
return $io->choice('Select one of the following tags to display its information', array_values($matchingTags));
|
||||
}
|
||||
|
||||
private function findServiceIdsContaining(ContainerBuilder $container, string $name, bool $showHidden): array
|
||||
{
|
||||
$serviceIds = $container->getServiceIds();
|
||||
$foundServiceIds = $foundServiceIdsIgnoringBackslashes = [];
|
||||
foreach ($serviceIds as $serviceId) {
|
||||
if (!$showHidden && str_starts_with($serviceId, '.')) {
|
||||
continue;
|
||||
}
|
||||
if (!$showHidden && $container->hasDefinition($serviceId) && $container->getDefinition($serviceId)->hasTag('container.excluded')) {
|
||||
continue;
|
||||
}
|
||||
if (false !== stripos(str_replace('\\', '', $serviceId), $name)) {
|
||||
$foundServiceIdsIgnoringBackslashes[] = $serviceId;
|
||||
}
|
||||
if ('' === $name || false !== stripos($serviceId, $name)) {
|
||||
$foundServiceIds[] = $serviceId;
|
||||
}
|
||||
}
|
||||
|
||||
return $foundServiceIds ?: $foundServiceIdsIgnoringBackslashes;
|
||||
}
|
||||
|
||||
private function findTagsContaining(ContainerBuilder $container, string $tagName): array
|
||||
{
|
||||
$tags = $container->findTags();
|
||||
$foundTags = [];
|
||||
foreach ($tags as $tag) {
|
||||
if (str_contains($tag, $tagName)) {
|
||||
$foundTags[] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
return $foundTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function filterToServiceTypes(string $serviceId): bool
|
||||
{
|
||||
// filter out things that could not be valid class names
|
||||
if (!preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))^(?&V)(?:\\\\(?&V))*+(?: \$(?&V))?$/', $serviceId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the id has a \, assume it is a class
|
||||
if (str_contains($serviceId, '\\')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return class_exists($serviceId) || interface_exists($serviceId, false);
|
||||
}
|
||||
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return (new DescriptorHelper())->getFormats();
|
||||
}
|
||||
}
|
114
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ContainerLintCommand.php
vendored
Normal file
114
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/ContainerLintCommand.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Config\ConfigCache;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
|
||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||
use Symfony\Component\DependencyInjection\Compiler\ResolveFactoryClassPass;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
#[AsCommand(name: 'lint:container', description: 'Ensure that arguments injected into services match type declarations')]
|
||||
final class ContainerLintCommand extends Command
|
||||
{
|
||||
private ContainerBuilder $container;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp('This command parses service definitions and ensures that injected values match the type declarations of each services\' class.')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
try {
|
||||
$container = $this->getContainerBuilder();
|
||||
} catch (RuntimeException $e) {
|
||||
$errorIo->error($e->getMessage());
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
$container->setParameter('container.build_time', time());
|
||||
|
||||
try {
|
||||
$container->compile();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$errorIo->error($e->getMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->success('The container was linted successfully: all services are injected with values that are compatible with their type declarations.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function getContainerBuilder(): ContainerBuilder
|
||||
{
|
||||
if (isset($this->container)) {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
$kernelContainer = $kernel->getContainer();
|
||||
|
||||
if (!$kernel->isDebug() || !$kernelContainer->getParameter('debug.container.dump') || !(new ConfigCache($kernelContainer->getParameter('debug.container.dump'), true))->isFresh()) {
|
||||
if (!$kernel instanceof Kernel) {
|
||||
throw new RuntimeException(sprintf('This command does not support the application kernel: "%s" does not extend "%s".', get_debug_type($kernel), Kernel::class));
|
||||
}
|
||||
|
||||
$buildContainer = \Closure::bind(function (): ContainerBuilder {
|
||||
$this->initializeBundles();
|
||||
|
||||
return $this->buildContainer();
|
||||
}, $kernel, $kernel::class);
|
||||
$container = $buildContainer();
|
||||
} else {
|
||||
if (!$kernelContainer instanceof Container) {
|
||||
throw new RuntimeException(sprintf('This command does not support the application container: "%s" does not extend "%s".', get_debug_type($kernelContainer), Container::class));
|
||||
}
|
||||
|
||||
(new XmlFileLoader($container = new ContainerBuilder($parameterBag = new EnvPlaceholderParameterBag()), new FileLocator()))->load($kernelContainer->getParameter('debug.container.dump'));
|
||||
|
||||
$refl = new \ReflectionProperty($parameterBag, 'resolved');
|
||||
$refl->setValue($parameterBag, true);
|
||||
|
||||
$container->getCompilerPassConfig()->setBeforeOptimizationPasses([]);
|
||||
$container->getCompilerPassConfig()->setOptimizationPasses([new ResolveFactoryClassPass()]);
|
||||
$container->getCompilerPassConfig()->setBeforeRemovingPasses([]);
|
||||
}
|
||||
|
||||
$container->setParameter('container.build_hash', 'lint_container');
|
||||
$container->setParameter('container.build_id', 'lint_container');
|
||||
|
||||
$container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
|
||||
|
||||
return $this->container = $container;
|
||||
}
|
||||
}
|
200
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php
vendored
Normal file
200
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\Descriptor;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Target;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
|
||||
|
||||
/**
|
||||
* A console command for autowiring information.
|
||||
*
|
||||
* @author Ryan Weaver <ryan@knpuniversity.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'debug:autowiring', description: 'List classes/interfaces you can use for autowiring')]
|
||||
class DebugAutowiringCommand extends ContainerDebugCommand
|
||||
{
|
||||
private ?FileLinkFormatter $fileLinkFormatter;
|
||||
|
||||
public function __construct(?string $name = null, ?FileLinkFormatter $fileLinkFormatter = null)
|
||||
{
|
||||
$this->fileLinkFormatter = $fileLinkFormatter;
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('search', InputArgument::OPTIONAL, 'A search filter'),
|
||||
new InputOption('all', null, InputOption::VALUE_NONE, 'Show also services that are not aliased'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command displays the classes and interfaces that
|
||||
you can use as type-hints for autowiring:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
You can also pass a search term to filter the list:
|
||||
|
||||
<info>php %command.full_name% log</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
$container = $this->getContainerBuilder($this->getApplication()->getKernel());
|
||||
$serviceIds = $container->getServiceIds();
|
||||
$serviceIds = array_filter($serviceIds, $this->filterToServiceTypes(...));
|
||||
|
||||
if ($search = $input->getArgument('search')) {
|
||||
$searchNormalized = preg_replace('/[^a-zA-Z0-9\x7f-\xff $]++/', '', $search);
|
||||
|
||||
$serviceIds = array_filter($serviceIds, fn ($serviceId) => false !== stripos(str_replace('\\', '', $serviceId), $searchNormalized) && !str_starts_with($serviceId, '.'));
|
||||
|
||||
if (!$serviceIds) {
|
||||
$errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"', $search));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
$reverseAliases = [];
|
||||
|
||||
foreach ($container->getAliases() as $id => $alias) {
|
||||
if ('.' === ($id[0] ?? null)) {
|
||||
$reverseAliases[(string) $alias][] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($serviceIds, 'strnatcmp');
|
||||
|
||||
$io->title('Autowirable Types');
|
||||
$io->text('The following classes & interfaces can be used as type-hints when autowiring:');
|
||||
if ($search) {
|
||||
$io->text(sprintf('(only showing classes/interfaces matching <comment>%s</comment>)', $search));
|
||||
}
|
||||
$hasAlias = [];
|
||||
$all = $input->getOption('all');
|
||||
$previousId = '-';
|
||||
$serviceIdsNb = 0;
|
||||
foreach ($serviceIds as $serviceId) {
|
||||
if ($container->hasDefinition($serviceId) && $container->getDefinition($serviceId)->hasTag('container.excluded')) {
|
||||
continue;
|
||||
}
|
||||
$text = [];
|
||||
$resolvedServiceId = $serviceId;
|
||||
if (!str_starts_with($serviceId, $previousId.' $')) {
|
||||
$text[] = '';
|
||||
$previousId = preg_replace('/ \$.*/', '', $serviceId);
|
||||
if ('' !== $description = Descriptor::getClassDescription($previousId, $resolvedServiceId)) {
|
||||
if (isset($hasAlias[$previousId])) {
|
||||
continue;
|
||||
}
|
||||
$text[] = $description;
|
||||
}
|
||||
}
|
||||
|
||||
$serviceLine = sprintf('<fg=yellow>%s</>', $serviceId);
|
||||
if ('' !== $fileLink = $this->getFileLink($previousId)) {
|
||||
$serviceLine = substr($serviceId, \strlen($previousId));
|
||||
$serviceLine = sprintf('<fg=yellow;href=%s>%s</>', $fileLink, $previousId).('' !== $serviceLine ? sprintf('<fg=yellow>%s</>', $serviceLine) : '');
|
||||
}
|
||||
|
||||
if ($container->hasAlias($serviceId)) {
|
||||
$hasAlias[$serviceId] = true;
|
||||
$serviceAlias = $container->getAlias($serviceId);
|
||||
$alias = (string) $serviceAlias;
|
||||
|
||||
$target = null;
|
||||
foreach ($reverseAliases[(string) $serviceAlias] ?? [] as $id) {
|
||||
if (!str_starts_with($id, '.'.$previousId.' $')) {
|
||||
continue;
|
||||
}
|
||||
$target = substr($id, \strlen($previousId) + 3);
|
||||
|
||||
if ($previousId.' $'.(new Target($target))->getParsedName() === $serviceId) {
|
||||
$serviceLine .= ' - <fg=magenta>target:</><fg=cyan>'.$target.'</>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($container->hasDefinition($serviceAlias) && $decorated = $container->getDefinition($serviceAlias)->getTag('container.decorator')) {
|
||||
$alias = $decorated[0]['id'];
|
||||
}
|
||||
|
||||
if ($alias !== $target) {
|
||||
$serviceLine .= ' - <fg=magenta>alias:</><fg=cyan>'.$alias.'</>';
|
||||
}
|
||||
|
||||
if ($serviceAlias->isDeprecated()) {
|
||||
$serviceLine .= ' - <fg=magenta>deprecated</>';
|
||||
}
|
||||
} elseif (!$all) {
|
||||
++$serviceIdsNb;
|
||||
continue;
|
||||
} elseif ($container->getDefinition($serviceId)->isDeprecated()) {
|
||||
$serviceLine .= ' - <fg=magenta>deprecated</>';
|
||||
}
|
||||
$text[] = $serviceLine;
|
||||
$io->text($text);
|
||||
}
|
||||
|
||||
$io->newLine();
|
||||
|
||||
if (0 < $serviceIdsNb) {
|
||||
$io->text(sprintf('%s more concrete service%s would be displayed when adding the "--all" option.', $serviceIdsNb, $serviceIdsNb > 1 ? 's' : ''));
|
||||
}
|
||||
if ($all) {
|
||||
$io->text('Pro-tip: use interfaces in your type-hints instead of classes to benefit from the dependency inversion principle.');
|
||||
}
|
||||
|
||||
$io->newLine();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function getFileLink(string $class): string
|
||||
{
|
||||
if (null === $this->fileLinkFormatter
|
||||
|| (null === $r = $this->getContainerBuilder($this->getApplication()->getKernel())->getReflectionClass($class, false))) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string) $this->fileLinkFormatter->format($r->getFileName(), $r->getStartLine());
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('search')) {
|
||||
$container = $this->getContainerBuilder($this->getApplication()->getKernel());
|
||||
|
||||
$suggestions->suggestValues(array_filter($container->getServiceIds(), $this->filterToServiceTypes(...)));
|
||||
}
|
||||
}
|
||||
}
|
163
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php
vendored
Normal file
163
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Contracts\Service\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* A console command for retrieving information about event dispatcher.
|
||||
*
|
||||
* @author Matthieu Auger <mail@matthieuauger.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'debug:event-dispatcher', description: 'Display configured listeners for an application')]
|
||||
class EventDispatcherDebugCommand extends Command
|
||||
{
|
||||
private const DEFAULT_DISPATCHER = 'event_dispatcher';
|
||||
|
||||
private ContainerInterface $dispatchers;
|
||||
|
||||
public function __construct(ContainerInterface $dispatchers)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->dispatchers = $dispatchers;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('event', InputArgument::OPTIONAL, 'An event name or a part of the event name'),
|
||||
new InputOption('dispatcher', null, InputOption::VALUE_REQUIRED, 'To view events of a specific event dispatcher', self::DEFAULT_DISPATCHER),
|
||||
new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command displays all configured listeners:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
To get specific listeners for an event, specify its name:
|
||||
|
||||
<info>php %command.full_name% kernel.request</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$options = [];
|
||||
$dispatcherServiceName = $input->getOption('dispatcher');
|
||||
if (!$this->dispatchers->has($dispatcherServiceName)) {
|
||||
$io->getErrorStyle()->error(sprintf('Event dispatcher "%s" is not available.', $dispatcherServiceName));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$dispatcher = $this->dispatchers->get($dispatcherServiceName);
|
||||
|
||||
if ($event = $input->getArgument('event')) {
|
||||
if ($dispatcher->hasListeners($event)) {
|
||||
$options = ['event' => $event];
|
||||
} else {
|
||||
// if there is no direct match, try find partial matches
|
||||
$events = $this->searchForEvent($dispatcher, $event);
|
||||
if (0 === \count($events)) {
|
||||
$io->getErrorStyle()->warning(sprintf('The event "%s" does not have any registered listeners.', $event));
|
||||
|
||||
return 0;
|
||||
} elseif (1 === \count($events)) {
|
||||
$options = ['event' => $events[array_key_first($events)]];
|
||||
} else {
|
||||
$options = ['events' => $events];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$helper = new DescriptorHelper();
|
||||
|
||||
if (self::DEFAULT_DISPATCHER !== $dispatcherServiceName) {
|
||||
$options['dispatcher_service_name'] = $dispatcherServiceName;
|
||||
}
|
||||
|
||||
$options['format'] = $input->getOption('format');
|
||||
$options['raw_text'] = $input->getOption('raw');
|
||||
$options['output'] = $io;
|
||||
$helper->describe($io, $dispatcher, $options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('event')) {
|
||||
$dispatcherServiceName = $input->getOption('dispatcher');
|
||||
if ($this->dispatchers->has($dispatcherServiceName)) {
|
||||
$dispatcher = $this->dispatchers->get($dispatcherServiceName);
|
||||
$suggestions->suggestValues(array_keys($dispatcher->getListeners()));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('dispatcher')) {
|
||||
if ($this->dispatchers instanceof ServiceProviderInterface) {
|
||||
$suggestions->suggestValues(array_keys($this->dispatchers->getProvidedServices()));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
}
|
||||
}
|
||||
|
||||
private function searchForEvent(EventDispatcherInterface $dispatcher, string $needle): array
|
||||
{
|
||||
$output = [];
|
||||
$lcNeedle = strtolower($needle);
|
||||
$allEvents = array_keys($dispatcher->getListeners());
|
||||
foreach ($allEvents as $event) {
|
||||
if (str_contains(strtolower($event), $lcNeedle)) {
|
||||
$output[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return (new DescriptorHelper())->getFormats();
|
||||
}
|
||||
}
|
175
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/RouterDebugCommand.php
vendored
Normal file
175
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/RouterDebugCommand.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* A console command for retrieving information about routes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'debug:router', description: 'Display current routes for an application')]
|
||||
class RouterDebugCommand extends Command
|
||||
{
|
||||
use BuildDebugContainerTrait;
|
||||
|
||||
private RouterInterface $router;
|
||||
private ?FileLinkFormatter $fileLinkFormatter;
|
||||
|
||||
public function __construct(RouterInterface $router, ?FileLinkFormatter $fileLinkFormatter = null)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->router = $router;
|
||||
$this->fileLinkFormatter = $fileLinkFormatter;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('name', InputArgument::OPTIONAL, 'A route name'),
|
||||
new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'),
|
||||
new InputOption('show-aliases', null, InputOption::VALUE_NONE, 'Show aliases in overview'),
|
||||
new InputOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())), 'txt'),
|
||||
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> displays the configured routes:
|
||||
|
||||
<info>php %command.full_name%</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException When route does not exist
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$name = $input->getArgument('name');
|
||||
$helper = new DescriptorHelper($this->fileLinkFormatter);
|
||||
$routes = $this->router->getRouteCollection();
|
||||
$container = null;
|
||||
if ($this->fileLinkFormatter) {
|
||||
$container = fn () => $this->getContainerBuilder($this->getApplication()->getKernel());
|
||||
}
|
||||
|
||||
if ($name) {
|
||||
$route = $routes->get($name);
|
||||
$matchingRoutes = $this->findRouteNameContaining($name, $routes);
|
||||
|
||||
if (!$input->isInteractive() && !$route && \count($matchingRoutes) > 1) {
|
||||
$helper->describe($io, $this->findRouteContaining($name, $routes), [
|
||||
'format' => $input->getOption('format'),
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'show_controllers' => $input->getOption('show-controllers'),
|
||||
'show_aliases' => $input->getOption('show-aliases'),
|
||||
'output' => $io,
|
||||
]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$route && $matchingRoutes) {
|
||||
$default = 1 === \count($matchingRoutes) ? $matchingRoutes[0] : null;
|
||||
$name = $io->choice('Select one of the matching routes', $matchingRoutes, $default);
|
||||
$route = $routes->get($name);
|
||||
}
|
||||
|
||||
if (!$route) {
|
||||
throw new InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
$helper->describe($io, $route, [
|
||||
'format' => $input->getOption('format'),
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'name' => $name,
|
||||
'output' => $io,
|
||||
'container' => $container,
|
||||
]);
|
||||
} else {
|
||||
$helper->describe($io, $routes, [
|
||||
'format' => $input->getOption('format'),
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'show_controllers' => $input->getOption('show-controllers'),
|
||||
'show_aliases' => $input->getOption('show-aliases'),
|
||||
'output' => $io,
|
||||
'container' => $container,
|
||||
]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function findRouteNameContaining(string $name, RouteCollection $routes): array
|
||||
{
|
||||
$foundRoutesNames = [];
|
||||
foreach ($routes as $routeName => $route) {
|
||||
if (false !== stripos($routeName, $name)) {
|
||||
$foundRoutesNames[] = $routeName;
|
||||
}
|
||||
}
|
||||
|
||||
return $foundRoutesNames;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||||
$suggestions->suggestValues(array_keys($this->router->getRouteCollection()->all()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues($this->getAvailableFormatOptions());
|
||||
}
|
||||
}
|
||||
|
||||
private function findRouteContaining(string $name, RouteCollection $routes): RouteCollection
|
||||
{
|
||||
$foundRoutes = new RouteCollection();
|
||||
foreach ($routes as $routeName => $route) {
|
||||
if (false !== stripos($routeName, $name)) {
|
||||
$foundRoutes->add($routeName, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return $foundRoutes;
|
||||
}
|
||||
|
||||
private function getAvailableFormatOptions(): array
|
||||
{
|
||||
return (new DescriptorHelper())->getFormats();
|
||||
}
|
||||
}
|
121
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/RouterMatchCommand.php
vendored
Normal file
121
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/RouterMatchCommand.php
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\Routing\Matcher\TraceableUrlMatcher;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* A console command to test route matching.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'router:match', description: 'Help debug routes by simulating a path info match')]
|
||||
class RouterMatchCommand extends Command
|
||||
{
|
||||
private RouterInterface $router;
|
||||
private iterable $expressionLanguageProviders;
|
||||
|
||||
/**
|
||||
* @param iterable<mixed, ExpressionFunctionProviderInterface> $expressionLanguageProviders
|
||||
*/
|
||||
public function __construct(RouterInterface $router, iterable $expressionLanguageProviders = [])
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->router = $router;
|
||||
$this->expressionLanguageProviders = $expressionLanguageProviders;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('path_info', InputArgument::REQUIRED, 'A path info'),
|
||||
new InputOption('method', null, InputOption::VALUE_REQUIRED, 'Set the HTTP method'),
|
||||
new InputOption('scheme', null, InputOption::VALUE_REQUIRED, 'Set the URI scheme (usually http or https)'),
|
||||
new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Set the URI host'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> shows which routes match a given request and which don't and for what reason:
|
||||
|
||||
<info>php %command.full_name% /foo</info>
|
||||
|
||||
or
|
||||
|
||||
<info>php %command.full_name% /foo --method POST --scheme https --host symfony.com --verbose</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$context = $this->router->getContext();
|
||||
if (null !== $method = $input->getOption('method')) {
|
||||
$context->setMethod($method);
|
||||
}
|
||||
if (null !== $scheme = $input->getOption('scheme')) {
|
||||
$context->setScheme($scheme);
|
||||
}
|
||||
if (null !== $host = $input->getOption('host')) {
|
||||
$context->setHost($host);
|
||||
}
|
||||
|
||||
$matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $context);
|
||||
foreach ($this->expressionLanguageProviders as $provider) {
|
||||
$matcher->addExpressionLanguageProvider($provider);
|
||||
}
|
||||
|
||||
$traces = $matcher->getTraces($input->getArgument('path_info'));
|
||||
|
||||
$io->newLine();
|
||||
|
||||
$matches = false;
|
||||
foreach ($traces as $trace) {
|
||||
if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) {
|
||||
$io->text(sprintf('Route <info>"%s"</> almost matches but %s', $trace['name'], lcfirst($trace['log'])));
|
||||
} elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) {
|
||||
$io->success(sprintf('Route "%s" matches', $trace['name']));
|
||||
|
||||
$routerDebugCommand = $this->getApplication()->find('debug:router');
|
||||
$routerDebugCommand->run(new ArrayInput(['name' => $trace['name']]), $output);
|
||||
|
||||
$matches = true;
|
||||
} elseif ($input->getOption('verbose')) {
|
||||
$io->text(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log']));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$matches) {
|
||||
$io->error(sprintf('None of the routes match the path "%s"', $input->getArgument('path_info')));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
102
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsDecryptToLocalCommand.php
vendored
Normal file
102
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsDecryptToLocalCommand.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:decrypt-to-local', description: 'Decrypt all secrets and stores them in the local vault')]
|
||||
final class SecretsDecryptToLocalCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Force overriding of secrets that already exist in the local vault')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command decrypts all secrets and copies them in the local vault.
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
|
||||
When the <info>--force</info> option is provided, secrets that already exist in the local vault are overridden.
|
||||
|
||||
<info>%command.full_name% --force</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
|
||||
|
||||
if (null === $this->localVault) {
|
||||
$io->error('The local vault is disabled.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$secrets = $this->vault->list(true);
|
||||
|
||||
$io->comment(sprintf('%d secret%s found in the vault.', \count($secrets), 1 !== \count($secrets) ? 's' : ''));
|
||||
|
||||
$skipped = 0;
|
||||
if (!$input->getOption('force')) {
|
||||
foreach ($this->localVault->list() as $k => $v) {
|
||||
if (isset($secrets[$k])) {
|
||||
++$skipped;
|
||||
unset($secrets[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($skipped > 0) {
|
||||
$io->warning([
|
||||
sprintf('%d secret%s already overridden in the local vault and will be skipped.', $skipped, 1 !== $skipped ? 's are' : ' is'),
|
||||
'Use the --force flag to override these.',
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($secrets as $k => $v) {
|
||||
if (null === $v) {
|
||||
$io->error($this->vault->getLastMessage() ?? sprintf('Secret "%s" has been skipped as there was an error reading it.', $k));
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->localVault->seal($k, $v);
|
||||
$io->note($this->localVault->getLastMessage());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:encrypt-from-local', description: 'Encrypt all local secrets to the vault')]
|
||||
final class SecretsEncryptFromLocalCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command encrypts all locally overridden secrets to the vault.
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
|
||||
|
||||
if (null === $this->localVault) {
|
||||
$io->error('The local vault is disabled.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
foreach ($this->vault->list(true) as $name => $value) {
|
||||
$localValue = $this->localVault->reveal($name);
|
||||
|
||||
if (null !== $localValue && $value !== $localValue) {
|
||||
$this->vault->seal($name, $localValue);
|
||||
} elseif (null !== $message = $this->localVault->getLastMessage()) {
|
||||
$io->error($message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
124
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsGenerateKeysCommand.php
vendored
Normal file
124
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsGenerateKeysCommand.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:generate-keys', description: 'Generate new encryption keys')]
|
||||
final class SecretsGenerateKeysCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption('local', 'l', InputOption::VALUE_NONE, 'Update the local vault.')
|
||||
->addOption('rotate', 'r', InputOption::VALUE_NONE, 'Re-encrypt existing secrets with the newly generated keys.')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command generates a new encryption key.
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
|
||||
If encryption keys already exist, the command must be called with
|
||||
the <info>--rotate</info> option in order to override those keys and re-encrypt
|
||||
existing secrets.
|
||||
|
||||
<info>%command.full_name% --rotate</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
|
||||
$vault = $input->getOption('local') ? $this->localVault : $this->vault;
|
||||
|
||||
if (null === $vault) {
|
||||
$io->success('The local vault is disabled.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!$input->getOption('rotate')) {
|
||||
if ($vault->generateKeys()) {
|
||||
$io->success($vault->getLastMessage());
|
||||
|
||||
if ($this->vault === $vault) {
|
||||
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$io->warning($vault->getLastMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$secrets = [];
|
||||
foreach ($vault->list(true) as $name => $value) {
|
||||
if (null === $value) {
|
||||
$io->error($vault->getLastMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$secrets[$name] = $value;
|
||||
}
|
||||
|
||||
if (!$vault->generateKeys(true)) {
|
||||
$io->warning($vault->getLastMessage());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->success($vault->getLastMessage());
|
||||
|
||||
if ($secrets) {
|
||||
foreach ($secrets as $name => $value) {
|
||||
$vault->seal($name, $value);
|
||||
}
|
||||
|
||||
$io->comment('Existing secrets have been rotated to the new keys.');
|
||||
}
|
||||
|
||||
if ($this->vault === $vault) {
|
||||
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
105
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsListCommand.php
vendored
Normal file
105
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsListCommand.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Dumper;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:list', description: 'List all secrets')]
|
||||
final class SecretsListCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption('reveal', 'r', InputOption::VALUE_NONE, 'Display decrypted values alongside names')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command list all stored secrets.
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
|
||||
When the option <info>--reveal</info> is provided, the decrypted secrets are also displayed.
|
||||
|
||||
<info>%command.full_name% --reveal</info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
|
||||
|
||||
$io->comment('Use <info>"%env(<name>)%"</info> to reference a secret in a config file.');
|
||||
|
||||
if (!$reveal = $input->getOption('reveal')) {
|
||||
$io->comment(sprintf('To reveal the secrets run <info>php %s %s --reveal</info>', $_SERVER['PHP_SELF'], $this->getName()));
|
||||
}
|
||||
|
||||
$secrets = $this->vault->list($reveal);
|
||||
$localSecrets = $this->localVault?->list($reveal);
|
||||
|
||||
$rows = [];
|
||||
|
||||
$dump = new Dumper($output);
|
||||
$dump = fn ($v) => null === $v ? '******' : $dump($v);
|
||||
|
||||
foreach ($secrets as $name => $value) {
|
||||
$rows[$name] = [$name, $dump($value)];
|
||||
}
|
||||
|
||||
if (null !== $message = $this->vault->getLastMessage()) {
|
||||
$io->comment($message);
|
||||
}
|
||||
|
||||
foreach ($localSecrets ?? [] as $name => $value) {
|
||||
if (isset($rows[$name])) {
|
||||
$rows[$name][] = $dump($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->localVault && null !== $message = $this->localVault->getLastMessage()) {
|
||||
$io->comment($message);
|
||||
}
|
||||
|
||||
(new SymfonyStyle($input, $output))
|
||||
->table(['Secret', 'Value'] + (null !== $localSecrets ? [2 => 'Local Value'] : []), $rows);
|
||||
|
||||
$io->comment("Local values override secret values.\nUse <info>secrets:set --local</info> to define them.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
100
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsRemoveCommand.php
vendored
Normal file
100
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsRemoveCommand.php
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:remove', description: 'Remove a secret from the vault')]
|
||||
final class SecretsRemoveCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('name', InputArgument::REQUIRED, 'The name of the secret')
|
||||
->addOption('local', 'l', InputOption::VALUE_NONE, 'Update the local vault.')
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command removes a secret from the vault.
|
||||
|
||||
<info>%command.full_name% <name></info>
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
|
||||
$vault = $input->getOption('local') ? $this->localVault : $this->vault;
|
||||
|
||||
if (null === $vault) {
|
||||
$io->success('The local vault is disabled.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($vault->remove($name = $input->getArgument('name'))) {
|
||||
$io->success($vault->getLastMessage() ?? 'Secret was removed from the vault.');
|
||||
} else {
|
||||
$io->comment($vault->getLastMessage() ?? 'Secret was not found in the vault.');
|
||||
}
|
||||
|
||||
if ($this->vault === $vault && null !== $this->localVault->reveal($name)) {
|
||||
$io->comment('Note that this secret is overridden in the local vault.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if (!$input->mustSuggestArgumentValuesFor('name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$vaultKeys = array_keys($this->vault->list(false));
|
||||
if ($input->getOption('local')) {
|
||||
if (null === $this->localVault) {
|
||||
return;
|
||||
}
|
||||
$vaultKeys = array_intersect($vaultKeys, array_keys($this->localVault->list(false)));
|
||||
}
|
||||
|
||||
$suggestions->suggestValues($vaultKeys);
|
||||
}
|
||||
}
|
147
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsSetCommand.php
vendored
Normal file
147
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/SecretsSetCommand.php
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'secrets:set', description: 'Set a secret in the vault')]
|
||||
final class SecretsSetCommand extends Command
|
||||
{
|
||||
private AbstractVault $vault;
|
||||
private ?AbstractVault $localVault;
|
||||
|
||||
public function __construct(AbstractVault $vault, ?AbstractVault $localVault = null)
|
||||
{
|
||||
$this->vault = $vault;
|
||||
$this->localVault = $localVault;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('name', InputArgument::REQUIRED, 'The name of the secret')
|
||||
->addArgument('file', InputArgument::OPTIONAL, 'A file where to read the secret from or "-" for reading from STDIN')
|
||||
->addOption('local', 'l', InputOption::VALUE_NONE, 'Update the local vault.')
|
||||
->addOption('random', 'r', InputOption::VALUE_OPTIONAL, 'Generate a random value.', false)
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command stores a secret in the vault.
|
||||
|
||||
<info>%command.full_name% <name></info>
|
||||
|
||||
To reference secrets in services.yaml or any other config
|
||||
files, use <info>"%env(<name>)%"</info>.
|
||||
|
||||
By default, the secret value should be entered interactively.
|
||||
Alternatively, provide a file where to read the secret from:
|
||||
|
||||
<info>php %command.full_name% <name> filename</info>
|
||||
|
||||
Use "-" as a file name to read from STDIN:
|
||||
|
||||
<info>cat filename | php %command.full_name% <name> -</info>
|
||||
|
||||
Use <info>--local</info> to override secrets for local needs.
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
|
||||
$io = new SymfonyStyle($input, $errOutput);
|
||||
$name = $input->getArgument('name');
|
||||
$vault = $input->getOption('local') ? $this->localVault : $this->vault;
|
||||
|
||||
if (null === $vault) {
|
||||
$io->error('The local vault is disabled.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($this->localVault === $vault && !\array_key_exists($name, $this->vault->list())) {
|
||||
$io->error(sprintf('Secret "%s" does not exist in the vault, you cannot override it locally.', $name));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (0 < $random = $input->getOption('random') ?? 16) {
|
||||
$value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_');
|
||||
} elseif (!$file = $input->getArgument('file')) {
|
||||
$value = $io->askHidden('Please type the secret value');
|
||||
|
||||
if (null === $value) {
|
||||
$io->warning('No value provided: using empty string');
|
||||
$value = '';
|
||||
}
|
||||
} elseif ('-' === $file) {
|
||||
$value = file_get_contents('php://stdin');
|
||||
} elseif (is_file($file) && is_readable($file)) {
|
||||
$value = file_get_contents($file);
|
||||
} elseif (!is_file($file)) {
|
||||
throw new \InvalidArgumentException(sprintf('File not found: "%s".', $file));
|
||||
} elseif (!is_readable($file)) {
|
||||
throw new \InvalidArgumentException(sprintf('File is not readable: "%s".', $file));
|
||||
}
|
||||
|
||||
if ($vault->generateKeys()) {
|
||||
$io->success($vault->getLastMessage());
|
||||
|
||||
if ($this->vault === $vault) {
|
||||
$io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
|
||||
}
|
||||
}
|
||||
|
||||
$vault->seal($name, $value);
|
||||
|
||||
$io->success($vault->getLastMessage() ?? 'Secret was successfully stored in the vault.');
|
||||
|
||||
if (0 < $random) {
|
||||
$errOutput->write(' // The generated random value is: <comment>');
|
||||
$output->write($value);
|
||||
$errOutput->writeln('</comment>');
|
||||
$io->newLine();
|
||||
}
|
||||
|
||||
if ($this->vault === $vault && null !== $this->localVault->reveal($name)) {
|
||||
$io->comment('Note that this secret is overridden in the local vault.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||||
$suggestions->suggestValues(array_keys($this->vault->list(false)));
|
||||
}
|
||||
}
|
||||
}
|
410
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php
vendored
Normal file
410
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Translation\Catalogue\MergeOperation;
|
||||
use Symfony\Component\Translation\DataCollectorTranslator;
|
||||
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
||||
use Symfony\Component\Translation\LoggingTranslator;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
|
||||
use Symfony\Component\Translation\Translator;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* Helps finding unused or missing translation messages in a given locale
|
||||
* and comparing them with the fallback ones.
|
||||
*
|
||||
* @author Florian Voutzinos <florian@voutzinos.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'debug:translation', description: 'Display translation messages information')]
|
||||
class TranslationDebugCommand extends Command
|
||||
{
|
||||
public const EXIT_CODE_GENERAL_ERROR = 64;
|
||||
public const EXIT_CODE_MISSING = 65;
|
||||
public const EXIT_CODE_UNUSED = 66;
|
||||
public const EXIT_CODE_FALLBACK = 68;
|
||||
public const MESSAGE_MISSING = 0;
|
||||
public const MESSAGE_UNUSED = 1;
|
||||
public const MESSAGE_EQUALS_FALLBACK = 2;
|
||||
|
||||
private TranslatorInterface $translator;
|
||||
private TranslationReaderInterface $reader;
|
||||
private ExtractorInterface $extractor;
|
||||
private ?string $defaultTransPath;
|
||||
private ?string $defaultViewsPath;
|
||||
private array $transPaths;
|
||||
private array $codePaths;
|
||||
private array $enabledLocales;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, TranslationReaderInterface $reader, ExtractorInterface $extractor, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->translator = $translator;
|
||||
$this->reader = $reader;
|
||||
$this->extractor = $extractor;
|
||||
$this->defaultTransPath = $defaultTransPath;
|
||||
$this->defaultViewsPath = $defaultViewsPath;
|
||||
$this->transPaths = $transPaths;
|
||||
$this->codePaths = $codePaths;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
|
||||
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
|
||||
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'),
|
||||
new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Display only missing messages'),
|
||||
new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Display only unused messages'),
|
||||
new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command helps finding unused or missing translation
|
||||
messages and comparing them with the fallback ones by inspecting the
|
||||
templates and translation files of a given bundle or the default translations directory.
|
||||
|
||||
You can display information about bundle translations in a specific locale:
|
||||
|
||||
<info>php %command.full_name% en AcmeDemoBundle</info>
|
||||
|
||||
You can also specify a translation domain for the search:
|
||||
|
||||
<info>php %command.full_name% --domain=messages en AcmeDemoBundle</info>
|
||||
|
||||
You can only display missing messages:
|
||||
|
||||
<info>php %command.full_name% --only-missing en AcmeDemoBundle</info>
|
||||
|
||||
You can only display unused messages:
|
||||
|
||||
<info>php %command.full_name% --only-unused en AcmeDemoBundle</info>
|
||||
|
||||
You can display information about application translations in a specific locale:
|
||||
|
||||
<info>php %command.full_name% en</info>
|
||||
|
||||
You can display information about translations in all registered bundles in a specific locale:
|
||||
|
||||
<info>php %command.full_name% --all en</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$locale = $input->getArgument('locale');
|
||||
$domain = $input->getOption('domain');
|
||||
|
||||
$exitCode = self::SUCCESS;
|
||||
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
// Define Root Paths
|
||||
$transPaths = $this->getRootTransPaths();
|
||||
$codePaths = $this->getRootCodePaths($kernel);
|
||||
|
||||
// Override with provided Bundle info
|
||||
if (null !== $input->getArgument('bundle')) {
|
||||
try {
|
||||
$bundle = $kernel->getBundle($input->getArgument('bundle'));
|
||||
$bundleDir = $bundle->getPath();
|
||||
$transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations'];
|
||||
$codePaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates'];
|
||||
if ($this->defaultTransPath) {
|
||||
$transPaths[] = $this->defaultTransPath;
|
||||
}
|
||||
if ($this->defaultViewsPath) {
|
||||
$codePaths[] = $this->defaultViewsPath;
|
||||
}
|
||||
} catch (\InvalidArgumentException) {
|
||||
// such a bundle does not exist, so treat the argument as path
|
||||
$path = $input->getArgument('bundle');
|
||||
|
||||
$transPaths = [$path.'/translations'];
|
||||
$codePaths = [$path.'/templates'];
|
||||
|
||||
if (!is_dir($transPaths[0])) {
|
||||
throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0]));
|
||||
}
|
||||
}
|
||||
} elseif ($input->getOption('all')) {
|
||||
foreach ($kernel->getBundles() as $bundle) {
|
||||
$bundleDir = $bundle->getPath();
|
||||
$transPaths[] = is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundle->getPath().'/translations';
|
||||
$codePaths[] = is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundle->getPath().'/templates';
|
||||
}
|
||||
}
|
||||
|
||||
// Extract used messages
|
||||
$extractedCatalogue = $this->extractMessages($locale, $codePaths);
|
||||
|
||||
// Load defined messages
|
||||
$currentCatalogue = $this->loadCurrentMessages($locale, $transPaths);
|
||||
|
||||
// Merge defined and extracted messages to get all message ids
|
||||
$mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue);
|
||||
$allMessages = $mergeOperation->getResult()->all($domain);
|
||||
if (null !== $domain) {
|
||||
$allMessages = [$domain => $allMessages];
|
||||
}
|
||||
|
||||
// No defined or extracted messages
|
||||
if (!$allMessages || null !== $domain && empty($allMessages[$domain])) {
|
||||
$outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale);
|
||||
|
||||
if (null !== $domain) {
|
||||
$outputMessage .= sprintf(' and domain "%s"', $domain);
|
||||
}
|
||||
|
||||
$io->getErrorStyle()->warning($outputMessage);
|
||||
|
||||
return self::EXIT_CODE_GENERAL_ERROR;
|
||||
}
|
||||
|
||||
// Load the fallback catalogues
|
||||
$fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths);
|
||||
|
||||
// Display header line
|
||||
$headers = ['State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)];
|
||||
foreach ($fallbackCatalogues as $fallbackCatalogue) {
|
||||
$headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale());
|
||||
}
|
||||
$rows = [];
|
||||
// Iterate all message ids and determine their state
|
||||
foreach ($allMessages as $domain => $messages) {
|
||||
foreach (array_keys($messages) as $messageId) {
|
||||
$value = $currentCatalogue->get($messageId, $domain);
|
||||
$states = [];
|
||||
|
||||
if ($extractedCatalogue->defines($messageId, $domain)) {
|
||||
if (!$currentCatalogue->defines($messageId, $domain)) {
|
||||
$states[] = self::MESSAGE_MISSING;
|
||||
|
||||
if (!$input->getOption('only-unused')) {
|
||||
$exitCode |= self::EXIT_CODE_MISSING;
|
||||
}
|
||||
}
|
||||
} elseif ($currentCatalogue->defines($messageId, $domain)) {
|
||||
$states[] = self::MESSAGE_UNUSED;
|
||||
|
||||
if (!$input->getOption('only-missing')) {
|
||||
$exitCode |= self::EXIT_CODE_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
if (!\in_array(self::MESSAGE_UNUSED, $states) && $input->getOption('only-unused')
|
||||
|| !\in_array(self::MESSAGE_MISSING, $states) && $input->getOption('only-missing')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($fallbackCatalogues as $fallbackCatalogue) {
|
||||
if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) {
|
||||
$states[] = self::MESSAGE_EQUALS_FALLBACK;
|
||||
|
||||
$exitCode |= self::EXIT_CODE_FALLBACK;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$row = [$this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value)];
|
||||
foreach ($fallbackCatalogues as $fallbackCatalogue) {
|
||||
$row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain));
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$io->table($headers, $rows);
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('locale')) {
|
||||
$suggestions->suggestValues($this->enabledLocales);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
if ($input->mustSuggestArgumentValuesFor('bundle')) {
|
||||
$availableBundles = [];
|
||||
foreach ($kernel->getBundles() as $bundle) {
|
||||
$availableBundles[] = $bundle->getName();
|
||||
|
||||
if ($extension = $bundle->getContainerExtension()) {
|
||||
$availableBundles[] = $extension->getAlias();
|
||||
}
|
||||
}
|
||||
|
||||
$suggestions->suggestValues($availableBundles);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('domain')) {
|
||||
$locale = $input->getArgument('locale');
|
||||
|
||||
$mergeOperation = new MergeOperation(
|
||||
$this->extractMessages($locale, $this->getRootCodePaths($kernel)),
|
||||
$this->loadCurrentMessages($locale, $this->getRootTransPaths())
|
||||
);
|
||||
|
||||
$suggestions->suggestValues($mergeOperation->getDomains());
|
||||
}
|
||||
}
|
||||
|
||||
private function formatState(int $state): string
|
||||
{
|
||||
if (self::MESSAGE_MISSING === $state) {
|
||||
return '<error> missing </error>';
|
||||
}
|
||||
|
||||
if (self::MESSAGE_UNUSED === $state) {
|
||||
return '<comment> unused </comment>';
|
||||
}
|
||||
|
||||
if (self::MESSAGE_EQUALS_FALLBACK === $state) {
|
||||
return '<info> fallback </info>';
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
private function formatStates(array $states): string
|
||||
{
|
||||
$result = [];
|
||||
foreach ($states as $state) {
|
||||
$result[] = $this->formatState($state);
|
||||
}
|
||||
|
||||
return implode(' ', $result);
|
||||
}
|
||||
|
||||
private function formatId(string $id): string
|
||||
{
|
||||
return sprintf('<fg=cyan;options=bold>%s</>', $id);
|
||||
}
|
||||
|
||||
private function sanitizeString(string $string, int $length = 40): string
|
||||
{
|
||||
$string = trim(preg_replace('/\s+/', ' ', $string));
|
||||
|
||||
if (false !== $encoding = mb_detect_encoding($string, null, true)) {
|
||||
if (mb_strlen($string, $encoding) > $length) {
|
||||
return mb_substr($string, 0, $length - 3, $encoding).'...';
|
||||
}
|
||||
} elseif (\strlen($string) > $length) {
|
||||
return substr($string, 0, $length - 3).'...';
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
private function extractMessages(string $locale, array $transPaths): MessageCatalogue
|
||||
{
|
||||
$extractedCatalogue = new MessageCatalogue($locale);
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path) || is_file($path)) {
|
||||
$this->extractor->extract($path, $extractedCatalogue);
|
||||
}
|
||||
}
|
||||
|
||||
return $extractedCatalogue;
|
||||
}
|
||||
|
||||
private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue
|
||||
{
|
||||
$currentCatalogue = new MessageCatalogue($locale);
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path)) {
|
||||
$this->reader->read($path, $currentCatalogue);
|
||||
}
|
||||
}
|
||||
|
||||
return $currentCatalogue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MessageCatalogue[]
|
||||
*/
|
||||
private function loadFallbackCatalogues(string $locale, array $transPaths): array
|
||||
{
|
||||
$fallbackCatalogues = [];
|
||||
if ($this->translator instanceof Translator || $this->translator instanceof DataCollectorTranslator || $this->translator instanceof LoggingTranslator) {
|
||||
foreach ($this->translator->getFallbackLocales() as $fallbackLocale) {
|
||||
if ($fallbackLocale === $locale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fallbackCatalogue = new MessageCatalogue($fallbackLocale);
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path)) {
|
||||
$this->reader->read($path, $fallbackCatalogue);
|
||||
}
|
||||
}
|
||||
$fallbackCatalogues[] = $fallbackCatalogue;
|
||||
}
|
||||
}
|
||||
|
||||
return $fallbackCatalogues;
|
||||
}
|
||||
|
||||
private function getRootTransPaths(): array
|
||||
{
|
||||
$transPaths = $this->transPaths;
|
||||
if ($this->defaultTransPath) {
|
||||
$transPaths[] = $this->defaultTransPath;
|
||||
}
|
||||
|
||||
return $transPaths;
|
||||
}
|
||||
|
||||
private function getRootCodePaths(KernelInterface $kernel): array
|
||||
{
|
||||
$codePaths = $this->codePaths;
|
||||
$codePaths[] = $kernel->getProjectDir().'/src';
|
||||
if ($this->defaultViewsPath) {
|
||||
$codePaths[] = $this->defaultViewsPath;
|
||||
}
|
||||
|
||||
return $codePaths;
|
||||
}
|
||||
}
|
447
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php
vendored
Normal file
447
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Translation\Catalogue\MergeOperation;
|
||||
use Symfony\Component\Translation\Catalogue\TargetOperation;
|
||||
use Symfony\Component\Translation\Extractor\ExtractorInterface;
|
||||
use Symfony\Component\Translation\MessageCatalogue;
|
||||
use Symfony\Component\Translation\MessageCatalogueInterface;
|
||||
use Symfony\Component\Translation\Reader\TranslationReaderInterface;
|
||||
use Symfony\Component\Translation\Writer\TranslationWriterInterface;
|
||||
|
||||
/**
|
||||
* A command that parses templates to extract translation messages and adds them
|
||||
* into the translation files.
|
||||
*
|
||||
* @author Michel Salib <michelsalib@hotmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'translation:extract', description: 'Extract missing translations keys from code to translation files')]
|
||||
class TranslationUpdateCommand extends Command
|
||||
{
|
||||
private const ASC = 'asc';
|
||||
private const DESC = 'desc';
|
||||
private const SORT_ORDERS = [self::ASC, self::DESC];
|
||||
private const FORMATS = [
|
||||
'xlf12' => ['xlf', '1.2'],
|
||||
'xlf20' => ['xlf', '2.0'],
|
||||
];
|
||||
|
||||
private TranslationWriterInterface $writer;
|
||||
private TranslationReaderInterface $reader;
|
||||
private ExtractorInterface $extractor;
|
||||
private string $defaultLocale;
|
||||
private ?string $defaultTransPath;
|
||||
private ?string $defaultViewsPath;
|
||||
private array $transPaths;
|
||||
private array $codePaths;
|
||||
private array $enabledLocales;
|
||||
|
||||
public function __construct(TranslationWriterInterface $writer, TranslationReaderInterface $reader, ExtractorInterface $extractor, string $defaultLocale, ?string $defaultTransPath = null, ?string $defaultViewsPath = null, array $transPaths = [], array $codePaths = [], array $enabledLocales = [])
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (!method_exists($writer, 'getFormats')) {
|
||||
throw new \InvalidArgumentException(sprintf('The writer class "%s" does not implement the "getFormats()" method.', $writer::class));
|
||||
}
|
||||
|
||||
$this->writer = $writer;
|
||||
$this->reader = $reader;
|
||||
$this->extractor = $extractor;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
$this->defaultTransPath = $defaultTransPath;
|
||||
$this->defaultViewsPath = $defaultViewsPath;
|
||||
$this->transPaths = $transPaths;
|
||||
$this->codePaths = $codePaths;
|
||||
$this->enabledLocales = $enabledLocales;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('locale', InputArgument::REQUIRED, 'The locale'),
|
||||
new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages'),
|
||||
new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'),
|
||||
new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'xlf12'),
|
||||
new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'),
|
||||
new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'),
|
||||
new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'),
|
||||
new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to extract'),
|
||||
new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically (only works with --dump-messages)', 'asc'),
|
||||
new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command extracts translation strings from templates
|
||||
of a given bundle or the default translations directory. It can display them or merge
|
||||
the new ones into the translation files.
|
||||
|
||||
When new translation strings are found it can automatically add a prefix to the translation
|
||||
message.
|
||||
|
||||
Example running against a Bundle (AcmeBundle)
|
||||
|
||||
<info>php %command.full_name% --dump-messages en AcmeBundle</info>
|
||||
<info>php %command.full_name% --force --prefix="new_" fr AcmeBundle</info>
|
||||
|
||||
Example running against default messages directory
|
||||
|
||||
<info>php %command.full_name% --dump-messages en</info>
|
||||
<info>php %command.full_name% --force --prefix="new_" fr</info>
|
||||
|
||||
You can sort the output with the <comment>--sort</> flag:
|
||||
|
||||
<info>php %command.full_name% --dump-messages --sort=asc en AcmeBundle</info>
|
||||
<info>php %command.full_name% --dump-messages --sort=desc fr</info>
|
||||
|
||||
You can dump a tree-like structure using the yaml format with <comment>--as-tree</> flag:
|
||||
|
||||
<info>php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
|
||||
// check presence of force or dump-message
|
||||
if (true !== $input->getOption('force') && true !== $input->getOption('dump-messages')) {
|
||||
$errorIo->error('You must choose one of --force or --dump-messages');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$format = $input->getOption('format');
|
||||
$xliffVersion = '1.2';
|
||||
|
||||
if (\array_key_exists($format, self::FORMATS)) {
|
||||
[$format, $xliffVersion] = self::FORMATS[$format];
|
||||
}
|
||||
|
||||
// check format
|
||||
$supportedFormats = $this->writer->getFormats();
|
||||
if (!\in_array($format, $supportedFormats, true)) {
|
||||
$errorIo->error(['Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).', xlf12 and xlf20.']);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
|
||||
// Define Root Paths
|
||||
$transPaths = $this->getRootTransPaths();
|
||||
$codePaths = $this->getRootCodePaths($kernel);
|
||||
|
||||
$currentName = 'default directory';
|
||||
|
||||
// Override with provided Bundle info
|
||||
if (null !== $input->getArgument('bundle')) {
|
||||
try {
|
||||
$foundBundle = $kernel->getBundle($input->getArgument('bundle'));
|
||||
$bundleDir = $foundBundle->getPath();
|
||||
$transPaths = [is_dir($bundleDir.'/Resources/translations') ? $bundleDir.'/Resources/translations' : $bundleDir.'/translations'];
|
||||
$codePaths = [is_dir($bundleDir.'/Resources/views') ? $bundleDir.'/Resources/views' : $bundleDir.'/templates'];
|
||||
if ($this->defaultTransPath) {
|
||||
$transPaths[] = $this->defaultTransPath;
|
||||
}
|
||||
if ($this->defaultViewsPath) {
|
||||
$codePaths[] = $this->defaultViewsPath;
|
||||
}
|
||||
$currentName = $foundBundle->getName();
|
||||
} catch (\InvalidArgumentException) {
|
||||
// such a bundle does not exist, so treat the argument as path
|
||||
$path = $input->getArgument('bundle');
|
||||
|
||||
$transPaths = [$path.'/translations'];
|
||||
$codePaths = [$path.'/templates'];
|
||||
|
||||
if (!is_dir($transPaths[0])) {
|
||||
throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$io->title('Translation Messages Extractor and Dumper');
|
||||
$io->comment(sprintf('Generating "<info>%s</info>" translation files for "<info>%s</info>"', $input->getArgument('locale'), $currentName));
|
||||
|
||||
$io->comment('Parsing templates...');
|
||||
$extractedCatalogue = $this->extractMessages($input->getArgument('locale'), $codePaths, $input->getOption('prefix'));
|
||||
|
||||
$io->comment('Loading translation files...');
|
||||
$currentCatalogue = $this->loadCurrentMessages($input->getArgument('locale'), $transPaths);
|
||||
|
||||
if (null !== $domain = $input->getOption('domain')) {
|
||||
$currentCatalogue = $this->filterCatalogue($currentCatalogue, $domain);
|
||||
$extractedCatalogue = $this->filterCatalogue($extractedCatalogue, $domain);
|
||||
}
|
||||
|
||||
// process catalogues
|
||||
$operation = $input->getOption('clean')
|
||||
? new TargetOperation($currentCatalogue, $extractedCatalogue)
|
||||
: new MergeOperation($currentCatalogue, $extractedCatalogue);
|
||||
|
||||
// Exit if no messages found.
|
||||
if (!\count($operation->getDomains())) {
|
||||
$errorIo->warning('No translation messages were found.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
$resultMessage = 'Translation files were successfully updated';
|
||||
|
||||
$operation->moveMessagesToIntlDomainsIfPossible('new');
|
||||
|
||||
// show compiled list of messages
|
||||
if (true === $input->getOption('dump-messages')) {
|
||||
$extractedMessagesCount = 0;
|
||||
$io->newLine();
|
||||
foreach ($operation->getDomains() as $domain) {
|
||||
$newKeys = array_keys($operation->getNewMessages($domain));
|
||||
$allKeys = array_keys($operation->getMessages($domain));
|
||||
|
||||
$list = array_merge(
|
||||
array_diff($allKeys, $newKeys),
|
||||
array_map(fn ($id) => sprintf('<fg=green>%s</>', $id), $newKeys),
|
||||
array_map(fn ($id) => sprintf('<fg=red>%s</>', $id), array_keys($operation->getObsoleteMessages($domain)))
|
||||
);
|
||||
|
||||
$domainMessagesCount = \count($list);
|
||||
|
||||
if ($sort = $input->getOption('sort')) {
|
||||
$sort = strtolower($sort);
|
||||
if (!\in_array($sort, self::SORT_ORDERS, true)) {
|
||||
$errorIo->error(['Wrong sort order', 'Supported formats are: '.implode(', ', self::SORT_ORDERS).'.']);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (self::DESC === $sort) {
|
||||
rsort($list);
|
||||
} else {
|
||||
sort($list);
|
||||
}
|
||||
}
|
||||
|
||||
$io->section(sprintf('Messages extracted for domain "<info>%s</info>" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : ''));
|
||||
$io->listing($list);
|
||||
|
||||
$extractedMessagesCount += $domainMessagesCount;
|
||||
}
|
||||
|
||||
if ('xlf' === $format) {
|
||||
$io->comment(sprintf('Xliff output version is <info>%s</info>', $xliffVersion));
|
||||
}
|
||||
|
||||
$resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was');
|
||||
}
|
||||
|
||||
// save the files
|
||||
if (true === $input->getOption('force')) {
|
||||
$io->comment('Writing files...');
|
||||
|
||||
$bundleTransPath = false;
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path)) {
|
||||
$bundleTransPath = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bundleTransPath) {
|
||||
$bundleTransPath = end($transPaths);
|
||||
}
|
||||
|
||||
$this->writer->write($operation->getResult(), $format, ['path' => $bundleTransPath, 'default_locale' => $this->defaultLocale, 'xliff_version' => $xliffVersion, 'as_tree' => $input->getOption('as-tree'), 'inline' => $input->getOption('as-tree') ?? 0]);
|
||||
|
||||
if (true === $input->getOption('dump-messages')) {
|
||||
$resultMessage .= ' and translation files were updated';
|
||||
}
|
||||
}
|
||||
|
||||
$io->success($resultMessage.'.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('locale')) {
|
||||
$suggestions->suggestValues($this->enabledLocales);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var KernelInterface $kernel */
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
if ($input->mustSuggestArgumentValuesFor('bundle')) {
|
||||
$bundles = [];
|
||||
|
||||
foreach ($kernel->getBundles() as $bundle) {
|
||||
$bundles[] = $bundle->getName();
|
||||
if ($bundle->getContainerExtension()) {
|
||||
$bundles[] = $bundle->getContainerExtension()->getAlias();
|
||||
}
|
||||
}
|
||||
|
||||
$suggestions->suggestValues($bundles);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('format')) {
|
||||
$suggestions->suggestValues(array_merge(
|
||||
$this->writer->getFormats(),
|
||||
array_keys(self::FORMATS)
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('domain') && $locale = $input->getArgument('locale')) {
|
||||
$extractedCatalogue = $this->extractMessages($locale, $this->getRootCodePaths($kernel), $input->getOption('prefix'));
|
||||
|
||||
$currentCatalogue = $this->loadCurrentMessages($locale, $this->getRootTransPaths());
|
||||
|
||||
// process catalogues
|
||||
$operation = $input->getOption('clean')
|
||||
? new TargetOperation($currentCatalogue, $extractedCatalogue)
|
||||
: new MergeOperation($currentCatalogue, $extractedCatalogue);
|
||||
|
||||
$suggestions->suggestValues($operation->getDomains());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('sort')) {
|
||||
$suggestions->suggestValues(self::SORT_ORDERS);
|
||||
}
|
||||
}
|
||||
|
||||
private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue
|
||||
{
|
||||
$filteredCatalogue = new MessageCatalogue($catalogue->getLocale());
|
||||
|
||||
// extract intl-icu messages only
|
||||
$intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
|
||||
if ($intlMessages = $catalogue->all($intlDomain)) {
|
||||
$filteredCatalogue->add($intlMessages, $intlDomain);
|
||||
}
|
||||
|
||||
// extract all messages and subtract intl-icu messages
|
||||
if ($messages = array_diff($catalogue->all($domain), $intlMessages)) {
|
||||
$filteredCatalogue->add($messages, $domain);
|
||||
}
|
||||
foreach ($catalogue->getResources() as $resource) {
|
||||
$filteredCatalogue->addResource($resource);
|
||||
}
|
||||
|
||||
if ($metadata = $catalogue->getMetadata('', $intlDomain)) {
|
||||
foreach ($metadata as $k => $v) {
|
||||
$filteredCatalogue->setMetadata($k, $v, $intlDomain);
|
||||
}
|
||||
}
|
||||
|
||||
if ($metadata = $catalogue->getMetadata('', $domain)) {
|
||||
foreach ($metadata as $k => $v) {
|
||||
$filteredCatalogue->setMetadata($k, $v, $domain);
|
||||
}
|
||||
}
|
||||
|
||||
return $filteredCatalogue;
|
||||
}
|
||||
|
||||
private function extractMessages(string $locale, array $transPaths, string $prefix): MessageCatalogue
|
||||
{
|
||||
$extractedCatalogue = new MessageCatalogue($locale);
|
||||
$this->extractor->setPrefix($prefix);
|
||||
$transPaths = $this->filterDuplicateTransPaths($transPaths);
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path) || is_file($path)) {
|
||||
$this->extractor->extract($path, $extractedCatalogue);
|
||||
}
|
||||
}
|
||||
|
||||
return $extractedCatalogue;
|
||||
}
|
||||
|
||||
private function filterDuplicateTransPaths(array $transPaths): array
|
||||
{
|
||||
$transPaths = array_filter(array_map('realpath', $transPaths));
|
||||
|
||||
sort($transPaths);
|
||||
|
||||
$filteredPaths = [];
|
||||
|
||||
foreach ($transPaths as $path) {
|
||||
foreach ($filteredPaths as $filteredPath) {
|
||||
if (str_starts_with($path, $filteredPath.\DIRECTORY_SEPARATOR)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$filteredPaths[] = $path;
|
||||
}
|
||||
|
||||
return $filteredPaths;
|
||||
}
|
||||
|
||||
private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue
|
||||
{
|
||||
$currentCatalogue = new MessageCatalogue($locale);
|
||||
foreach ($transPaths as $path) {
|
||||
if (is_dir($path)) {
|
||||
$this->reader->read($path, $currentCatalogue);
|
||||
}
|
||||
}
|
||||
|
||||
return $currentCatalogue;
|
||||
}
|
||||
|
||||
private function getRootTransPaths(): array
|
||||
{
|
||||
$transPaths = $this->transPaths;
|
||||
if ($this->defaultTransPath) {
|
||||
$transPaths[] = $this->defaultTransPath;
|
||||
}
|
||||
|
||||
return $transPaths;
|
||||
}
|
||||
|
||||
private function getRootCodePaths(KernelInterface $kernel): array
|
||||
{
|
||||
$codePaths = $this->codePaths;
|
||||
$codePaths[] = $kernel->getProjectDir().'/src';
|
||||
if ($this->defaultViewsPath) {
|
||||
$codePaths[] = $this->defaultViewsPath;
|
||||
}
|
||||
|
||||
return $codePaths;
|
||||
}
|
||||
}
|
161
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php
vendored
Normal file
161
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Completion\CompletionInput;
|
||||
use Symfony\Component\Console\Completion\CompletionSuggestions;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use Symfony\Component\Workflow\Definition;
|
||||
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
|
||||
use Symfony\Component\Workflow\Dumper\MermaidDumper;
|
||||
use Symfony\Component\Workflow\Dumper\PlantUmlDumper;
|
||||
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
|
||||
use Symfony\Component\Workflow\Marking;
|
||||
use Symfony\Component\Workflow\StateMachine;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'workflow:dump', description: 'Dump a workflow')]
|
||||
class WorkflowDumpCommand extends Command
|
||||
{
|
||||
/**
|
||||
* string is the service id.
|
||||
*
|
||||
* @var array<string, Definition>
|
||||
*/
|
||||
private array $definitions = [];
|
||||
|
||||
private ServiceLocator $workflows;
|
||||
|
||||
private const DUMP_FORMAT_OPTIONS = [
|
||||
'puml',
|
||||
'mermaid',
|
||||
'dot',
|
||||
];
|
||||
|
||||
public function __construct($workflows)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if ($workflows instanceof ServiceLocator) {
|
||||
$this->workflows = $workflows;
|
||||
} elseif (\is_array($workflows)) {
|
||||
$this->definitions = $workflows;
|
||||
trigger_deprecation('symfony/framework-bundle', '6.2', 'Passing an array of definitions in "%s()" is deprecated. Inject a ServiceLocator filled with all workflows instead.', __METHOD__);
|
||||
} else {
|
||||
throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be an array or a ServiceLocator, "%s" given.', __METHOD__, \gettype($workflows)));
|
||||
}
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->setDefinition([
|
||||
new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'),
|
||||
new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'),
|
||||
new InputOption('label', 'l', InputOption::VALUE_REQUIRED, 'Label a graph'),
|
||||
new InputOption('with-metadata', null, InputOption::VALUE_NONE, 'Include the workflow\'s metadata in the dumped graph', null),
|
||||
new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format ['.implode('|', self::DUMP_FORMAT_OPTIONS).']', 'dot'),
|
||||
])
|
||||
->setHelp(<<<'EOF'
|
||||
The <info>%command.name%</info> command dumps the graphical representation of a
|
||||
workflow in different formats
|
||||
|
||||
<info>DOT</info>: %command.full_name% <workflow name> | dot -Tpng > workflow.png
|
||||
<info>PUML</info>: %command.full_name% <workflow name> --dump-format=puml | java -jar plantuml.jar -p > workflow.png
|
||||
<info>MERMAID</info>: %command.full_name% <workflow name> --dump-format=mermaid | mmdc -o workflow.svg
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$workflowName = $input->getArgument('name');
|
||||
|
||||
if (isset($this->workflows)) {
|
||||
if (!$this->workflows->has($workflowName)) {
|
||||
throw new InvalidArgumentException(sprintf('The workflow named "%s" cannot be found.', $workflowName));
|
||||
}
|
||||
$workflow = $this->workflows->get($workflowName);
|
||||
$type = $workflow instanceof StateMachine ? 'state_machine' : 'workflow';
|
||||
$definition = $workflow->getDefinition();
|
||||
} elseif (isset($this->definitions['workflow.'.$workflowName])) {
|
||||
$definition = $this->definitions['workflow.'.$workflowName];
|
||||
$type = 'workflow';
|
||||
} elseif (isset($this->definitions['state_machine.'.$workflowName])) {
|
||||
$definition = $this->definitions['state_machine.'.$workflowName];
|
||||
$type = 'state_machine';
|
||||
}
|
||||
|
||||
if (null === $definition) {
|
||||
throw new InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $workflowName));
|
||||
}
|
||||
|
||||
switch ($input->getOption('dump-format')) {
|
||||
case 'puml':
|
||||
$transitionType = 'workflow' === $type ? PlantUmlDumper::WORKFLOW_TRANSITION : PlantUmlDumper::STATEMACHINE_TRANSITION;
|
||||
$dumper = new PlantUmlDumper($transitionType);
|
||||
break;
|
||||
|
||||
case 'mermaid':
|
||||
$transitionType = 'workflow' === $type ? MermaidDumper::TRANSITION_TYPE_WORKFLOW : MermaidDumper::TRANSITION_TYPE_STATEMACHINE;
|
||||
$dumper = new MermaidDumper($transitionType);
|
||||
break;
|
||||
|
||||
case 'dot':
|
||||
default:
|
||||
$dumper = ('workflow' === $type) ? new GraphvizDumper() : new StateMachineGraphvizDumper();
|
||||
}
|
||||
|
||||
$marking = new Marking();
|
||||
|
||||
foreach ($input->getArgument('marking') as $place) {
|
||||
$marking->mark($place);
|
||||
}
|
||||
|
||||
$options = [
|
||||
'name' => $workflowName,
|
||||
'with-metadata' => $input->getOption('with-metadata'),
|
||||
'nofooter' => true,
|
||||
'label' => $input->getOption('label'),
|
||||
];
|
||||
$output->writeln($dumper->dump($definition, $marking, $options));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
|
||||
{
|
||||
if ($input->mustSuggestArgumentValuesFor('name')) {
|
||||
if (isset($this->workflows)) {
|
||||
$suggestions->suggestValues(array_keys($this->workflows->getProvidedServices()));
|
||||
} else {
|
||||
$suggestions->suggestValues(array_keys($this->definitions));
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->mustSuggestOptionValuesFor('dump-format')) {
|
||||
$suggestions->suggestValues(self::DUMP_FORMAT_OPTIONS);
|
||||
}
|
||||
}
|
||||
}
|
57
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/XliffLintCommand.php
vendored
Normal file
57
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/XliffLintCommand.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Translation\Command\XliffLintCommand as BaseLintCommand;
|
||||
|
||||
/**
|
||||
* Validates XLIFF files syntax and outputs encountered errors.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'lint:xliff', description: 'Lint an XLIFF file and outputs encountered errors')]
|
||||
class XliffLintCommand extends BaseLintCommand
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$directoryIteratorProvider = function ($directory, $default) {
|
||||
if (!is_dir($directory)) {
|
||||
$directory = $this->getApplication()->getKernel()->locateResource($directory);
|
||||
}
|
||||
|
||||
return $default($directory);
|
||||
};
|
||||
|
||||
$isReadableProvider = fn ($fileOrDirectory, $default) => str_starts_with($fileOrDirectory, '@') || $default($fileOrDirectory);
|
||||
|
||||
parent::__construct(null, $directoryIteratorProvider, $isReadableProvider);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this->setHelp($this->getHelp().<<<'EOF'
|
||||
|
||||
Or find all files in a bundle:
|
||||
|
||||
<info>php %command.full_name% @AcmeDemoBundle</info>
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
56
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/YamlLintCommand.php
vendored
Normal file
56
plugins/simplesaml/lib/vendor/symfony/framework-bundle/Command/YamlLintCommand.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Yaml\Command\LintCommand as BaseLintCommand;
|
||||
|
||||
/**
|
||||
* Validates YAML files syntax and outputs encountered errors.
|
||||
*
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
#[AsCommand(name: 'lint:yaml', description: 'Lint a YAML file and outputs encountered errors')]
|
||||
class YamlLintCommand extends BaseLintCommand
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$directoryIteratorProvider = function ($directory, $default) {
|
||||
if (!is_dir($directory)) {
|
||||
$directory = $this->getApplication()->getKernel()->locateResource($directory);
|
||||
}
|
||||
|
||||
return $default($directory);
|
||||
};
|
||||
|
||||
$isReadableProvider = fn ($fileOrDirectory, $default) => str_starts_with($fileOrDirectory, '@') || $default($fileOrDirectory);
|
||||
|
||||
parent::__construct(null, $directoryIteratorProvider, $isReadableProvider);
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this->setHelp($this->getHelp().<<<'EOF'
|
||||
|
||||
Or find all files in a bundle:
|
||||
|
||||
<info>php %command.full_name% @AcmeDemoBundle</info>
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user