Issues (163)

src/Console/Command/AddPrefixCommand.php (8 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\Console\Command;
16
17
use Fidry\Console\Application\Application;
18
use Fidry\Console\Command\Command;
19
use Fidry\Console\Command\CommandAware;
20
use Fidry\Console\Command\CommandAwareness;
21
use Fidry\Console\Command\Configuration as CommandConfiguration;
22
use Fidry\Console\ExitCode;
23
use Fidry\Console\Input\IO;
24
use Humbug\PhpScoper\Configuration\Configuration;
25
use Humbug\PhpScoper\Configuration\ConfigurationFactory;
26
use Humbug\PhpScoper\Console\ConfigLoader;
27
use Humbug\PhpScoper\Console\ConsoleScoper;
28
use Humbug\PhpScoper\Scoper\ScoperFactory;
29
use InvalidArgumentException;
30
use Symfony\Component\Console\Exception\RuntimeException;
31
use Symfony\Component\Console\Input\InputArgument;
32
use Symfony\Component\Console\Input\InputOption;
33
use Symfony\Component\Filesystem\Filesystem;
34
use Symfony\Component\Filesystem\Path;
35
use function array_map;
36
use function is_dir;
37
use function is_writable;
38
use function Safe\getcwd;
39
use function Safe\sprintf;
40
use const DIRECTORY_SEPARATOR;
41
42
/**
43
 * @private
44
 */
45
final class AddPrefixCommand implements Command, CommandAware
46
{
47
    use CommandAwareness;
48
49
    private const PATH_ARG = 'paths';
50
    private const PREFIX_OPT = 'prefix';
51
    private const OUTPUT_DIR_OPT = 'output-dir';
52
    private const FORCE_OPT = 'force';
53
    private const STOP_ON_FAILURE_OPT = 'stop-on-failure';
54
    private const CONFIG_FILE_OPT = 'config';
55 16
    private const NO_CONFIG_OPT = 'no-config';
56
57 16
    private Filesystem $fileSystem;
58
    private ScoperFactory $scoperFactory;
59 16
    private bool $init = false;
60 16
    private Application $application;
61
    private ConfigurationFactory $configFactory;
62
63
    public function __construct(
64
        Filesystem $fileSystem,
65
        ScoperFactory $scoperFactory,
66 16
        Application $application,
67
        ConfigurationFactory $configFactory
68 16
    ) {
69
        $this->fileSystem = $fileSystem;
70
        $this->scoperFactory = $scoperFactory;
71 16
        $this->application = $application;
72 16
        $this->configFactory = $configFactory;
73 16
    }
74 16
75 16
    public function getConfiguration(): CommandConfiguration
76 16
    {
77
        return new CommandConfiguration(
78 16
            'add-prefix',
79 16
            'Goes through all the PHP files found in the given paths to apply the given prefix to namespaces & FQNs.',
80 16
            '',
81 16
            [
82 16
                new InputArgument(
83
                    self::PATH_ARG,
84 16
                    InputArgument::IS_ARRAY,
85 16
                    'The path(s) to process.',
86 16
                ),
87 16
            ],
88 16
            [
89 16
                ChangeableDirectory::createOption(),
90
                new InputOption(
91 16
                    self::PREFIX_OPT,
92 16
                    'p',
93 16
                    InputOption::VALUE_REQUIRED,
94 16
                    'The namespace prefix to add.',
95 16
                    '',
96
                ),
97 16
                new InputOption(
98 16
                    self::OUTPUT_DIR_OPT,
99 16
                    'o',
100 16
                    InputOption::VALUE_REQUIRED,
101 16
                    'The output directory in which the prefixed code will be dumped.',
102
                    'build',
103 16
                ),
104 16
                new InputOption(
105 16
                    self::FORCE_OPT,
106 16
                    'f',
107 16
                    InputOption::VALUE_NONE,
108 16
                    'Deletes any existing content in the output directory without any warning.',
109 16
                ),
110
                new InputOption(
111
                    self::STOP_ON_FAILURE_OPT,
112 16
                    's',
113 16
                    InputOption::VALUE_NONE,
114 16
                    'Stops on failure.',
115 16
                ),
116 16
                new InputOption(
117
                    self::CONFIG_FILE_OPT,
118
                    'c',
119
                    InputOption::VALUE_REQUIRED,
120
                    sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

120
                    /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
121
                        'Configuration file. Will use "%s" if found by default.',
122
                        ConfigurationFactory::DEFAULT_FILE_NAME,
123
                    ),
124 14
                ),
125
                new InputOption(
126 14
                    self::NO_CONFIG_OPT,
127 14
                    null,
128
                    InputOption::VALUE_NONE,
129 14
                    'Do not look for a configuration file.',
130
                ),
131 14
            ],
132 14
        );
133 14
    }
134
135 14
    public function execute(IO $io): int
136 12
    {
137
        $io->newLine();
138 12
139
        ChangeableDirectory::changeWorkingDirectory($io);
140
141
        // Only get current working directory _after_ we changed to the desired
142 12
        // working directory
143 12
        $cwd = getcwd();
144 12
145
        $paths = $this->getPathArguments($io, $cwd);
146
        $outputDir = $this->getOutputDir($io, $cwd);
147 12
148 12
        $this->checkOutputDir($io, $outputDir);
149 12
150
        $config = $this->retrieveConfig($io, $paths, $cwd);
151
152
        $this->getScoper()->scope(
153 12
            $io,
154 12
            $config,
155 12
            $paths,
156 12
            $outputDir,
157 12
            $io->getOption(self::STOP_ON_FAILURE_OPT)->asBoolean(),
158 12
        );
159 12
160 12
        return ExitCode::SUCCESS;
161
    }
162
163
    /**
164
     * @return non-empty-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
165
     */
166
    private function getOutputDir(IO $io, string $cwd): string
167
    {
168
        return $this->canonicalizePath(
169
            $io->getOption(self::OUTPUT_DIR_OPT)->asString(),
170 12
            $cwd,
171
        );
172 12
    }
173
174
    private function checkOutputDir(IO $io, string $outputDir): void
175
    {
176
        if (!$this->fileSystem->exists($outputDir)) {
177
            return;
178 12
        }
179
180
        self::checkPathIsWriteable($outputDir);
181
182
        $canDeleteFile = self::canDeleteOutputDir($io, $outputDir);
183
184
        if (!$canDeleteFile) {
185
            throw new RuntimeException('Cannot delete the output directory. Interrupting the process.');
186
        }
187
188 12
        $this->fileSystem->remove($outputDir);
189
    }
190 12
191
    private static function checkPathIsWriteable(string $path): void
192 12
    {
193 12
        if (!is_writable($path)) {
194
            throw new RuntimeException(
195 12
                sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

195
                /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
196 12
                    'Expected "<comment>%s</comment>" to be writeable.',
197
                    $path,
198 12
                ),
199 12
            );
200
        }
201
    }
