123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- <?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\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;
- use Symfony\Component\DependencyInjection\Definition;
- use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
- use Symfony\Component\DependencyInjection\Reference;
- use Symfony\Component\EventDispatcher\EventDispatcherInterface;
- use Symfony\Component\Routing\Route;
- use Symfony\Component\Routing\RouteCollection;
- /**
- * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
- *
- * @internal
- */
- class JsonDescriptor extends Descriptor
- {
- protected function describeRouteCollection(RouteCollection $routes, array $options = [])
- {
- $data = [];
- foreach ($routes->all() as $name => $route) {
- $data[$name] = $this->getRouteData($route);
- }
- $this->writeData($data, $options);
- }
- protected function describeRoute(Route $route, array $options = [])
- {
- $this->writeData($this->getRouteData($route), $options);
- }
- protected function describeContainerParameters(ParameterBag $parameters, array $options = [])
- {
- $this->writeData($this->sortParameters($parameters), $options);
- }
- protected function describeContainerTags(ContainerBuilder $builder, array $options = [])
- {
- $showHidden = isset($options['show_hidden']) && $options['show_hidden'];
- $data = [];
- foreach ($this->findDefinitionsByTag($builder, $showHidden) as $tag => $definitions) {
- $data[$tag] = [];
- foreach ($definitions as $definition) {
- $data[$tag][] = $this->getContainerDefinitionData($definition, true);
- }
- }
- $this->writeData($data, $options);
- }
- protected function describeContainerService($service, array $options = [], ContainerBuilder $builder = null)
- {
- if (!isset($options['id'])) {
- throw new \InvalidArgumentException('An "id" option must be provided.');
- }
- if ($service instanceof Alias) {
- $this->describeContainerAlias($service, $options, $builder);
- } elseif ($service instanceof Definition) {
- $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options);
- } else {
- $this->writeData(\get_class($service), $options);
- }
- }
- protected function describeContainerServices(ContainerBuilder $builder, array $options = [])
- {
- $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' => []];
- if (isset($options['filter'])) {
- $serviceIds = array_filter($serviceIds, $options['filter']);
- }
- foreach ($serviceIds as $serviceId) {
- $service = $this->resolveServiceDefinition($builder, $serviceId);
- if ($showHidden xor '.' === ($serviceId[0] ?? null)) {
- continue;
- }
- if ($service instanceof Alias) {
- $data['aliases'][$serviceId] = $this->getContainerAliasData($service);
- } elseif ($service instanceof Definition) {
- $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments);
- } else {
- $data['services'][$serviceId] = \get_class($service);
- }
- }
- $this->writeData($data, $options);
- }
- protected function describeContainerDefinition(Definition $definition, array $options = [])
- {
- $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options);
- }
- protected function describeContainerAlias(Alias $alias, array $options = [], ContainerBuilder $builder = null)
- {
- if (!$builder) {
- $this->writeData($this->getContainerAliasData($alias), $options);
- return;
- }
- $this->writeData(
- [$this->getContainerAliasData($alias), $this->getContainerDefinitionData($builder->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'])],
- array_merge($options, ['id' => (string) $alias])
- );
- }
- protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = [])
- {
- $this->writeData($this->getEventDispatcherListenersData($eventDispatcher, \array_key_exists('event', $options) ? $options['event'] : null), $options);
- }
- protected function describeCallable($callable, array $options = [])
- {
- $this->writeData($this->getCallableData($callable), $options);
- }
- protected function describeContainerParameter($parameter, array $options = [])
- {
- $key = $options['parameter'] ?? '';
- $this->writeData([$key => $parameter], $options);
- }
- 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 = $options['json_encoding'] ?? 0;
- $this->write(json_encode($data, $flags | \JSON_PRETTY_PRINT)."\n");
- }
- protected function getRouteData(Route $route): array
- {
- $data = [
- 'path' => $route->getPath(),
- 'pathRegex' => $route->compile()->getRegex(),
- 'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY',
- 'hostRegex' => '' !== $route->getHost() ? $route->compile()->getHostRegex() : '',
- 'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
- 'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
- 'class' => \get_class($route),
- 'defaults' => $route->getDefaults(),
- 'requirements' => $route->getRequirements() ?: 'NO CUSTOM',
- 'options' => $route->getOptions(),
- ];
- if ('' !== $route->getCondition()) {
- $data['condition'] = $route->getCondition();
- }
- return $data;
- }
- private function getContainerDefinitionData(Definition $definition, bool $omitTags = false, bool $showArguments = false): array
- {
- $data = [
- 'class' => (string) $definition->getClass(),
- 'public' => $definition->isPublic() && !$definition->isPrivate(),
- 'synthetic' => $definition->isSynthetic(),
- 'lazy' => $definition->isLazy(),
- 'shared' => $definition->isShared(),
- 'abstract' => $definition->isAbstract(),
- 'autowire' => $definition->isAutowired(),
- 'autoconfigure' => $definition->isAutoconfigured(),
- ];
- if ('' !== $classDescription = $this->getClassDescription((string) $definition->getClass())) {
- $data['description'] = $classDescription;
- }
- if ($showArguments) {
- $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments);
- }
- $data['file'] = $definition->getFile();
- if ($factory = $definition->getFactory()) {
- if (\is_array($factory)) {
- if ($factory[0] instanceof Reference) {
- $data['factory_service'] = (string) $factory[0];
- } elseif ($factory[0] instanceof Definition) {
- throw new \InvalidArgumentException('Factory is not describable.');
- } else {
- $data['factory_class'] = $factory[0];
- }
- $data['factory_method'] = $factory[1];
- } else {
- $data['factory_function'] = $factory;
- }
- }
- $calls = $definition->getMethodCalls();
- if (\count($calls) > 0) {
- $data['calls'] = [];
- foreach ($calls as $callData) {
- $data['calls'][] = $callData[0];
- }
- }
- if (!$omitTags) {
- $data['tags'] = [];
- foreach ($this->sortTagsByPriority($definition->getTags()) as $tagName => $tagData) {
- foreach ($tagData as $parameters) {
- $data['tags'][] = ['name' => $tagName, 'parameters' => $parameters];
- }
- }
- }
- return $data;
- }
- private function getContainerAliasData(Alias $alias): array
- {
- return [
- 'service' => (string) $alias,
- 'public' => $alias->isPublic() && !$alias->isPrivate(),
- ];
- }
- private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, string $event = null): array
- {
- $data = [];
- $registeredListeners = $eventDispatcher->getListeners($event);
- if (null !== $event) {
- foreach ($registeredListeners as $listener) {
- $l = $this->getCallableData($listener);
- $l['priority'] = $eventDispatcher->getListenerPriority($event, $listener);
- $data[] = $l;
- }
- } else {
- ksort($registeredListeners);
- foreach ($registeredListeners as $eventListened => $eventListeners) {
- foreach ($eventListeners as $eventListener) {
- $l = $this->getCallableData($eventListener);
- $l['priority'] = $eventDispatcher->getListenerPriority($eventListened, $eventListener);
- $data[$eventListened][] = $l;
- }
- }
- }
- return $data;
- }
- private function getCallableData($callable): array
- {
- $data = [];
- if (\is_array($callable)) {
- $data['type'] = 'function';
- if (\is_object($callable[0])) {
- $data['name'] = $callable[1];
- $data['class'] = \get_class($callable[0]);
- } else {
- if (0 !== strpos($callable[1], 'parent::')) {
- $data['name'] = $callable[1];
- $data['class'] = $callable[0];
- $data['static'] = true;
- } else {
- $data['name'] = substr($callable[1], 8);
- $data['class'] = $callable[0];
- $data['static'] = true;
- $data['parent'] = true;
- }
- }
- return $data;
- }
- if (\is_string($callable)) {
- $data['type'] = 'function';
- if (false === strpos($callable, '::')) {
- $data['name'] = $callable;
- } else {
- $callableParts = explode('::', $callable);
- $data['name'] = $callableParts[1];
- $data['class'] = $callableParts[0];
- $data['static'] = true;
- }
- return $data;
- }
- if ($callable instanceof \Closure) {
- $data['type'] = 'closure';
- $r = new \ReflectionFunction($callable);
- if (false !== strpos($r->name, '{closure}')) {
- return $data;
- }
- $data['name'] = $r->name;
- if ($class = $r->getClosureScopeClass()) {
- $data['class'] = $class->name;
- if (!$r->getClosureThis()) {
- $data['static'] = true;
- }
- }
- return $data;
- }
- if (method_exists($callable, '__invoke')) {
- $data['type'] = 'object';
- $data['name'] = \get_class($callable);
- return $data;
- }
- throw new \InvalidArgumentException('Callable is not describable.');
- }
- private function describeValue($value, bool $omitTags, bool $showArguments)
- {
- if (\is_array($value)) {
- $data = [];
- foreach ($value as $k => $v) {
- $data[$k] = $this->describeValue($v, $omitTags, $showArguments);
- }
- return $data;
- }
- if ($value instanceof ServiceClosureArgument) {
- $value = $value->getValues()[0];
- }
- if ($value instanceof Reference) {
- return [
- 'type' => 'service',
- 'id' => (string) $value,
- ];
- }
- if ($value instanceof AbstractArgument) {
- return ['type' => 'abstract', 'text' => $value->getText()];
- }
- if ($value instanceof ArgumentInterface) {
- return $this->describeValue($value->getValues(), $omitTags, $showArguments);
- }
- if ($value instanceof Definition) {
- return $this->getContainerDefinitionData($value, $omitTags, $showArguments);
- }
- return $value;
- }
- }
|