1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Console\Descriptor;
13
14use Symfony\Component\Console\Application;
15use Symfony\Component\Console\Command\Command;
16use Symfony\Component\Console\Input\InputArgument;
17use Symfony\Component\Console\Input\InputDefinition;
18use Symfony\Component\Console\Input\InputOption;
19
20/**
21 * JSON descriptor.
22 *
23 * @author Jean-François Simon <contact@jfsimon.fr>
24 *
25 * @internal
26 */
27class JsonDescriptor extends Descriptor
28{
29    /**
30     * {@inheritdoc}
31     */
32    protected function describeInputArgument(InputArgument $argument, array $options = [])
33    {
34        $this->writeData($this->getInputArgumentData($argument), $options);
35    }
36
37    /**
38     * {@inheritdoc}
39     */
40    protected function describeInputOption(InputOption $option, array $options = [])
41    {
42        $this->writeData($this->getInputOptionData($option), $options);
43    }
44
45    /**
46     * {@inheritdoc}
47     */
48    protected function describeInputDefinition(InputDefinition $definition, array $options = [])
49    {
50        $this->writeData($this->getInputDefinitionData($definition), $options);
51    }
52
53    /**
54     * {@inheritdoc}
55     */
56    protected function describeCommand(Command $command, array $options = [])
57    {
58        $this->writeData($this->getCommandData($command), $options);
59    }
60
61    /**
62     * {@inheritdoc}
63     */
64    protected function describeApplication(Application $application, array $options = [])
65    {
66        $describedNamespace = $options['namespace'] ?? null;
67        $description = new ApplicationDescription($application, $describedNamespace, true);
68        $commands = [];
69
70        foreach ($description->getCommands() as $command) {
71            $commands[] = $this->getCommandData($command);
72        }
73
74        $data = [];
75        if ('UNKNOWN' !== $application->getName()) {
76            $data['application']['name'] = $application->getName();
77            if ('UNKNOWN' !== $application->getVersion()) {
78                $data['application']['version'] = $application->getVersion();
79            }
80        }
81
82        $data['commands'] = $commands;
83
84        if ($describedNamespace) {
85            $data['namespace'] = $describedNamespace;
86        } else {
87            $data['namespaces'] = array_values($description->getNamespaces());
88        }
89
90        $this->writeData($data, $options);
91    }
92
93    /**
94     * Writes data as json.
95     */
96    private function writeData(array $data, array $options)
97    {
98        $flags = $options['json_encoding'] ?? 0;
99
100        $this->write(json_encode($data, $flags));
101    }
102
103    private function getInputArgumentData(InputArgument $argument): array
104    {
105        return [
106            'name' => $argument->getName(),
107            'is_required' => $argument->isRequired(),
108            'is_array' => $argument->isArray(),
109            'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $argument->getDescription()),
110            'default' => \INF === $argument->getDefault() ? 'INF' : $argument->getDefault(),
111        ];
112    }
113
114    private function getInputOptionData(InputOption $option): array
115    {
116        return [
117            'name' => '--'.$option->getName(),
118            'shortcut' => $option->getShortcut() ? '-'.str_replace('|', '|-', $option->getShortcut()) : '',
119            'accept_value' => $option->acceptValue(),
120            'is_value_required' => $option->isValueRequired(),
121            'is_multiple' => $option->isArray(),
122            'description' => preg_replace('/\s*[\r\n]\s*/', ' ', $option->getDescription()),
123            'default' => \INF === $option->getDefault() ? 'INF' : $option->getDefault(),
124        ];
125    }
126
127    private function getInputDefinitionData(InputDefinition $definition): array
128    {
129        $inputArguments = [];
130        foreach ($definition->getArguments() as $name => $argument) {
131            $inputArguments[$name] = $this->getInputArgumentData($argument);
132        }
133
134        $inputOptions = [];
135        foreach ($definition->getOptions() as $name => $option) {
136            $inputOptions[$name] = $this->getInputOptionData($option);
137        }
138
139        return ['arguments' => $inputArguments, 'options' => $inputOptions];
140    }
141
142    private function getCommandData(Command $command): array
143    {
144        $command->mergeApplicationDefinition(false);
145
146        return [
147            'name' => $command->getName(),
148            'usage' => array_merge([$command->getSynopsis()], $command->getUsages(), $command->getAliases()),
149            'description' => $command->getDescription(),
150            'help' => $command->getProcessedHelp(),
151            'definition' => $this->getInputDefinitionData($command->getDefinition()),
152            'hidden' => $command->isHidden(),
153        ];
154    }
155}
156