feature #7887 [FrameworkBundle] adds routing/container descriptors (jfsimon)

This PR was squashed before being merged into the master branch (closes #7887).

Discussion
----------

[FrameworkBundle] adds routing/container descriptors

The goal of this PR is to add descriptors (as in #7454) for routing and container. This will permit add a `--format` option to `router:debug` and `container:debug` commands (with `txt`, `json`, `xml` and `md` formats).

Commits
-------

22f9bc8 [FrameworkBundle] adds routing/container descriptors
This commit is contained in:
Fabien Potencier 2013-10-01 16:13:14 +02:00
commit 31fb8c7cc3
62 changed files with 2158 additions and 462 deletions

View File

@ -11,12 +11,11 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
@ -46,7 +45,9 @@ class ContainerDebugCommand extends ContainerAwareCommand
new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Show all services with a specific tag'),
new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays tagged services for an application'),
new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'),
new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application')
new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output description in other formats'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'),
))
->setDescription('Displays current services for an application')
->setHelp(<<<EOF
@ -92,50 +93,37 @@ EOF
{
$this->validateInput($input);
$this->containerBuilder = $this->getContainerBuilder();
if ($input->getOption('parameters')) {
$parameters = $this->getContainerBuilder()->getParameterBag()->all();
// Sort parameters alphabetically
ksort($parameters);
$this->outputParameters($output, $parameters);
return;
}
$parameter = $input->getOption('parameter');
if (null !== $parameter) {
$output->write($this->formatParameter($this->getContainerBuilder()->getParameter($parameter)));
return;
}
if ($input->getOption('tags')) {
$this->outputTags($output, $input->getOption('show-private'));
return;
}
$tag = $input->getOption('tag');
if (null !== $tag) {
$serviceIds = array_keys($this->containerBuilder->findTaggedServiceIds($tag));
$object = $this->getContainerBuilder()->getParameterBag();
$options = array();
} elseif ($parameter = $input->getOption('parameter')) {
$object = $this->getContainerBuilder();
$options = array('parameter' => $parameter);
} elseif ($input->getOption('tags')) {
$object = $this->getContainerBuilder();
$options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private'));
} elseif ($tag = $input->getOption('tag')) {
$object = $this->getContainerBuilder();
$options = array('tag' => $tag, 'show_private' => $input->getOption('show-private'));
} elseif ($name = $input->getArgument('name')) {
$object = $this->getContainerBuilder();
$options = array('id' => $name);
} else {
$serviceIds = $this->containerBuilder->getServiceIds();
$object = $this->getContainerBuilder();
$options = array('show_private' => $input->getOption('show-private'));
}
// sort so that it reads like an index of services
asort($serviceIds);
$name = $input->getArgument('name');
if ($name) {
$this->outputService($output, $name);
} else {
$this->outputServices($output, $serviceIds, $input->getOption('show-private'), $tag);
}
$helper = new DescriptorHelper();
$helper->describe($output, $object, $input->getOption('format'), $input->getOption('raw'), $options);
}
/**
* Validates input arguments and options.
*
* @param InputInterface $input
*
* @throws \InvalidArgumentException
*/
protected function validateInput(InputInterface $input)
{
$options = array('tags', 'tag', 'parameters', 'parameter');
@ -155,211 +143,6 @@ EOF
}
}
protected function outputServices(OutputInterface $output, $serviceIds, $showPrivate = false, $showTagAttributes = null)
{
// set the label to specify public or public+private
if ($showPrivate) {
$label = '<comment>Public</comment> and <comment>private</comment> services';
} else {
$label = '<comment>Public</comment> services';
}
if ($showTagAttributes) {
$label .= ' with tag <info>'.$showTagAttributes.'</info>';
}
$output->writeln($this->getHelper('formatter')->formatSection('container', $label));
// loop through to get space needed and filter private services
$maxName = 4;
$maxScope = 6;
$maxTags = array();
foreach ($serviceIds as $key => $serviceId) {
$definition = $this->resolveServiceDefinition($serviceId);
if ($definition instanceof Definition) {
// filter out private services unless shown explicitly
if (!$showPrivate && !$definition->isPublic()) {
unset($serviceIds[$key]);
continue;
}
if (strlen($definition->getScope()) > $maxScope) {
$maxScope = strlen($definition->getScope());
}
if (null !== $showTagAttributes) {
$tags = $definition->getTag($showTagAttributes);
foreach ($tags as $tag) {
foreach ($tag as $key => $value) {
if (!isset($maxTags[$key])) {
$maxTags[$key] = strlen($key);
}
if (strlen($value) > $maxTags[$key]) {
$maxTags[$key] = strlen($value);
}
}
}
}
}
if (strlen($serviceId) > $maxName) {
$maxName = strlen($serviceId);
}
}
$format = '%-'.$maxName.'s ';
$format .= implode("", array_map(function($length) { return "%-{$length}s "; }, $maxTags));
$format .= '%-'.$maxScope.'s %s';
// the title field needs extra space to make up for comment tags
$format1 = '%-'.($maxName + 19).'s ';
$format1 .= implode("", array_map(function($length) { return '%-'.($length + 19).'s '; }, $maxTags));
$format1 .= '%-'.($maxScope + 19).'s %s';
$tags = array();
foreach ($maxTags as $tagName => $length) {
$tags[] = '<comment>'.$tagName.'</comment>';
}
$output->writeln(vsprintf($format1, $this->buildArgumentsArray('<comment>Service Id</comment>', '<comment>Scope</comment>', '<comment>Class Name</comment>', $tags)));
foreach ($serviceIds as $serviceId) {
$definition = $this->resolveServiceDefinition($serviceId);
if ($definition instanceof Definition) {
$lines = array();
if (null !== $showTagAttributes) {
foreach ($definition->getTag($showTagAttributes) as $key => $tag) {
$tagValues = array();
foreach (array_keys($maxTags) as $tagName) {
$tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : "";
}
if (0 === $key) {
$lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass(), $tagValues);
} else {
$lines[] = $this->buildArgumentsArray(' "', '', '', $tagValues);
}
}
} else {
$lines[] = $this->buildArgumentsArray($serviceId, $definition->getScope(), $definition->getClass());
}
foreach ($lines as $arguments) {
$output->writeln(vsprintf($format, $arguments));
}
} elseif ($definition instanceof Alias) {
$alias = $definition;
$output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, 'n/a', sprintf('<comment>alias for</comment> <info>%s</info>', (string) $alias), count($maxTags) ? array_fill(0, count($maxTags), "") : array())));
} else {
// we have no information (happens with "service_container")
$service = $definition;
$output->writeln(vsprintf($format, $this->buildArgumentsArray($serviceId, '', get_class($service), count($maxTags) ? array_fill(0, count($maxTags), "") : array())));
}
}
}
protected function buildArgumentsArray($serviceId, $scope, $className, array $tagAttributes = array())
{
$arguments = array($serviceId);
foreach ($tagAttributes as $tagAttribute) {
$arguments[] = $tagAttribute;
}
$arguments[] = $scope;
$arguments[] = $className;
return $arguments;
}
/**
* Renders detailed service information about one service
*/
protected function outputService(OutputInterface $output, $serviceId)
{
$definition = $this->resolveServiceDefinition($serviceId);
$label = sprintf('Information for service <info>%s</info>', $serviceId);
$output->writeln($this->getHelper('formatter')->formatSection('container', $label));
$output->writeln('');
if ($definition instanceof Definition) {
$output->writeln(sprintf('<comment>Service Id</comment> %s', $serviceId));
$output->writeln(sprintf('<comment>Class</comment> %s', $definition->getClass() ?: "-"));
$tags = $definition->getTags();
if (count($tags)) {
$output->writeln('<comment>Tags</comment>');
foreach ($tags as $tagName => $tagData) {
foreach ($tagData as $singleTagData) {
$output->writeln(sprintf(' - %-30s (%s)', $tagName, implode(', ', array_map(function($key, $value) {
return sprintf('<info>%s</info>: %s', $key, $value);
}, array_keys($singleTagData), array_values($singleTagData)))));
}
}
} else {
$output->writeln('<comment>Tags</comment> -');
}
$output->writeln(sprintf('<comment>Scope</comment> %s', $definition->getScope()));
$public = $definition->isPublic() ? 'yes' : 'no';
$output->writeln(sprintf('<comment>Public</comment> %s', $public));
$synthetic = $definition->isSynthetic() ? 'yes' : 'no';
$output->writeln(sprintf('<comment>Synthetic</comment> %s', $synthetic));
$file = $definition->getFile() ? $definition->getFile() : '-';
$output->writeln(sprintf('<comment>Required File</comment> %s', $file));
} elseif ($definition instanceof Alias) {
$alias = $definition;
$output->writeln(sprintf('This service is an alias for the service <info>%s</info>', (string) $alias));
} else {
// edge case (but true for "service_container", all we have is the service itself
$service = $definition;
$output->writeln(sprintf('<comment>Service Id</comment> %s', $serviceId));
$output->writeln(sprintf('<comment>Class</comment> %s', get_class($service)));
}
}
protected function outputParameters(OutputInterface $output, $parameters)
{
$output->writeln($this->getHelper('formatter')->formatSection('container', 'List of parameters'));
$terminalDimensions = $this->getApplication()->getTerminalDimensions();
$maxTerminalWidth = $terminalDimensions[0];
$maxParameterWidth = 0;
$maxValueWidth = 0;
// Determine max parameter & value length
foreach ($parameters as $parameter => $value) {
$parameterWidth = strlen($parameter);
if ($parameterWidth > $maxParameterWidth) {
$maxParameterWidth = $parameterWidth;
}
$valueWith = strlen($this->formatParameter($value));
if ($valueWith > $maxValueWidth) {
$maxValueWidth = $valueWith;
}
}
$maxValueWidth = min($maxValueWidth, $maxTerminalWidth - $maxParameterWidth - 1);
$formatTitle = '%-'.($maxParameterWidth + 19).'s %-'.($maxValueWidth + 19).'s';
$format = '%-'.$maxParameterWidth.'s %-'.$maxValueWidth.'s';
$output->writeln(sprintf($formatTitle, '<comment>Parameter</comment>', '<comment>Value</comment>'));
foreach ($parameters as $parameter => $value) {
$splits = str_split($this->formatParameter($value), $maxValueWidth);
foreach ($splits as $index => $split) {
if (0 === $index) {
$output->writeln(sprintf($format, $parameter, $split));
} else {
$output->writeln(sprintf($format, ' ', $split));
}
}
}
}
/**
* Loads the ContainerBuilder from the cache.
*
@ -384,77 +167,4 @@ EOF
return $container;
}
/**
* Given an array of service IDs, this returns the array of corresponding
* Definition and Alias objects that those ids represent.
*
* @param string $serviceId The service id to resolve
*
* @return Definition|Alias
*/
protected function resolveServiceDefinition($serviceId)
{
if ($this->containerBuilder->hasDefinition($serviceId)) {
return $this->containerBuilder->getDefinition($serviceId);
}
// Some service IDs don't have a Definition, they're simply an Alias
if ($this->containerBuilder->hasAlias($serviceId)) {
return $this->containerBuilder->getAlias($serviceId);
}
// the service has been injected in some special way, just return the service
return $this->containerBuilder->get($serviceId);
}
/**
* Renders list of tagged services grouped by tag
*
* @param OutputInterface $output
* @param Boolean $showPrivate
*/
protected function outputTags(OutputInterface $output, $showPrivate = false)
{
$tags = $this->containerBuilder->findTags();
asort($tags);
$label = 'Tagged services';
$output->writeln($this->getHelper('formatter')->formatSection('container', $label));
foreach ($tags as $tag) {
$serviceIds = $this->containerBuilder->findTaggedServiceIds($tag);
foreach ($serviceIds as $serviceId => $attributes) {
$definition = $this->resolveServiceDefinition($serviceId);
if ($definition instanceof Definition) {
if (!$showPrivate && !$definition->isPublic()) {
unset($serviceIds[$serviceId]);
continue;
}
}
}
if (count($serviceIds) === 0) {
continue;
}
$output->writeln($this->getHelper('formatter')->formatSection('tag', $tag));
foreach ($serviceIds as $serviceId => $attributes) {
$output->writeln($serviceId);
}
$output->writeln('');
}
}
protected function formatParameter($value)
{
if (is_bool($value) || is_array($value) || (null === $value)) {
return json_encode($value);
}
return $value;
}
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Bundle\FrameworkBundle\Command;
use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@ -50,7 +51,9 @@ class RouterDebugCommand extends ContainerAwareCommand
->setName('router:debug')
->setDefinition(array(
new InputArgument('name', InputArgument::OPTIONAL, 'A route name'),
new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview')
new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'),
new InputOption('format', null, InputOption::VALUE_REQUIRED, 'To output route(s) in other formats'),
new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'),
))
->setDescription('Displays current routes for an application')
->setHelp(<<<EOF
@ -70,151 +73,21 @@ EOF
protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getArgument('name');
$helper = new DescriptorHelper();
if ($name) {
$this->outputRoute($output, $name);
} else {
$this->outputRoutes($output, null, $input->getOption('show-controllers'));
}
}
protected function outputRoutes(OutputInterface $output, $routes = null, $showControllers = false)
{
if (null === $routes) {
$routes = $this->getContainer()->get('router')->getRouteCollection()->all();
}
$output->writeln($this->getHelper('formatter')->formatSection('router', 'Current routes'));
$maxName = strlen('name');
$maxMethod = strlen('method');
$maxScheme = strlen('scheme');
$maxHost = strlen('host');
$maxPath = strlen('path');
foreach ($routes as $name => $route) {
$method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
$scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
$host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
$path = $route->getPath();
$maxName = max($maxName, strlen($name));
$maxMethod = max($maxMethod, strlen($method));
$maxScheme = max($maxScheme, strlen($scheme));
$maxHost = max($maxHost, strlen($host));
$maxPath = max($maxPath, strlen($path));
}
$format = '%-'.$maxName.'s %-'.$maxMethod.'s %-'.$maxScheme.'s %-'.$maxHost.'s %s';
$formatHeader = '%-'.($maxName + 19).'s %-'.($maxMethod + 19).'s %-'.($maxScheme + 19).'s %-'.($maxHost + 19).'s %-'.($maxPath + 19).'s';
if ($showControllers) {
$format = str_replace('s %s', 's %-'.$maxPath.'s %s', $format);
$formatHeader = $formatHeader . ' %s';
}
if ($showControllers) {
$output->writeln(sprintf($formatHeader, '<comment>Name</comment>', '<comment>Method</comment>', '<comment>Scheme</comment>', '<comment>Host</comment>', '<comment>Path</comment>', '<comment>Controller</comment>'));
} else {
$output->writeln(sprintf($formatHeader, '<comment>Name</comment>', '<comment>Method</comment>', '<comment>Scheme</comment>', '<comment>Host</comment>', '<comment>Path</comment>'));
}
foreach ($routes as $name => $route) {
$method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
$scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
$host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
if ($showControllers) {
$defaultData = $route->getDefaults();
$controller = $defaultData['_controller'] ? $defaultData['_controller'] : '';
if ($controller instanceof \Closure) {
$controller = 'Closure';
} else {
if (is_object($controller)) {
$controller = get_class($controller);
}
}
$output->writeln(sprintf($format, $name, $method, $scheme, $host, $route->getPath(), $controller), OutputInterface::OUTPUT_RAW);
} else {
$output->writeln(sprintf($format, $name, $method, $scheme, $host, $route->getPath()), OutputInterface::OUTPUT_RAW);
$route = $this->getContainer()->get('router')->getRouteCollection()->get($name);
if (!$route) {
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
}
$helper->describe($output, $route, $input->getOption('format'), $input->getOption('raw'), array('name' => $name));
} else {
$routes = $this->getContainer()->get('router')->getRouteCollection();
$helper->describe($output, $routes, array(
'format' => $input->getOption('format'),
'raw_text' => $input->getOption('raw'),
'show_controllers' => $input->getOption('show-controllers'),
));
}
}
/**
* @throws \InvalidArgumentException When route does not exist
*/
protected function outputRoute(OutputInterface $output, $name)
{
$route = $this->getContainer()->get('router')->getRouteCollection()->get($name);
if (!$route) {
throw new \InvalidArgumentException(sprintf('The route "%s" does not exist.', $name));
}
$output->writeln($this->getHelper('formatter')->formatSection('router', sprintf('Route "%s"', $name)));
$method = $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY';
$scheme = $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY';
$host = '' !== $route->getHost() ? $route->getHost() : 'ANY';
$output->write('<comment>Name</comment> ');
$output->writeln($name, OutputInterface::OUTPUT_RAW);
$output->write('<comment>Path</comment> ');
$output->writeln($route->getPath(), OutputInterface::OUTPUT_RAW);
$output->write('<comment>Host</comment> ');
$output->writeln($host, OutputInterface::OUTPUT_RAW);
$output->write('<comment>Scheme</comment> ');
$output->writeln($scheme, OutputInterface::OUTPUT_RAW);
$output->write('<comment>Method</comment> ');
$output->writeln($method, OutputInterface::OUTPUT_RAW);
$output->write('<comment>Class</comment> ');
$output->writeln(get_class($route), OutputInterface::OUTPUT_RAW);
$output->write('<comment>Defaults</comment> ');
$output->writeln($this->formatConfigs($route->getDefaults()), OutputInterface::OUTPUT_RAW);
$output->write('<comment>Requirements</comment> ');
// we do not want to show the schemes and methods again that are also in the requirements for BC
$requirements = $route->getRequirements();
unset($requirements['_scheme'], $requirements['_method']);
$output->writeln($this->formatConfigs($requirements) ?: 'NO CUSTOM', OutputInterface::OUTPUT_RAW);
$output->write('<comment>Options</comment> ');
$output->writeln($this->formatConfigs($route->getOptions()), OutputInterface::OUTPUT_RAW);
$output->write('<comment>Path-Regex</comment> ');
$output->writeln($route->compile()->getRegex(), OutputInterface::OUTPUT_RAW);
if (null !== $route->compile()->getHostRegex()) {
$output->write('<comment>Host-Regex</comment> ');
$output->writeln($route->compile()->getHostRegex(), OutputInterface::OUTPUT_RAW);
}
}
protected function formatValue($value)
{
if (is_object($value)) {
return sprintf('object(%s)', get_class($value));
}
if (is_string($value)) {
return $value;
}
return preg_replace("/\n\s*/s", '', var_export($value, true));
}
private function formatConfigs(array $array)
{
$string = '';
ksort($array);
foreach ($array as $name => $value) {
$string .= ($string ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
}
return $string;
}
}

View File

@ -0,0 +1,273 @@
<?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\Descriptor\DescriptorInterface;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
abstract class Descriptor implements DescriptorInterface
{
/**
* @var OutputInterface
*/
private $output;
/**
* {@inheritdoc}
*/
public function describe(OutputInterface $output, $object, array $options = array())
{
$this->output = $output;
switch (true) {
case $object instanceof RouteCollection:
$this->describeRouteCollection($object, $options);
break;
case $object instanceof Route:
$this->describeRoute($object, $options);
break;
case $object instanceof ParameterBag:
$this->describeContainerParameters($object, $options);
break;
case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']:
$this->describeContainerTags($object, $options);
break;
case $object instanceof ContainerBuilder && isset($options['id']):
$this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options);
break;
case $object instanceof ContainerBuilder && isset($options['parameter']):
$this->formatParameter($object->getParameter($options['parameter']));
break;
case $object instanceof ContainerBuilder:
$this->describeContainerServices($object, $options);
break;
case $object instanceof Definition:
$this->describeContainerDefinition($object, $options);
break;
case $object instanceof Alias:
$this->describeContainerAlias($object, $options);
break;
default:
throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_class($object)));
}
}
/**
* Writes content to output.
*
* @param string $content
* @param boolean $decorated
*/
protected function write($content, $decorated = false)
{
$this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW);
}
/**
* Writes content to output.
*
* @param TableHelper $table
* @param boolean $decorated
*/
protected function renderTable(TableHelper $table, $decorated = false)
{
if (!$decorated) {
$table->setCellRowFormat('%s');
$table->setCellHeaderFormat('%s');
}
$table->render($this->output);
}
/**
* Describes an InputArgument instance.
*
* @param RouteCollection $routes
* @param array $options
*/
abstract protected function describeRouteCollection(RouteCollection $routes, array $options = array());
/**
* Describes an InputOption instance.
*
* @param Route $route
* @param array $options
*/
abstract protected function describeRoute(Route $route, array $options = array());
/**
* Describes container parameters.
*
* @param ParameterBag $parameters
* @param array $options
*/
abstract protected function describeContainerParameters(ParameterBag $parameters, array $options = array());
/**
* Describes container tags.
*
* @param ContainerBuilder $builder
* @param array $options
*/
abstract protected function describeContainerTags(ContainerBuilder $builder, array $options = array());
/**
* Describes a container service by its name.
*
* Common options are:
* * name: name of described service
*
* @param Definition|Alias|object $service
* @param array $options
*/
abstract protected function describeContainerService($service, array $options = array());
/**
* Describes container services.
*
* Common options are:
* * tag: filters described services by given tag
*
* @param ContainerBuilder $builder
* @param array $options
*/
abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = array());
/**
* Describes a service definition.
*
* @param Definition $definition
* @param array $options
*/
abstract protected function describeContainerDefinition(Definition $definition, array $options = array());
/**
* Describes a service alias.
*
* @param Alias $alias
* @param array $options
*/
abstract protected function describeContainerAlias(Alias $alias, array $options = array());
/**
* Formats a value as string.
*
* @param mixed $value
*
* @return string
*/
protected function formatValue($value)
{
if (is_object($value)) {
return sprintf('object(%s)', get_class($value));
}
if (is_string($value)) {
return $value;
}
return preg_replace("/\n\s*/s", '', var_export($value, true));
}
/**
* Formats a parameter.
*
* @param mixed $value
*
* @return string
*/
protected function formatParameter($value)
{
if (is_bool($value) || is_array($value) || (null === $value)) {
return json_encode($value);
}
return (string) $value;
}
/**
* @param ContainerBuilder $builder
* @param string $serviceId
*
* @return mixed
*/
protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceId)
{
if ($builder->hasDefinition($serviceId)) {
return $builder->getDefinition($serviceId);
}
// Some service IDs don't have a Definition, they're simply an Alias
if ($builder->hasAlias($serviceId)) {
return $builder->getAlias($serviceId);
}
// the service has been injected in some special way, just return the service
return $builder->get($serviceId);
}
/**
* @param ContainerBuilder $builder
* @param boolean $showPrivate
*
* @return array
*/
protected function findDefinitionsByTag(ContainerBuilder $builder, $showPrivate)
{
$definitions = array();
$tags = $builder->findTags();
asort($tags);
foreach ($tags as $tag) {
foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
if (!$definition instanceof Definition || !$showPrivate && !$definition->isPublic()) {
continue;
}
if (!isset($definitions[$tag])) {
$definitions[$tag] = array();
}
$definitions[$tag][$serviceId] = $definition;
}
}
return $definitions;
}
protected function sortParameters(ParameterBag $parameters)
{
$parameters = $parameters->all();
ksort($parameters);
return $parameters;
}
protected function sortServiceIds(array $serviceIds)
{
asort($serviceIds);
return $serviceIds;
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class JsonDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeRouteCollection(RouteCollection $routes, array $options = array())
{
$data = array();
foreach ($routes->all() as $name => $route) {
$data[$name] = $this->getRouteData($route);
}
$this->writeData($data, $options);
}
/**
* {@inheritdoc}
*/
protected function describeRoute(Route $route, array $options = array())
{
$this->writeData($this->getRouteData($route), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
{
$this->writeData($this->sortParameters($parameters), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$data = array();
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
$data[$tag] = array();
foreach ($definitions as $definition) {
$data[$tag][] = $this->getContainerDefinitionData($definition, true);
}
}
$this->writeData($data, $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = array())
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
}
if ($service instanceof Alias) {
$this->writeData($this->getContainerAliasData($service), $options);
} elseif ($service instanceof Definition) {
$this->writeData($this->getContainerDefinitionData($service), $options);
} else {
$this->writeData(get_class($service), $options);
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
{
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$showPrivate = isset($options['show_private']) && $options['show_private'];
$data = array('definitions' => array(), 'aliases' => array(), 'services' => array());
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if ($service instanceof Alias) {
$data['aliases'][$serviceId] = $this->getContainerAliasData($service);
} elseif ($service instanceof Definition) {
if (($showPrivate || $service->isPublic())) {
$data['definitions'][$serviceId] = $this->getContainerDefinitionData($service);
}
} else {
$data['services'][$serviceId] = get_class($service);
}
}
$this->writeData($data, $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerDefinition(Definition $definition, array $options = array())
{
$this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags']), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerAlias(Alias $alias, array $options = array())
{
$this->writeData($this->getContainerAliasData($alias), $options);
}
/**
* Writes data as json.
*
* @param array $data
* @param array $options
*
* @return array|string
*/
private function writeData(array $data, array $options)
{
$this->write(json_encode($data, isset($options['json_encoding']) ? $options['json_encoding'] : 0));
}
/**
* @param Route $route
*
* @return array
*/
protected function getRouteData(Route $route)
{
$requirements = $route->getRequirements();
unset($requirements['_scheme'], $requirements['_method']);
return array(
'path' => $route->getPath(),
'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY',
'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
'class' => get_class($route),
'defaults' => $route->getDefaults(),
'requirements' => $requirements ?: 'NO CUSTOM',
'options' => $route->getOptions(),
'pathRegex' => $route->compile()->getRegex(),
);
}
/**
* @param Definition $definition
* @param boolean $omitTags
*
* @return array
*/
private function getContainerDefinitionData(Definition $definition, $omitTags = false)
{
$data = array(
'class' => (string) $definition->getClass(),
'scope' => $definition->getScope(),
'public' => $definition->isPublic(),
'synthetic' => $definition->isSynthetic(),
'file' => $definition->getFile(),
);
if (!$omitTags) {
$data['tags'] = array();
if (count($definition->getTags())) {
foreach ($definition->getTags() as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$data['tags'][] = array('name' => $tagName, 'parameters' => $parameters);
}
}
}
}
return $data;
}
/**
* @param Alias $alias
*
* @return array
*/
private function getContainerAliasData(Alias $alias)
{
return array(
'service' => (string) $alias,
'public' => $alias->isPublic(),
);
}
}

View File

@ -0,0 +1,213 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class MarkdownDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeRouteCollection(RouteCollection $routes, array $options = array())
{
$first = true;
foreach ($routes->all() as $name => $route) {
if ($first) {
$first = false;
} else {
$this->write("\n\n");
}
$this->describeRoute($route, array('name' => $name));
}
}
/**
* {@inheritdoc}
*/
protected function describeRoute(Route $route, array $options = array())
{
$requirements = $route->getRequirements();
unset($requirements['_scheme'], $requirements['_method']);
$output = '- Path: '.$route->getPath()
."\n".'- Host: '.('' !== $route->getHost() ? $route->getHost() : 'ANY')
."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY')
."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')
."\n".'- Class: '.get_class($route)
."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults())
."\n".'- Requirements: '.$this->formatRouterConfig($requirements) ?: 'NONE'
."\n".'- Options: '.$this->formatRouterConfig($route->getOptions())
."\n".'- Path-Regex: '.$route->compile()->getRegex();
$this->write(isset($options['name'])
? $options['name']."\n".str_repeat('-', strlen($options['name']))."\n".$output
: $output);
}
/**
* {@inheritdoc}
*/
protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
{
$this->write("Container parameters\n====================\n");
foreach ($this->sortParameters($parameters) as $key => $value) {
$this->write(sprintf("\n- `%s`: `%s`", $key, $this->formatParameter($value)));
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$this->write("Container tags\n==============");
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
$this->write("\n\n".$tag."\n".str_repeat('-', strlen($tag)));
foreach ($definitions as $serviceId => $definition) {
$this->write("\n\n");
$this->describeContainerDefinition($definition, array('omit_tags' => true, 'id' => $serviceId));
}
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = array())
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
}
$childOptions = array('id' => $options['id'], 'as_array' => true);
if ($service instanceof Alias) {
$this->describeContainerAlias($service, $childOptions);
} elseif ($service instanceof Definition) {
$this->describeContainerDefinition($service, $childOptions);
} else {
$this->write(sprintf("**`%s`:** `%s`", $options['id'], get_class($service)));
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$title = $showPrivate ? 'Public and private services' : 'Public 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();
$showPrivate = isset($options['show_private']) && $options['show_private'];
$services = array('definitions' => array(), 'aliases' => array(), 'services' => array());
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if ($service instanceof Alias) {
$services['aliases'][$serviceId] = $service;
} elseif ($service instanceof Definition) {
if (($showPrivate || $service->isPublic())) {
$services['definitions'][$serviceId] = $service;
}
} else {
$services['services'][$serviceId] = $service;
}
}
if (!empty($services['definitions'])) {
$this->write("\n\nDefinitions\n-----------");
foreach ($services['definitions'] as $id => $service) {
$this->write("\n\n");
$this->describeContainerDefinition($service, array('id' => $id));
}
}
if (!empty($services['aliases'])) {
$this->write("\n\nAliases\n-------");
foreach ($services['aliases'] as $id => $service) {
$this->write("\n\n");
$this->describeContainerAlias($service, array('id' => $id));
}
}
if (!empty($services['services'])) {
$this->write("\n\nServices\n--------\n");
foreach ($services['services'] as $id => $service) {
$this->write("\n");
$this->write(sprintf('- `%s`: `%s`', $id, get_class($service)));
}
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerDefinition(Definition $definition, array $options = array())
{
$output = '- Class: `'.$definition->getClass().'`'
."\n".'- Scope: `'.$definition->getScope().'`'
."\n".'- Public: '.($definition->isPublic() ? 'yes' : 'no')
."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no');
if ($definition->getFile()) {
$output .= "\n".'- File: `'.$definition->getFile().'`';
}
if (!(isset($options['omit_tags']) && $options['omit_tags'])) {
foreach ($definition->getTags() as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$output .= "\n".'- Tag: `'.$tagName.'`';
foreach ($parameters as $name => $value) {
$output .= "\n".' - '.ucfirst($name).': '.$value;
}
}
}
}
$this->write(isset($options['id']) ? sprintf("**`%s`:**\n%s", $options['id'], $output) : $output);
}
/**
* {@inheritdoc}
*/
protected function describeContainerAlias(Alias $alias, array $options = array())
{
$output = '- Service: `'.$alias.'`'
."\n".'- Public: '.($alias->isPublic() ? 'yes' : 'no');
$this->write(isset($options['id']) ? sprintf("**`%s`:**\n%s", $options['id'], $output) : $output);
}
private function formatRouterConfig(array $array)
{
if (!count($array)) {
return 'NONE';
}
$string = '';
ksort($array);
foreach ($array as $name => $value) {
$string .= "\n".' - `'.$name.'`: '.$this->formatValue($value);
}
return $string;
}
}

View File

@ -0,0 +1,308 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\Console\Helper\TableHelper;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class TextDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeRouteCollection(RouteCollection $routes, array $options = array())
{
$showControllers = isset($options['show_controllers']) && $options['show_controllers'];
$headers = array('Name', 'Method', 'Scheme', 'Host', 'Path');
$table = new TableHelper();
$table->setHeaders($showControllers ? array_merge($headers, array('Controller')) : $headers);
foreach ($routes->all() as $name => $route) {
$row = array(
$name,
$route->getMethods() ? implode('|', $route->getMethods()) : 'ANY',
$route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY',
'' !== $route->getHost() ? $route->getHost() : 'ANY',
$route->getPath(),
);
if ($showControllers) {
$defaultData = $route->getDefaults();
$controller = $defaultData['_controller'] ? $defaultData['_controller'] : '';
if ($controller instanceof \Closure) {
$controller = 'Closure';
} else {
if (is_object($controller)) {
$controller = get_class($controller);
}
}
$row[] = $controller;
}
$table->addRow($row);
}
$this->writeText($this->formatSection('router', 'Current routes')."\n", $options);
$this->renderTable($table, !(isset($options['raw_output']) && $options['raw_output']));
}
/**
* {@inheritdoc}
*/
protected function describeRoute(Route $route, array $options = array())
{
$requirements = $route->getRequirements();
unset($requirements['_scheme'], $requirements['_method']);
// fixme: values were originally written as raw
$description = array(
'<comment>Path</comment> '.$route->getPath(),
'<comment>Host</comment> '.('' !== $route->getHost() ? $route->getHost() : 'ANY'),
'<comment>Scheme</comment> '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'),
'<comment>Method</comment> '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'),
'<comment>Class</comment> '.get_class($route),
'<comment>Defaults</comment> '.$this->formatRouterConfig($route->getDefaults()),
'<comment>Requirements</comment> '.$this->formatRouterConfig($requirements) ?: 'NO CUSTOM',
'<comment>Options</comment> '.$this->formatRouterConfig($route->getOptions()),
'<comment>Path-Regex</comment> '.$route->compile()->getRegex(),
);
if (isset($options['name'])) {
array_unshift($description, '<comment>Name</comment> '.$options['name']);
array_unshift($description, $this->formatSection('router', sprintf('Route "%s"', $options['name'])));
}
if (null !== $route->compile()->getHostRegex()) {
$description[] = '<comment>Host-Regex</comment> '.$route->compile()->getHostRegex();
}
$this->writeText(implode("\n", $description), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
{
$table = new TableHelper();
$table->setHeaders(array('Parameter', 'Value'));
foreach ($this->sortParameters($parameters) as $parameter => $value) {
$table->addRow(array($parameter, $this->formatParameter($value)));
}
$this->writeText($this->formatSection('container', 'List of parameters')."\n", $options);
$this->renderTable($table, !(isset($options['raw_output']) && $options['raw_output']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$description = array($this->formatSection('container', 'Tagged services'));
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
$description[] = $this->formatSection('tag', $tag);
$description = array_merge($description, array_keys($definitions));
$description[] = '';
}
$this->writeText(implode("\n", $description), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = array())
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
}
if ($service instanceof Alias) {
$this->describeContainerAlias($service, $options);
} elseif ($service instanceof Definition) {
$this->describeContainerDefinition($service, $options);
} else {
$description = $this->formatSection('container', sprintf('Information for service <info>%s</info>', $options['id']))
."\n".sprintf('<comment>Service Id</comment> %s', isset($options['id']) ? $options['id'] : '-')
."\n".sprintf('<comment>Class</comment> %s', get_class($service));
$this->writeText($description, $options);
}
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
{
$showPrivate = isset($options['show_private']) && $options['show_private'];
$showTag = isset($options['tag']) ? $options['tag'] : null;
if ($showPrivate) {
$label = '<comment>Public</comment> and <comment>private</comment> services';
} else {
$label = '<comment>Public</comment> services';
}
if ($showTag) {
$label .= ' with tag <info>'.$options['tag'].'</info>';
}
$this->writeText($this->formatSection('container', $label)."\n", $options);
$serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds();
$maxTags = array();
foreach ($serviceIds as $key => $serviceId) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
if ($definition instanceof Definition) {
// filter out private services unless shown explicitly
if (!$showPrivate && !$definition->isPublic()) {
unset($serviceIds[$key]);
continue;
}
if ($showTag) {
$tags = $definition->getTag($showTag);
foreach ($tags as $tag) {
foreach ($tag as $key => $value) {
if (!isset($maxTags[$key])) {
$maxTags[$key] = strlen($key);
}
if (strlen($value) > $maxTags[$key]) {
$maxTags[$key] = strlen($value);
}
}
}
}
}
}
$tagsCount = count($maxTags);
$tagsNames = array_keys($maxTags);
$table = new TableHelper();
$table->setHeaders(array_merge(array('Service ID'), $tagsNames, array('Scope', 'Class name')));
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
$definition = $this->resolveServiceDefinition($builder, $serviceId);
if ($definition instanceof Definition) {
if ($showTag) {
foreach ($definition->getTag($showTag) as $key => $tag) {
$tagValues = array();
foreach ($tagsNames as $tagName) {
$tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : "";
}
if (0 === $key) {
$table->addRow(array_merge(array($serviceId), $tagValues, array($definition->getScope(), $definition->getClass())));
} else {
$table->addRow(array_merge(array(' "'), $tagValues, array('', '')));
}
}
} else {
$table->addRow(array($serviceId, $definition->getScope(), $definition->getClass()));
}
} elseif ($definition instanceof Alias) {
$alias = $definition;
$table->addRow(array_merge(array($serviceId, 'n/a', sprintf('alias for "%s"', $alias)), $tagsCount ? array_fill(0, $tagsCount, "") : array()));
} else {
// we have no information (happens with "service_container")
$table->addRow(array_merge(array($serviceId, '', get_class($definition)), $tagsCount ? array_fill(0, $tagsCount, "") : array()));
}
}
$this->renderTable($table);
}
/**
* {@inheritdoc}
*/
protected function describeContainerDefinition(Definition $definition, array $options = array())
{
$description = isset($options['id'])
? array($this->formatSection('container', sprintf('Information for service <info>%s</info>', $options['id'])))
: array();
$description[] = sprintf('<comment>Service Id</comment> %s', isset($options['id']) ? $options['id'] : '-');
$description[] = sprintf('<comment>Class</comment> %s', $definition->getClass() ?: "-");
$tags = $definition->getTags();
if (count($tags)) {
$description[] = '<comment>Tags</comment>';
foreach ($tags as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$description[] = sprintf(' - %-30s (%s)', $tagName, implode(', ', array_map(function($key, $value) {
return sprintf('<info>%s</info>: %s', $key, $value);
}, array_keys($parameters), array_values($parameters))));
}
}
} else {
$description[] = '<comment>Tags</comment> -';
}
$description[] = sprintf('<comment>Scope</comment> %s', $definition->getScope());
$description[] = sprintf('<comment>Public</comment> %s', $definition->isPublic() ? 'yes' : 'no');
$description[] = sprintf('<comment>Synthetic</comment> %s', $definition->isSynthetic() ? 'yes' : 'no');
$description[] = sprintf('<comment>Required File</comment> %s', $definition->getFile() ? $definition->getFile() : '-');
$this->writeText(implode("\n", $description), $options);
}
/**
* {@inheritdoc}
*/
protected function describeContainerAlias(Alias $alias, array $options = array())
{
$this->writeText(sprintf('This service is an alias for the service <info>%s</info>', (string) $alias), $options);
}
/**
* @param array $array
*
* @return string
*/
private function formatRouterConfig(array $array)
{
$string = '';
ksort($array);
foreach ($array as $name => $value) {
$string .= ($string ? "\n".str_repeat(' ', 13) : '').$name.': '.$this->formatValue($value);
}
return $string;
}
/**
* @param string $section
* @param string $message
*
* @return string
*/
private function formatSection($section, $message)
{
return sprintf('<info>[%s]</info> %s', $section, $message);
}
/**
* @param string $content
* @param array $options
*/
private function writeText($content, array $options = array())
{
$this->write(
isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content,
isset($options['raw_output']) ? !$options['raw_output'] : true
);
}
}

View File

@ -0,0 +1,341 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class XmlDescriptor extends Descriptor
{
/**
* {@inheritdoc}
*/
protected function describeRouteCollection(RouteCollection $routes, array $options = array())
{
$this->writeDocument($this->getRouteCollectionDocument($routes));
}
/**
* {@inheritdoc}
*/
protected function describeRoute(Route $route, array $options = array())
{
$this->writeDocument($this->getRouteDocument($route, isset($options['name']) ? $options['name'] : null));
}
/**
* {@inheritdoc}
*/
protected function describeContainerParameters(ParameterBag $parameters, array $options = array())
{
$this->writeDocument($this->getContainerParametersDocument($parameters));
}
/**
* {@inheritdoc}
*/
protected function describeContainerTags(ContainerBuilder $builder, array $options = array())
{
$this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_private']) && $options['show_private']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerService($service, array $options = array())
{
if (!isset($options['id'])) {
throw new \InvalidArgumentException('An "id" option must be provided.');
}
$this->writeDocument($this->getContainerServiceDocument($service, $options['id']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerServices(ContainerBuilder $builder, array $options = array())
{
$this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerDefinition(Definition $definition, array $options = array())
{
$this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags']));
}
/**
* {@inheritdoc}
*/
protected function describeContainerAlias(Alias $alias, array $options = array())
{
$this->writeDocument($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null));
}
/**
* Writes DOM document.
*
* @param \DOMDocument $dom
*
* @return \DOMDocument|string
*/
private function writeDocument(\DOMDocument $dom)
{
$dom->formatOutput = true;
$this->write($dom->saveXML());
}
/**
* @param RouteCollection $routes
*
* @return \DOMDocument
*/
private function getRouteCollectionDocument(RouteCollection $routes)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($routesXML = $dom->createElement('routes'));
foreach ($routes->all() as $name => $route) {
$routeXML = $this->getRouteDocument($route, $name);
$routesXML->appendChild($routesXML->ownerDocument->importNode($routeXML->childNodes->item(0), true));
}
return $dom;
}
/**
* @param Route $route
* @param string|null $name
*
* @return \DOMDocument
*/
private function getRouteDocument(Route $route, $name = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($routeXML = $dom->createElement('route'));
if ($name) {
$routeXML->setAttribute('name', $name);
}
$routeXML->setAttribute('path', $route->getPath());
$routeXML->setAttribute('class', get_class($route));
$routeXML->setAttribute('path_regex', $route->compile()->getRegex());
if ('' !== $route->getHost()) {
$routeXML->appendChild($hostXML = $dom->createElement('host'));
$hostXML->setAttribute('regex', $route->compile()->getHostRegex());
$hostXML->appendChild(new \DOMText($route->getHost()));
}
foreach ($route->getSchemes() as $scheme) {
$routeXML->appendChild($schemeXML = $dom->createElement('scheme'));
$schemeXML->appendChild(new \DOMText($scheme));
}
foreach ($route->getMethods() as $method) {
$routeXML->appendChild($methodXML = $dom->createElement('method'));
$methodXML->appendChild(new \DOMText($method));
}
if (count($route->getDefaults())) {
$routeXML->appendChild($defaultsXML = $dom->createElement('defaults'));
foreach ($route->getDefaults() as $attribute => $value) {
$defaultsXML->appendChild($defaultXML = $dom->createElement('default'));
$defaultXML->setAttribute('key', $attribute);
$defaultXML->appendChild(new \DOMText($this->formatValue($value)));
}
}
$requirements = $route->getRequirements();
unset($requirements['_scheme'], $requirements['_method']);
if (count($requirements)) {
$routeXML->appendChild($requirementsXML = $dom->createElement('requirements'));
foreach ($requirements as $attribute => $pattern) {
$requirementsXML->appendChild($requirementXML = $dom->createElement('requirement'));
$requirementXML->setAttribute('key', $attribute);
$requirementXML->appendChild(new \DOMText($pattern));
}
}
if (count($route->getOptions())) {
$routeXML->appendChild($optionsXML = $dom->createElement('options'));
foreach ($route->getOptions() as $name => $value) {
$optionsXML->appendChild($optionXML = $dom->createElement('option'));
$optionXML->setAttribute('key', $name);
$optionXML->appendChild(new \DOMText($this->formatValue($value)));
}
}
return $dom;
}
/**
* @param ParameterBag $parameters
*
* @return \DOMDocument
*/
private function getContainerParametersDocument(ParameterBag $parameters)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($parametersXML = $dom->createElement('parameters'));
foreach ($this->sortParameters($parameters) as $key => $value) {
$parametersXML->appendChild($parameterXML = $dom->createElement('parameter'));
$parameterXML->setAttribute('key', $key);
$parameterXML->appendChild(new \DOMText($this->formatParameter($value)));
}
return $dom;
}
/**
* @param ContainerBuilder $builder
* @param boolean $showPrivate
*
* @return \DOMDocument
*/
private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($containerXML = $dom->createElement('container'));
foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) {
$containerXML->appendChild($tagXML = $dom->createElement('tag'));
$tagXML->setAttribute('name', $tag);
foreach ($definitions as $serviceId => $definition) {
$definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true);
$tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true));
}
}
return $dom;
}
/**
* @param mixed $service
* @param string $id
*
* @return \DOMDocument
*/
private function getContainerServiceDocument($service, $id)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
if ($service instanceof Alias) {
$dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true));
} elseif ($service instanceof Definition) {
$dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id)->childNodes->item(0), true));
} else {
$dom->appendChild($serviceXML = $dom->createElement('service'));
$serviceXML->setAttribute('id', $id);
$serviceXML->setAttribute('class', get_class($service));
}
return $dom;
}
/**
* @param ContainerBuilder $builder
* @param string|null $tag
* @param boolean $showPrivate
*
* @return \DOMDocument
*/
private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($containerXML = $dom->createElement('container'));
$serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds();
foreach ($this->sortServiceIds($serviceIds) as $serviceId) {
$service = $this->resolveServiceDefinition($builder, $serviceId);
if ($service instanceof Definition && !($showPrivate || $service->isPublic())) {
continue;
}
$serviceXML = $this->getContainerServiceDocument($service, $serviceId);
$containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true));
}
return $dom;
}
/**
* @param Definition $definition
* @param string|null $id
* @param boolean $omitTags
*
* @return \DOMDocument
*/
private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($serviceXML = $dom->createElement('definition'));
if ($id) {
$serviceXML->setAttribute('id', $id);
}
$serviceXML->setAttribute('class', $definition->getClass());
$serviceXML->setAttribute('scope', $definition->getScope());
$serviceXML->setAttribute('public', $definition->isPublic() ? 'true' : 'false');
$serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false');
$serviceXML->setAttribute('file', $definition->getFile());
if (!$omitTags) {
$tags = $definition->getTags();
if (count($tags) > 0) {
$serviceXML->appendChild($tagsXML = $dom->createElement('tags'));
foreach ($tags as $tagName => $tagData) {
foreach ($tagData as $parameters) {
$tagsXML->appendChild($tagXML = $dom->createElement('tag'));
$tagXML->setAttribute('name', $tagName);
foreach ($parameters as $name => $value) {
$tagXML->appendChild($parameterXML = $dom->createElement('parameter'));
$parameterXML->setAttribute('name', $name);
$parameterXML->textContent = $value;
}
}
}
}
}
return $dom;
}
/**
* @param Alias $alias
* @param string|null $id
*
* @return \DOMDocument
*/
private function getContainerAliasDocument(Alias $alias, $id = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->appendChild($aliasXML = $dom->createElement('alias'));
if ($id) {
$aliasXML->setAttribute('id', $id);
}
$aliasXML->setAttribute('service', (string) $alias);
$aliasXML->setAttribute('public', $alias->isPublic() ? 'true' : 'false');
return $dom;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Console\Helper;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor;
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;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class DescriptorHelper extends BaseDescriptorHelper
{
/**
* Constructor.
*/
public function __construct()
{
$this
->register('txt', new TextDescriptor())
->register('xml', new XmlDescriptor())
->register('json', new JsonDescriptor())
->register('md', new MarkdownDescriptor())
;
}
}

View File

@ -0,0 +1,131 @@
<?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\Tests\Console\Descriptor;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
abstract class AbstractDescriptorTest extends \PHPUnit_Framework_TestCase
{
/** @dataProvider getDescribeRouteCollectionTestData */
public function testDescribeRouteCollection(RouteCollection $routes, $expectedDescription)
{
$this->assertDescription($expectedDescription, $routes);
}
public function getDescribeRouteCollectionTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getRouteCollections());
}
/** @dataProvider getDescribeRouteTestData */
public function testDescribeRoute(Route $route, $expectedDescription)
{
$this->assertDescription($expectedDescription, $route);
}
public function getDescribeRouteTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getRoutes());
}
/** @dataProvider getDescribeContainerParametersTestData */
public function testDescribeContainerParameters(ParameterBag $parameters, $expectedDescription)
{
$this->assertDescription($expectedDescription, $parameters);
}
public function getDescribeContainerParametersTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getContainerParameters());
}
/** @dataProvider getDescribeContainerBuilderTestData */
public function testDescribeContainerBuilder(ContainerBuilder $builder, $expectedDescription, array $options)
{
$this->assertDescription($expectedDescription, $builder, $options);
}
public function getDescribeContainerBuilderTestData()
{
return $this->getContainerBuilderDescriptionTestData(ObjectsProvider::getContainerBuilders());
}
/** @dataProvider getDescribeContainerDefinitionTestData */
public function testDescribeContainerDefinition(Definition $definition, $expectedDescription)
{
$this->assertDescription($expectedDescription, $definition);
}
public function getDescribeContainerDefinitionTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitions());
}
/** @dataProvider getDescribeContainerAliasTestData */
public function testDescribeContainerAlias(Alias $alias, $expectedDescription)
{
$this->assertDescription($expectedDescription, $alias);
}
public function getDescribeContainerAliasTestData()
{
return $this->getDescriptionTestData(ObjectsProvider::getContainerAliases());
}
abstract protected function getDescriptor();
abstract protected function getFormat();
private function assertDescription($expectedDescription, $describedObject, array $options = array())
{
$options['raw_output'] = true;
$output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true);
$this->getDescriptor()->describe($output, $describedObject, $options);
$this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch())));
}
private function getDescriptionTestData(array $objects)
{
$data = array();
foreach ($objects as $name => $object) {
$description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat()));
$data[] = array($object, $description);
}
return $data;
}
private function getContainerBuilderDescriptionTestData(array $objects)
{
$variations = array(
'services' => array('show_private' => true),
'public' => array('show_private' => false),
'tag1' => array('show_private' => true, 'tag' => 'tag1'),
'tags' => array('group_by' => 'tags', 'show_private' => true)
);
$data = array();
foreach ($objects as $name => $object) {
foreach ($variations as $suffix => $options) {
$description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat()));
$data[] = array($object, $description, $options);
}
}
return $data;
}
}

