migration symfony 5 4 (#300)

* symfony 5.4 (diff dev)

* symfony 5.4 (working)

* symfony 5.4 (update autoload)

* symfony 5.4 (remove swiftmailer mailer implementation)

* symfony 5.4 (php doc and split Global accessor class)


### Impacted packages:

composer require php:">=7.2.5 <8.0.0" symfony/console:5.4.* symfony/dotenv:5.4.* symfony/framework-bundle:5.4.* symfony/twig-bundle:5.4.* symfony/yaml:5.4.* --update-with-dependencies

composer require symfony/stopwatch:5.4.* symfony/web-profiler-bundle:5.4.* --dev --update-with-dependencies
This commit is contained in:
bdalsass
2022-06-16 09:13:24 +02:00
committed by GitHub
parent abb13b70b9
commit 79da71ecf8
2178 changed files with 87439 additions and 59451 deletions

View File

@@ -13,12 +13,12 @@ namespace Symfony\Bundle\FrameworkBundle\Console;
use Symfony\Component\Console\Application as BaseApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\ListCommand;
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\Debug\Exception\FatalThrowableError;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\Kernel;
@@ -41,19 +41,29 @@ class Application extends BaseApplication
$inputDefinition = $this->getDefinition();
$inputDefinition->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', $kernel->getEnvironment()));
$inputDefinition->addOption(new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switches off debug mode.'));
$inputDefinition->addOption(new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switch off debug mode.'));
}
/**
* Gets the Kernel associated with this Console.
*
* @return KernelInterface A KernelInterface instance
* @return KernelInterface
*/
public function getKernel()
{
return $this->kernel;
}
/**
* {@inheritdoc}
*/
public function reset()
{
if ($this->kernel->getContainer()->has('services_resetter')) {
$this->kernel->getContainer()->get('services_resetter')->reset();
}
}
/**
* Runs the current application.
*
@@ -61,16 +71,14 @@ class Application extends BaseApplication
*/
public function doRun(InputInterface $input, OutputInterface $output)
{
$this->kernel->boot();
$this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher'));
$this->registerCommands();
if ($this->registrationErrors) {
$this->renderRegistrationErrors($input, $output);
}
$this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher'));
return parent::doRun($input, $output);
}
@@ -79,17 +87,29 @@ class Application extends BaseApplication
*/
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
if ($this->registrationErrors) {
$this->renderRegistrationErrors($input, $output);
if (!$command instanceof ListCommand) {
if ($this->registrationErrors) {
$this->renderRegistrationErrors($input, $output);
$this->registrationErrors = [];
}
return parent::doRunCommand($command, $input, $output);
}
return parent::doRunCommand($command, $input, $output);
$returnCode = parent::doRunCommand($command, $input, $output);
if ($this->registrationErrors) {
$this->renderRegistrationErrors($input, $output);
$this->registrationErrors = [];
}
return $returnCode;
}
/**
* {@inheritdoc}
*/
public function find($name)
public function find(string $name)
{
$this->registerCommands();
@@ -99,7 +119,7 @@ class Application extends BaseApplication
/**
* {@inheritdoc}
*/
public function get($name)
public function get(string $name)
{
$this->registerCommands();
@@ -115,7 +135,7 @@ class Application extends BaseApplication
/**
* {@inheritdoc}
*/
public function all($namespace = null)
public function all(string $namespace = null)
{
$this->registerCommands();
@@ -127,7 +147,7 @@ class Application extends BaseApplication
*/
public function getLongVersion()
{
return parent::getLongVersion().sprintf(' (kernel: <comment>%s</>, env: <comment>%s</>, debug: <comment>%s</>)', $this->kernel->getName(), $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false');
return parent::getLongVersion().sprintf(' (env: <comment>%s</>, debug: <comment>%s</>) <bg=#0057B7;fg=#FFDD00>#StandWith</><bg=#FFDD00;fg=#0057B7>Ukraine</> <href=https://sf.to/ukraine>https://sf.to/ukraine</>', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false');
}
public function add(Command $command)
@@ -153,10 +173,8 @@ class Application extends BaseApplication
if ($bundle instanceof Bundle) {
try {
$bundle->registerCommands($this);
} catch (\Exception $e) {
$this->registrationErrors[] = $e;
} catch (\Throwable $e) {
$this->registrationErrors[] = new FatalThrowableError($e);
$this->registrationErrors[] = $e;
}
}
}
@@ -171,10 +189,8 @@ class Application extends BaseApplication
if (!isset($lazyCommandIds[$id])) {
try {
$this->add($container->get($id));
} catch (\Exception $e) {
$this->registrationErrors[] = $e;
} catch (\Throwable $e) {
$this->registrationErrors[] = new FatalThrowableError($e);
$this->registrationErrors[] = $e;
}
}
}
@@ -190,9 +206,7 @@ class Application extends BaseApplication
(new SymfonyStyle($input, $output))->warning('Some commands could not be registered:');
foreach ($this->registrationErrors as $error) {
$this->doRenderException($error, $output);
$this->doRenderThrowable($error, $output);
}
$this->registrationErrors = [];
}
}

View File

@@ -11,10 +11,12 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Console\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -50,6 +52,9 @@ abstract class Descriptor implements DescriptorInterface
case $object instanceof ParameterBag:
$this->describeContainerParameters($object, $options);
break;
case $object instanceof ContainerBuilder && !empty($options['env-vars']):
$this->describeContainerEnvVars($this->getContainerEnvVars($object), $options);
break;
case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']:
$this->describeContainerTags($object, $options);
break;
@@ -59,6 +64,9 @@ abstract class Descriptor implements DescriptorInterface
case $object instanceof ContainerBuilder && isset($options['parameter']):
$this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options);
break;
case $object instanceof ContainerBuilder && isset($options['deprecations']):
$this->describeContainerDeprecations($object, $options);
break;
case $object instanceof ContainerBuilder:
$this->describeContainerServices($object, $options);
break;
@@ -75,27 +83,16 @@ abstract class Descriptor implements DescriptorInterface
$this->describeCallable($object, $options);
break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', \get_class($object)));
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object)));
}
}
/**
* Returns the output.
*
* @return OutputInterface The output
*/
protected function getOutput()
protected function getOutput(): OutputInterface
{
return $this->output;
}
/**
* Writes content to output.
*
* @param string $content
* @param bool $decorated
*/
protected function write($content, $decorated = false)
protected function write(string $content, bool $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
@@ -116,7 +113,7 @@ abstract class Descriptor implements DescriptorInterface
*
* @param Definition|Alias|object $service
*/
abstract protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null);
abstract protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null);
/**
* Describes container services.
@@ -126,12 +123,16 @@ abstract class Descriptor implements DescriptorInterface
*/
abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = []);
abstract protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void;
abstract protected function describeContainerDefinition(Definition $definition, array $options = []);
abstract protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null);
abstract protected function describeContainerParameter($parameter, array $options = []);
abstract protected function describeContainerEnvVars(array $envs, array $options = []);
/**
* Describes event dispatcher listeners.
*
@@ -151,11 +152,13 @@ abstract class Descriptor implements DescriptorInterface
* Formats a value as string.
*
* @param mixed $value
*
* @return string
*/
protected function formatValue($value)
protected function formatValue($value): string
{
if ($value instanceof \UnitEnum) {
return ltrim(var_export($value, true), '\\');
}
if (\is_object($value)) {
return sprintf('object(%s)', \get_class($value));
}
@@ -171,11 +174,23 @@ abstract class Descriptor implements DescriptorInterface
* Formats a parameter.
*
* @param mixed $value
*
* @return string
*/
protected function formatParameter($value)
protected function formatParameter($value): string
{
if ($value instanceof \UnitEnum) {
return ltrim(var_export($value, true), '\\');
}
// Recursively search for enum values, so we can replace it
// before json_encode (which will not display anything for \UnitEnum otherwise)
if (\is_array($value)) {
array_walk_recursive($value, static function (&$value) {
if ($value instanceof \UnitEnum) {
$value = ltrim(var_export($value, true), '\\');
}
});
}
if (\is_bool($value) || \is_array($value) || (null === $value)) {
$jsonString = json_encode($value);
@@ -190,11 +205,9 @@ abstract class Descriptor implements DescriptorInterface
}
/**
* @param string $serviceId
*
* @return mixed
*/
protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceId)
protected function resolveServiceDefinition(ContainerBuilder $builder, string $serviceId)
{
if ($builder->hasDefinition($serviceId)) {
return $builder->getDefinition($serviceId);
@@ -205,16 +218,15 @@ abstract class Descriptor implements DescriptorInterface
return $builder->getAlias($serviceId);
}
if ('service_container' === $serviceId) {
return (new Definition(ContainerInterface::class))->setPublic(true)->setSynthetic(true);
}
// the service has been injected in some special way, just return the service
return $builder->get($serviceId);
}
/**
* @param bool $showPrivate
*
* @return array
*/
protected function findDefinitionsByTag(ContainerBuilder $builder, $showPrivate)
protected function findDefinitionsByTag(ContainerBuilder $builder, bool $showHidden): array
{
$definitions = [];
$tags = $builder->findTags();
@@ -224,7 +236,7 @@ abstract class Descriptor implements DescriptorInterface
foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
if (!$definition instanceof Definition || !$showPrivate && !$definition->isPublic()) {
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
continue;
}
@@ -253,4 +265,116 @@ abstract class Descriptor implements DescriptorInterface
return $serviceIds;
}
protected function sortTaggedServicesByPriority(array $services): array
{
$maxPriority = [];
foreach ($services as $service => $tags) {
$maxPriority[$service] = \PHP_INT_MIN;
foreach ($tags as $tag) {
$currentPriority = $tag['priority'] ?? 0;
if ($maxPriority[$service] < $currentPriority) {
$maxPriority[$service] = $currentPriority;
}
}
}
uasort($maxPriority, function ($a, $b) {
return $b <=> $a;
});
return array_keys($maxPriority);
}
protected function sortTagsByPriority(array $tags): array
{
$sortedTags = [];
foreach ($tags as $tagName => $tag) {
$sortedTags[$tagName] = $this->sortByPriority($tag);
}
return $sortedTags;
}
protected function sortByPriority(array $tag): array
{
usort($tag, function ($a, $b) {
return ($b['priority'] ?? 0) <=> ($a['priority'] ?? 0);
});
return $tag;
}
public static function getClassDescription(string $class, string &$resolvedClass = null): string
{
$resolvedClass = $class;
try {
$resource = new ClassExistenceResource($class, false);
// isFresh() will explode ONLY if a parent class/trait does not exist
$resource->isFresh(0);
$r = new \ReflectionClass($class);
$resolvedClass = $r->name;
if ($docComment = $r->getDocComment()) {
$docComment = preg_split('#\n\s*\*\s*[\n@]#', substr($docComment, 3, -2), 2)[0];
return trim(preg_replace('#\s*\n\s*\*\s*#', ' ', $docComment));
}
} catch (\ReflectionException $e) {
}
return '';
}
private function getContainerEnvVars(ContainerBuilder $container): array
{
if (!$container->hasParameter('debug.container.dump')) {
return [];
}
if (!is_file($container->getParameter('debug.container.dump'))) {
return [];
}
$file = file_get_contents($container->getParameter('debug.container.dump'));
preg_match_all('{%env\(((?:\w++:)*+\w++)\)%}', $file, $envVars);
$envVars = array_unique($envVars[1]);
$bag = $container->getParameterBag();
$getDefaultParameter = function (string $name) {
return parent::get($name);
};
$getDefaultParameter = $getDefaultParameter->bindTo($bag, \get_class($bag));
$getEnvReflection = new \ReflectionMethod($container, 'getEnv');
$getEnvReflection->setAccessible(true);
$envs = [];
foreach ($envVars as $env) {
$processor = 'string';
if (false !== $i = strrpos($name = $env, ':')) {
$name = substr($env, $i + 1);
$processor = substr($env, 0, $i);
}
$defaultValue = ($hasDefault = $container->hasParameter("env($name)")) ? $getDefaultParameter("env($name)") : null;
if (false === ($runtimeValue = $_ENV[$name] ?? $_SERVER[$name] ?? getenv($name))) {
$runtimeValue = null;
}
$processedValue = ($hasRuntime = null !== $runtimeValue) || $hasDefault ? $getEnvReflection->invoke($container, $env) : null;
$envs["$name$processor"] = [
'name' => $name,
'processor' => $processor,
'default_available' => $hasDefault,
'default_value' => $defaultValue,
'runtime_available' => $hasRuntime,
'runtime_value' => $runtimeValue,
'processed_value' => $processedValue,
];
}
ksort($envs);
return array_values($envs);
}
}

