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( |
|
|
|
|
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 |
|
|
|
|
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( |
|
|
|
|
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( |
|
|
|
|
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 |
|
|
|
|
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 |
|
|
|
|
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( |
|
|
|
|
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 |
|
|
|
|
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
|
|
|
|
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.