Generator   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Test Coverage

Coverage 30.51%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 97
c 1
b 0
f 0
dl 0
loc 151
ccs 18
cts 59
cp 0.3051
rs 10
wmc 14

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getHelp() 0 3 1
A configure() 0 18 1
C execute() 0 76 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace loophp\PhptreeAstGenerator\Command;
6
7
use InvalidArgumentException;
8
use loophp\phptree\Exporter\Gv;
9
use loophp\phptree\Exporter\Image;
10
use loophp\phptree\Importer\MicrosoftTolerantPhpParser;
11
use loophp\phptree\Importer\NikicPhpParser;
12
use loophp\PhptreeAstGenerator\Exporter\FancyExporter;
13
use loophp\PhptreeAstGenerator\Exporter\MicrosoftFancyExporter;
14
use Microsoft\PhpParser\Parser;
15
use PhpParser\ParserFactory;
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Input\InputArgument;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Input\InputOption;
20
use Symfony\Component\Console\Output\OutputInterface;
21
22
use function is_string;
23
24
class Generator extends Command
25
{
26
    /**
27
     * @return string
28
     */
29 1
    public function getHelp(): string
30
    {
31
        return <<<'EOF'
32 1
The <info>%command.name%</info> command generates an abstract syntax tree.
33
34
There are only 2 output formats that are supported:
35
* <info>dot</info> (The GraphViz format)
36
* <info>image</info> (any image format supported by GraphViz)
37
38
By default, the <info>dot</info> format is used. Use the <comment>-c</comment> option to change it.
39
40
The generator supports 2 parsers:
41
* <href=https://github.com/nikic/php-parser>nikic/php-parser</>
42
* <href=https://github.com/microsoft/tolerant-php-parser>microsoft/tolerant-php-parser</>
43
44
By default, the <info>nikic</info> parser is used. Use the <comment>-p</comment> option to change it.
45
46
The following command will generate a dot script, ready to be used by GraphViz:
47
48
    <info>$ php %command.full_name% /path/to/any/php/file</info>
49
    
50
Use the <comment>-c</comment> option to enable a <comment>fancy</comment> and user-friendly export,
51
easier to read and less verbose. 
52
53
If you want to set a <comment>destination</comment>, use the <comment>-d</comment> option.
54
55
The following command will generate a dot script, and save it in a file:
56
57
    <info>$ php %command.full_name% -d graph.dot /path/to/any/php/file</info>
58
59
You can change the <comment>type</comment> of export format by using the <comment>-t</comment> option.
60
61
The following command will generate an PNG image, and save it in a file:
62
63
    <info>$ php %command.full_name% -t image -f png -d graph.png /path/to/any/php/file</info>
64
65
Use the <comment>-f</comment> option to change the image <comment>format</comment>, default is SVG.
66
67
For more help:
68
69
    <info>$ php %command.full_name% -h</info>
70
EOF;
71
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 1
    protected function configure(): void
77
    {
78
        $this
79 1
            ->setName('generate-nikic')
80 1
            ->setDescription('Generate an Abstract Syntax Tree using nikic/php-parser parser.')
81 1
            ->setHelp($this->getHelp())
82 1
            ->addArgument('filepath', InputArgument::REQUIRED, 'Filepath to the PHP code.')
83 1
            ->addOption('parser', 'p', InputOption::VALUE_OPTIONAL, 'The parser (nikic, microsoft)', 'nikic')
84 1
            ->addOption('type', 't', InputOption::VALUE_OPTIONAL, 'The exporter type (dot, image)', 'dot')
85 1
            ->addOption('format', 'f', InputOption::VALUE_OPTIONAL, 'The export format (png, jpg, svg)', 'svg')
86 1
            ->addOption(
87 1
                'destination',
88 1
                'd',
89 1
                InputOption::VALUE_OPTIONAL,
90 1
                'The export destination (a filepath or inline)',
91 1
                'inline'
92
            )
93 1
            ->addOption('fancy', 'c', InputOption::VALUE_OPTIONAL, 'Use the fancy exporter ?', false);
94 1
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected function execute(InputInterface $input, OutputInterface $output)
100
    {
101
        $filepath = $input->getArgument('filepath');
102
103
        if (false === is_string($filepath)) {
104
            throw new InvalidArgumentException('Filepath must be a string.');
105
        }
106
107
        if (false === $filepath = realpath($filepath)) {
0 ignored issues
show
Bug introduced by
It seems like $filepath can also be of type string[]; however, parameter $path of realpath() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

107
        if (false === $filepath = realpath(/** @scrutinizer ignore-type */ $filepath)) {
Loading history...
108
            throw new InvalidArgumentException('Unable to access the given filepath.');
109
        }
110
111
        if (false === $fileContent = file_get_contents($filepath)) {
112
            throw new InvalidArgumentException('Unable to get the content of given filepath.');
113
        }
114
115
        $type = $input->getOption('type');
116
        $format = $input->getOption('format');
117
118
        if (false === is_string($format)) {
119
            $format = 'svg';
120
        }
121
122
        $parser = $input->getOption('parser');
123
124
        switch ($parser) {
125
            default:
126
                $importer = new NikicPhpParser();
127
                $data = (new ParserFactory())->create(ParserFactory::PREFER_PHP7)
128
                    ->parse($fileContent);
129
                $tree = $importer->import($data);
130
131
                break;
132
            case 'microsoft':
133
                $importer = new MicrosoftTolerantPhpParser();
134
                $data = (new Parser())->parseSourceFile($fileContent);
135
                $tree = $importer->import($data);
136
137
                break;
138
        }
139
140
        $exporter = new Gv();
141
142
        switch ($type) {
143
            case 'image':
144
                $exporter = (new Image())->setFormat($format);
145
146
                break;
147
        }
148
149
        if (false !== $input->getOption('fancy')) {
150
            // Todo: merge this decorator into a single class.
151
            if ('nikic' === $parser) {
152
                $exporter = new FancyExporter($exporter);
153
            }
154
155
            if ('microsoft' === $parser) {
156
                $exporter = new MicrosoftFancyExporter($exporter);
157
            }
158
        }
159
160
        $export = $exporter->export($tree);
161
162
        $destination = $input->getOption('destination');
163
164
        if (false === is_string($destination)) {
165
            return 1;
166
        }
167
168
        if ('inline' === $destination) {
0 ignored issues
show
introduced by
The condition 'inline' === $destination is always false.
Loading history...
169
            $output->writeln($export);
170
171
            return 0;
172
        }
173
174
        return (int) file_put_contents($destination, $export);
0 ignored issues
show
Bug introduced by
It seems like $destination can also be of type string[]; however, parameter $filename of file_put_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
        return (int) file_put_contents(/** @scrutinizer ignore-type */ $destination, $export);
Loading history...
175
    }
176
}
177