View File

@ -0,0 +1,27 @@
<?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\Tests\Console\Descriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor;
class JsonDescriptorTest extends AbstractDescriptorTest
{
protected function getDescriptor()
{
return new JsonDescriptor();
}
protected function getFormat()
{
return 'json';
}
}

View File

@ -0,0 +1,27 @@
<?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\Tests\Console\Descriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor;
class MarkdownDescriptorTest extends AbstractDescriptorTest
{
protected function getDescriptor()
{
return new MarkdownDescriptor();
}
protected function getFormat()
{
return 'md';
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class ObjectsProvider
{
public static function getRouteCollections()
{
$collection1 = new RouteCollection();
foreach (self::getRoutes() as $name => $route) {
$collection1->add($name, $route);
}
return array('route_collection_1' => $collection1);
}
public static function getRoutes()
{
return array(
'route_1' => new Route(
'/hello/{name}',
array('name' => 'Joseph'),
array('name' => '[a-z]+'),
array('opt1' => 'val1', 'opt2' => 'val2'),
'localhost',
array('http', 'https'),
array('get', 'head')
),
'route_2' => new Route(
'/name/add',
array(),
array(),
array('opt1' => 'val1', 'opt2' => 'val2'),
'localhost',
array('http', 'https'),
array('put', 'post')
),
);
}
public static function getContainerParameters()
{
return array(
'parameters_1' => new ParameterBag(array(
'integer' => 12,
'string' => 'Hello world!',
'boolean' => true,
'array' => array(12, 'Hello world!', true),
)),
);
}
public static function getContainerBuilders()
{
$builder1 = new ContainerBuilder();
$builder1->setDefinitions(self::getContainerDefinitions());
$builder1->setAliases(self::getContainerAliases());
return array('builder_1' => $builder1);
}
public static function getContainerDefinitions()
{
$definition1 = new Definition('Full\\Qualified\\Class1');
$definition2 = new Definition('Full\\Qualified\\Class2');
return array(
'definition_1' => $definition1
->setPublic(true)
->setSynthetic(false),
'definition_2' => $definition2
->setPublic(false)
->setSynthetic(true)
->setFile('/path/to/file')
->addTag('tag1', array('attr1' => 'val1', 'attr2' => 'val2'))
->addTag('tag1', array('attr3' => 'val3'))
->addTag('tag2'),
);
}
public static function getContainerAliases()
{
return array(
'alias_1' => new Alias('service_1', true),
'alias_2' => new Alias('service_2', false),
);
}
}

View File

@ -0,0 +1,27 @@
<?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\Tests\Console\Descriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor;
class TextDescriptorTest extends AbstractDescriptorTest
{
protected function getDescriptor()
{
return new TextDescriptor();
}
protected function getFormat()
{
return 'txt';
}
}

View File

@ -0,0 +1,27 @@
<?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\Tests\Console\Descriptor;
use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor;
class XmlDescriptorTest extends AbstractDescriptorTest
{
protected function getDescriptor()
{
return new XmlDescriptor();
}
protected function getFormat()
{
return 'xml';
}
}

View File

@ -0,0 +1 @@
{"service":"service_1","public":true}

View File

@ -0,0 +1,2 @@
- Service: `service_1`
- Public: yes

View File

@ -0,0 +1 @@
This service is an alias for the service <info>service_1</info>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<alias service="service_1" public="true"/>

View File

@ -0,0 +1 @@
{"service":"service_2","public":false}

View File

@ -0,0 +1,2 @@
- Service: `service_2`
- Public: no

View File

@ -0,0 +1 @@
This service is an alias for the service <info>service_2</info>

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<alias service="service_2" public="false"/>

View File

@ -0,0 +1 @@
{"definitions":{"definition_1":{"class":"Full\\Qualified\\Class1","scope":"container","public":true,"synthetic":false,"file":null,"tags":[]}},"aliases":{"alias_1":{"service":"service_1","public":true},"alias_2":{"service":"service_2","public":false}},"services":{"service_container":"Symfony\\Component\\DependencyInjection\\ContainerBuilder"}}

View File

@ -0,0 +1,27 @@
Public services
===============
Definitions
-----------
**`definition_1`:**
- Class: `Full\Qualified\Class1`
- Scope: `container`
- Public: yes
- Synthetic: no
Aliases
-------
**`alias_1`:**
- Service: `service_1`
- Public: yes
**`alias_2`:**
- Service: `service_2`
- Public: no
Services
--------
- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder`

View File

@ -0,0 +1,9 @@
<info>[container]</info> <comment>Public</comment> services
+-------------------+-----------+--------------------------------------------------------+
| Service ID | Scope | Class name |
+-------------------+-----------+--------------------------------------------------------+
| alias_1 | n/a | alias for "service_1" |
| alias_2 | n/a | alias for "service_2" |
| definition_1 | container | Full\Qualified\Class1 |
| service_container | | Symfony\Component\DependencyInjection\ContainerBuilder |
+-------------------+-----------+--------------------------------------------------------+

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" file=""/>
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerBuilder"/>
</container>

View File

@ -0,0 +1 @@
{"definitions":{"definition_1":{"class":"Full\\Qualified\\Class1","scope":"container","public":true,"synthetic":false,"file":null,"tags":[]},"definition_2":{"class":"Full\\Qualified\\Class2","scope":"container","public":false,"synthetic":true,"file":"\/path\/to\/file","tags":[{"name":"tag1","parameters":{"attr1":"val1","attr2":"val2"}},{"name":"tag1","parameters":{"attr3":"val3"}},{"name":"tag2","parameters":[]}]}},"aliases":{"alias_1":{"service":"service_1","public":true},"alias_2":{"service":"service_2","public":false}},"services":{"service_container":"Symfony\\Component\\DependencyInjection\\ContainerBuilder"}}

View File

@ -0,0 +1,40 @@
Public and private services
===========================
Definitions
-----------
**`definition_1`:**
- Class: `Full\Qualified\Class1`
- Scope: `container`
- Public: yes
- Synthetic: no
**`definition_2`:**
- Class: `Full\Qualified\Class2`
- Scope: `container`
- Public: no
- Synthetic: yes
- File: `/path/to/file`
- Tag: `tag1`
- Attr1: val1
- Attr2: val2
- Tag: `tag1`
- Attr3: val3
- Tag: `tag2`
Aliases
-------
**`alias_1`:**
- Service: `service_1`
- Public: yes
**`alias_2`:**
- Service: `service_2`
- Public: no
Services
--------
- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder`

View File

@ -0,0 +1,10 @@
<info>[container]</info> <comment>Public</comment> and <comment>private</comment> services
+-------------------+-----------+--------------------------------------------------------+
| Service ID | Scope | Class name |
+-------------------+-----------+--------------------------------------------------------+
| alias_1 | n/a | alias for "service_1" |
| alias_2 | n/a | alias for "service_2" |
| definition_1 | container | Full\Qualified\Class1 |
| definition_2 | container | Full\Qualified\Class2 |
| service_container | | Symfony\Component\DependencyInjection\ContainerBuilder |
+-------------------+-----------+--------------------------------------------------------+

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<alias id="alias_1" service="service_1" public="true"/>
<alias id="alias_2" service="service_2" public="false"/>
<definition id="definition_1" class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" file=""/>
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" file="/path/to/file">
<tags>
<tag name="tag1">
<parameter name="attr1"/>
<parameter name="attr2"/>
</tag>
<tag name="tag1">
<parameter name="attr3"/>
</tag>
<tag name="tag2"/>
</tags>
</definition>
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerBuilder"/>
</container>

View File

@ -0,0 +1 @@
{"definitions":{"definition_2":{"class":"Full\\Qualified\\Class2","scope":"container","public":false,"synthetic":true,"file":"\/path\/to\/file","tags":[{"name":"tag1","parameters":{"attr1":"val1","attr2":"val2"}},{"name":"tag1","parameters":{"attr3":"val3"}},{"name":"tag2","parameters":[]}]}},"aliases":[],"services":[]}

View File

@ -0,0 +1,18 @@
Public and private services with tag `tag1`
===========================================
Definitions
-----------
**`definition_2`:**
- Class: `Full\Qualified\Class2`
- Scope: `container`
- Public: no
- Synthetic: yes
- File: `/path/to/file`
- Tag: `tag1`
- Attr1: val1
- Attr2: val2
- Tag: `tag1`
- Attr3: val3
- Tag: `tag2`

View File

@ -0,0 +1,7 @@
<info>[container]</info> <comment>Public</comment> and <comment>private</comment> services with tag <info>tag1</info>
+--------------+-------+-------+-------+-----------+-----------------------+
| Service ID | attr1 | attr2 | attr3 | Scope | Class name |
+--------------+-------+-------+-------+-----------+-----------------------+
| definition_2 | val1 | val2 | | container | Full\Qualified\Class2 |
| " | | | val3 | | |
+--------------+-------+-------+-------+-----------+-----------------------+

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" file="/path/to/file">
<tags>
<tag name="tag1">
<parameter name="attr1"/>
<parameter name="attr2"/>
</tag>
<tag name="tag1">
<parameter name="attr3"/>
</tag>
<tag name="tag2"/>
</tags>
</definition>
</container>

View File

@ -0,0 +1 @@
{"tag1":[{"class":"Full\\Qualified\\Class2","scope":"container","public":false,"synthetic":true,"file":"\/path\/to\/file"}],"tag2":[{"class":"Full\\Qualified\\Class2","scope":"container","public":false,"synthetic":true,"file":"\/path\/to\/file"}]}

View File

@ -0,0 +1,22 @@
Container tags
==============
tag1
----
**`definition_2`:**
- Class: `Full\Qualified\Class2`
- Scope: `container`
- Public: no
- Synthetic: yes
- File: `/path/to/file`
tag2
----
**`definition_2`:**
- Class: `Full\Qualified\Class2`
- Scope: `container`
- Public: no
- Synthetic: yes
- File: `/path/to/file`

View File

@ -0,0 +1,6 @@
<info>[container]</info> Tagged services
<info>[tag]</info> tag1
definition_2
<info>[tag]</info> tag2
definition_2

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<container>
<tag name="tag1">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" file="/path/to/file"/>
</tag>
<tag name="tag2">
<definition id="definition_2" class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" file="/path/to/file"/>
</tag>
</container>

View File

@ -0,0 +1 @@
{"class":"Full\\Qualified\\Class1","scope":"container","public":true,"synthetic":false,"file":null,"tags":[]}

View File

@ -0,0 +1,4 @@
- Class: `Full\Qualified\Class1`
- Scope: `container`
- Public: yes
- Synthetic: no

View File

@ -0,0 +1,7 @@
<comment>Service Id</comment> -
<comment>Class</comment> Full\Qualified\Class1
<comment>Tags</comment> -
<comment>Scope</comment> container
<comment>Public</comment> yes
<comment>Synthetic</comment> no
<comment>Required File</comment> -

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class1" scope="container" public="true" synthetic="false" file=""/>

View File

@ -0,0 +1 @@
{"class":"Full\\Qualified\\Class2","scope":"container","public":false,"synthetic":true,"file":"\/path\/to\/file","tags":[{"name":"tag1","parameters":{"attr1":"val1","attr2":"val2"}},{"name":"tag1","parameters":{"attr3":"val3"}},{"name":"tag2","parameters":[]}]}

View File

@ -0,0 +1,11 @@
- Class: `Full\Qualified\Class2`
- Scope: `container`
- Public: no
- Synthetic: yes
- File: `/path/to/file`
- Tag: `tag1`
- Attr1: val1
- Attr2: val2
- Tag: `tag1`
- Attr3: val3
- Tag: `tag2`

View File

@ -0,0 +1,10 @@
<comment>Service Id</comment> -
<comment>Class</comment> Full\Qualified\Class2
<comment>Tags</comment>
- tag1 (<info>attr1</info>: val1, <info>attr2</info>: val2)
- tag1 (<info>attr3</info>: val3)
- tag2 ()
<comment>Scope</comment> container
<comment>Public</comment> no
<comment>Synthetic</comment> yes
<comment>Required File</comment> /path/to/file

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<definition class="Full\Qualified\Class2" scope="container" public="false" synthetic="true" file="/path/to/file">
<tags>
<tag name="tag1">
<parameter name="attr1"/>
<parameter name="attr2"/>
</tag>
<tag name="tag1">
<parameter name="attr3"/>
</tag>
<tag name="tag2"/>
</tags>
</definition>

View File

@ -0,0 +1 @@
{"array":[12,"Hello world!",true],"boolean":true,"integer":12,"string":"Hello world!"}

View File

@ -0,0 +1,7 @@
Container parameters
====================
- `array`: `[12,"Hello world!",true]`
- `boolean`: `true`
- `integer`: `12`
- `string`: `Hello world!`

View File

@ -0,0 +1,9 @@
<info>[container]</info> List of parameters
+-----------+--------------------------+
| Parameter | Value |
+-----------+--------------------------+
| array | [12,"Hello world!",true] |
| boolean | true |
| integer | 12 |
| string | Hello world! |
+-----------+--------------------------+

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<parameters>
<parameter key="array">[12,"Hello world!",true]</parameter>
<parameter key="boolean">true</parameter>
<parameter key="integer">12</parameter>
<parameter key="string">Hello world!</parameter>
</parameters>

View File

@ -0,0 +1 @@
{"path":"\/hello\/{name}","host":"localhost","scheme":"http|https","method":"GET|HEAD","class":"Symfony\\Component\\Routing\\Route","defaults":{"name":"Joseph"},"requirements":{"name":"[a-z]+"},"options":{"compiler_class":"Symfony\\Component\\Routing\\RouteCompiler","opt1":"val1","opt2":"val2"},"pathRegex":"#^\/hello(?:\/(?P<name>[a-z]+))?$#s"}

View File

@ -0,0 +1,9 @@
- Path: /hello/{name}
- Host: localhost
- Scheme: http|https
- Method: GET|HEAD
- Class: Symfony\Component\Routing\Route
- Defaults:
- `name`: Joseph
- Requirements:
- `name`: [a-z]+

View File

@ -0,0 +1,12 @@
<comment>Path</comment> /hello/{name}
<comment>Host</comment> localhost
<comment>Scheme</comment> http|https
<comment>Method</comment> GET|HEAD
<comment>Class</comment> Symfony\Component\Routing\Route
<comment>Defaults</comment> name: Joseph
<comment>Requirements</comment> name: [a-z]+
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler
opt1: val1
opt2: val2
<comment>Path-Regex</comment> #^/hello(?:/(?P<name>[a-z]+))?$#s
<comment>Host-Regex</comment> #^localhost$#s

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<route path="/hello/{name}" class="Symfony\Component\Routing\Route" path_regex="#^/hello(?:/(?P&lt;name&gt;[a-z]+))?$#s">
<host regex="#^localhost$#s">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>GET</method>
<method>HEAD</method>
<defaults>
<default key="name">Joseph</default>
</defaults>
<requirements>
<requirement key="name">[a-z]+</requirement>
</requirements>
<options>
<option key="compiler_class">Symfony\Component\Routing\RouteCompiler</option>
<option key="opt1">val1</option>
<option key="opt2">val2</option>
</options>
</route>

View File

@ -0,0 +1 @@
{"path":"\/name\/add","host":"localhost","scheme":"http|https","method":"PUT|POST","class":"Symfony\\Component\\Routing\\Route","defaults":[],"requirements":"NO CUSTOM","options":{"compiler_class":"Symfony\\Component\\Routing\\RouteCompiler","opt1":"val1","opt2":"val2"},"pathRegex":"#^\/name\/add$#s"}

View File

@ -0,0 +1,7 @@
- Path: /name/add
- Host: localhost
- Scheme: http|https
- Method: PUT|POST
- Class: Symfony\Component\Routing\Route
- Defaults: NONE
- Requirements: NONE

View File

@ -0,0 +1,12 @@
<comment>Path</comment> /name/add
<comment>Host</comment> localhost
<comment>Scheme</comment> http|https
<comment>Method</comment> PUT|POST
<comment>Class</comment> Symfony\Component\Routing\Route
<comment>Defaults</comment>
<comment>Requirements</comment>
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler
opt1: val1
opt2: val2
<comment>Path-Regex</comment> #^/name/add$#s
<comment>Host-Regex</comment> #^localhost$#s

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<route path="/name/add" class="Symfony\Component\Routing\Route" path_regex="#^/name/add$#s">
<host regex="#^localhost$#s">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>PUT</method>
<method>POST</method>
<options>
<option key="compiler_class">Symfony\Component\Routing\RouteCompiler</option>
<option key="opt1">val1</option>
<option key="opt2">val2</option>
</options>
</route>

View File

@ -0,0 +1 @@
{"route_1":{"path":"\/hello\/{name}","host":"localhost","scheme":"http|https","method":"GET|HEAD","class":"Symfony\\Component\\Routing\\Route","defaults":{"name":"Joseph"},"requirements":{"name":"[a-z]+"},"options":{"compiler_class":"Symfony\\Component\\Routing\\RouteCompiler","opt1":"val1","opt2":"val2"},"pathRegex":"#^\/hello(?:\/(?P<name>[a-z]+))?$#s"},"route_2":{"path":"\/name\/add","host":"localhost","scheme":"http|https","method":"PUT|POST","class":"Symfony\\Component\\Routing\\Route","defaults":[],"requirements":"NO CUSTOM","options":{"compiler_class":"Symfony\\Component\\Routing\\RouteCompiler","opt1":"val1","opt2":"val2"},"pathRegex":"#^\/name\/add$#s"}}

View File

@ -0,0 +1,21 @@
route_1
-------
- Path: /hello/{name}
- Host: localhost
- Scheme: http|https
- Method: GET|HEAD
- Class: Symfony\Component\Routing\Route
- Defaults:
- `name`: Joseph
- Requirements:
- `name`: [a-z]+
route_2
-------
- Path: /name/add
- Host: localhost
- Scheme: http|https
- Method: PUT|POST
- Class: Symfony\Component\Routing\Route
- Defaults: NONE
- Requirements: NONE

View File

@ -0,0 +1,7 @@
<info>[router]</info> Current routes
+---------+----------+------------+-----------+---------------+
| Name | Method | Scheme | Host | Path |
+---------+----------+------------+-----------+---------------+
| route_1 | GET|HEAD | http|https | localhost | /hello/{name} |
| route_2 | PUT|POST | http|https | localhost | /name/add |
+---------+----------+------------+-----------+---------------+

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<routes>
<route name="route_1" path="/hello/{name}" class="Symfony\Component\Routing\Route" path_regex="#^/hello(?:/(?P&lt;name&gt;[a-z]+))?$#s">
<host regex="#^localhost$#s">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>GET</method>
<method>HEAD</method>
<defaults>
<default key="name">Joseph</default>
</defaults>
<requirements>
<requirement key="name">[a-z]+</requirement>
</requirements>
<options>
<option key="compiler_class">Symfony\Component\Routing\RouteCompiler</option>
<option key="opt1">val1</option>
<option key="opt2">val2</option>
</options>
</route>
<route name="route_2" path="/name/add" class="Symfony\Component\Routing\Route" path_regex="#^/name/add$#s">
<host regex="#^localhost$#s">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>PUT</method>
<method>POST</method>
<options>
<option key="compiler_class">Symfony\Component\Routing\RouteCompiler</option>
<option key="opt1">val1</option>
<option key="opt2">val2</option>
</options>
</route>
</routes>