View File

@@ -11,7 +11,10 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -51,10 +54,10 @@ class JsonDescriptor extends Descriptor
protected function describeContainerTags(ContainerBuilder $builder, array $options = [])
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
$data = [];
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) {
$data[$tag] = [];
foreach ($definitions as $definition) {
$data[$tag][] = $this->getContainerDefinitionData($definition, true);
@@ -64,10 +67,7 @@ class JsonDescriptor extends Descriptor
$this->writeData($data, $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null)
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null)
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -82,13 +82,12 @@ class JsonDescriptor extends Descriptor
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
{
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$showPrivate = isset($options['show_private']) && $options['show_private'];
$serviceIds = isset($options['tag']) && $options['tag']
? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag']))
: $this->sortServiceIds($builder->getServiceIds());
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
$omitTags = isset($options['omit_tags']) && $options['omit_tags'];
$showArguments = isset($options['show_arguments']) && $options['show_arguments'];
$data = ['definitions' => [], 'aliases' => [], 'services' => []];
@@ -97,17 +96,17 @@ class JsonDescriptor extends Descriptor
$serviceIds = array_filter($serviceIds, $options['filter']);
}
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
foreach ($serviceIds as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
continue;
}
if ($service instanceof Alias) {
if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) {
$data['aliases'][$serviceId] = $this->getContainerAliasData($service);
}
$data['aliases'][$serviceId] = $this->getContainerAliasData($service);
} elseif ($service instanceof Definition) {
if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) {
$data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments);
}
$data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments);
} else {
$data['services'][$serviceId] = \get_class($service);
}
@@ -135,17 +134,11 @@ class JsonDescriptor extends Descriptor
);
}
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
$this->writeData($this->getEventDispatcherListenersData($eventDispatcher, \array_key_exists('event', $options) ? $options['event'] : null), $options);
$this->writeData($this->getEventDispatcherListenersData($eventDispatcher, $options), $options);
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = [])
{
$this->writeData($this->getCallableData($callable), $options);
@@ -153,27 +146,58 @@ class JsonDescriptor extends Descriptor
protected function describeContainerParameter($parameter, array $options = [])
{
$key = isset($options['parameter']) ? $options['parameter'] : '';
$key = $options['parameter'] ?? '';
$this->writeData([$key => $parameter], $options);
}
/**
* Writes data as json.
*/
protected function describeContainerEnvVars(array $envs, array $options = [])
{
throw new LogicException('Using the JSON format to debug environment variables is not supported.');
}
protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void
{
$containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class'));
if (!file_exists($containerDeprecationFilePath)) {
throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.');
}
$logs = unserialize(file_get_contents($containerDeprecationFilePath));
$formattedLogs = [];
$remainingCount = 0;
foreach ($logs as $log) {
$formattedLogs[] = [
'message' => $log['message'],
'file' => $log['file'],
'line' => $log['line'],
'count' => $log['count'],
];
$remainingCount += $log['count'];
}
$this->writeData(['remainingCount' => $remainingCount, 'deprecations' => $formattedLogs], $options);
}
private function writeData(array $data, array $options)
{
$flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0;
$flags = $options['json_encoding'] ?? 0;
// Recursively search for enum values, so we can replace it
// before json_encode (which will not display anything for \UnitEnum otherwise)
array_walk_recursive($data, static function (&$value) {
if ($value instanceof \UnitEnum) {
$value = ltrim(var_export($value, true), '\\');
}
});
$this->write(json_encode($data, $flags | \JSON_PRETTY_PRINT)."\n");
}
/**
* @return array
*/
protected function getRouteData(Route $route)
protected function getRouteData(Route $route): array
{
return [
$data = [
'path' => $route->getPath(),
'pathRegex' => $route->compile()->getRegex(),
'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY',
@@ -185,14 +209,15 @@ class JsonDescriptor extends Descriptor
'requirements' => $route->getRequirements() ?: 'NO CUSTOM',
'options' => $route->getOptions(),
];
if ('' !== $route->getCondition()) {
$data['condition'] = $route->getCondition();
}
return $data;
}
/**
* @param bool $omitTags
*
* @return array
*/
private function getContainerDefinitionData(Definition $definition, $omitTags = false, $showArguments = false)
private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false): array
{
$data = [
'class' => (string) $definition->getClass(),
@@ -205,11 +230,8 @@ class JsonDescriptor extends Descriptor
'autoconfigure' => $definition->isAutoconfigured(),
];
// forward compatibility with DependencyInjection component in version 4.0
if (method_exists($definition, 'getAutowiringTypes')) {
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
$data['autowiring_types'][] = $autowiringType;
}
if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) {
$data['description'] = $classDescription;
}
if ($showArguments) {
@@ -243,7 +265,7 @@ class JsonDescriptor extends Descriptor
if (!$omitTags) {
$data['tags'] = [];
foreach ($definition->getTags() as $tagName => $tagData) {
foreach ($this->sortTagsByPriority($definition->getTags()) as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$data['tags'][] = ['name' => $tagName, 'parameters' => $parameters];
}
@@ -253,10 +275,7 @@ class JsonDescriptor extends Descriptor
return $data;
}
/**
* @return array
*/
private function getContainerAliasData(Alias $alias)
private function getContainerAliasData(Alias $alias): array
{
return [
'service' => (string) $alias,
@@ -264,23 +283,19 @@ class JsonDescriptor extends Descriptor
];
}
/**
* @param string|null $event
*
* @return array
*/
private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, $event = null)
private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, array $options): array
{
$data = [];
$event = \array_key_exists('event', $options) ? $options['event'] : null;
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
foreach ($registeredListeners as $listener) {
foreach ($eventDispatcher->getListeners($event) as $listener) {
$l = $this->getCallableData($listener);
$l['priority'] = $eventDispatcher->getListenerPriority($event, $listener);
$data[] = $l;
}
} else {
$registeredListeners = \array_key_exists('events', $options) ? array_combine($options['events'], array_map(function ($event) use ($eventDispatcher) { return $eventDispatcher->getListeners($event); }, $options['events'])) : $eventDispatcher->getListeners();
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
@@ -295,12 +310,7 @@ class JsonDescriptor extends Descriptor
return $data;
}
/**
* @param callable $callable
*
* @return array
*/
private function getCallableData($callable)
private function getCallableData($callable): array
{
$data = [];
@@ -311,7 +321,7 @@ class JsonDescriptor extends Descriptor
$data['name'] = $callable[1];
$data['class'] = \get_class($callable[0]);
} else {
if (0 !== strpos($callable[1], 'parent::')) {
if (!str_starts_with($callable[1], 'parent::')) {
$data['name'] = $callable[1];
$data['class'] = $callable[0];
$data['static'] = true;
@@ -329,7 +339,7 @@ class JsonDescriptor extends Descriptor
if (\is_string($callable)) {
$data['type'] = 'function';
if (false === strpos($callable, '::')) {
if (!str_contains($callable, '::')) {
$data['name'] = $callable;
} else {
$callableParts = explode('::', $callable);
@@ -346,7 +356,7 @@ class JsonDescriptor extends Descriptor
$data['type'] = 'closure';
$r = new \ReflectionFunction($callable);
if (false !== strpos($r->name, '{closure}')) {
if (str_contains($r->name, '{closure}')) {
return $data;
}
$data['name'] = $r->name;
@@ -371,7 +381,7 @@ class JsonDescriptor extends Descriptor
throw new \InvalidArgumentException('Callable is not describable.');
}
private function describeValue($value, $omitTags, $showArguments)
private function describeValue($value, bool $omitTags, bool $showArguments)
{
if (\is_array($value)) {
$data = [];
@@ -393,6 +403,10 @@ class JsonDescriptor extends Descriptor
];
}
if ($value instanceof AbstractArgument) {
return ['type' => 'abstract', 'text' => $value->getText()];
}
if ($value instanceof ArgumentInterface) {
return $this->describeValue($value->getValues(), $omitTags, $showArguments);
}

View File

@@ -11,6 +11,8 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
@@ -54,6 +56,10 @@ class MarkdownDescriptor extends Descriptor
."\n".'- Requirements: '.($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM')
."\n".'- Options: '.$this->formatRouterConfig($route->getOptions());
if ('' !== $route->getCondition()) {
$output .= "\n".'- Condition: '.$route->getCondition();
}
$this->write(isset($options['name'])
? $options['name']."\n".str_repeat('-', \strlen($options['name']))."\n\n".$output
: $output);
@@ -70,10 +76,10 @@ class MarkdownDescriptor extends Descriptor
protected function describeContainerTags(ContainerBuilder $builder, array $options = [])
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
$this->write("Container tags\n==============");
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) {
$this->write("\n\n".$tag."\n".str_repeat('-', \strlen($tag)));
foreach ($definitions as $serviceId => $definition) {
$this->write("\n\n");
@@ -82,10 +88,7 @@ class MarkdownDescriptor extends Descriptor
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null)
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null)
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -102,20 +105,46 @@ class MarkdownDescriptor extends Descriptor
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void
{
$containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class'));
if (!file_exists($containerDeprecationFilePath)) {
throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.');
}
$logs = unserialize(file_get_contents($containerDeprecationFilePath));
if (0 === \count($logs)) {
$this->write("## There are no deprecations in the logs!\n");
return;
}
$formattedLogs = [];
$remainingCount = 0;
foreach ($logs as $log) {
$formattedLogs[] = sprintf("- %sx: \"%s\" in %s:%s\n", $log['count'], $log['message'], $log['file'], $log['line']);
$remainingCount += $log['count'];
}
$this->write(sprintf("## Remaining deprecations (%s)\n\n", $remainingCount));
foreach ($formattedLogs as $formattedLog) {
$this->write($formattedLog);
}
}
protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
$title = $showPrivate ? 'Public and private services' : 'Public services';
$title = $showHidden ? 'Hidden services' : 'Services';
if (isset($options['tag'])) {
$title .= ' with tag `'.$options['tag'].'`';
}
$this->write($title."\n".str_repeat('=', \strlen($title)));
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$serviceIds = isset($options['tag']) && $options['tag']
? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag']))
: $this->sortServiceIds($builder->getServiceIds());
$showArguments = isset($options['show_arguments']) && $options['show_arguments'];
$services = ['definitions' => [], 'aliases' => [], 'services' => []];
@@ -123,17 +152,17 @@ class MarkdownDescriptor extends Descriptor
$serviceIds = array_filter($serviceIds, $options['filter']);
}
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
foreach ($serviceIds as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
continue;
}
if ($service instanceof Alias) {
if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) {
$services['aliases'][$serviceId] = $service;
}
$services['aliases'][$serviceId] = $service;
} elseif ($service instanceof Definition) {
if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) {
$services['definitions'][$serviceId] = $service;
}
$services['definitions'][$serviceId] = $service;
} else {
$services['services'][$serviceId] = $service;
}
@@ -166,7 +195,13 @@ class MarkdownDescriptor extends Descriptor
protected function describeContainerDefinition(Definition $definition, array $options = [])
{
$output = '- Class: `'.$definition->getClass().'`'
$output = '';
if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) {
$output .= '- Description: `'.$classDescription.'`'."\n";
}
$output .= '- Class: `'.$definition->getClass().'`'
."\n".'- Public: '.($definition->isPublic() && !$definition->isPrivate() ? 'yes' : 'no')
."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no')
."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no')
@@ -176,13 +211,6 @@ class MarkdownDescriptor extends Descriptor
."\n".'- Autoconfigured: '.($definition->isAutoconfigured() ? 'yes' : 'no')
;
// forward compatibility with DependencyInjection component in version 4.0
if (method_exists($definition, 'getAutowiringTypes')) {
foreach ($definition->getAutowiringTypes(false) as $autowiringType) {
$output .= "\n".'- Autowiring Type: `'.$autowiringType.'`';
}
}
if (isset($options['show_arguments']) && $options['show_arguments']) {
$output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no');
}
@@ -212,7 +240,7 @@ class MarkdownDescriptor extends Descriptor
}
if (!(isset($options['omit_tags']) && $options['omit_tags'])) {
foreach ($definition->getTags() as $tagName => $tagData) {
foreach ($this->sortTagsByPriority($definition->getTags()) as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$output .= "\n".'- Tag: `'.$tagName.'`';
foreach ($parameters as $name => $value) {
@@ -251,21 +279,32 @@ class MarkdownDescriptor extends Descriptor
$this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter);
}
/**
* {@inheritdoc}
*/
protected function describeContainerEnvVars(array $envs, array $options = [])
{
throw new LogicException('Using the markdown format to debug environment variables is not supported.');
}
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
$event = \array_key_exists('event', $options) ? $options['event'] : null;
$event = $options['event'] ?? null;
$dispatcherServiceName = $options['dispatcher_service_name'] ?? null;
$title = 'Registered listeners';
if (null !== $dispatcherServiceName) {
$title .= sprintf(' of event dispatcher "%s"', $dispatcherServiceName);
}
if (null !== $event) {
$title .= sprintf(' for event `%s` ordered by descending priority', $event);
$registeredListeners = $eventDispatcher->getListeners($event);
} else {
// Try to see if "events" exists
$registeredListeners = \array_key_exists('events', $options) ? array_combine($options['events'], array_map(function ($event) use ($eventDispatcher) { return $eventDispatcher->getListeners($event); }, $options['events'])) : $eventDispatcher->getListeners();
}
$this->write(sprintf('# %s', $title)."\n");
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
foreach ($registeredListeners as $order => $listener) {
$this->write("\n".sprintf('## Listener %d', $order + 1)."\n");
@@ -287,9 +326,6 @@ class MarkdownDescriptor extends Descriptor
}
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = [])
{
$string = '';
@@ -301,7 +337,7 @@ class MarkdownDescriptor extends Descriptor
$string .= "\n".sprintf('- Name: `%s`', $callable[1]);
$string .= "\n".sprintf('- Class: `%s`', \get_class($callable[0]));
} else {
if (0 !== strpos($callable[1], 'parent::')) {
if (!str_starts_with($callable[1], 'parent::')) {
$string .= "\n".sprintf('- Name: `%s`', $callable[1]);
$string .= "\n".sprintf('- Class: `%s`', $callable[0]);
$string .= "\n- Static: yes";
@@ -319,7 +355,7 @@ class MarkdownDescriptor extends Descriptor
if (\is_string($callable)) {
$string .= "\n- Type: `function`";
if (false === strpos($callable, '::')) {
if (!str_contains($callable, '::')) {
$string .= "\n".sprintf('- Name: `%s`', $callable);
} else {
$callableParts = explode('::', $callable);
@@ -336,7 +372,7 @@ class MarkdownDescriptor extends Descriptor
$string .= "\n- Type: `closure`";
$r = new \ReflectionFunction($callable);
if (false !== strpos($r->name, '{closure}')) {
if (str_contains($r->name, '{closure}')) {
return $this->write($string."\n");
}
$string .= "\n".sprintf('- Name: `%s`', $r->name);
@@ -361,10 +397,7 @@ class MarkdownDescriptor extends Descriptor
throw new \InvalidArgumentException('Callable is not describable.');
}
/**
* @return string
*/
private function formatRouterConfig(array $array)
private function formatRouterConfig(array $array): string
{
if (!$array) {
return 'NONE';

View File

@@ -12,17 +12,21 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Helper\Dumper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -33,6 +37,13 @@ use Symfony\Component\Routing\RouteCollection;
*/
class TextDescriptor extends Descriptor
{
private $fileLinkFormatter;
public function __construct(FileLinkFormatter $fileLinkFormatter = null)
{
$this->fileLinkFormatter = $fileLinkFormatter;
}
protected function describeRouteCollection(RouteCollection $routes, array $options = [])
{
$showControllers = isset($options['show_controllers']) && $options['show_controllers'];
@@ -44,17 +55,18 @@ class TextDescriptor extends Descriptor
$tableRows = [];
foreach ($routes->all() as $name => $route) {
$controller = $route->getDefault('_controller');
$row = [
$name,
$route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
$route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
'' !== $route->getHost() ? $route->getHost() : 'ANY',
$route->getPath(),
$this->formatControllerLink($controller, $route->getPath(), $options['container'] ?? null),
];
if ($showControllers) {
$controller = $route->getDefault('_controller');
$row[] = $controller ? $this->formatCallable($controller) : '';
$row[] = $controller ? $this->formatControllerLink($controller, $this->formatCallable($controller), $options['container'] ?? null) : '';
}
$tableRows[] = $row;
@@ -71,9 +83,14 @@ class TextDescriptor extends Descriptor
protected function describeRoute(Route $route, array $options = [])
{
$defaults = $route->getDefaults();
if (isset($defaults['_controller'])) {
$defaults['_controller'] = $this->formatControllerLink($defaults['_controller'], $this->formatCallable($defaults['_controller']), $options['container'] ?? null);
}
$tableHeaders = ['Property', 'Value'];
$tableRows = [
['Route Name', isset($options['name']) ? $options['name'] : ''],
['Route Name', $options['name'] ?? ''],
['Path', $route->getPath()],
['Path Regex', $route->compile()->getRegex()],
['Host', ('' !== $route->getHost() ? $route->getHost() : 'ANY')],
@@ -82,11 +99,12 @@ class TextDescriptor extends Descriptor
['Method', ($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')],
['Requirements', ($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM')],
['Class', \get_class($route)],
['Defaults', $this->formatRouterConfig($route->getDefaults())],
['Defaults', $this->formatRouterConfig($defaults)],
['Options', $this->formatRouterConfig($route->getOptions())],
];
if (isset($options['callable'])) {
$tableRows[] = ['Callable', $options['callable']];
if ('' !== $route->getCondition()) {
$tableRows[] = ['Condition', $route->getCondition()];
}
$table = new Table($this->getOutput());
@@ -109,24 +127,21 @@ class TextDescriptor extends Descriptor
protected function describeContainerTags(ContainerBuilder $builder, array $options = [])
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
if ($showPrivate) {
$options['output']->title('Symfony Container Public and Private Tags');
if ($showHidden) {
$options['output']->title('Symfony Container Hidden Tags');
} else {
$options['output']->title('Symfony Container Public Tags');
$options['output']->title('Symfony Container Tags');
}
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) {
$options['output']->section(sprintf('"%s" tag', $tag));
$options['output']->listing(array_keys($definitions));
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null)
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null)
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -141,24 +156,21 @@ class TextDescriptor extends Descriptor
$options['output']->table(
['Service ID', 'Class'],
[
[isset($options['id']) ? $options['id'] : '-', \get_class($service)],
[$options['id'] ?? '-', \get_class($service)],
]
);
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showTag = isset($options['tag']) ? $options['tag'] : null;
$showHidden = isset($options['show_hidden']) && $options['show_hidden'];
$showTag = $options['tag'] ?? null;
if ($showPrivate) {
$title = 'Symfony Container Public and Private Services';
if ($showHidden) {
$title = 'Symfony Container Hidden Services';
} else {
$title = 'Symfony Container Public Services';
$title = 'Symfony Container Services';
}
if ($showTag) {
@@ -167,7 +179,9 @@ class TextDescriptor extends Descriptor
$options['output']->title($title);
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$serviceIds = isset($options['tag']) && $options['tag']
? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($options['tag']))
: $this->sortServiceIds($builder->getServiceIds());
$maxTags = [];
if (isset($options['filter'])) {
@@ -176,12 +190,14 @@ class TextDescriptor extends Descriptor
foreach ($serviceIds as $key => $serviceId) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
// filter out hidden services unless shown explicitly
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
unset($serviceIds[$key]);
continue;
}
if ($definition instanceof Definition) {
// filter out private services unless shown explicitly
if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) {
unset($serviceIds[$key]);
continue;
}
if ($showTag) {
$tags = $definition->getTag($showTag);
foreach ($tags as $tag) {
@@ -195,11 +211,6 @@ class TextDescriptor extends Descriptor
}
}
}
} elseif ($definition instanceof Alias) {
if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) {
unset($serviceIds[$key]);
continue;
}
}
}
@@ -209,21 +220,21 @@ class TextDescriptor extends Descriptor
$tableHeaders = array_merge(['Service ID'], $tagsNames, ['Class name']);
$tableRows = [];
$rawOutput = isset($options['raw_text']) && $options['raw_text'];
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
foreach ($serviceIds as $serviceId) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
$styledServiceId = $rawOutput ? $serviceId : sprintf('<fg=cyan>%s</fg=cyan>', OutputFormatter::escape($serviceId));
if ($definition instanceof Definition) {
if ($showTag) {
foreach ($definition->getTag($showTag) as $key => $tag) {
foreach ($this->sortByPriority($definition->getTag($showTag)) as $key => $tag) {
$tagValues = [];
foreach ($tagsNames as $tagName) {
$tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : '';
$tagValues[] = $tag[$tagName] ?? '';
}
if (0 === $key) {
$tableRows[] = array_merge([$serviceId], $tagValues, [$definition->getClass()]);
} else {
$tableRows[] = array_merge([' "'], $tagValues, ['']);
$tableRows[] = array_merge([' (same service as previous, another tag)'], $tagValues, ['']);
}
}
} else {
@@ -246,9 +257,13 @@ class TextDescriptor extends Descriptor
$options['output']->title(sprintf('Information for Service "<info>%s</info>"', $options['id']));
}
if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) {
$options['output']->text($classDescription."\n");
}
$tableHeaders = ['Option', 'Value'];
$tableRows[] = ['Service ID', isset($options['id']) ? $options['id'] : '-'];
$tableRows[] = ['Service ID', $options['id'] ?? '-'];
$tableRows[] = ['Class', $definition->getClass() ?: '-'];
$omitTags = isset($options['omit_tags']) && $options['omit_tags'];
@@ -291,11 +306,6 @@ class TextDescriptor extends Descriptor
$tableRows[] = ['Autowired', $definition->isAutowired() ? 'yes' : 'no'];
$tableRows[] = ['Autoconfigured', $definition->isAutoconfigured() ? 'yes' : 'no'];
// forward compatibility with DependencyInjection component in version 4.0
if (method_exists($definition, 'getAutowiringTypes') && $autowiringTypes = $definition->getAutowiringTypes(false)) {
$tableRows[] = ['Autowiring Types', implode(', ', $autowiringTypes)];
}
if ($definition->getFile()) {
$tableRows[] = ['Required File', $definition->getFile() ?: '-'];
}
@@ -330,8 +340,18 @@ class TextDescriptor extends Descriptor
} else {
$argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues()));
}
foreach ($argument->getValues() as $ref) {
$argumentsInformation[] = sprintf('- Service(%s)', $ref);
}
} elseif ($argument instanceof ServiceLocatorArgument) {
$argumentsInformation[] = sprintf('Service locator (%d element(s))', \count($argument->getValues()));
} elseif ($argument instanceof Definition) {
$argumentsInformation[] = 'Inlined Service';
} elseif ($argument instanceof \UnitEnum) {
$argumentsInformation[] = ltrim(var_export($argument, true), '\\');
} elseif ($argument instanceof AbstractArgument) {
$argumentsInformation[] = sprintf('Abstract argument (%s)', $argument->getText());
} else {
$argumentsInformation[] = \is_array($argument) ? sprintf('Array (%d element(s))', \count($argument)) : $argument;
}
@@ -343,9 +363,39 @@ class TextDescriptor extends Descriptor
$options['output']->table($tableHeaders, $tableRows);
}
protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void
{
$containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class'));
if (!file_exists($containerDeprecationFilePath)) {
$options['output']->warning('The deprecation file does not exist, please try warming the cache first.');
return;
}
$logs = unserialize(file_get_contents($containerDeprecationFilePath));
if (0 === \count($logs)) {
$options['output']->success('There are no deprecations in the logs!');
return;
}
$formattedLogs = [];
$remainingCount = 0;
foreach ($logs as $log) {
$formattedLogs[] = sprintf("%sx: %s\n in %s:%s", $log['count'], $log['message'], $log['file'], $log['line']);
$remainingCount += $log['count'];
}
$options['output']->title(sprintf('Remaining deprecations (%s)', $remainingCount));
$options['output']->listing($formattedLogs);
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null)
{
$options['output']->comment(sprintf('This service is an alias for the service <info>%s</info>', (string) $alias));
if ($alias->isPublic() && !$alias->isPrivate()) {
$options['output']->comment(sprintf('This service is a <info>public</info> alias for the service <info>%s</info>', (string) $alias));
} else {
$options['output']->comment(sprintf('This service is a <comment>private</comment> alias for the service <info>%s</info>', (string) $alias));
}
if (!$builder) {
return;
@@ -364,22 +414,89 @@ class TextDescriptor extends Descriptor
]);
}
/**
* {@inheritdoc}
*/
protected function describeContainerEnvVars(array $envs, array $options = [])
{
$dump = new Dumper($this->output);
$options['output']->title('Symfony Container Environment Variables');
if (null !== $name = $options['name'] ?? null) {
$options['output']->comment('Displaying detailed environment variable usage matching '.$name);
$matches = false;
foreach ($envs as $env) {
if ($name === $env['name'] || false !== stripos($env['name'], $name)) {
$matches = true;
$options['output']->section('%env('.$env['processor'].':'.$env['name'].')%');
$options['output']->table([], [
['<info>Default value</>', $env['default_available'] ? $dump($env['default_value']) : 'n/a'],
['<info>Real value</>', $env['runtime_available'] ? $dump($env['runtime_value']) : 'n/a'],
['<info>Processed value</>', $env['default_available'] || $env['runtime_available'] ? $dump($env['processed_value']) : 'n/a'],
]);
}
}
if (!$matches) {
$options['output']->block('None of the environment variables match this name.');
} else {
$options['output']->comment('Note real values might be different between web and CLI.');
}
return;
}
if (!$envs) {
$options['output']->block('No environment variables are being used.');
return;
}
$rows = [];
$missing = [];
foreach ($envs as $env) {
if (isset($rows[$env['name']])) {
continue;
}
$rows[$env['name']] = [
$env['name'],
$env['default_available'] ? $dump($env['default_value']) : 'n/a',
$env['runtime_available'] ? $dump($env['runtime_value']) : 'n/a',
];
if (!$env['default_available'] && !$env['runtime_available']) {
$missing[$env['name']] = true;
}
}
$options['output']->table(['Name', 'Default value', 'Real value'], $rows);
$options['output']->comment('Note real values might be different between web and CLI.');
if ($missing) {
$options['output']->warning('The following variables are missing:');
$options['output']->listing(array_keys($missing));
}
}
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
$event = \array_key_exists('event', $options) ? $options['event'] : null;
$event = $options['event'] ?? null;
$dispatcherServiceName = $options['dispatcher_service_name'] ?? null;
$title = 'Registered Listeners';
if (null !== $dispatcherServiceName) {
$title .= sprintf(' of Event Dispatcher "%s"', $dispatcherServiceName);
}
if (null !== $event) {
$title = sprintf('Registered Listeners for "%s" Event', $event);
$title .= sprintf(' for "%s" Event', $event);
$registeredListeners = $eventDispatcher->getListeners($event);
} else {
$title = 'Registered Listeners Grouped by Event';
$title .= ' Grouped by Event';
// Try to see if "events" exists
$registeredListeners = \array_key_exists('events', $options) ? array_combine($options['events'], array_map(function ($event) use ($eventDispatcher) { return $eventDispatcher->getListeners($event); }, $options['events'])) : $eventDispatcher->getListeners();
}
$options['output']->title($title);
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
$this->renderEventListenerTable($eventDispatcher, $event, $registeredListeners, $options['output']);
} else {
@@ -391,15 +508,12 @@ class TextDescriptor extends Descriptor
}
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = [])
{
$this->writeText($this->formatCallable($callable), $options);
}
private function renderEventListenerTable(EventDispatcherInterface $eventDispatcher, $event, array $eventListeners, SymfonyStyle $io)
private function renderEventListenerTable(EventDispatcherInterface $eventDispatcher, string $event, array $eventListeners, SymfonyStyle $io)
{
$tableHeaders = ['Order', 'Callable', 'Priority'];
$tableRows = [];
@@ -411,10 +525,7 @@ class TextDescriptor extends Descriptor
$io->table($tableHeaders, $tableRows);
}
/**
* @return string
*/
private function formatRouterConfig(array $config)
private function formatRouterConfig(array $config): string
{
if (empty($config)) {
return 'NONE';
@@ -430,12 +541,61 @@ class TextDescriptor extends Descriptor
return trim($configAsString);
}
/**
* @param callable $callable
*
* @return string
*/
private function formatCallable($callable)
private function formatControllerLink($controller, string $anchorText, callable $getContainer = null): string
{
if (null === $this->fileLinkFormatter) {
return $anchorText;
}
try {
if (null === $controller) {
return $anchorText;
} elseif (\is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif ($controller instanceof \Closure) {
$r = new \ReflectionFunction($controller);
} elseif (method_exists($controller, '__invoke')) {
$r = new \ReflectionMethod($controller, '__invoke');
} elseif (!\is_string($controller)) {
return $anchorText;
} elseif (str_contains($controller, '::')) {
$r = new \ReflectionMethod($controller);
} else {
$r = new \ReflectionFunction($controller);
}
} catch (\ReflectionException $e) {
if (\is_array($controller)) {
$controller = implode('::', $controller);
}
$id = $controller;
$method = '__invoke';
if ($pos = strpos($controller, '::')) {
$id = substr($controller, 0, $pos);
$method = substr($controller, $pos + 2);
}
if (!$getContainer || !($container = $getContainer()) || !$container->has($id)) {
return $anchorText;
}
try {
$r = new \ReflectionMethod($container->findDefinition($id)->getClass(), $method);
} catch (\ReflectionException $e) {
return $anchorText;
}
}
$fileLink = $this->fileLinkFormatter->format($r->getFileName(), $r->getStartLine());
if ($fileLink) {
return sprintf('<href=%s>%s</>', $fileLink, $anchorText);
}
return $anchorText;
}
private function formatCallable($callable): string
{
if (\is_array($callable)) {
if (\is_object($callable[0])) {
@@ -451,7 +611,7 @@ class TextDescriptor extends Descriptor
if ($callable instanceof \Closure) {
$r = new \ReflectionFunction($callable);
if (false !== strpos($r->name, '{closure}')) {
if (str_contains($r->name, '{closure}')) {
return 'Closure()';
}
if ($class = $r->getClosureScopeClass()) {
@@ -468,10 +628,7 @@ class TextDescriptor extends Descriptor
throw new \InvalidArgumentException('Callable is not describable.');
}
/**
* @param string $content
*/
private function writeText($content, array $options = [])
private function writeText(string $content, array $options = [])
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,

View File

@@ -11,9 +11,13 @@
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Console\Exception\LogicException;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -36,7 +40,7 @@ class XmlDescriptor extends Descriptor
protected function describeRoute(Route $route, array $options = [])
{
$this->writeDocument($this->getRouteDocument($route, isset($options['name']) ? $options['name'] : null));
$this->writeDocument($this->getRouteDocument($route, $options['name'] ?? null));
}
protected function describeContainerParameters(ParameterBag $parameters, array $options = [])
@@ -46,13 +50,10 @@ class XmlDescriptor extends Descriptor
protected function describeContainerTags(ContainerBuilder $builder, array $options = [])
{
$this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_private']) && $options['show_private']));
$this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_hidden']) && $options['show_hidden']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null)
protected function describeContainerService(object $service, array $options = [], ContainerBuilder $builder = null)
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
@@ -61,23 +62,20 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $builder, isset($options['show_arguments']) && $options['show_arguments']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
{
$this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private'], isset($options['show_arguments']) && $options['show_arguments'], isset($options['filter']) ? $options['filter'] : null));
$this->writeDocument($this->getContainerServicesDocument($builder, $options['tag'] ?? null, isset($options['show_hidden']) && $options['show_hidden'], isset($options['show_arguments']) && $options['show_arguments'], $options['filter'] ?? null));
}
protected function describeContainerDefinition(Definition $definition, array $options = [])
{
$this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']));
$this->writeDocument($this->getContainerDefinitionDocument($definition, $options['id'] ?? null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']));
}
protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)->childNodes->item(0), true));
$dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, $options['id'] ?? null)->childNodes->item(0), true));
if (!$builder) {
$this->writeDocument($dom);
@@ -90,17 +88,11 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($dom);
}
/**
* {@inheritdoc}
*/
protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
{
$this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, \array_key_exists('event', $options) ? $options['event'] : null));
$this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, $options));
}
/**
* {@inheritdoc}
*/
protected function describeCallable($callable, array $options = [])
{
$this->writeDocument($this->getCallableDocument($callable));
@@ -111,19 +103,46 @@ class XmlDescriptor extends Descriptor
$this->writeDocument($this->getContainerParameterDocument($parameter, $options));
}
/**
* Writes DOM document.
*/
protected function describeContainerEnvVars(array $envs, array $options = [])
{
throw new LogicException('Using the XML format to debug environment variables is not supported.');
}
protected function describeContainerDeprecations(ContainerBuilder $builder, array $options = []): void
{
$containerDeprecationFilePath = sprintf('%s/%sDeprecations.log', $builder->getParameter('kernel.build_dir'), $builder->getParameter('kernel.container_class'));
if (!file_exists($containerDeprecationFilePath)) {
throw new RuntimeException('The deprecation file does not exist, please try warming the cache first.');
}
$logs = unserialize(file_get_contents($containerDeprecationFilePath));
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($deprecationsXML = $dom->createElement('deprecations'));
$formattedLogs = [];
$remainingCount = 0;
foreach ($logs as $log) {
$deprecationsXML->appendChild($deprecationXML = $dom->createElement('deprecation'));
$deprecationXML->setAttribute('count', $log['count']);
$deprecationXML->appendChild($dom->createElement('message', $log['message']));
$deprecationXML->appendChild($dom->createElement('file', $log['file']));
$deprecationXML->appendChild($dom->createElement('line', $log['line']));
$remainingCount += $log['count'];
}
$deprecationsXML->setAttribute('remainingCount', $remainingCount);
$this->writeDocument($dom);
}
private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}
/**
* @return \DOMDocument
*/
private function getRouteCollectionDocument(RouteCollection $routes)
private function getRouteCollectionDocument(RouteCollection $routes): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($routesXML = $dom->createElement('routes'));
@@ -136,12 +155,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param string|null $name
*
* @return \DOMDocument
*/
private function getRouteDocument(Route $route, $name = null)
private function getRouteDocument(Route $route, string $name = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($routeXML = $dom->createElement('route'));
@@ -201,13 +215,15 @@ class XmlDescriptor extends Descriptor
}
}
if ('' !== $route->getCondition()) {
$routeXML->appendChild($conditionXML = $dom->createElement('condition'));
$conditionXML->appendChild(new \DOMText($route->getCondition()));
}
return $dom;
}
/**
* @return \DOMDocument
*/
private function getContainerParametersDocument(ParameterBag $parameters)
private function getContainerParametersDocument(ParameterBag $parameters): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($parametersXML = $dom->createElement('parameters'));
@@ -221,17 +237,12 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param bool $showPrivate
*
* @return \DOMDocument
*/
private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false)
private function getContainerTagsDocument(ContainerBuilder $builder, bool $showHidden = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($containerXML = $dom->createElement('container'));
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) {
$containerXML->appendChild($tagXML = $dom->createElement('tag'));
$tagXML->setAttribute('name', $tag);
@@ -244,14 +255,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param mixed $service
* @param string $id
* @param bool $showArguments
*
* @return \DOMDocument
*/
private function getContainerServiceDocument($service, $id, ContainerBuilder $builder = null, $showArguments = false)
private function getContainerServiceDocument(object $service, string $id, ContainerBuilder $builder = null, bool $showArguments = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
@@ -271,29 +275,22 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param string|null $tag
* @param bool $showPrivate
* @param bool $showArguments
* @param callable $filter
*
* @return \DOMDocument
*/
private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false, $showArguments = false, $filter = null)
private function getContainerServicesDocument(ContainerBuilder $builder, string $tag = null, bool $showHidden = false, bool $showArguments = false, callable $filter = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($containerXML = $dom->createElement('container'));
$serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds();
$serviceIds = $tag
? $this->sortTaggedServicesByPriority($builder->findTaggedServiceIds($tag))
: $this->sortServiceIds($builder->getServiceIds());
if ($filter) {
$serviceIds = array_filter($serviceIds, $filter);
}
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
foreach ($serviceIds as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if (($service instanceof Definition || $service instanceof Alias) && !($showPrivate || ($service->isPublic() && !$service->isPrivate()))) {
if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
continue;
}
@@ -304,13 +301,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param string|null $id
* @param bool $omitTags
*
* @return \DOMDocument
*/
private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false, $showArguments = false)
private function getContainerDefinitionDocument(Definition $definition, string $id = null, bool $omitTags = false, bool $showArguments = false): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($serviceXML = $dom->createElement('definition'));
@@ -319,7 +310,12 @@ class XmlDescriptor extends Descriptor
$serviceXML->setAttribute('id', $id);
}
$serviceXML->setAttribute('class', $definition->getClass());
if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) {
$serviceXML->appendChild($descriptionXML = $dom->createElement('description'));
$descriptionXML->appendChild($dom->createCDATASection($classDescription));
}
$serviceXML->setAttribute('class', $definition->getClass() ?? '');
if ($factory = $definition->getFactory()) {
$serviceXML->appendChild($factoryXML = $dom->createElement('factory'));
@@ -345,7 +341,7 @@ class XmlDescriptor extends Descriptor
$serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false');
$serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false');
$serviceXML->setAttribute('autoconfigured', $definition->isAutoconfigured() ? 'true' : 'false');
$serviceXML->setAttribute('file', $definition->getFile());
$serviceXML->setAttribute('file', $definition->getFile() ?? '');
$calls = $definition->getMethodCalls();
if (\count($calls) > 0) {
@@ -353,6 +349,9 @@ class XmlDescriptor extends Descriptor
foreach ($calls as $callData) {
$callsXML->appendChild($callXML = $dom->createElement('call'));
$callXML->setAttribute('method', $callData[0]);
if ($callData[2] ?? false) {
$callXML->setAttribute('returns-clone', 'true');
}
}
}
@@ -363,7 +362,7 @@ class XmlDescriptor extends Descriptor
}
if (!$omitTags) {
if ($tags = $definition->getTags()) {
if ($tags = $this->sortTagsByPriority($definition->getTags())) {
$serviceXML->appendChild($tagsXML = $dom->createElement('tags'));
foreach ($tags as $tagName => $tagData) {
foreach ($tagData as $parameters) {
@@ -385,7 +384,7 @@ class XmlDescriptor extends Descriptor
/**
* @return \DOMNode[]
*/
private function getArgumentNodes(array $arguments, \DOMDocument $dom)
private function getArgumentNodes(array $arguments, \DOMDocument $dom): array
{
$nodes = [];
@@ -403,20 +402,26 @@ class XmlDescriptor extends Descriptor
if ($argument instanceof Reference) {
$argumentXML->setAttribute('type', 'service');
$argumentXML->setAttribute('id', (string) $argument);
} elseif ($argument instanceof IteratorArgument) {
$argumentXML->setAttribute('type', 'iterator');
} elseif ($argument instanceof IteratorArgument || $argument instanceof ServiceLocatorArgument) {
$argumentXML->setAttribute('type', $argument instanceof IteratorArgument ? 'iterator' : 'service_locator');
foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) {
$argumentXML->appendChild($childArgumentXML);
}
} elseif ($argument instanceof Definition) {
$argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true)->childNodes->item(0), true));
} elseif ($argument instanceof AbstractArgument) {
$argumentXML->setAttribute('type', 'abstract');
$argumentXML->appendChild(new \DOMText($argument->getText()));
} elseif (\is_array($argument)) {
$argumentXML->setAttribute('type', 'collection');
foreach ($this->getArgumentNodes($argument, $dom) as $childArgumentXML) {
$argumentXML->appendChild($childArgumentXML);
}
} elseif ($argument instanceof \UnitEnum) {
$argumentXML->setAttribute('type', 'constant');
$argumentXML->appendChild(new \DOMText(ltrim(var_export($argument, true), '\\')));
} else {
$argumentXML->appendChild(new \DOMText($argument));
}
@@ -427,12 +432,7 @@ class XmlDescriptor extends Descriptor
return $nodes;
}
/**
* @param string|null $id
*
* @return \DOMDocument
*/
private function getContainerAliasDocument(Alias $alias, $id = null)
private function getContainerAliasDocument(Alias $alias, string $id = null): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($aliasXML = $dom->createElement('alias'));
@@ -447,10 +447,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @return \DOMDocument
*/
private function getContainerParameterDocument($parameter, $options = [])
private function getContainerParameterDocument($parameter, array $options = []): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($parameterXML = $dom->createElement('parameter'));
@@ -464,20 +461,18 @@ class XmlDescriptor extends Descriptor
return $dom;
}
/**
* @param string|null $event
*
* @return \DOMDocument
*/
private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, $event = null)
private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, array $options): \DOMDocument
{
$event = \array_key_exists('event', $options) ? $options['event'] : null;
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher'));
$registeredListeners = $eventDispatcher->getListeners($event);
if (null !== $event) {
$registeredListeners = $eventDispatcher->getListeners($event);
$this->appendEventListenerDocument($eventDispatcher, $event, $eventDispatcherXML, $registeredListeners);
} else {
// Try to see if "events" exists
$registeredListeners = \array_key_exists('events', $options) ? array_combine($options['events'], array_map(function ($event) use ($eventDispatcher) { return $eventDispatcher->getListeners($event); }, $options['events'])) : $eventDispatcher->getListeners();
ksort($registeredListeners);
foreach ($registeredListeners as $eventListened => $eventListeners) {
@@ -491,7 +486,7 @@ class XmlDescriptor extends Descriptor
return $dom;
}
private function appendEventListenerDocument(EventDispatcherInterface $eventDispatcher, $event, \DOMElement $element, array $eventListeners)
private function appendEventListenerDocument(EventDispatcherInterface $eventDispatcher, string $event, \DOMElement $element, array $eventListeners)
{
foreach ($eventListeners as $listener) {
$callableXML = $this->getCallableDocument($listener);
@@ -501,12 +496,7 @@ class XmlDescriptor extends Descriptor
}
}
/**
* @param callable $callable
*
* @return \DOMDocument
*/
private function getCallableDocument($callable)
private function getCallableDocument($callable): \DOMDocument
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($callableXML = $dom->createElement('callable'));
@@ -518,7 +508,7 @@ class XmlDescriptor extends Descriptor
$callableXML->setAttribute('name', $callable[1]);
$callableXML->setAttribute('class', \get_class($callable[0]));
} else {
if (0 !== strpos($callable[1], 'parent::')) {
if (!str_starts_with($callable[1], 'parent::')) {
$callableXML->setAttribute('name', $callable[1]);
$callableXML->setAttribute('class', $callable[0]);
$callableXML->setAttribute('static', 'true');
@@ -536,7 +526,7 @@ class XmlDescriptor extends Descriptor
if (\is_string($callable)) {
$callableXML->setAttribute('type', 'function');
if (false === strpos($callable, '::')) {
if (!str_contains($callable, '::')) {
$callableXML->setAttribute('name', $callable);
} else {
$callableParts = explode('::', $callable);
@@ -553,7 +543,7 @@ class XmlDescriptor extends Descriptor
$callableXML->setAttribute('type', 'closure');
$r = new \ReflectionFunction($callable);
if (false !== strpos($r->name, '{closure}')) {
if (str_contains($r->name, '{closure}')) {
return $dom;
}
$callableXML->setAttribute('name', $r->name);

View File

@@ -16,6 +16,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor;
use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
@@ -24,10 +25,10 @@ use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper;
*/
class DescriptorHelper extends BaseDescriptorHelper
{
public function __construct()
public function __construct(FileLinkFormatter $fileLinkFormatter = null)
{
$this
->register('txt', new TextDescriptor())
->register('txt', new TextDescriptor($fileLinkFormatter))
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())