202
203 12
    private static function canDeleteOutputDir(IO $io, string $outputDir): bool
204 12
    {
205 12
        if ($io->getOption(self::FORCE_OPT)->asBoolean()) {
206 12
            return true;
207 12
        }
208 12
209 12
        $question = sprintf(
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

209
        $question = /** @scrutinizer ignore-deprecated */ sprintf(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
210 12
            is_dir($outputDir)
211 12
                ? 'The output directory "<comment>%s</comment>" already exists. Continuing will erase its content, do you wish to proceed?'
212
                : 'Expected "<comment>%s</comment>" to be a directory but found a file instead. It will be  removed, do you wish to proceed?',
213
            $outputDir,
214
        );
215 12
216
        return $io->confirm($question, false);
217 12
    }
218 12
219
    /**
220
     * @param list<non-empty-string> $paths
0 ignored issues
show
The type Humbug\PhpScoper\Console\Command\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
221 12
     */
222
    private function retrieveConfig(IO $io, array $paths, string $cwd): Configuration
223
    {
224 12
        $configLoader = new ConfigLoader(
225
            $this->getCommandRegistry(),
226 12
            $this->fileSystem,
227
            $this->configFactory,
228
        );
229
230
        return $configLoader->loadConfig(
231
            $io,
232
            $io->getOption(self::PREFIX_OPT)->asString(),
233
            $io->getOption(self::NO_CONFIG_OPT)->asBoolean(),
234
            $this->getConfigFilePath($io, $cwd),
235
            ConfigurationFactory::DEFAULT_FILE_NAME,
236 12
            $this->init,
237
            $paths,
238
            $cwd,
239
        );
240
    }
241
242
    /**
243
     * @return non-empty-string|null
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string|null at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string|null.
Loading history...
244
     */
245
    private function getConfigFilePath(IO $io, string $cwd): ?string
246
    {
247 12
        $configFilePath = (string) $io->getOption(self::CONFIG_FILE_OPT)->asNullableString();
248 2
249 2
        return '' === $configFilePath ? null : $this->canonicalizePath($configFilePath, $cwd);
250 2
    }
251 2
252 2
    /**
253
     * @return list<non-empty-string> List of absolute canonical paths
254 2
     */
255 2
    private function getPathArguments(IO $io, string $cwd): array
256
    {
257
        return array_map(
0 ignored issues
show
Bug Best Practice introduced by
The expression return array_map(functio...asNonEmptyStringList()) returns the type array which is incompatible with the documented return type Humbug\PhpScoper\Console\Command\list.
Loading history...
258 2
            fn (string $path) => $this->canonicalizePath($path, $cwd),
259
            $io->getArgument(self::PATH_ARG)->asNonEmptyStringList(),
260
        );
261
    }
262 2
263
    /**
264 2
     * @return non-empty-string Absolute canonical path
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
265
     */
266
    private function canonicalizePath(string $path, string $cwd): string
267 12
    {
268
        $canonicalPath = Path::canonicalize(
269 12
            $this->fileSystem->isAbsolutePath($path)
270 11
                ? $path
271
                : $cwd.DIRECTORY_SEPARATOR.$path,
272
        );
273
274 14
        if ('' === $canonicalPath) {
275
            throw new InvalidArgumentException('Cannot canonicalize empty path and empty working directory');
276 14
        }
277
278 14
        return $canonicalPath;
279 13
    }
280
281
    private function getScoper(): ConsoleScoper
282 14
    {
283
        return new ConsoleScoper(
284
            $this->fileSystem,
285 14
            $this->application,
286
            $this->scoperFactory,
287 14
        );
288 14
    }
289
}
290