Passed
Push — master ( 4165df...b83769 )
by Théo
01:49
created

Info   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 21
dl 0
loc 223
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
B render() 0 22 5
B configure() 0 43 1
B execute() 0 53 6
C renderContents() 0 46 9
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Command;
16
17
use DirectoryIterator;
18
use Phar;
19
use PharFileInfo;
20
use Symfony\Component\Console\Command\Command;
21
use Symfony\Component\Console\Input\InputArgument;
22
use Symfony\Component\Console\Input\InputInterface;
23
use Symfony\Component\Console\Input\InputOption;
24
use Symfony\Component\Console\Output\OutputInterface;
25
use Traversable;
26
27
final class Info extends Command
28
{
29
    private const PHAR_ARG = 'phar';
30
    private const LIST_OPT = 'list';
31
    private const METADATA_OPT = 'metadata';
32
    private const MODE_OPT = 'mode';
33
34
    /**
35
     * The list of recognized compression algorithms.
36
     *
37
     * @var array
38
     */
39
    private const ALGORITHMS = [
40
        Phar::BZ2 => 'BZ2',
41
        Phar::GZ => 'GZ',
42
        Phar::TAR => 'TAR',
43
        Phar::ZIP => 'ZIP',
44
    ];
45
46
    /**
47
     * The list of recognized file compression algorithms.
48
     *
49
     * @var array
50
     */
51
    private const FILE_ALGORITHMS = [
52
        Phar::BZ2 => 'BZ2',
53
        Phar::GZ => 'GZ',
54
    ];
55
56
    /**
57
     * @override
58
     */
59
    public function execute(InputInterface $input, OutputInterface $output): int
60
    {
61
        if (null === ($file = $input->getArgument(self::PHAR_ARG))) {
62
            $this->render(
63
                $output,
64
                [
65
                    'API Version' => Phar::apiVersion(),
66
                    'Supported Compression' => Phar::getSupportedCompression(),
67
                    'Supported Signatures' => Phar::getSupportedSignatures(),
68
                ]
69
            );
70
71
            return 0;
72
        }
73
74
        $phar = new Phar($file);
75
        $signature = $phar->getSignature();
76
77
        $this->render(
78
            $output,
79
            [
80
                'API Version' => $phar->getVersion(),
81
                'Archive Compression' => $phar->isCompressed()
82
                    ? self::ALGORITHMS[$phar->isCompressed()]
83
                    : 'None',
84
                'Signature' => $signature['hash_type'],
85
                'Signature Hash' => $signature['hash'],
86
            ]
87
        );
88
89
        if ($input->getOption(self::LIST_OPT)) {
90
            $output->writeln('');
91
            $output->writeln('<comment>Contents:</comment>');
92
93
            $root = 'phar://'.str_replace('\\', '/', realpath($file)).'/';
94
95
            $this->renderContents(
96
                $output,
97
                $phar,
98
                ('indent' === $input->getOption(self::MODE_OPT)) ? 0 : false,
99
                $root,
100
                $phar,
101
                $root
102
            );
103
        }
104
105
        if ($input->getOption(self::METADATA_OPT)) {
106
            $output->writeln('');
107
            $output->writeln('<comment>Metadata:</comment>');
108
            $output->writeln(var_export($phar->getMetadata(), true));
109
        }
110
111
        return 0;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    protected function configure(): void
118
    {
119
        $this->setName('info');
120
        $this->setDescription(
121
            'Displays information about the PHAR extension or file'
122
        );
123
        $this->setHelp(
124
            <<<'HELP'
125
The <info>%command.name%</info> command will display information about the Phar extension,
126
or the Phar file if specified.
127
128
If the <info>phar</info> argument <comment>(the PHAR file path)</comment> is provided, information
129
about the PHAR file itself will be displayed.
130
131
If the <info>--list|-l</info> option is used, the contents of the PHAR file will
132
be listed. By default, the list is shown as an indented tree. You may
133
instead choose to view a flat listing, by setting the <info>--mode|-m</info> option
134
to <comment>flat</comment>.
135
HELP
136
        );
137
        $this->addArgument(
138
            self::PHAR_ARG,
139
            InputArgument::OPTIONAL,
140
            'The Phar file.'
141
        );
142
        $this->addOption(
143
            self::LIST_OPT,
144
            'l',
145
            InputOption::VALUE_NONE,
146
            'List the contents of the Phar?'
147
        );
148
        $this->addOption(
149
            self::METADATA_OPT,
150
            null,
151
            InputOption::VALUE_NONE,
152
            'Display metadata?'
153
        );
154
        $this->addOption(
155
            self::MODE_OPT,
156
            'm',
157
            InputOption::VALUE_OPTIONAL,
158
            'The listing mode. (default: indent, options: indent, flat)',
159
            'indent'
160
        );
161
    }
162
163
    /**
164
     * Renders the list of attributes.
165
     *
166
     * @param OutputInterface $output     the output
167
     * @param array           $attributes the list of attributes
168
     */
169
    private function render(OutputInterface $output, array $attributes): void
170
    {
171
        $out = false;
172
173
        foreach ($attributes as $name => $value) {
174
            if ($out) {
175
                $output->writeln('');
176
            }
177
178
            $output->write("<comment>$name:</comment>");
179
180
            if (is_array($value)) {
181
                $output->writeln('');
182
183
                foreach ($value as $v) {
184
                    $output->writeln("  - $v");
185
                }
186
            } else {
187
                $output->writeln(" $value");
188
            }
189
190
            $out = true;
191
        }
192
    }
193
194
    /**
195
     * Renders the contents of an iterator.
196
     *
197
     * @param OutputInterface $output the output handler
198
     * @param Traversable     $list   the traversable list
199
     * @param bool|int        $indent the indentation level
200
     * @param string          $base   the base path
201
     * @param Phar            $phar   the PHP archive
202
     * @param string          $root   the root path to remove
203
     */
204
    private function renderContents(
205
        OutputInterface $output,
206
        Traversable $list,
207
        $indent,
208
        string $base,
209
        Phar $phar,
210
        string $root
211
    ): void {
212
        foreach ($list as $item) {
213
            /** @var PharFileInfo $item */
214
            $item = $phar[str_replace($root, '', $item->getPathname())];
215
216
            if (false !== $indent) {
217
                $output->write(str_repeat(' ', $indent));
0 ignored issues
show
Bug introduced by
It seems like $indent can also be of type true; however, parameter $multiplier of str_repeat() does only seem to accept integer, 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

217
                $output->write(str_repeat(' ', /** @scrutinizer ignore-type */ $indent));
Loading history...
218
219
                $path = $item->getFilename();
220
221
                if ($item->isDir()) {
222
                    $path .= '/';
223
                }
224
            } else {
225
                $path = str_replace($base, '', $item->getPathname());
226
            }
227
228
            if ($item->isDir()) {
229
                $output->writeln("<info>$path</info>");
230
            } else {
231
                $compression = '';
232
233
                foreach (self::FILE_ALGORITHMS as $code => $name) {
234
                    if ($item->isCompressed($code)) {
235
                        $compression = " <fg=cyan>[$name]</fg=cyan>";
236
                    }
237
                }
238
239
                $output->writeln($path.$compression);
240
            }
241
242
            if ($item->isDir()) {
243
                $this->renderContents(
244
                    $output,
245
                    new DirectoryIterator($item->getPathname()),
246
                    (false === $indent) ? $indent : $indent + 2,
247
                    $base,
248
                    $phar,
249
                    $root
250
                );
251
            }
252
        }
253
    }
254
}
255