@@ -19,72 +19,72 @@ |
||
19 | 19 | |
20 | 20 | class Copier |
21 | 21 | { |
22 | - /** |
|
23 | - * The only path variable with a leading slash. |
|
24 | - * All directories in project end with a slash. |
|
25 | - * |
|
26 | - * @var string |
|
27 | - */ |
|
28 | - protected string $workingDir; |
|
29 | - |
|
30 | - protected string $targetDir; |
|
31 | - |
|
32 | - protected array $filepaths; |
|
33 | - |
|
34 | - /** @var Filesystem */ |
|
35 | - protected Filesystem $filesystem; |
|
36 | - |
|
37 | - /** |
|
38 | - * Copier constructor. |
|
39 | - * @param array<string, ComposerPackage> $filepaths |
|
40 | - * @param string $workingDir |
|
41 | - * @param string $relativeTargetDir |
|
42 | - */ |
|
43 | - public function __construct(array $filepaths, string $workingDir, string $relativeTargetDir) |
|
44 | - { |
|
45 | - $this->filepaths = array_keys($filepaths); |
|
46 | - |
|
47 | - $this->workingDir = $workingDir; |
|
48 | - |
|
49 | - $this->targetDir = $relativeTargetDir; |
|
50 | - |
|
51 | - $this->filesystem = new Filesystem(new Local($this->workingDir)); |
|
52 | - } |
|
53 | - |
|
54 | - /** |
|
55 | - * If the target dir does not exist, create it. |
|
56 | - * If it already exists, delete any files we're about to copy. |
|
57 | - * |
|
58 | - * @return void |
|
59 | - */ |
|
60 | - public function prepareTarget(): void |
|
61 | - { |
|
62 | - if (! $this->filesystem->has($this->targetDir)) { |
|
63 | - $this->filesystem->createDir($this->targetDir); |
|
64 | - } else { |
|
65 | - foreach ($this->filepaths as $vendorRelativeFilepath) { |
|
66 | - $projectRelativeFilepath = $this->targetDir . $vendorRelativeFilepath; |
|
67 | - |
|
68 | - if ($this->filesystem->has($projectRelativeFilepath)) { |
|
69 | - $this->filesystem->delete($projectRelativeFilepath); |
|
70 | - } |
|
71 | - } |
|
72 | - } |
|
73 | - } |
|
74 | - |
|
75 | - |
|
76 | - /** |
|
77 | - * |
|
78 | - */ |
|
79 | - public function copy(): void |
|
80 | - { |
|
81 | - |
|
82 | - foreach ($this->filepaths as $relativeFilepath) { |
|
83 | - $sourceFileRelativePath = 'vendor' . DIRECTORY_SEPARATOR . $relativeFilepath; |
|
84 | - |
|
85 | - $targetFileRelativePath = $this->targetDir . $relativeFilepath; |
|
86 | - |
|
87 | - $this->filesystem->copy($sourceFileRelativePath, $targetFileRelativePath); |
|
88 | - } |
|
89 | - } |
|
22 | + /** |
|
23 | + * The only path variable with a leading slash. |
|
24 | + * All directories in project end with a slash. |
|
25 | + * |
|
26 | + * @var string |
|
27 | + */ |
|
28 | + protected string $workingDir; |
|
29 | + |
|
30 | + protected string $targetDir; |
|
31 | + |
|
32 | + protected array $filepaths; |
|
33 | + |
|
34 | + /** @var Filesystem */ |
|
35 | + protected Filesystem $filesystem; |
|
36 | + |
|
37 | + /** |
|
38 | + * Copier constructor. |
|
39 | + * @param array<string, ComposerPackage> $filepaths |
|
40 | + * @param string $workingDir |
|
41 | + * @param string $relativeTargetDir |
|
42 | + */ |
|
43 | + public function __construct(array $filepaths, string $workingDir, string $relativeTargetDir) |
|
44 | + { |
|
45 | + $this->filepaths = array_keys($filepaths); |
|
46 | + |
|
47 | + $this->workingDir = $workingDir; |
|
48 | + |
|
49 | + $this->targetDir = $relativeTargetDir; |
|
50 | + |
|
51 | + $this->filesystem = new Filesystem(new Local($this->workingDir)); |
|
52 | + } |
|
53 | + |
|
54 | + /** |
|
55 | + * If the target dir does not exist, create it. |
|
56 | + * If it already exists, delete any files we're about to copy. |
|
57 | + * |
|
58 | + * @return void |
|
59 | + */ |
|
60 | + public function prepareTarget(): void |
|
61 | + { |
|
62 | + if (! $this->filesystem->has($this->targetDir)) { |
|
63 | + $this->filesystem->createDir($this->targetDir); |
|
64 | + } else { |
|
65 | + foreach ($this->filepaths as $vendorRelativeFilepath) { |
|
66 | + $projectRelativeFilepath = $this->targetDir . $vendorRelativeFilepath; |
|
67 | + |
|
68 | + if ($this->filesystem->has($projectRelativeFilepath)) { |
|
69 | + $this->filesystem->delete($projectRelativeFilepath); |
|
70 | + } |
|
71 | + } |
|
72 | + } |
|
73 | + } |
|
74 | + |
|
75 | + |
|
76 | + /** |
|
77 | + * |
|
78 | + */ |
|
79 | + public function copy(): void |
|
80 | + { |
|
81 | + |
|
82 | + foreach ($this->filepaths as $relativeFilepath) { |
|
83 | + $sourceFileRelativePath = 'vendor' . DIRECTORY_SEPARATOR . $relativeFilepath; |
|
84 | + |
|
85 | + $targetFileRelativePath = $this->targetDir . $relativeFilepath; |
|
86 | + |
|
87 | + $this->filesystem->copy($sourceFileRelativePath, $targetFileRelativePath); |
|
88 | + } |
|
89 | + } |
|
90 | 90 | } |
@@ -12,396 +12,396 @@ |
||
12 | 12 | |
13 | 13 | class StraussConfig |
14 | 14 | { |
15 | - /** |
|
16 | - * The output directory. |
|
17 | - * |
|
18 | - * Probably `strauss/` or `src/strauss/`. |
|
19 | - * |
|
20 | - * @var string |
|
21 | - */ |
|
22 | - protected $targetDirectory = 'strauss'; |
|
23 | - |
|
24 | - /** |
|
25 | - * `namespacePrefix` is the prefix to be given to any namespaces. |
|
26 | - * Presumably this will take the form `My_Project_Namespace\dep_directory`. |
|
27 | - * |
|
28 | - * @link https://www.php-fig.org/psr/psr-4/ |
|
29 | - * |
|
30 | - * @var string |
|
31 | - */ |
|
32 | - protected $namespacePrefix; |
|
33 | - |
|
34 | - /** |
|
35 | - * @var string |
|
36 | - */ |
|
37 | - protected $classmapPrefix; |
|
38 | - |
|
39 | - /** |
|
40 | - * Packages to copy and (maybe) prefix. |
|
41 | - * |
|
42 | - * If this is empty, the "requires" list in the project composer.json is used. |
|
43 | - * |
|
44 | - * @var array |
|
45 | - */ |
|
46 | - protected array $packages = []; |
|
47 | - |
|
48 | - // Back-compatibility with Mozart. |
|
49 | - private array $excludePackages; |
|
50 | - |
|
51 | - /** |
|
52 | - * @var array{packages?: string[], namespaces?: string[], filePatterns?: string[]} |
|
53 | - */ |
|
54 | - protected array $excludeFromCopy = array(); |
|
55 | - |
|
56 | - /** |
|
57 | - * @var array{packages?: string[], namespaces?: string[], filePatterns?: string[]} |
|
58 | - */ |
|
59 | - protected array $excludeFromPrefix = array('filePatterns'=>array('/^psr.*$/')); |
|
60 | - |
|
61 | - |
|
62 | - /** |
|
63 | - * An array of autoload keys to replace packages' existing autoload key. |
|
64 | - * |
|
65 | - * e.g. when |
|
66 | - * * A package has no autoloader |
|
67 | - * * A package specified both a PSR-4 and a classmap but only needs one |
|
68 | - * ... |
|
69 | - * |
|
70 | - * @var array |
|
71 | - */ |
|
72 | - protected $overrideAutoload = []; |
|
73 | - |
|
74 | - /** |
|
75 | - * After completing `strauss compose` should the source files be deleted? |
|
76 | - * This does not affect symlinked directories. |
|
77 | - * |
|
78 | - * @var bool |
|
79 | - */ |
|
80 | - protected $deleteVendorFiles = false; |
|
81 | - |
|
82 | - protected bool $classmapOutput; |
|
83 | - |
|
84 | - protected array $namespaceReplacementPatterns = array(); |
|
85 | - |
|
86 | - |
|
87 | - /** |
|
88 | - * Read any existing Mozart config. |
|
89 | - * Overwrite it with any Strauss config. |
|
90 | - * Provide sensible defaults. |
|
91 | - * |
|
92 | - * @throws Exception |
|
93 | - */ |
|
94 | - public function __construct(Composer $composer) |
|
95 | - { |
|
96 | - |
|
97 | - $configExtraSettings = null; |
|
98 | - |
|
99 | - // Backwards compatibility with Mozart. |
|
100 | - if (isset($composer->getPackage()->getExtra()['mozart'])) { |
|
101 | - $configExtraSettings = (object)$composer->getPackage()->getExtra()['mozart']; |
|
102 | - |
|
103 | - // Default setting for Mozart. |
|
104 | - $this->setDeleteVendorFiles(true); |
|
105 | - } |
|
106 | - |
|
107 | - if (isset($composer->getPackage()->getExtra()['strauss'])) { |
|
108 | - $configExtraSettings = (object)$composer->getPackage()->getExtra()['strauss']; |
|
109 | - } |
|
110 | - |
|
111 | - if (!is_null($configExtraSettings)) { |
|
112 | - $mapper = (new JsonMapperFactory())->bestFit(); |
|
113 | - |
|
114 | - $rename = new Rename(); |
|
115 | - $rename->addMapping(StraussConfig::class, 'dep_directory', 'targetDirectory'); |
|
116 | - $rename->addMapping(StraussConfig::class, 'dep_namespace', 'namespacePrefix'); |
|
117 | - |
|
118 | - $rename->addMapping(StraussConfig::class, 'exclude_packages', 'excludePackages'); |
|
119 | - $rename->addMapping(StraussConfig::class, 'delete_vendor_directories', 'deleteVendorFiles'); |
|
120 | - |
|
121 | - $mapper->unshift($rename); |
|
122 | - $mapper->push(new \JsonMapper\Middleware\CaseConversion( |
|
123 | - \JsonMapper\Enums\TextNotation::UNDERSCORE(), |
|
124 | - \JsonMapper\Enums\TextNotation::CAMEL_CASE() |
|
125 | - )); |
|
126 | - |
|
127 | - $mapper->mapObject($configExtraSettings, $this); |
|
128 | - } |
|
129 | - |
|
130 | - // Defaults. |
|
131 | - // * Use PSR-4 autoloader key |
|
132 | - // * Use PSR-0 autoloader key |
|
133 | - // * Use the package name |
|
134 | - if (! isset($this->namespacePrefix)) { |
|
135 | - if (isset($composer->getPackage()->getAutoload()['psr-4'])) { |
|
136 | - $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-4'])); |
|
137 | - } elseif (isset($composer->getPackage()->getAutoload()['psr-0'])) { |
|
138 | - $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-0'])); |
|
139 | - } elseif ('__root__' !== $composer->getPackage()->getName()) { |
|
140 | - $packageName = $composer->getPackage()->getName(); |
|
141 | - $namespacePrefix = preg_replace('/[^\w\/]+/', '_', $packageName); |
|
142 | - $namespacePrefix = str_replace('/', '\\', $namespacePrefix) . '\\'; |
|
143 | - $namespacePrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) { |
|
144 | - return strtoupper($match[0]); |
|
145 | - }, $namespacePrefix); |
|
146 | - $this->setNamespacePrefix($namespacePrefix); |
|
147 | - } elseif (isset($this->classmapPrefix)) { |
|
148 | - $namespacePrefix = rtrim($this->getClassmapPrefix(), '_'); |
|
149 | - $this->setNamespacePrefix($namespacePrefix); |
|
150 | - } |
|
151 | - } |
|
152 | - |
|
153 | - if (! isset($this->classmapPrefix)) { |
|
154 | - if (isset($composer->getPackage()->getAutoload()['psr-4'])) { |
|
155 | - $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-4']); |
|
156 | - $classmapPrefix = str_replace("\\", "_", $autoloadKey); |
|
157 | - $this->setClassmapPrefix($classmapPrefix); |
|
158 | - } elseif (isset($composer->getPackage()->getAutoload()['psr-0'])) { |
|
159 | - $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-0']); |
|
160 | - $classmapPrefix = str_replace("\\", "_", $autoloadKey); |
|
161 | - $this->setClassmapPrefix($classmapPrefix); |
|
162 | - } elseif ('__root__' !== $composer->getPackage()->getName()) { |
|
163 | - $packageName = $composer->getPackage()->getName(); |
|
164 | - $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $packageName); |
|
165 | - $classmapPrefix = str_replace('/', '\\', $classmapPrefix); |
|
166 | - // Uppercase the first letter of each word. |
|
167 | - $classmapPrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) { |
|
168 | - return strtoupper($match[0]); |
|
169 | - }, $classmapPrefix); |
|
170 | - $classmapPrefix = str_replace("\\", "_", $classmapPrefix); |
|
171 | - $this->setClassmapPrefix($classmapPrefix); |
|
172 | - } elseif (isset($this->namespacePrefix)) { |
|
173 | - $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $this->getNamespacePrefix()); |
|
174 | - $classmapPrefix = rtrim($classmapPrefix, '_') . '_'; |
|
175 | - $this->setClassmapPrefix($classmapPrefix); |
|
176 | - } |
|
177 | - } |
|
178 | - |
|
179 | - if (!isset($this->namespacePrefix) || !isset($this->classmapPrefix)) { |
|
180 | - throw new Exception('Prefix not set. Please set `namespace_prefix`, `classmap_prefix` in composer.json/extra/strauss.'); |
|
181 | - } |
|
182 | - |
|
183 | - if (empty($this->packages)) { |
|
184 | - $this->packages = array_map(function (\Composer\Package\Link $element) { |
|
185 | - return $element->getTarget(); |
|
186 | - }, $composer->getPackage()->getRequires()); |
|
187 | - } |
|
188 | - |
|
189 | - // If the bool flag for classmapOutput wasn't set in the Json config. |
|
190 | - if (!isset($this->classmapOutput)) { |
|
191 | - $this->classmapOutput = true; |
|
192 | - // Check each autoloader. |
|
193 | - foreach ($composer->getPackage()->getAutoload() as $autoload) { |
|
194 | - // To see if one of its paths. |
|
195 | - foreach ($autoload as $path) { |
|
196 | - // Matches the target directory. |
|
197 | - if (trim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR === $this->getTargetDirectory()) { |
|
198 | - $this->classmapOutput = false; |
|
199 | - break 2; |
|
200 | - } |
|
201 | - } |
|
202 | - } |
|
203 | - } |
|
204 | - |
|
205 | - // TODO: Throw an exception if any regex patterns in config are invalid. |
|
206 | - // https://stackoverflow.com/questions/4440626/how-can-i-validate-regex |
|
207 | - // preg_match('~Valid(Regular)Expression~', null) === false); |
|
208 | - } |
|
209 | - |
|
210 | - /** |
|
211 | - * `target_directory` will always be returned without a leading slash and with a trailing slash. |
|
212 | - * |
|
213 | - * @return string |
|
214 | - */ |
|
215 | - public function getTargetDirectory(): string |
|
216 | - { |
|
217 | - return trim($this->targetDirectory, DIRECTORY_SEPARATOR . '\\/') . DIRECTORY_SEPARATOR; |
|
218 | - } |
|
219 | - |
|
220 | - /** |
|
221 | - * @param string $targetDirectory |
|
222 | - */ |
|
223 | - public function setTargetDirectory(string $targetDirectory): void |
|
224 | - { |
|
225 | - $this->targetDirectory = trim( |
|
226 | - preg_replace( |
|
227 | - '/[\/\\\\]+/', |
|
228 | - DIRECTORY_SEPARATOR, |
|
229 | - $targetDirectory |
|
230 | - ), |
|
231 | - DIRECTORY_SEPARATOR |
|
232 | - ) |
|
233 | - . DIRECTORY_SEPARATOR ; |
|
234 | - } |
|
235 | - |
|
236 | - /** |
|
237 | - * @return string |
|
238 | - */ |
|
239 | - public function getNamespacePrefix(): string |
|
240 | - { |
|
241 | - return trim($this->namespacePrefix, '\\'); |
|
242 | - } |
|
243 | - |
|
244 | - /** |
|
245 | - * @param string $namespacePrefix |
|
246 | - */ |
|
247 | - public function setNamespacePrefix(string $namespacePrefix): void |
|
248 | - { |
|
249 | - $this->namespacePrefix = $namespacePrefix; |
|
250 | - } |
|
251 | - |
|
252 | - /** |
|
253 | - * @return string |
|
254 | - */ |
|
255 | - public function getClassmapPrefix(): string |
|
256 | - { |
|
257 | - return $this->classmapPrefix; |
|
258 | - } |
|
259 | - |
|
260 | - /** |
|
261 | - * @param string $classmapPrefix |
|
262 | - */ |
|
263 | - public function setClassmapPrefix(string $classmapPrefix): void |
|
264 | - { |
|
265 | - $this->classmapPrefix = $classmapPrefix; |
|
266 | - } |
|
267 | - |
|
268 | - public function setExcludeFromCopy(array $excludeFromCopy): void |
|
269 | - { |
|
270 | - $this->excludeFromCopy = $excludeFromCopy; |
|
271 | - } |
|
272 | - public function getExcludePackagesFromCopy(): array |
|
273 | - { |
|
274 | - return $this->excludeFromCopy['packages'] ?? array(); |
|
275 | - } |
|
276 | - |
|
277 | - public function getExcludeNamespacesFromCopy(): array |
|
278 | - { |
|
279 | - return $this->excludeFromCopy['namespaces'] ?? array(); |
|
280 | - } |
|
281 | - |
|
282 | - public function getExcludeFilePatternsFromCopy(): array |
|
283 | - { |
|
284 | - return $this->excludeFromCopy['filePatterns'] ?? array(); |
|
285 | - } |
|
286 | - |
|
287 | - |
|
288 | - public function setExcludeFromPrefix(array $excludeFromPrefix): void |
|
289 | - { |
|
290 | - $this->excludeFromPrefix = $excludeFromPrefix; |
|
291 | - } |
|
292 | - |
|
293 | - /** |
|
294 | - * When prefixing, do not prefix these packages (which have been copied). |
|
295 | - * |
|
296 | - * @var string[] |
|
297 | - */ |
|
298 | - public function getExcludePackagesFromPrefixing(): array |
|
299 | - { |
|
300 | - return $this->excludeFromPrefix['packages'] ?? array(); |
|
301 | - } |
|
302 | - |
|
303 | - public function getExcludeNamespacesFromPrefixing(): array |
|
304 | - { |
|
305 | - return $this->excludeFromPrefix['namespaces'] ?? array(); |
|
306 | - } |
|
307 | - |
|
308 | - public function getExcludeFilePatternsFromPrefixing(): array |
|
309 | - { |
|
310 | - return $this->excludeFromPrefix['filePatterns'] ?? array(); |
|
311 | - } |
|
312 | - |
|
313 | - |
|
314 | - /** |
|
315 | - * @return array |
|
316 | - */ |
|
317 | - public function getOverrideAutoload(): array |
|
318 | - { |
|
319 | - return $this->overrideAutoload; |
|
320 | - } |
|
321 | - |
|
322 | - /** |
|
323 | - * @param array $overrideAutoload |
|
324 | - */ |
|
325 | - public function setOverrideAutoload(array $overrideAutoload): void |
|
326 | - { |
|
327 | - $this->overrideAutoload = $overrideAutoload; |
|
328 | - } |
|
329 | - |
|
330 | - /** |
|
331 | - * @return bool |
|
332 | - */ |
|
333 | - public function isDeleteVendorFiles(): bool |
|
334 | - { |
|
335 | - return $this->deleteVendorFiles; |
|
336 | - } |
|
337 | - |
|
338 | - /** |
|
339 | - * @param bool $deleteVendorFiles |
|
340 | - */ |
|
341 | - public function setDeleteVendorFiles(bool $deleteVendorFiles): void |
|
342 | - { |
|
343 | - $this->deleteVendorFiles = $deleteVendorFiles; |
|
344 | - } |
|
345 | - |
|
346 | - /** |
|
347 | - * @return array |
|
348 | - */ |
|
349 | - public function getPackages(): array |
|
350 | - { |
|
351 | - return $this->packages; |
|
352 | - } |
|
353 | - |
|
354 | - /** |
|
355 | - * @param array $packages |
|
356 | - */ |
|
357 | - public function setPackages(array $packages): void |
|
358 | - { |
|
359 | - $this->packages = $packages; |
|
360 | - } |
|
361 | - |
|
362 | - /** |
|
363 | - * @return bool |
|
364 | - */ |
|
365 | - public function isClassmapOutput(): bool |
|
366 | - { |
|
367 | - return $this->classmapOutput; |
|
368 | - } |
|
369 | - |
|
370 | - /** |
|
371 | - * @param bool $classmapOutput |
|
372 | - */ |
|
373 | - public function setClassmapOutput(bool $classmapOutput): void |
|
374 | - { |
|
375 | - $this->classmapOutput = $classmapOutput; |
|
376 | - } |
|
377 | - |
|
378 | - /** |
|
379 | - * Backwards compatability with Mozart. |
|
380 | - */ |
|
381 | - public function setExcludePackages(array $excludePackages) |
|
382 | - { |
|
383 | - |
|
384 | - if (! isset($this->excludeFromPrefix)) { |
|
385 | - $this->excludeFromPrefix = array(); |
|
386 | - } |
|
387 | - |
|
388 | - $this->excludeFromPrefix['packages'] = $excludePackages; |
|
389 | - } |
|
390 | - |
|
391 | - |
|
392 | - /** |
|
393 | - * @return array |
|
394 | - */ |
|
395 | - public function getNamespaceReplacementPatterns(): array |
|
396 | - { |
|
397 | - return $this->namespaceReplacementPatterns; |
|
398 | - } |
|
399 | - |
|
400 | - /** |
|
401 | - * @param array $namespaceReplacementPatterns |
|
402 | - */ |
|
403 | - public function setNamespaceReplacementPatterns(array $namespaceReplacementPatterns): void |
|
404 | - { |
|
405 | - $this->namespaceReplacementPatterns = $namespaceReplacementPatterns; |
|
406 | - } |
|
15 | + /** |
|
16 | + * The output directory. |
|
17 | + * |
|
18 | + * Probably `strauss/` or `src/strauss/`. |
|
19 | + * |
|
20 | + * @var string |
|
21 | + */ |
|
22 | + protected $targetDirectory = 'strauss'; |
|
23 | + |
|
24 | + /** |
|
25 | + * `namespacePrefix` is the prefix to be given to any namespaces. |
|
26 | + * Presumably this will take the form `My_Project_Namespace\dep_directory`. |
|
27 | + * |
|
28 | + * @link https://www.php-fig.org/psr/psr-4/ |
|
29 | + * |
|
30 | + * @var string |
|
31 | + */ |
|
32 | + protected $namespacePrefix; |
|
33 | + |
|
34 | + /** |
|
35 | + * @var string |
|
36 | + */ |
|
37 | + protected $classmapPrefix; |
|
38 | + |
|
39 | + /** |
|
40 | + * Packages to copy and (maybe) prefix. |
|
41 | + * |
|
42 | + * If this is empty, the "requires" list in the project composer.json is used. |
|
43 | + * |
|
44 | + * @var array |
|
45 | + */ |
|
46 | + protected array $packages = []; |
|
47 | + |
|
48 | + // Back-compatibility with Mozart. |
|
49 | + private array $excludePackages; |
|
50 | + |
|
51 | + /** |
|
52 | + * @var array{packages?: string[], namespaces?: string[], filePatterns?: string[]} |
|
53 | + */ |
|
54 | + protected array $excludeFromCopy = array(); |
|
55 | + |
|
56 | + /** |
|
57 | + * @var array{packages?: string[], namespaces?: string[], filePatterns?: string[]} |
|
58 | + */ |
|
59 | + protected array $excludeFromPrefix = array('filePatterns'=>array('/^psr.*$/')); |
|
60 | + |
|
61 | + |
|
62 | + /** |
|
63 | + * An array of autoload keys to replace packages' existing autoload key. |
|
64 | + * |
|
65 | + * e.g. when |
|
66 | + * * A package has no autoloader |
|
67 | + * * A package specified both a PSR-4 and a classmap but only needs one |
|
68 | + * ... |
|
69 | + * |
|
70 | + * @var array |
|
71 | + */ |
|
72 | + protected $overrideAutoload = []; |
|
73 | + |
|
74 | + /** |
|
75 | + * After completing `strauss compose` should the source files be deleted? |
|
76 | + * This does not affect symlinked directories. |
|
77 | + * |
|
78 | + * @var bool |
|
79 | + */ |
|
80 | + protected $deleteVendorFiles = false; |
|
81 | + |
|
82 | + protected bool $classmapOutput; |
|
83 | + |
|
84 | + protected array $namespaceReplacementPatterns = array(); |
|
85 | + |
|
86 | + |
|
87 | + /** |
|
88 | + * Read any existing Mozart config. |
|
89 | + * Overwrite it with any Strauss config. |
|
90 | + * Provide sensible defaults. |
|
91 | + * |
|
92 | + * @throws Exception |
|
93 | + */ |
|
94 | + public function __construct(Composer $composer) |
|
95 | + { |
|
96 | + |
|
97 | + $configExtraSettings = null; |
|
98 | + |
|
99 | + // Backwards compatibility with Mozart. |
|
100 | + if (isset($composer->getPackage()->getExtra()['mozart'])) { |
|
101 | + $configExtraSettings = (object)$composer->getPackage()->getExtra()['mozart']; |
|
102 | + |
|
103 | + // Default setting for Mozart. |
|
104 | + $this->setDeleteVendorFiles(true); |
|
105 | + } |
|
106 | + |
|
107 | + if (isset($composer->getPackage()->getExtra()['strauss'])) { |
|
108 | + $configExtraSettings = (object)$composer->getPackage()->getExtra()['strauss']; |
|
109 | + } |
|
110 | + |
|
111 | + if (!is_null($configExtraSettings)) { |
|
112 | + $mapper = (new JsonMapperFactory())->bestFit(); |
|
113 | + |
|
114 | + $rename = new Rename(); |
|
115 | + $rename->addMapping(StraussConfig::class, 'dep_directory', 'targetDirectory'); |
|
116 | + $rename->addMapping(StraussConfig::class, 'dep_namespace', 'namespacePrefix'); |
|
117 | + |
|
118 | + $rename->addMapping(StraussConfig::class, 'exclude_packages', 'excludePackages'); |
|
119 | + $rename->addMapping(StraussConfig::class, 'delete_vendor_directories', 'deleteVendorFiles'); |
|
120 | + |
|
121 | + $mapper->unshift($rename); |
|
122 | + $mapper->push(new \JsonMapper\Middleware\CaseConversion( |
|
123 | + \JsonMapper\Enums\TextNotation::UNDERSCORE(), |
|
124 | + \JsonMapper\Enums\TextNotation::CAMEL_CASE() |
|
125 | + )); |
|
126 | + |
|
127 | + $mapper->mapObject($configExtraSettings, $this); |
|
128 | + } |
|
129 | + |
|
130 | + // Defaults. |
|
131 | + // * Use PSR-4 autoloader key |
|
132 | + // * Use PSR-0 autoloader key |
|
133 | + // * Use the package name |
|
134 | + if (! isset($this->namespacePrefix)) { |
|
135 | + if (isset($composer->getPackage()->getAutoload()['psr-4'])) { |
|
136 | + $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-4'])); |
|
137 | + } elseif (isset($composer->getPackage()->getAutoload()['psr-0'])) { |
|
138 | + $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-0'])); |
|
139 | + } elseif ('__root__' !== $composer->getPackage()->getName()) { |
|
140 | + $packageName = $composer->getPackage()->getName(); |
|
141 | + $namespacePrefix = preg_replace('/[^\w\/]+/', '_', $packageName); |
|
142 | + $namespacePrefix = str_replace('/', '\\', $namespacePrefix) . '\\'; |
|
143 | + $namespacePrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) { |
|
144 | + return strtoupper($match[0]); |
|
145 | + }, $namespacePrefix); |
|
146 | + $this->setNamespacePrefix($namespacePrefix); |
|
147 | + } elseif (isset($this->classmapPrefix)) { |
|
148 | + $namespacePrefix = rtrim($this->getClassmapPrefix(), '_'); |
|
149 | + $this->setNamespacePrefix($namespacePrefix); |
|
150 | + } |
|
151 | + } |
|
152 | + |
|
153 | + if (! isset($this->classmapPrefix)) { |
|
154 | + if (isset($composer->getPackage()->getAutoload()['psr-4'])) { |
|
155 | + $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-4']); |
|
156 | + $classmapPrefix = str_replace("\\", "_", $autoloadKey); |
|
157 | + $this->setClassmapPrefix($classmapPrefix); |
|
158 | + } elseif (isset($composer->getPackage()->getAutoload()['psr-0'])) { |
|
159 | + $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-0']); |
|
160 | + $classmapPrefix = str_replace("\\", "_", $autoloadKey); |
|
161 | + $this->setClassmapPrefix($classmapPrefix); |
|
162 | + } elseif ('__root__' !== $composer->getPackage()->getName()) { |
|
163 | + $packageName = $composer->getPackage()->getName(); |
|
164 | + $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $packageName); |
|
165 | + $classmapPrefix = str_replace('/', '\\', $classmapPrefix); |
|
166 | + // Uppercase the first letter of each word. |
|
167 | + $classmapPrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) { |
|
168 | + return strtoupper($match[0]); |
|
169 | + }, $classmapPrefix); |
|
170 | + $classmapPrefix = str_replace("\\", "_", $classmapPrefix); |
|
171 | + $this->setClassmapPrefix($classmapPrefix); |
|
172 | + } elseif (isset($this->namespacePrefix)) { |
|
173 | + $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $this->getNamespacePrefix()); |
|
174 | + $classmapPrefix = rtrim($classmapPrefix, '_') . '_'; |
|
175 | + $this->setClassmapPrefix($classmapPrefix); |
|
176 | + } |
|
177 | + } |
|
178 | + |
|
179 | + if (!isset($this->namespacePrefix) || !isset($this->classmapPrefix)) { |
|
180 | + throw new Exception('Prefix not set. Please set `namespace_prefix`, `classmap_prefix` in composer.json/extra/strauss.'); |
|
181 | + } |
|
182 | + |
|
183 | + if (empty($this->packages)) { |
|
184 | + $this->packages = array_map(function (\Composer\Package\Link $element) { |
|
185 | + return $element->getTarget(); |
|
186 | + }, $composer->getPackage()->getRequires()); |
|
187 | + } |
|
188 | + |
|
189 | + // If the bool flag for classmapOutput wasn't set in the Json config. |
|
190 | + if (!isset($this->classmapOutput)) { |
|
191 | + $this->classmapOutput = true; |
|
192 | + // Check each autoloader. |
|
193 | + foreach ($composer->getPackage()->getAutoload() as $autoload) { |
|
194 | + // To see if one of its paths. |
|
195 | + foreach ($autoload as $path) { |
|
196 | + // Matches the target directory. |
|
197 | + if (trim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR === $this->getTargetDirectory()) { |
|
198 | + $this->classmapOutput = false; |
|
199 | + break 2; |
|
200 | + } |
|
201 | + } |
|
202 | + } |
|
203 | + } |
|
204 | + |
|
205 | + // TODO: Throw an exception if any regex patterns in config are invalid. |
|
206 | + // https://stackoverflow.com/questions/4440626/how-can-i-validate-regex |
|
207 | + // preg_match('~Valid(Regular)Expression~', null) === false); |
|
208 | + } |
|
209 | + |
|
210 | + /** |
|
211 | + * `target_directory` will always be returned without a leading slash and with a trailing slash. |
|
212 | + * |
|
213 | + * @return string |
|
214 | + */ |
|
215 | + public function getTargetDirectory(): string |
|
216 | + { |
|
217 | + return trim($this->targetDirectory, DIRECTORY_SEPARATOR . '\\/') . DIRECTORY_SEPARATOR; |
|
218 | + } |
|
219 | + |
|
220 | + /** |
|
221 | + * @param string $targetDirectory |
|
222 | + */ |
|
223 | + public function setTargetDirectory(string $targetDirectory): void |
|
224 | + { |
|
225 | + $this->targetDirectory = trim( |
|
226 | + preg_replace( |
|
227 | + '/[\/\\\\]+/', |
|
228 | + DIRECTORY_SEPARATOR, |
|
229 | + $targetDirectory |
|
230 | + ), |
|
231 | + DIRECTORY_SEPARATOR |
|
232 | + ) |
|
233 | + . DIRECTORY_SEPARATOR ; |
|
234 | + } |
|
235 | + |
|
236 | + /** |
|
237 | + * @return string |
|
238 | + */ |
|
239 | + public function getNamespacePrefix(): string |
|
240 | + { |
|
241 | + return trim($this->namespacePrefix, '\\'); |
|
242 | + } |
|
243 | + |
|
244 | + /** |
|
245 | + * @param string $namespacePrefix |
|
246 | + */ |
|
247 | + public function setNamespacePrefix(string $namespacePrefix): void |
|
248 | + { |
|
249 | + $this->namespacePrefix = $namespacePrefix; |
|
250 | + } |
|
251 | + |
|
252 | + /** |
|
253 | + * @return string |
|
254 | + */ |
|
255 | + public function getClassmapPrefix(): string |
|
256 | + { |
|
257 | + return $this->classmapPrefix; |
|
258 | + } |
|
259 | + |
|
260 | + /** |
|
261 | + * @param string $classmapPrefix |
|
262 | + */ |
|
263 | + public function setClassmapPrefix(string $classmapPrefix): void |
|
264 | + { |
|
265 | + $this->classmapPrefix = $classmapPrefix; |
|
266 | + } |
|
267 | + |
|
268 | + public function setExcludeFromCopy(array $excludeFromCopy): void |
|
269 | + { |
|
270 | + $this->excludeFromCopy = $excludeFromCopy; |
|
271 | + } |
|
272 | + public function getExcludePackagesFromCopy(): array |
|
273 | + { |
|
274 | + return $this->excludeFromCopy['packages'] ?? array(); |
|
275 | + } |
|
276 | + |
|
277 | + public function getExcludeNamespacesFromCopy(): array |
|
278 | + { |
|
279 | + return $this->excludeFromCopy['namespaces'] ?? array(); |
|
280 | + } |
|
281 | + |
|
282 | + public function getExcludeFilePatternsFromCopy(): array |
|
283 | + { |
|
284 | + return $this->excludeFromCopy['filePatterns'] ?? array(); |
|
285 | + } |
|
286 | + |
|
287 | + |
|
288 | + public function setExcludeFromPrefix(array $excludeFromPrefix): void |
|
289 | + { |
|
290 | + $this->excludeFromPrefix = $excludeFromPrefix; |
|
291 | + } |
|
292 | + |
|
293 | + /** |
|
294 | + * When prefixing, do not prefix these packages (which have been copied). |
|
295 | + * |
|
296 | + * @var string[] |
|
297 | + */ |
|
298 | + public function getExcludePackagesFromPrefixing(): array |
|
299 | + { |
|
300 | + return $this->excludeFromPrefix['packages'] ?? array(); |
|
301 | + } |
|
302 | + |
|
303 | + public function getExcludeNamespacesFromPrefixing(): array |
|
304 | + { |
|
305 | + return $this->excludeFromPrefix['namespaces'] ?? array(); |
|
306 | + } |
|
307 | + |
|
308 | + public function getExcludeFilePatternsFromPrefixing(): array |
|
309 | + { |
|
310 | + return $this->excludeFromPrefix['filePatterns'] ?? array(); |
|
311 | + } |
|
312 | + |
|
313 | + |
|
314 | + /** |
|
315 | + * @return array |
|
316 | + */ |
|
317 | + public function getOverrideAutoload(): array |
|
318 | + { |
|
319 | + return $this->overrideAutoload; |
|
320 | + } |
|
321 | + |
|
322 | + /** |
|
323 | + * @param array $overrideAutoload |
|
324 | + */ |
|
325 | + public function setOverrideAutoload(array $overrideAutoload): void |
|
326 | + { |
|
327 | + $this->overrideAutoload = $overrideAutoload; |
|
328 | + } |
|
329 | + |
|
330 | + /** |
|
331 | + * @return bool |
|
332 | + */ |
|
333 | + public function isDeleteVendorFiles(): bool |
|
334 | + { |
|
335 | + return $this->deleteVendorFiles; |
|
336 | + } |
|
337 | + |
|
338 | + /** |
|
339 | + * @param bool $deleteVendorFiles |
|
340 | + */ |
|
341 | + public function setDeleteVendorFiles(bool $deleteVendorFiles): void |
|
342 | + { |
|
343 | + $this->deleteVendorFiles = $deleteVendorFiles; |
|
344 | + } |
|
345 | + |
|
346 | + /** |
|
347 | + * @return array |
|
348 | + */ |
|
349 | + public function getPackages(): array |
|
350 | + { |
|
351 | + return $this->packages; |
|
352 | + } |
|
353 | + |
|
354 | + /** |
|
355 | + * @param array $packages |
|
356 | + */ |
|
357 | + public function setPackages(array $packages): void |
|
358 | + { |
|
359 | + $this->packages = $packages; |
|
360 | + } |
|
361 | + |
|
362 | + /** |
|
363 | + * @return bool |
|
364 | + */ |
|
365 | + public function isClassmapOutput(): bool |
|
366 | + { |
|
367 | + return $this->classmapOutput; |
|
368 | + } |
|
369 | + |
|
370 | + /** |
|
371 | + * @param bool $classmapOutput |
|
372 | + */ |
|
373 | + public function setClassmapOutput(bool $classmapOutput): void |
|
374 | + { |
|
375 | + $this->classmapOutput = $classmapOutput; |
|
376 | + } |
|
377 | + |
|
378 | + /** |
|
379 | + * Backwards compatability with Mozart. |
|
380 | + */ |
|
381 | + public function setExcludePackages(array $excludePackages) |
|
382 | + { |
|
383 | + |
|
384 | + if (! isset($this->excludeFromPrefix)) { |
|
385 | + $this->excludeFromPrefix = array(); |
|
386 | + } |
|
387 | + |
|
388 | + $this->excludeFromPrefix['packages'] = $excludePackages; |
|
389 | + } |
|
390 | + |
|
391 | + |
|
392 | + /** |
|
393 | + * @return array |
|
394 | + */ |
|
395 | + public function getNamespaceReplacementPatterns(): array |
|
396 | + { |
|
397 | + return $this->namespaceReplacementPatterns; |
|
398 | + } |
|
399 | + |
|
400 | + /** |
|
401 | + * @param array $namespaceReplacementPatterns |
|
402 | + */ |
|
403 | + public function setNamespaceReplacementPatterns(array $namespaceReplacementPatterns): void |
|
404 | + { |
|
405 | + $this->namespaceReplacementPatterns = $namespaceReplacementPatterns; |
|
406 | + } |
|
407 | 407 | } |
@@ -13,134 +13,134 @@ |
||
13 | 13 | |
14 | 14 | class ComposerPackage |
15 | 15 | { |
16 | - /** |
|
17 | - * The composer.json file as parsed by Composer. |
|
18 | - * |
|
19 | - * @see \Composer\Factory::create |
|
20 | - * |
|
21 | - * @var \Composer\Composer |
|
22 | - */ |
|
23 | - protected \Composer\Composer $composer; |
|
24 | - |
|
25 | - /** |
|
26 | - * The name of the project in composer.json. |
|
27 | - * |
|
28 | - * e.g. brianhenryie/my-project |
|
29 | - * |
|
30 | - * @var string |
|
31 | - */ |
|
32 | - protected string $name; |
|
33 | - |
|
34 | - protected string $path; |
|
35 | - |
|
36 | - /** |
|
37 | - * The discovered files, classmap, psr0 and psr4 autoload keys discovered (as parsed by Composer). |
|
38 | - * |
|
39 | - * @var array<string, array<string, string>> |
|
40 | - */ |
|
41 | - protected array $autoload = []; |
|
42 | - |
|
43 | - /** |
|
44 | - * The names in the composer.json's "requires" field (without versions). |
|
45 | - * |
|
46 | - * @var array |
|
47 | - */ |
|
48 | - protected array $requiresNames = []; |
|
49 | - |
|
50 | - protected string $license; |
|
51 | - |
|
52 | - /** |
|
53 | - * Create a PHP object to represent a composer package. |
|
54 | - * |
|
55 | - * @param string $absolutePath The absolute path to the vendor folder with the composer.json "name", |
|
56 | - * i.e. the domain/package definition, which is the vendor subdir from where the package's |
|
57 | - * composer.json should be read. |
|
58 | - * @param array $overrideAutoload Optional configuration to replace the package's own autoload definition with |
|
59 | - * another which Strauss can use. |
|
60 | - */ |
|
61 | - public function __construct(string $absolutePath, array $overrideAutoload = null) |
|
62 | - { |
|
63 | - |
|
64 | - if (is_dir($absolutePath)) { |
|
65 | - $absolutePath = rtrim($absolutePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'composer.json'; |
|
66 | - } |
|
67 | - |
|
68 | - $composer = Factory::create(new NullIO(), $absolutePath); |
|
69 | - |
|
70 | - $this->composer = $composer; |
|
71 | - |
|
72 | - $this->name = $composer->getPackage()->getName(); |
|
73 | - |
|
74 | - $relativePath = null; |
|
75 | - |
|
76 | - // TODO: Test on Windows (DIRECTORY_SEPARATOR). |
|
77 | - if (1 === preg_match('/vendor\/(\w*\/\w*)\/composer\.json/', $absolutePath, $output_array)) { |
|
78 | - $relativePath = $output_array[1]; |
|
79 | - } |
|
80 | - |
|
81 | - $this->path = $relativePath ?? $composer->getPackage()->getName(); |
|
82 | - |
|
83 | - if (!is_null($overrideAutoload)) { |
|
84 | - $composer->getPackage()->setAutoload($overrideAutoload); |
|
85 | - } |
|
86 | - |
|
87 | - $this->autoload = $composer->getPackage()->getAutoload(); |
|
88 | - |
|
89 | - foreach ($composer->getPackage()->getRequires() as $_name => $packageLink) { |
|
90 | - $this->requiresNames[] = $packageLink->getTarget(); |
|
91 | - } |
|
92 | - |
|
93 | - // Try to get the license from the package's composer.json, asssume proprietary (all rights reserved!). |
|
94 | - $this->license = !empty($composer->getPackage()->getLicense()) |
|
95 | - ? implode(',', $composer->getPackage()->getLicense()) |
|
96 | - : 'proprietary?'; |
|
97 | - } |
|
98 | - |
|
99 | - /** |
|
100 | - * Composer package project name. |
|
101 | - * |
|
102 | - * @return string |
|
103 | - */ |
|
104 | - public function getName(): string |
|
105 | - { |
|
106 | - return $this->name; |
|
107 | - } |
|
108 | - |
|
109 | - public function getPath(): string |
|
110 | - { |
|
111 | - return $this->path; |
|
112 | - } |
|
113 | - |
|
114 | - /** |
|
115 | - * |
|
116 | - * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => 'src' ]] |
|
117 | - * |
|
118 | - * @return array<string, array<int|string, string>> |
|
119 | - */ |
|
120 | - public function getAutoload(): array |
|
121 | - { |
|
122 | - return $this->autoload; |
|
123 | - } |
|
124 | - |
|
125 | - /** |
|
126 | - * The names of the packages in the composer.json's "requires" field (without version). |
|
127 | - * |
|
128 | - * Excludes PHP, ext-*, since we won't be copying or prefixing them. |
|
129 | - * |
|
130 | - * @return string[] |
|
131 | - */ |
|
132 | - public function getRequiresNames(): array |
|
133 | - { |
|
134 | - // Unset PHP, ext-*. |
|
135 | - $removePhpExt = function ($element) { |
|
136 | - return !( 0 === strpos($element, 'ext') || 'php' === $element ); |
|
137 | - }; |
|
138 | - |
|
139 | - return array_filter($this->requiresNames, $removePhpExt); |
|
140 | - } |
|
141 | - |
|
142 | - public function getLicense():string |
|
143 | - { |
|
144 | - return $this->license; |
|
145 | - } |
|
16 | + /** |
|
17 | + * The composer.json file as parsed by Composer. |
|
18 | + * |
|
19 | + * @see \Composer\Factory::create |
|
20 | + * |
|
21 | + * @var \Composer\Composer |
|
22 | + */ |
|
23 | + protected \Composer\Composer $composer; |
|
24 | + |
|
25 | + /** |
|
26 | + * The name of the project in composer.json. |
|
27 | + * |
|
28 | + * e.g. brianhenryie/my-project |
|
29 | + * |
|
30 | + * @var string |
|
31 | + */ |
|
32 | + protected string $name; |
|
33 | + |
|
34 | + protected string $path; |
|
35 | + |
|
36 | + /** |
|
37 | + * The discovered files, classmap, psr0 and psr4 autoload keys discovered (as parsed by Composer). |
|
38 | + * |
|
39 | + * @var array<string, array<string, string>> |
|
40 | + */ |
|
41 | + protected array $autoload = []; |
|
42 | + |
|
43 | + /** |
|
44 | + * The names in the composer.json's "requires" field (without versions). |
|
45 | + * |
|
46 | + * @var array |
|
47 | + */ |
|
48 | + protected array $requiresNames = []; |
|
49 | + |
|
50 | + protected string $license; |
|
51 | + |
|
52 | + /** |
|
53 | + * Create a PHP object to represent a composer package. |
|
54 | + * |
|
55 | + * @param string $absolutePath The absolute path to the vendor folder with the composer.json "name", |
|
56 | + * i.e. the domain/package definition, which is the vendor subdir from where the package's |
|
57 | + * composer.json should be read. |
|
58 | + * @param array $overrideAutoload Optional configuration to replace the package's own autoload definition with |
|
59 | + * another which Strauss can use. |
|
60 | + */ |
|
61 | + public function __construct(string $absolutePath, array $overrideAutoload = null) |
|
62 | + { |
|
63 | + |
|
64 | + if (is_dir($absolutePath)) { |
|
65 | + $absolutePath = rtrim($absolutePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'composer.json'; |
|
66 | + } |
|
67 | + |
|
68 | + $composer = Factory::create(new NullIO(), $absolutePath); |
|
69 | + |
|
70 | + $this->composer = $composer; |
|
71 | + |
|
72 | + $this->name = $composer->getPackage()->getName(); |
|
73 | + |
|
74 | + $relativePath = null; |
|
75 | + |
|
76 | + // TODO: Test on Windows (DIRECTORY_SEPARATOR). |
|
77 | + if (1 === preg_match('/vendor\/(\w*\/\w*)\/composer\.json/', $absolutePath, $output_array)) { |
|
78 | + $relativePath = $output_array[1]; |
|
79 | + } |
|
80 | + |
|
81 | + $this->path = $relativePath ?? $composer->getPackage()->getName(); |
|
82 | + |
|
83 | + if (!is_null($overrideAutoload)) { |
|
84 | + $composer->getPackage()->setAutoload($overrideAutoload); |
|
85 | + } |
|
86 | + |
|
87 | + $this->autoload = $composer->getPackage()->getAutoload(); |
|
88 | + |
|
89 | + foreach ($composer->getPackage()->getRequires() as $_name => $packageLink) { |
|
90 | + $this->requiresNames[] = $packageLink->getTarget(); |
|
91 | + } |
|
92 | + |
|
93 | + // Try to get the license from the package's composer.json, asssume proprietary (all rights reserved!). |
|
94 | + $this->license = !empty($composer->getPackage()->getLicense()) |
|
95 | + ? implode(',', $composer->getPackage()->getLicense()) |
|
96 | + : 'proprietary?'; |
|
97 | + } |
|
98 | + |
|
99 | + /** |
|
100 | + * Composer package project name. |
|
101 | + * |
|
102 | + * @return string |
|
103 | + */ |
|
104 | + public function getName(): string |
|
105 | + { |
|
106 | + return $this->name; |
|
107 | + } |
|
108 | + |
|
109 | + public function getPath(): string |
|
110 | + { |
|
111 | + return $this->path; |
|
112 | + } |
|
113 | + |
|
114 | + /** |
|
115 | + * |
|
116 | + * e.g. ['psr-4' => [ 'BrianHenryIE\Project' => 'src' ]] |
|
117 | + * |
|
118 | + * @return array<string, array<int|string, string>> |
|
119 | + */ |
|
120 | + public function getAutoload(): array |
|
121 | + { |
|
122 | + return $this->autoload; |
|
123 | + } |
|
124 | + |
|
125 | + /** |
|
126 | + * The names of the packages in the composer.json's "requires" field (without version). |
|
127 | + * |
|
128 | + * Excludes PHP, ext-*, since we won't be copying or prefixing them. |
|
129 | + * |
|
130 | + * @return string[] |
|
131 | + */ |
|
132 | + public function getRequiresNames(): array |
|
133 | + { |
|
134 | + // Unset PHP, ext-*. |
|
135 | + $removePhpExt = function ($element) { |
|
136 | + return !( 0 === strpos($element, 'ext') || 'php' === $element ); |
|
137 | + }; |
|
138 | + |
|
139 | + return array_filter($this->requiresNames, $removePhpExt); |
|
140 | + } |
|
141 | + |
|
142 | + public function getLicense():string |
|
143 | + { |
|
144 | + return $this->license; |
|
145 | + } |
|
146 | 146 | } |
@@ -9,48 +9,48 @@ |
||
9 | 9 | |
10 | 10 | class ProjectComposerPackage extends ComposerPackage |
11 | 11 | { |
12 | - protected string $author; |
|
13 | - |
|
14 | - protected string $vendorDirectory; |
|
15 | - |
|
16 | - public function __construct(string $absolutePath, array $overrideAutoload = null) |
|
17 | - { |
|
18 | - parent::__construct($absolutePath, $overrideAutoload); |
|
19 | - |
|
20 | - $authors = $this->composer->getPackage()->getAuthors(); |
|
21 | - if (empty($authors) || !isset($authors[0]['name'])) { |
|
22 | - $this->author = explode("/", $this->name, 2)[0]; |
|
23 | - } else { |
|
24 | - $this->author = $authors[0]['name']; |
|
25 | - } |
|
26 | - |
|
27 | - $vendorDirectory = $this->composer->getConfig()->get('vendor-dir'); |
|
28 | - if (is_string($vendorDirectory)) { |
|
29 | - $vendorDirectory = str_replace($absolutePath, '', (string) $vendorDirectory); |
|
30 | - $this->vendorDirectory = $vendorDirectory; |
|
31 | - } else { |
|
32 | - $this->vendorDirectory = 'vendor' . DIRECTORY_SEPARATOR; |
|
33 | - } |
|
34 | - } |
|
35 | - |
|
36 | - /** |
|
37 | - * @return StraussConfig |
|
38 | - * @throws \Exception |
|
39 | - */ |
|
40 | - public function getStraussConfig(): StraussConfig |
|
41 | - { |
|
42 | - |
|
43 | - return new StraussConfig($this->composer); |
|
44 | - } |
|
45 | - |
|
46 | - |
|
47 | - public function getAuthor(): string |
|
48 | - { |
|
49 | - return $this->author; |
|
50 | - } |
|
51 | - |
|
52 | - public function getVendorDirectory(): string |
|
53 | - { |
|
54 | - return rtrim($this->vendorDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
55 | - } |
|
12 | + protected string $author; |
|
13 | + |
|
14 | + protected string $vendorDirectory; |
|
15 | + |
|
16 | + public function __construct(string $absolutePath, array $overrideAutoload = null) |
|
17 | + { |
|
18 | + parent::__construct($absolutePath, $overrideAutoload); |
|
19 | + |
|
20 | + $authors = $this->composer->getPackage()->getAuthors(); |
|
21 | + if (empty($authors) || !isset($authors[0]['name'])) { |
|
22 | + $this->author = explode("/", $this->name, 2)[0]; |
|
23 | + } else { |
|
24 | + $this->author = $authors[0]['name']; |
|
25 | + } |
|
26 | + |
|
27 | + $vendorDirectory = $this->composer->getConfig()->get('vendor-dir'); |
|
28 | + if (is_string($vendorDirectory)) { |
|
29 | + $vendorDirectory = str_replace($absolutePath, '', (string) $vendorDirectory); |
|
30 | + $this->vendorDirectory = $vendorDirectory; |
|
31 | + } else { |
|
32 | + $this->vendorDirectory = 'vendor' . DIRECTORY_SEPARATOR; |
|
33 | + } |
|
34 | + } |
|
35 | + |
|
36 | + /** |
|
37 | + * @return StraussConfig |
|
38 | + * @throws \Exception |
|
39 | + */ |
|
40 | + public function getStraussConfig(): StraussConfig |
|
41 | + { |
|
42 | + |
|
43 | + return new StraussConfig($this->composer); |
|
44 | + } |
|
45 | + |
|
46 | + |
|
47 | + public function getAuthor(): string |
|
48 | + { |
|
49 | + return $this->author; |
|
50 | + } |
|
51 | + |
|
52 | + public function getVendorDirectory(): string |
|
53 | + { |
|
54 | + return rtrim($this->vendorDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
55 | + } |
|
56 | 56 | } |
@@ -15,152 +15,152 @@ discard block |
||
15 | 15 | class Autoload |
16 | 16 | { |
17 | 17 | |
18 | - /** @var Filesystem */ |
|
19 | - protected $filesystem; |
|
20 | - |
|
21 | - protected string $workingDir; |
|
22 | - |
|
23 | - protected StraussConfig $config; |
|
24 | - |
|
25 | - /** |
|
26 | - * The files autolaoders of packages that have been copied by Strauss. |
|
27 | - * Keyed by package path. |
|
28 | - * |
|
29 | - * @var array |
|
30 | - */ |
|
31 | - protected array $discoveredFilesAutoloaders; |
|
32 | - |
|
33 | - /** |
|
34 | - * Autoload constructor. |
|
35 | - * @param StraussConfig $config |
|
36 | - * @param string $workingDir |
|
37 | - * @param array<string, array<string>> $files |
|
38 | - */ |
|
39 | - public function __construct(StraussConfig $config, string $workingDir, array $discoveredFilesAutoloaders) |
|
40 | - { |
|
41 | - $this->config = $config; |
|
42 | - $this->workingDir = $workingDir; |
|
43 | - $this->discoveredFilesAutoloaders = $discoveredFilesAutoloaders; |
|
44 | - $this->filesystem = new Filesystem(new Local($workingDir)); |
|
45 | - } |
|
46 | - |
|
47 | - public function generate() |
|
48 | - { |
|
49 | - |
|
50 | - if (! $this->config->isClassmapOutput()) { |
|
51 | - return; |
|
52 | - } |
|
53 | - |
|
54 | - // TODO Don't do this if vendor is the target dir (i.e. in-situ updating). |
|
55 | - |
|
56 | - $this->generateClassmap(); |
|
57 | - |
|
58 | - $this->generateFilesAutoloader(); |
|
59 | - |
|
60 | - $this->generateAutoloadPhp(); |
|
61 | - } |
|
18 | + /** @var Filesystem */ |
|
19 | + protected $filesystem; |
|
20 | + |
|
21 | + protected string $workingDir; |
|
22 | + |
|
23 | + protected StraussConfig $config; |
|
24 | + |
|
25 | + /** |
|
26 | + * The files autolaoders of packages that have been copied by Strauss. |
|
27 | + * Keyed by package path. |
|
28 | + * |
|
29 | + * @var array |
|
30 | + */ |
|
31 | + protected array $discoveredFilesAutoloaders; |
|
32 | + |
|
33 | + /** |
|
34 | + * Autoload constructor. |
|
35 | + * @param StraussConfig $config |
|
36 | + * @param string $workingDir |
|
37 | + * @param array<string, array<string>> $files |
|
38 | + */ |
|
39 | + public function __construct(StraussConfig $config, string $workingDir, array $discoveredFilesAutoloaders) |
|
40 | + { |
|
41 | + $this->config = $config; |
|
42 | + $this->workingDir = $workingDir; |
|
43 | + $this->discoveredFilesAutoloaders = $discoveredFilesAutoloaders; |
|
44 | + $this->filesystem = new Filesystem(new Local($workingDir)); |
|
45 | + } |
|
62 | 46 | |
63 | - /** |
|
64 | - * Uses Composer's `ClassMapGenerator::createMap()` to scan the directories for classes and generate the map. |
|
65 | - * |
|
66 | - * createMap() returns the full local path, so we then replace the root of the path with a variable. |
|
67 | - * |
|
68 | - * @see ClassMapGenerator::dump() |
|
69 | - * |
|
70 | - */ |
|
71 | - protected function generateClassmap(): string |
|
72 | - { |
|
73 | - |
|
74 | - // Hyphen used to match WordPress Coding Standards. |
|
75 | - $output_filename = "autoload-classmap.php"; |
|
76 | - |
|
77 | - $targetDirectory = getcwd() |
|
78 | - . DIRECTORY_SEPARATOR |
|
79 | - . ltrim($this->config->getTargetDirectory(), DIRECTORY_SEPARATOR); |
|
80 | - |
|
81 | - $dirs = array( |
|
82 | - $targetDirectory |
|
83 | - ); |
|
84 | - |
|
85 | - $dirname = ''; |
|
86 | - |
|
87 | - foreach ($dirs as $dir) { |
|
88 | - if (!is_dir($dir)) { |
|
89 | - continue; |
|
90 | - } |
|
47 | + public function generate() |
|
48 | + { |
|
49 | + |
|
50 | + if (! $this->config->isClassmapOutput()) { |
|
51 | + return; |
|
52 | + } |
|
53 | + |
|
54 | + // TODO Don't do this if vendor is the target dir (i.e. in-situ updating). |
|
55 | + |
|
56 | + $this->generateClassmap(); |
|
57 | + |
|
58 | + $this->generateFilesAutoloader(); |
|
59 | + |
|
60 | + $this->generateAutoloadPhp(); |
|
61 | + } |
|
62 | + |
|
63 | + /** |
|
64 | + * Uses Composer's `ClassMapGenerator::createMap()` to scan the directories for classes and generate the map. |
|
65 | + * |
|
66 | + * createMap() returns the full local path, so we then replace the root of the path with a variable. |
|
67 | + * |
|
68 | + * @see ClassMapGenerator::dump() |
|
69 | + * |
|
70 | + */ |
|
71 | + protected function generateClassmap(): string |
|
72 | + { |
|
73 | + |
|
74 | + // Hyphen used to match WordPress Coding Standards. |
|
75 | + $output_filename = "autoload-classmap.php"; |
|
76 | + |
|
77 | + $targetDirectory = getcwd() |
|
78 | + . DIRECTORY_SEPARATOR |
|
79 | + . ltrim($this->config->getTargetDirectory(), DIRECTORY_SEPARATOR); |
|
80 | + |
|
81 | + $dirs = array( |
|
82 | + $targetDirectory |
|
83 | + ); |
|
84 | + |
|
85 | + $dirname = ''; |
|
86 | + |
|
87 | + foreach ($dirs as $dir) { |
|
88 | + if (!is_dir($dir)) { |
|
89 | + continue; |
|
90 | + } |
|
91 | + |
|
92 | + $dirMap = ClassMapGenerator::createMap($dir); |
|
93 | + |
|
94 | + $dirname = preg_replace('/[^a-z]/i', '', str_replace(getcwd(), '', $dir)); |
|
95 | + |
|
96 | + array_walk( |
|
97 | + $dirMap, |
|
98 | + function (&$filepath, $_class) use ($dir, $dirname) { |
|
99 | + $filepath = "\${$dirname} . '" |
|
100 | + . DIRECTORY_SEPARATOR |
|
101 | + . ltrim(str_replace($dir, '', $filepath), DIRECTORY_SEPARATOR) . "'"; |
|
102 | + } |
|
103 | + ); |
|
104 | + |
|
105 | + ob_start(); |
|
106 | + |
|
107 | + echo "<?php\n\n"; |
|
108 | + echo "// {$output_filename} @generated by Strauss\n\n"; |
|
109 | + echo "\${$dirname} = dirname(__FILE__);\n\n"; |
|
110 | + echo "return array(\n"; |
|
111 | + foreach ($dirMap as $class => $file) { |
|
112 | + echo " '{$class}' => {$file},\n"; |
|
113 | + } |
|
114 | + echo ");"; |
|
115 | + |
|
116 | + file_put_contents($dir . $output_filename, ob_get_clean()); |
|
117 | + } |
|
118 | + |
|
119 | + return $dirname; |
|
120 | + } |
|
121 | + |
|
122 | + protected function generateFilesAutoloader() |
|
123 | + { |
|
124 | + |
|
125 | + // Hyphen used to match WordPress Coding Standards. |
|
126 | + $outputFilename = "autoload-files.php"; |
|
127 | + |
|
128 | + $filesAutoloaders = $this->discoveredFilesAutoloaders; |
|
129 | + |
|
130 | + if (empty($filesAutoloaders)) { |
|
131 | + return; |
|
132 | + } |
|
133 | + |
|
134 | + $targetDirectory = getcwd() |
|
135 | + . DIRECTORY_SEPARATOR |
|
136 | + . ltrim($this->config->getTargetDirectory(), DIRECTORY_SEPARATOR); |
|
91 | 137 | |
92 | - $dirMap = ClassMapGenerator::createMap($dir); |
|
138 | + $dirname = preg_replace('/[^a-z]/i', '', str_replace(getcwd(), '', $targetDirectory)); |
|
93 | 139 | |
94 | - $dirname = preg_replace('/[^a-z]/i', '', str_replace(getcwd(), '', $dir)); |
|
140 | + ob_start(); |
|
95 | 141 | |
96 | - array_walk( |
|
97 | - $dirMap, |
|
98 | - function (&$filepath, $_class) use ($dir, $dirname) { |
|
99 | - $filepath = "\${$dirname} . '" |
|
100 | - . DIRECTORY_SEPARATOR |
|
101 | - . ltrim(str_replace($dir, '', $filepath), DIRECTORY_SEPARATOR) . "'"; |
|
102 | - } |
|
103 | - ); |
|
104 | - |
|
105 | - ob_start(); |
|
142 | + echo "<?php\n\n"; |
|
143 | + echo "// {$outputFilename} @generated by Strauss\n"; |
|
144 | + echo "// @see https://github.com/BrianHenryIE/strauss/\n\n"; |
|
106 | 145 | |
107 | - echo "<?php\n\n"; |
|
108 | - echo "// {$output_filename} @generated by Strauss\n\n"; |
|
109 | - echo "\${$dirname} = dirname(__FILE__);\n\n"; |
|
110 | - echo "return array(\n"; |
|
111 | - foreach ($dirMap as $class => $file) { |
|
112 | - echo " '{$class}' => {$file},\n"; |
|
113 | - } |
|
114 | - echo ");"; |
|
146 | + foreach ($filesAutoloaders as $packagePath => $files) { |
|
147 | + foreach ($files as $file) { |
|
148 | + $filepath = DIRECTORY_SEPARATOR . $packagePath . DIRECTORY_SEPARATOR . $file; |
|
149 | + $filePathinfo = pathinfo(__DIR__ . $filepath); |
|
150 | + if (!isset($filePathinfo['extension']) || 'php' !== $filePathinfo['extension']) { |
|
151 | + continue; |
|
152 | + } |
|
153 | + echo "require_once __DIR__ . '{$filepath}';\n"; |
|
154 | + } |
|
155 | + } |
|
115 | 156 | |
116 | - file_put_contents($dir . $output_filename, ob_get_clean()); |
|
117 | - } |
|
157 | + file_put_contents($targetDirectory . $outputFilename, ob_get_clean()); |
|
158 | + } |
|
118 | 159 | |
119 | - return $dirname; |
|
120 | - } |
|
160 | + protected function generateAutoloadPhp() |
|
161 | + { |
|
121 | 162 | |
122 | - protected function generateFilesAutoloader() |
|
123 | - { |
|
124 | - |
|
125 | - // Hyphen used to match WordPress Coding Standards. |
|
126 | - $outputFilename = "autoload-files.php"; |
|
127 | - |
|
128 | - $filesAutoloaders = $this->discoveredFilesAutoloaders; |
|
129 | - |
|
130 | - if (empty($filesAutoloaders)) { |
|
131 | - return; |
|
132 | - } |
|
133 | - |
|
134 | - $targetDirectory = getcwd() |
|
135 | - . DIRECTORY_SEPARATOR |
|
136 | - . ltrim($this->config->getTargetDirectory(), DIRECTORY_SEPARATOR); |
|
137 | - |
|
138 | - $dirname = preg_replace('/[^a-z]/i', '', str_replace(getcwd(), '', $targetDirectory)); |
|
139 | - |
|
140 | - ob_start(); |
|
141 | - |
|
142 | - echo "<?php\n\n"; |
|
143 | - echo "// {$outputFilename} @generated by Strauss\n"; |
|
144 | - echo "// @see https://github.com/BrianHenryIE/strauss/\n\n"; |
|
145 | - |
|
146 | - foreach ($filesAutoloaders as $packagePath => $files) { |
|
147 | - foreach ($files as $file) { |
|
148 | - $filepath = DIRECTORY_SEPARATOR . $packagePath . DIRECTORY_SEPARATOR . $file; |
|
149 | - $filePathinfo = pathinfo(__DIR__ . $filepath); |
|
150 | - if (!isset($filePathinfo['extension']) || 'php' !== $filePathinfo['extension']) { |
|
151 | - continue; |
|
152 | - } |
|
153 | - echo "require_once __DIR__ . '{$filepath}';\n"; |
|
154 | - } |
|
155 | - } |
|
156 | - |
|
157 | - file_put_contents($targetDirectory . $outputFilename, ob_get_clean()); |
|
158 | - } |
|
159 | - |
|
160 | - protected function generateAutoloadPhp() |
|
161 | - { |
|
162 | - |
|
163 | - $autoloadPhp = <<<'EOD' |
|
163 | + $autoloadPhp = <<<'EOD' |
|
164 | 164 | <?php |
165 | 165 | // autoload.php @generated by Strauss |
166 | 166 | |
@@ -183,9 +183,9 @@ discard block |
||
183 | 183 | } |
184 | 184 | EOD; |
185 | 185 | |
186 | - $relativeFilepath = $this->config->getTargetDirectory() . 'autoload.php'; |
|
187 | - $absoluteFilepath = $this->workingDir . $relativeFilepath; |
|
186 | + $relativeFilepath = $this->config->getTargetDirectory() . 'autoload.php'; |
|
187 | + $absoluteFilepath = $this->workingDir . $relativeFilepath; |
|
188 | 188 | |
189 | - file_put_contents($absoluteFilepath, $autoloadPhp); |
|
190 | - } |
|
189 | + file_put_contents($absoluteFilepath, $autoloadPhp); |
|
190 | + } |
|
191 | 191 | } |
@@ -14,73 +14,73 @@ |
||
14 | 14 | class Cleanup |
15 | 15 | { |
16 | 16 | |
17 | - /** @var Filesystem */ |
|
18 | - protected Filesystem $filesystem; |
|
17 | + /** @var Filesystem */ |
|
18 | + protected Filesystem $filesystem; |
|
19 | 19 | |
20 | - protected bool $isDeleteVendorFiles; |
|
20 | + protected bool $isDeleteVendorFiles; |
|
21 | 21 | |
22 | - protected string $vendorDirectory = 'vendor'. DIRECTORY_SEPARATOR; |
|
22 | + protected string $vendorDirectory = 'vendor'. DIRECTORY_SEPARATOR; |
|
23 | 23 | |
24 | - public function __construct(StraussConfig $config, string $workingDir) |
|
25 | - { |
|
24 | + public function __construct(StraussConfig $config, string $workingDir) |
|
25 | + { |
|
26 | 26 | |
27 | - $this->isDeleteVendorFiles = $config->isDeleteVendorFiles(); |
|
27 | + $this->isDeleteVendorFiles = $config->isDeleteVendorFiles(); |
|
28 | 28 | |
29 | - $this->filesystem = new Filesystem(new Local($workingDir)); |
|
30 | - } |
|
31 | - |
|
32 | - /** |
|
33 | - * Maybe delete the source files that were copied (depending on config), |
|
34 | - * then delete empty directories. |
|
35 | - * |
|
36 | - * @param array $sourceFiles |
|
37 | - */ |
|
38 | - public function cleanup(array $sourceFiles) |
|
39 | - { |
|
40 | - |
|
41 | - // TODO Don't do this if vendor is the target dir (i.e. in-situ updating). |
|
42 | - |
|
43 | - if ($this->isDeleteVendorFiles) { |
|
44 | - foreach ($sourceFiles as $sourceFile) { |
|
45 | - $relativeFilepath = $this->vendorDirectory . $sourceFile; |
|
46 | - |
|
47 | - $this->filesystem->delete($relativeFilepath); |
|
48 | - } |
|
49 | - |
|
50 | - // Get the root folders of the moved files. |
|
51 | - $rootSourceDirectories = []; |
|
52 | - foreach ($sourceFiles as $sourceFile) { |
|
53 | - $arr = explode("/", $sourceFile, 2); |
|
54 | - $dir = $arr[0]; |
|
55 | - $rootSourceDirectories[ $dir ] = $dir; |
|
56 | - } |
|
57 | - $rootSourceDirectories = array_keys($rootSourceDirectories); |
|
58 | - |
|
59 | - |
|
60 | - $finder = new Finder(); |
|
61 | - |
|
62 | - foreach ($rootSourceDirectories as $rootSourceDirectory) { |
|
63 | - if (!is_dir($rootSourceDirectory) || is_link($rootSourceDirectory)) { |
|
64 | - continue; |
|
65 | - } |
|
66 | - |
|
67 | - $finder->directories()->path($rootSourceDirectory); |
|
68 | - |
|
69 | - foreach ($finder as $directory) { |
|
70 | - $metadata = $this->filesystem->getMetadata($directory); |
|
71 | - |
|
72 | - if ($this->dirIsEmpty($directory)) { |
|
73 | - $this->filesystem->deleteDir($directory); |
|
74 | - } |
|
75 | - } |
|
76 | - } |
|
77 | - } |
|
78 | - } |
|
79 | - |
|
80 | - // TODO: Use Symphony or Flysystem functions. |
|
81 | - protected function dirIsEmpty(string $dir): bool |
|
82 | - { |
|
83 | - $di = new RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS); |
|
84 | - return iterator_count($di) === 0; |
|
85 | - } |
|
29 | + $this->filesystem = new Filesystem(new Local($workingDir)); |
|
30 | + } |
|
31 | + |
|
32 | + /** |
|
33 | + * Maybe delete the source files that were copied (depending on config), |
|
34 | + * then delete empty directories. |
|
35 | + * |
|
36 | + * @param array $sourceFiles |
|
37 | + */ |
|
38 | + public function cleanup(array $sourceFiles) |
|
39 | + { |
|
40 | + |
|
41 | + // TODO Don't do this if vendor is the target dir (i.e. in-situ updating). |
|
42 | + |
|
43 | + if ($this->isDeleteVendorFiles) { |
|
44 | + foreach ($sourceFiles as $sourceFile) { |
|
45 | + $relativeFilepath = $this->vendorDirectory . $sourceFile; |
|
46 | + |
|
47 | + $this->filesystem->delete($relativeFilepath); |
|
48 | + } |
|
49 | + |
|
50 | + // Get the root folders of the moved files. |
|
51 | + $rootSourceDirectories = []; |
|
52 | + foreach ($sourceFiles as $sourceFile) { |
|
53 | + $arr = explode("/", $sourceFile, 2); |
|
54 | + $dir = $arr[0]; |
|
55 | + $rootSourceDirectories[ $dir ] = $dir; |
|
56 | + } |
|
57 | + $rootSourceDirectories = array_keys($rootSourceDirectories); |
|
58 | + |
|
59 | + |
|
60 | + $finder = new Finder(); |
|
61 | + |
|
62 | + foreach ($rootSourceDirectories as $rootSourceDirectory) { |
|
63 | + if (!is_dir($rootSourceDirectory) || is_link($rootSourceDirectory)) { |
|
64 | + continue; |
|
65 | + } |
|
66 | + |
|
67 | + $finder->directories()->path($rootSourceDirectory); |
|
68 | + |
|
69 | + foreach ($finder as $directory) { |
|
70 | + $metadata = $this->filesystem->getMetadata($directory); |
|
71 | + |
|
72 | + if ($this->dirIsEmpty($directory)) { |
|
73 | + $this->filesystem->deleteDir($directory); |
|
74 | + } |
|
75 | + } |
|
76 | + } |
|
77 | + } |
|
78 | + } |
|
79 | + |
|
80 | + // TODO: Use Symphony or Flysystem functions. |
|
81 | + protected function dirIsEmpty(string $dir): bool |
|
82 | + { |
|
83 | + $di = new RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS); |
|
84 | + return iterator_count($di) === 0; |
|
85 | + } |
|
86 | 86 | } |
@@ -16,180 +16,180 @@ |
||
16 | 16 | class FileEnumerator |
17 | 17 | { |
18 | 18 | |
19 | - /** |
|
20 | - * The only path variable with a leading slash. |
|
21 | - * All directories in project end with a slash. |
|
22 | - * |
|
23 | - * @var string |
|
24 | - */ |
|
25 | - protected string $workingDir; |
|
26 | - |
|
27 | - /** @var ComposerPackage[] */ |
|
28 | - protected array $dependencies; |
|
29 | - |
|
30 | - protected array $excludePackageNames = array(); |
|
31 | - protected array $excludeNamespaces = array(); |
|
32 | - protected array $excludeFilePatterns = array(); |
|
33 | - |
|
34 | - /** @var Filesystem */ |
|
35 | - protected Filesystem $filesystem; |
|
36 | - |
|
37 | - /** |
|
38 | - * Complete list of files specified in packages autoloaders. |
|
39 | - * |
|
40 | - * Relative filepaths as key, with their dependency as the value. |
|
41 | - * |
|
42 | - * Relative from vendor/ |
|
43 | - * |
|
44 | - * @var array<string, ComposerPackage> |
|
45 | - */ |
|
46 | - protected array $filesWithDependencies = []; |
|
47 | - |
|
48 | - /** |
|
49 | - * Record the files autolaoders for later use in building our own autoloader. |
|
50 | - * |
|
51 | - * @var array |
|
52 | - */ |
|
53 | - protected array $filesAutoloaders = []; |
|
54 | - |
|
55 | - /** |
|
56 | - * Copier constructor. |
|
57 | - * @param ComposerPackage[] $dependencies |
|
58 | - * @param string $workingDir |
|
59 | - */ |
|
60 | - public function __construct( |
|
61 | - array $dependencies, |
|
62 | - string $workingDir, |
|
63 | - StraussConfig $config |
|
64 | - ) { |
|
65 | - $this->workingDir = $workingDir; |
|
66 | - |
|
67 | - $this->dependencies = $dependencies; |
|
68 | - |
|
69 | - $this->excludeNamespaces = $config->getExcludeNamespacesFromCopy(); |
|
70 | - $this->excludePackageNames = $config->getExcludePackagesFromCopy(); |
|
71 | - $this->excludeFilePatterns = $config->getExcludeFilePatternsFromCopy(); |
|
72 | - |
|
73 | - $this->filesystem = new Filesystem(new Local($this->workingDir)); |
|
74 | - } |
|
75 | - |
|
76 | - /** |
|
77 | - * Read the autoload keys of the dependencies and generate a list of the files referenced. |
|
78 | - */ |
|
79 | - public function compileFileList() |
|
80 | - { |
|
81 | - |
|
82 | - // TODO: read 'vendor' from composer.json. |
|
83 | - $prefixToRemove = $this->workingDir .'vendor'. DIRECTORY_SEPARATOR; |
|
84 | - |
|
85 | - foreach ($this->dependencies as $dependency) { |
|
86 | - if (in_array($dependency->getName(), $this->excludePackageNames)) { |
|
87 | - continue; |
|
88 | - } |
|
89 | - |
|
90 | - $packagePath = $this->workingDir . 'vendor' . DIRECTORY_SEPARATOR |
|
91 | - . $dependency->getPath() . DIRECTORY_SEPARATOR; |
|
92 | - |
|
93 | - /** |
|
94 | - * Where $dependency->autoload is ~ |
|
95 | - * |
|
96 | - * [ "psr-4" => [ "BrianHenryIE\Strauss" => "src" ] ] |
|
97 | - * Exclude "exclude-from-classmap" |
|
98 | - * @see https://getcomposer.org/doc/04-schema.md#exclude-files-from-classmaps |
|
99 | - */ |
|
100 | - $autoloaders = array_filter($dependency->getAutoload(), function ($type) { |
|
101 | - return 'exclude-from-classmap' !== $type; |
|
102 | - }, ARRAY_FILTER_USE_KEY); |
|
103 | - |
|
104 | - foreach ($autoloaders as $type => $value) { |
|
105 | - // Might have to switch/case here. |
|
106 | - |
|
107 | - if ('files' === $type) { |
|
108 | - $this->filesAutoloaders[$dependency->getPath()] = $value; |
|
109 | - } |
|
110 | - |
|
111 | - foreach ($value as $namespace => $namespace_relative_path) { |
|
112 | - if (!empty($namespace) && in_array($namespace, $this->excludeNamespaces)) { |
|
113 | - continue; |
|
114 | - } |
|
115 | - |
|
116 | - if (is_file($packagePath . $namespace_relative_path)) { |
|
117 | - // $finder->files()->name($file)->in($source_path); |
|
118 | - |
|
119 | - $relativeFilepath = str_replace($prefixToRemove, '', $packagePath . $namespace_relative_path); |
|
120 | - $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
19 | + /** |
|
20 | + * The only path variable with a leading slash. |
|
21 | + * All directories in project end with a slash. |
|
22 | + * |
|
23 | + * @var string |
|
24 | + */ |
|
25 | + protected string $workingDir; |
|
26 | + |
|
27 | + /** @var ComposerPackage[] */ |
|
28 | + protected array $dependencies; |
|
29 | + |
|
30 | + protected array $excludePackageNames = array(); |
|
31 | + protected array $excludeNamespaces = array(); |
|
32 | + protected array $excludeFilePatterns = array(); |
|
33 | + |
|
34 | + /** @var Filesystem */ |
|
35 | + protected Filesystem $filesystem; |
|
36 | + |
|
37 | + /** |
|
38 | + * Complete list of files specified in packages autoloaders. |
|
39 | + * |
|
40 | + * Relative filepaths as key, with their dependency as the value. |
|
41 | + * |
|
42 | + * Relative from vendor/ |
|
43 | + * |
|
44 | + * @var array<string, ComposerPackage> |
|
45 | + */ |
|
46 | + protected array $filesWithDependencies = []; |
|
47 | + |
|
48 | + /** |
|
49 | + * Record the files autolaoders for later use in building our own autoloader. |
|
50 | + * |
|
51 | + * @var array |
|
52 | + */ |
|
53 | + protected array $filesAutoloaders = []; |
|
54 | + |
|
55 | + /** |
|
56 | + * Copier constructor. |
|
57 | + * @param ComposerPackage[] $dependencies |
|
58 | + * @param string $workingDir |
|
59 | + */ |
|
60 | + public function __construct( |
|
61 | + array $dependencies, |
|
62 | + string $workingDir, |
|
63 | + StraussConfig $config |
|
64 | + ) { |
|
65 | + $this->workingDir = $workingDir; |
|
66 | + |
|
67 | + $this->dependencies = $dependencies; |
|
68 | + |
|
69 | + $this->excludeNamespaces = $config->getExcludeNamespacesFromCopy(); |
|
70 | + $this->excludePackageNames = $config->getExcludePackagesFromCopy(); |
|
71 | + $this->excludeFilePatterns = $config->getExcludeFilePatternsFromCopy(); |
|
72 | + |
|
73 | + $this->filesystem = new Filesystem(new Local($this->workingDir)); |
|
74 | + } |
|
75 | + |
|
76 | + /** |
|
77 | + * Read the autoload keys of the dependencies and generate a list of the files referenced. |
|
78 | + */ |
|
79 | + public function compileFileList() |
|
80 | + { |
|
81 | + |
|
82 | + // TODO: read 'vendor' from composer.json. |
|
83 | + $prefixToRemove = $this->workingDir .'vendor'. DIRECTORY_SEPARATOR; |
|
84 | + |
|
85 | + foreach ($this->dependencies as $dependency) { |
|
86 | + if (in_array($dependency->getName(), $this->excludePackageNames)) { |
|
87 | + continue; |
|
88 | + } |
|
89 | + |
|
90 | + $packagePath = $this->workingDir . 'vendor' . DIRECTORY_SEPARATOR |
|
91 | + . $dependency->getPath() . DIRECTORY_SEPARATOR; |
|
92 | + |
|
93 | + /** |
|
94 | + * Where $dependency->autoload is ~ |
|
95 | + * |
|
96 | + * [ "psr-4" => [ "BrianHenryIE\Strauss" => "src" ] ] |
|
97 | + * Exclude "exclude-from-classmap" |
|
98 | + * @see https://getcomposer.org/doc/04-schema.md#exclude-files-from-classmaps |
|
99 | + */ |
|
100 | + $autoloaders = array_filter($dependency->getAutoload(), function ($type) { |
|
101 | + return 'exclude-from-classmap' !== $type; |
|
102 | + }, ARRAY_FILTER_USE_KEY); |
|
103 | + |
|
104 | + foreach ($autoloaders as $type => $value) { |
|
105 | + // Might have to switch/case here. |
|
106 | + |
|
107 | + if ('files' === $type) { |
|
108 | + $this->filesAutoloaders[$dependency->getPath()] = $value; |
|
109 | + } |
|
110 | + |
|
111 | + foreach ($value as $namespace => $namespace_relative_path) { |
|
112 | + if (!empty($namespace) && in_array($namespace, $this->excludeNamespaces)) { |
|
113 | + continue; |
|
114 | + } |
|
115 | + |
|
116 | + if (is_file($packagePath . $namespace_relative_path)) { |
|
117 | + // $finder->files()->name($file)->in($source_path); |
|
118 | + |
|
119 | + $relativeFilepath = str_replace($prefixToRemove, '', $packagePath . $namespace_relative_path); |
|
120 | + $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
121 | 121 | |
122 | - $this->filesWithDependencies[$relativeFilepath] = $dependency; |
|
123 | - |
|
124 | - continue; |
|
125 | - } |
|
126 | - |
|
127 | - // else it is a directory. |
|
128 | - |
|
129 | - // trailingslashit(). |
|
130 | - $namespace_relative_path = rtrim($namespace_relative_path, DIRECTORY_SEPARATOR) |
|
131 | - . DIRECTORY_SEPARATOR; |
|
132 | - |
|
133 | - $sourcePath = $packagePath . $namespace_relative_path; |
|
134 | - |
|
135 | - // trailingslashit(). (to remove duplicates). |
|
136 | - $sourcePath = rtrim($sourcePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
137 | - |
|
138 | - $finder = new Finder(); |
|
139 | - $finder->files()->in($sourcePath)->followLinks(); |
|
140 | - |
|
141 | - foreach ($finder as $foundFile) { |
|
142 | - $filePath = $foundFile->getPathname(); |
|
143 | - |
|
144 | - $relativeFilepath = str_replace($prefixToRemove, '', $filePath); |
|
145 | - |
|
146 | - // TODO: Is this needed here?! If anything, it's the prefix that needs to be normalised a few |
|
147 | - // lines above before being used. |
|
148 | - // Replace multiple \ and/or / with OS native DIRECTORY_SEPARATOR. |
|
149 | - $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
150 | - |
|
151 | - foreach ($this->excludeFilePatterns as $excludePattern) { |
|
152 | - if (1 === preg_match($excludePattern, $relativeFilepath)) { |
|
153 | - continue 2; |
|
154 | - } |
|
155 | - } |
|
156 | - |
|
157 | - $this->filesWithDependencies[$relativeFilepath] = $dependency; |
|
158 | - } |
|
159 | - } |
|
160 | - } |
|
161 | - } |
|
162 | - } |
|
163 | - |
|
164 | - /** |
|
165 | - * Returns all found files. |
|
166 | - * |
|
167 | - * @return array<string, ComposerPackage> |
|
168 | - */ |
|
169 | - public function getAllFilesAndDependencyList(): array |
|
170 | - { |
|
171 | - return $this->filesWithDependencies; |
|
172 | - } |
|
173 | - |
|
174 | - /** |
|
175 | - * Returns found PHP files. |
|
176 | - * |
|
177 | - * @return array<string, ComposerPackage> |
|
178 | - */ |
|
179 | - public function getPhpFilesAndDependencyList(): array |
|
180 | - { |
|
181 | - return array_filter($this->filesWithDependencies, function ($value, $key) { |
|
182 | - return false !== strpos($key, '.php'); |
|
183 | - }, ARRAY_FILTER_USE_BOTH); |
|
184 | - } |
|
185 | - |
|
186 | - /** |
|
187 | - * Get the recorded files autoloaders. |
|
188 | - * |
|
189 | - * @return array<string, array<string>> |
|
190 | - */ |
|
191 | - public function getFilesAutoloaders(): array |
|
192 | - { |
|
193 | - return $this->filesAutoloaders; |
|
194 | - } |
|
122 | + $this->filesWithDependencies[$relativeFilepath] = $dependency; |
|
123 | + |
|
124 | + continue; |
|
125 | + } |
|
126 | + |
|
127 | + // else it is a directory. |
|
128 | + |
|
129 | + // trailingslashit(). |
|
130 | + $namespace_relative_path = rtrim($namespace_relative_path, DIRECTORY_SEPARATOR) |
|
131 | + . DIRECTORY_SEPARATOR; |
|
132 | + |
|
133 | + $sourcePath = $packagePath . $namespace_relative_path; |
|
134 | + |
|
135 | + // trailingslashit(). (to remove duplicates). |
|
136 | + $sourcePath = rtrim($sourcePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
|
137 | + |
|
138 | + $finder = new Finder(); |
|
139 | + $finder->files()->in($sourcePath)->followLinks(); |
|
140 | + |
|
141 | + foreach ($finder as $foundFile) { |
|
142 | + $filePath = $foundFile->getPathname(); |
|
143 | + |
|
144 | + $relativeFilepath = str_replace($prefixToRemove, '', $filePath); |
|
145 | + |
|
146 | + // TODO: Is this needed here?! If anything, it's the prefix that needs to be normalised a few |
|
147 | + // lines above before being used. |
|
148 | + // Replace multiple \ and/or / with OS native DIRECTORY_SEPARATOR. |
|
149 | + $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
150 | + |
|
151 | + foreach ($this->excludeFilePatterns as $excludePattern) { |
|
152 | + if (1 === preg_match($excludePattern, $relativeFilepath)) { |
|
153 | + continue 2; |
|
154 | + } |
|
155 | + } |
|
156 | + |
|
157 | + $this->filesWithDependencies[$relativeFilepath] = $dependency; |
|
158 | + } |
|
159 | + } |
|
160 | + } |
|
161 | + } |
|
162 | + } |
|
163 | + |
|
164 | + /** |
|
165 | + * Returns all found files. |
|
166 | + * |
|
167 | + * @return array<string, ComposerPackage> |
|
168 | + */ |
|
169 | + public function getAllFilesAndDependencyList(): array |
|
170 | + { |
|
171 | + return $this->filesWithDependencies; |
|
172 | + } |
|
173 | + |
|
174 | + /** |
|
175 | + * Returns found PHP files. |
|
176 | + * |
|
177 | + * @return array<string, ComposerPackage> |
|
178 | + */ |
|
179 | + public function getPhpFilesAndDependencyList(): array |
|
180 | + { |
|
181 | + return array_filter($this->filesWithDependencies, function ($value, $key) { |
|
182 | + return false !== strpos($key, '.php'); |
|
183 | + }, ARRAY_FILTER_USE_BOTH); |
|
184 | + } |
|
185 | + |
|
186 | + /** |
|
187 | + * Get the recorded files autoloaders. |
|
188 | + * |
|
189 | + * @return array<string, array<string>> |
|
190 | + */ |
|
191 | + public function getFilesAutoloaders(): array |
|
192 | + { |
|
193 | + return $this->filesAutoloaders; |
|
194 | + } |
|
195 | 195 | } |
@@ -25,170 +25,170 @@ discard block |
||
25 | 25 | class Licenser |
26 | 26 | { |
27 | 27 | |
28 | - protected string $workingDir; |
|
28 | + protected string $workingDir; |
|
29 | 29 | |
30 | - protected array $dependencies; |
|
30 | + protected array $dependencies; |
|
31 | 31 | |
32 | - // The author of the current project who is running Strauss to make the changes to the required libraries. |
|
33 | - protected string $author; |
|
32 | + // The author of the current project who is running Strauss to make the changes to the required libraries. |
|
33 | + protected string $author; |
|
34 | 34 | |
35 | - protected string $targetDirectory; |
|
35 | + protected string $targetDirectory; |
|
36 | 36 | |
37 | - /** |
|
38 | - * An array of files relative to the project vendor folder. |
|
39 | - * |
|
40 | - * @var string[] |
|
41 | - */ |
|
42 | - protected array $discoveredLicenseFiles = array(); |
|
37 | + /** |
|
38 | + * An array of files relative to the project vendor folder. |
|
39 | + * |
|
40 | + * @var string[] |
|
41 | + */ |
|
42 | + protected array $discoveredLicenseFiles = array(); |
|
43 | 43 | |
44 | - /** @var Filesystem */ |
|
45 | - protected $filesystem; |
|
44 | + /** @var Filesystem */ |
|
45 | + protected $filesystem; |
|
46 | 46 | |
47 | 47 | |
48 | - /** |
|
49 | - * Licenser constructor. |
|
50 | - * @param string $workingDir |
|
51 | - * @param array $dependencies Whose folders are searched for existing license.txt files. |
|
52 | - * @param string $author To add to each modified file's header |
|
53 | - */ |
|
54 | - public function __construct(StraussConfig $config, string $workingDir, array $dependencies, string $author) |
|
55 | - { |
|
56 | - $this->workingDir = $workingDir; |
|
57 | - $this->dependencies = $dependencies; |
|
58 | - $this->author = $author; |
|
48 | + /** |
|
49 | + * Licenser constructor. |
|
50 | + * @param string $workingDir |
|
51 | + * @param array $dependencies Whose folders are searched for existing license.txt files. |
|
52 | + * @param string $author To add to each modified file's header |
|
53 | + */ |
|
54 | + public function __construct(StraussConfig $config, string $workingDir, array $dependencies, string $author) |
|
55 | + { |
|
56 | + $this->workingDir = $workingDir; |
|
57 | + $this->dependencies = $dependencies; |
|
58 | + $this->author = $author; |
|
59 | 59 | |
60 | - $this->targetDirectory = $config->getTargetDirectory(); |
|
60 | + $this->targetDirectory = $config->getTargetDirectory(); |
|
61 | 61 | |
62 | - $this->filesystem = new Filesystem(new Local($workingDir)); |
|
63 | - } |
|
62 | + $this->filesystem = new Filesystem(new Local($workingDir)); |
|
63 | + } |
|
64 | 64 | |
65 | - public function copyLicenses(): void |
|
66 | - { |
|
67 | - $this->findLicenseFiles(); |
|
68 | - |
|
69 | - foreach ($this->getDiscoveredLicenseFiles() as $licenseFile) { |
|
70 | - $targetLicenseFile = $this->targetDirectory . $licenseFile; |
|
71 | - |
|
72 | - $targetLicenseFileDir = dirname($targetLicenseFile); |
|
73 | - |
|
74 | - // Don't try copy it if it's already there. |
|
75 | - if ($this->filesystem->has($targetLicenseFile)) { |
|
76 | - continue; |
|
77 | - } |
|
78 | - |
|
79 | - // Don't add licenses to non-existent directories – there were no files copied there! |
|
80 | - if (! $this->filesystem->has($targetLicenseFileDir)) { |
|
81 | - continue; |
|
82 | - } |
|
83 | - |
|
84 | - $this->filesystem->copy( |
|
85 | - 'vendor' . DIRECTORY_SEPARATOR . $licenseFile, |
|
86 | - $targetLicenseFile |
|
87 | - ); |
|
88 | - } |
|
89 | - } |
|
90 | - |
|
91 | - |
|
92 | - /** |
|
93 | - * @see https://www.phpliveregex.com/p/A5y |
|
94 | - */ |
|
95 | - public function findLicenseFiles(?Finder $finder = null) |
|
96 | - { |
|
97 | - |
|
98 | - // Include all license files in the dependency path. |
|
99 | - $finder = $finder ?? new Finder(); |
|
100 | - |
|
101 | - // TODO: read 'vendor' from composer.json. |
|
102 | - $prefixToRemove = 'vendor' . DIRECTORY_SEPARATOR; |
|
103 | - |
|
104 | - /** @var ComposerPackage $dependency */ |
|
105 | - foreach ($this->dependencies as $dependency) { |
|
106 | - $packagePath = 'vendor' . DIRECTORY_SEPARATOR . $dependency->getPath(); |
|
107 | - |
|
108 | - // If packages happen to have their vendor dir, i.e. locally required packages, don't included the licenses |
|
109 | - // from their vendor dir (they should be included otherwise anyway). |
|
110 | - // $dependency->getVendorDir() |
|
111 | - $finder->files()->in($packagePath)->followLinks()->exclude(array( 'vendor' ))->name('/^.*licen.e.*/i'); |
|
112 | - |
|
113 | - /** @var \SplFileInfo $foundFile */ |
|
114 | - foreach ($finder as $foundFile) { |
|
115 | - $filePath = $foundFile->getPathname(); |
|
116 | - |
|
117 | - $relativeFilepath = str_replace($prefixToRemove, '', $filePath); |
|
118 | - |
|
119 | - // Replace multiple \ and/or / with OS native DIRECTORY_SEPARATOR. |
|
120 | - $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
121 | - |
|
122 | - $this->discoveredLicenseFiles[$relativeFilepath] = $dependency->getName(); |
|
123 | - } |
|
124 | - } |
|
125 | - } |
|
126 | - |
|
127 | - public function getDiscoveredLicenseFiles(): array |
|
128 | - { |
|
129 | - return array_keys($this->discoveredLicenseFiles); |
|
130 | - } |
|
131 | - |
|
132 | - /** |
|
133 | - * @param array<string, ComposerPackage> $modifiedFiles |
|
134 | - */ |
|
135 | - public function addInformationToUpdatedFiles(array $modifiedFiles) |
|
136 | - { |
|
137 | - |
|
138 | - // e.g. "25-April-2021". |
|
139 | - $date = gmdate("d-F-Y", time()); |
|
140 | - |
|
141 | - foreach ($modifiedFiles as $relativeFilePath => $package) { |
|
142 | - $filepath = $this->targetDirectory . $relativeFilePath; |
|
143 | - |
|
144 | - $packageLicense = $package->getLicense(); |
|
145 | - |
|
146 | - // Throws an exception, but unlikely to happen. |
|
147 | - $contents = $this->filesystem->read($filepath); |
|
148 | - |
|
149 | - $updatedContents = $this->addChangeDeclarationToPhpString($contents, $date, $packageLicense); |
|
150 | - |
|
151 | - if ($updatedContents !== $contents) { |
|
152 | - $this->filesystem->put($filepath, $updatedContents); |
|
153 | - } |
|
154 | - } |
|
155 | - } |
|
156 | - |
|
157 | - /** |
|
158 | - * Given a php file as a string, edit it's header phpdoc, or add a header, to include: |
|
159 | - * |
|
160 | - * "Modified by {author} on {date} using Strauss. |
|
161 | - * @see https://github.com/BrianHenryIE/strauss" |
|
162 | - * |
|
163 | - * Should probably include the original license in each file since it'll often be a mix, with the parent |
|
164 | - * project often being a GPL WordPress plugin. |
|
165 | - * |
|
166 | - * Find the string between the end of php-opener and the first valid code. |
|
167 | - * First valid code will be a line whose first non-whitespace character is not / or * ?... NO! |
|
168 | - * If the first non whitespace string after php-opener is multiline-comment-opener, find the |
|
169 | - * closing multiline-comment-closer |
|
170 | - * / If there's already a comment, work within that comment |
|
171 | - * If there is no mention in the header of the license already, add it. |
|
172 | - * Add a note that changes have been made. |
|
173 | - * |
|
174 | - * @param string $phpString Code. |
|
175 | - */ |
|
176 | - public function addChangeDeclarationToPhpString( |
|
177 | - string $phpString, |
|
178 | - string $modifiedDate, |
|
179 | - string $packageLicense |
|
180 | - ) : string { |
|
181 | - |
|
182 | - $author = $this->author; |
|
183 | - |
|
184 | - $licenseDeclaration = "@license {$packageLicense}"; |
|
185 | - $modifiedDeclaration = "Modified by {$author} on {$modifiedDate} using Strauss."; |
|
186 | - $straussLink = "@see https://github.com/BrianHenryIE/strauss"; |
|
187 | - |
|
188 | - // php-open followed by some whitespace and new line until the first ... |
|
189 | - $noCommentBetweenPhpOpenAndFirstCodePattern = '~<\?php[\s\n]*[\w\\\?]+~'; |
|
190 | - |
|
191 | - $multilineCommentCapturePattern = ' |
|
65 | + public function copyLicenses(): void |
|
66 | + { |
|
67 | + $this->findLicenseFiles(); |
|
68 | + |
|
69 | + foreach ($this->getDiscoveredLicenseFiles() as $licenseFile) { |
|
70 | + $targetLicenseFile = $this->targetDirectory . $licenseFile; |
|
71 | + |
|
72 | + $targetLicenseFileDir = dirname($targetLicenseFile); |
|
73 | + |
|
74 | + // Don't try copy it if it's already there. |
|
75 | + if ($this->filesystem->has($targetLicenseFile)) { |
|
76 | + continue; |
|
77 | + } |
|
78 | + |
|
79 | + // Don't add licenses to non-existent directories – there were no files copied there! |
|
80 | + if (! $this->filesystem->has($targetLicenseFileDir)) { |
|
81 | + continue; |
|
82 | + } |
|
83 | + |
|
84 | + $this->filesystem->copy( |
|
85 | + 'vendor' . DIRECTORY_SEPARATOR . $licenseFile, |
|
86 | + $targetLicenseFile |
|
87 | + ); |
|
88 | + } |
|
89 | + } |
|
90 | + |
|
91 | + |
|
92 | + /** |
|
93 | + * @see https://www.phpliveregex.com/p/A5y |
|
94 | + */ |
|
95 | + public function findLicenseFiles(?Finder $finder = null) |
|
96 | + { |
|
97 | + |
|
98 | + // Include all license files in the dependency path. |
|
99 | + $finder = $finder ?? new Finder(); |
|
100 | + |
|
101 | + // TODO: read 'vendor' from composer.json. |
|
102 | + $prefixToRemove = 'vendor' . DIRECTORY_SEPARATOR; |
|
103 | + |
|
104 | + /** @var ComposerPackage $dependency */ |
|
105 | + foreach ($this->dependencies as $dependency) { |
|
106 | + $packagePath = 'vendor' . DIRECTORY_SEPARATOR . $dependency->getPath(); |
|
107 | + |
|
108 | + // If packages happen to have their vendor dir, i.e. locally required packages, don't included the licenses |
|
109 | + // from their vendor dir (they should be included otherwise anyway). |
|
110 | + // $dependency->getVendorDir() |
|
111 | + $finder->files()->in($packagePath)->followLinks()->exclude(array( 'vendor' ))->name('/^.*licen.e.*/i'); |
|
112 | + |
|
113 | + /** @var \SplFileInfo $foundFile */ |
|
114 | + foreach ($finder as $foundFile) { |
|
115 | + $filePath = $foundFile->getPathname(); |
|
116 | + |
|
117 | + $relativeFilepath = str_replace($prefixToRemove, '', $filePath); |
|
118 | + |
|
119 | + // Replace multiple \ and/or / with OS native DIRECTORY_SEPARATOR. |
|
120 | + $relativeFilepath = preg_replace('#[\\\/]+#', DIRECTORY_SEPARATOR, $relativeFilepath); |
|
121 | + |
|
122 | + $this->discoveredLicenseFiles[$relativeFilepath] = $dependency->getName(); |
|
123 | + } |
|
124 | + } |
|
125 | + } |
|
126 | + |
|
127 | + public function getDiscoveredLicenseFiles(): array |
|
128 | + { |
|
129 | + return array_keys($this->discoveredLicenseFiles); |
|
130 | + } |
|
131 | + |
|
132 | + /** |
|
133 | + * @param array<string, ComposerPackage> $modifiedFiles |
|
134 | + */ |
|
135 | + public function addInformationToUpdatedFiles(array $modifiedFiles) |
|
136 | + { |
|
137 | + |
|
138 | + // e.g. "25-April-2021". |
|
139 | + $date = gmdate("d-F-Y", time()); |
|
140 | + |
|
141 | + foreach ($modifiedFiles as $relativeFilePath => $package) { |
|
142 | + $filepath = $this->targetDirectory . $relativeFilePath; |
|
143 | + |
|
144 | + $packageLicense = $package->getLicense(); |
|
145 | + |
|
146 | + // Throws an exception, but unlikely to happen. |
|
147 | + $contents = $this->filesystem->read($filepath); |
|
148 | + |
|
149 | + $updatedContents = $this->addChangeDeclarationToPhpString($contents, $date, $packageLicense); |
|
150 | + |
|
151 | + if ($updatedContents !== $contents) { |
|
152 | + $this->filesystem->put($filepath, $updatedContents); |
|
153 | + } |
|
154 | + } |
|
155 | + } |
|
156 | + |
|
157 | + /** |
|
158 | + * Given a php file as a string, edit it's header phpdoc, or add a header, to include: |
|
159 | + * |
|
160 | + * "Modified by {author} on {date} using Strauss. |
|
161 | + * @see https://github.com/BrianHenryIE/strauss" |
|
162 | + * |
|
163 | + * Should probably include the original license in each file since it'll often be a mix, with the parent |
|
164 | + * project often being a GPL WordPress plugin. |
|
165 | + * |
|
166 | + * Find the string between the end of php-opener and the first valid code. |
|
167 | + * First valid code will be a line whose first non-whitespace character is not / or * ?... NO! |
|
168 | + * If the first non whitespace string after php-opener is multiline-comment-opener, find the |
|
169 | + * closing multiline-comment-closer |
|
170 | + * / If there's already a comment, work within that comment |
|
171 | + * If there is no mention in the header of the license already, add it. |
|
172 | + * Add a note that changes have been made. |
|
173 | + * |
|
174 | + * @param string $phpString Code. |
|
175 | + */ |
|
176 | + public function addChangeDeclarationToPhpString( |
|
177 | + string $phpString, |
|
178 | + string $modifiedDate, |
|
179 | + string $packageLicense |
|
180 | + ) : string { |
|
181 | + |
|
182 | + $author = $this->author; |
|
183 | + |
|
184 | + $licenseDeclaration = "@license {$packageLicense}"; |
|
185 | + $modifiedDeclaration = "Modified by {$author} on {$modifiedDate} using Strauss."; |
|
186 | + $straussLink = "@see https://github.com/BrianHenryIE/strauss"; |
|
187 | + |
|
188 | + // php-open followed by some whitespace and new line until the first ... |
|
189 | + $noCommentBetweenPhpOpenAndFirstCodePattern = '~<\?php[\s\n]*[\w\\\?]+~'; |
|
190 | + |
|
191 | + $multilineCommentCapturePattern = ' |
|
192 | 192 | ~ # Start the pattern |
193 | 193 | ( |
194 | 194 | <\?php[\S\s]* # match the beginning of the files php-open and following whitespace |
@@ -202,51 +202,51 @@ discard block |
||
202 | 202 | ~Ux'; // U: Non-greedy matching, x: ignore whitespace in pattern. |
203 | 203 | |
204 | 204 | |
205 | - $replaceInMultilineCommentFunction = function ($matches) use ( |
|
206 | - $licenseDeclaration, |
|
207 | - $modifiedDeclaration, |
|
208 | - $straussLink |
|
209 | - ) { |
|
205 | + $replaceInMultilineCommentFunction = function ($matches) use ( |
|
206 | + $licenseDeclaration, |
|
207 | + $modifiedDeclaration, |
|
208 | + $straussLink |
|
209 | + ) { |
|
210 | 210 | |
211 | - // Find the line prefix and use it, i.e. could be none, asterisk or space-asterisk. |
|
212 | - $commentLines = explode("\n", $matches[2]); |
|
211 | + // Find the line prefix and use it, i.e. could be none, asterisk or space-asterisk. |
|
212 | + $commentLines = explode("\n", $matches[2]); |
|
213 | 213 | |
214 | - if (isset($commentLines[1])&& 1 === preg_match('/^([\s\\\*]*)/', $commentLines[1], $output_array)) { |
|
215 | - $lineStart = $output_array[1]; |
|
216 | - } else { |
|
217 | - $lineStart = ' * '; |
|
218 | - } |
|
214 | + if (isset($commentLines[1])&& 1 === preg_match('/^([\s\\\*]*)/', $commentLines[1], $output_array)) { |
|
215 | + $lineStart = $output_array[1]; |
|
216 | + } else { |
|
217 | + $lineStart = ' * '; |
|
218 | + } |
|
219 | 219 | |
220 | - $appendString = "*\n"; |
|
220 | + $appendString = "*\n"; |
|
221 | 221 | |
222 | - // If the license is not already specified in the header, add it. |
|
223 | - if (false === strpos($matches[2], 'licen')) { |
|
224 | - $appendString .= "{$lineStart}{$licenseDeclaration}\n"; |
|
225 | - } |
|
222 | + // If the license is not already specified in the header, add it. |
|
223 | + if (false === strpos($matches[2], 'licen')) { |
|
224 | + $appendString .= "{$lineStart}{$licenseDeclaration}\n"; |
|
225 | + } |
|
226 | 226 | |
227 | - $appendString .= "{$lineStart}{$modifiedDeclaration}\n"; |
|
228 | - $appendString .= "{$lineStart}{$straussLink}\n"; |
|
227 | + $appendString .= "{$lineStart}{$modifiedDeclaration}\n"; |
|
228 | + $appendString .= "{$lineStart}{$straussLink}\n"; |
|
229 | 229 | |
230 | - $commentEnd = rtrim(rtrim($lineStart, ' '), '*').'*/'; |
|
230 | + $commentEnd = rtrim(rtrim($lineStart, ' '), '*').'*/'; |
|
231 | 231 | |
232 | - $replaceWith = $matches[1] . $matches[2] . $appendString . $commentEnd; |
|
232 | + $replaceWith = $matches[1] . $matches[2] . $appendString . $commentEnd; |
|
233 | 233 | |
234 | - return $replaceWith; |
|
235 | - }; |
|
234 | + return $replaceWith; |
|
235 | + }; |
|
236 | 236 | |
237 | - // If it's a simple case where there is no existing header, add the existing license. |
|
238 | - if (1 === preg_match($noCommentBetweenPhpOpenAndFirstCodePattern, $phpString)) { |
|
239 | - $modifiedComment = "/**\n * {$licenseDeclaration}\n *\n * {$modifiedDeclaration}\n * {$straussLink}\n */"; |
|
240 | - $updatedPhpString = preg_replace('~<\?php~', "<?php\n". $modifiedComment, $phpString, 1); |
|
241 | - } else { |
|
242 | - $updatedPhpString = preg_replace_callback( |
|
243 | - $multilineCommentCapturePattern, |
|
244 | - $replaceInMultilineCommentFunction, |
|
245 | - $phpString, |
|
246 | - 1 |
|
247 | - ); |
|
248 | - } |
|
237 | + // If it's a simple case where there is no existing header, add the existing license. |
|
238 | + if (1 === preg_match($noCommentBetweenPhpOpenAndFirstCodePattern, $phpString)) { |
|
239 | + $modifiedComment = "/**\n * {$licenseDeclaration}\n *\n * {$modifiedDeclaration}\n * {$straussLink}\n */"; |
|
240 | + $updatedPhpString = preg_replace('~<\?php~', "<?php\n". $modifiedComment, $phpString, 1); |
|
241 | + } else { |
|
242 | + $updatedPhpString = preg_replace_callback( |
|
243 | + $multilineCommentCapturePattern, |
|
244 | + $replaceInMultilineCommentFunction, |
|
245 | + $phpString, |
|
246 | + 1 |
|
247 | + ); |
|
248 | + } |
|
249 | 249 | |
250 | - return $updatedPhpString; |
|
251 | - } |
|
250 | + return $updatedPhpString; |
|
251 | + } |
|
252 | 252 | } |
@@ -11,118 +11,118 @@ discard block |
||
11 | 11 | |
12 | 12 | class Prefixer |
13 | 13 | { |
14 | - /** @var StraussConfig */ |
|
15 | - protected $config; |
|
16 | - |
|
17 | - /** @var Filesystem */ |
|
18 | - protected $filesystem; |
|
19 | - |
|
20 | - protected string $targetDirectory; |
|
21 | - protected string $namespacePrefix; |
|
22 | - protected string $classmapPrefix; |
|
23 | - |
|
24 | - protected array $excludePackageNamesFromPrefixing; |
|
25 | - protected array $excludeNamespacesFromPrefixing; |
|
26 | - protected array $excludeFilePatternsFromPrefixing; |
|
27 | - |
|
28 | - /** @var array<string, ComposerPackage> */ |
|
29 | - protected array $changedFiles = array(); |
|
30 | - |
|
31 | - public function __construct(StraussConfig $config, string $workingDir) |
|
32 | - { |
|
33 | - $this->filesystem = new Filesystem(new Local($workingDir)); |
|
34 | - |
|
35 | - $this->targetDirectory = $config->getTargetDirectory(); |
|
36 | - $this->namespacePrefix = $config->getNamespacePrefix(); |
|
37 | - $this->classmapPrefix = $config->getClassmapPrefix(); |
|
38 | - |
|
39 | - $this->excludePackageNamesFromPrefixing = $config->getExcludePackagesFromPrefixing(); |
|
40 | - $this->excludeNamespacesFromPrefixing = $config->getExcludeNamespacesFromPrefixing(); |
|
41 | - $this->excludeFilePatternsFromPrefixing = $config->getExcludeFilePatternsFromPrefixing(); |
|
42 | - } |
|
43 | - |
|
44 | - // Don't replace a classname if there's an import for a class with the same name. |
|
45 | - // but do replace \Classname always |
|
46 | - |
|
47 | - |
|
48 | - /** |
|
49 | - * @param array<string, string> $namespaceChanges |
|
50 | - * @param array<string, string> $classChanges |
|
51 | - * @param array<string, ComposerPackage> $phpFileList |
|
52 | - * @throws FileNotFoundException |
|
53 | - */ |
|
54 | - public function replaceInFiles(array $namespaceChanges, array $classChanges, array $phpFileList) |
|
55 | - { |
|
56 | - |
|
57 | - foreach ($phpFileList as $sourceRelativeFilePathFromVendor => $package) { |
|
58 | - // Skip excluded namespaces. |
|
59 | - if (in_array($package->getName(), $this->excludePackageNamesFromPrefixing)) { |
|
60 | - continue; |
|
61 | - } |
|
62 | - |
|
63 | - // Skip files whose filepath matches an excluded pattern. |
|
64 | - foreach ($this->excludeFilePatternsFromPrefixing as $excludePattern) { |
|
65 | - if (1 === preg_match($excludePattern, $sourceRelativeFilePathFromVendor)) { |
|
66 | - continue 2; |
|
67 | - } |
|
68 | - } |
|
69 | - |
|
70 | - $targetRelativeFilepathFromProject = |
|
71 | - $this->targetDirectory. $sourceRelativeFilePathFromVendor; |
|
72 | - |
|
73 | - // Throws an exception, but unlikely to happen. |
|
74 | - $contents = $this->filesystem->read($targetRelativeFilepathFromProject); |
|
75 | - |
|
76 | - $updatedContents = $this->replaceInString($namespaceChanges, $classChanges, $contents); |
|
77 | - |
|
78 | - if ($updatedContents !== $contents) { |
|
79 | - $this->changedFiles[$sourceRelativeFilePathFromVendor] = $package; |
|
80 | - $this->filesystem->put($targetRelativeFilepathFromProject, $updatedContents); |
|
81 | - } |
|
82 | - } |
|
83 | - } |
|
84 | - |
|
85 | - public function replaceInString(array $namespacesChanges, array $classes, string $contents): string |
|
86 | - { |
|
87 | - |
|
88 | - foreach ($namespacesChanges as $originalNamespace => $replacement) { |
|
89 | - if (in_array($originalNamespace, $this->excludeNamespacesFromPrefixing)) { |
|
90 | - continue; |
|
91 | - } |
|
92 | - |
|
93 | - $contents = $this->replaceNamespace($contents, $originalNamespace, $replacement); |
|
94 | - } |
|
95 | - |
|
96 | - foreach ($classes as $originalClassname) { |
|
97 | - $classmapPrefix = $this->classmapPrefix; |
|
98 | - |
|
99 | - $contents = $this->replaceClassname($contents, $originalClassname, $classmapPrefix); |
|
100 | - } |
|
101 | - |
|
102 | - return $contents; |
|
103 | - } |
|
104 | - |
|
105 | - /** |
|
106 | - * TODO: Test against traits. |
|
107 | - * |
|
108 | - * @param string $contents The text to make replacements in. |
|
109 | - * |
|
110 | - * @return string The updated text. |
|
111 | - */ |
|
112 | - public function replaceNamespace($contents, $originalNamespace, $replacement) |
|
113 | - { |
|
114 | - |
|
115 | - // When namespaces exist inside strings... |
|
116 | - $searchNamespace = |
|
117 | - preg_quote($originalNamespace, '/') |
|
118 | - . '|' |
|
119 | - . preg_quote(str_replace('\\', '\\\\', $originalNamespace), '/') |
|
120 | - . '|' |
|
121 | - . preg_quote('\\' . $originalNamespace, '/') |
|
122 | - . '|' |
|
123 | - . preg_quote('\\\\' . str_replace('\\', '\\\\', $originalNamespace), '/'); |
|
124 | - |
|
125 | - $pattern = " |
|
14 | + /** @var StraussConfig */ |
|
15 | + protected $config; |
|
16 | + |
|
17 | + /** @var Filesystem */ |
|
18 | + protected $filesystem; |
|
19 | + |
|
20 | + protected string $targetDirectory; |
|
21 | + protected string $namespacePrefix; |
|
22 | + protected string $classmapPrefix; |
|
23 | + |
|
24 | + protected array $excludePackageNamesFromPrefixing; |
|
25 | + protected array $excludeNamespacesFromPrefixing; |
|
26 | + protected array $excludeFilePatternsFromPrefixing; |
|
27 | + |
|
28 | + /** @var array<string, ComposerPackage> */ |
|
29 | + protected array $changedFiles = array(); |
|
30 | + |
|
31 | + public function __construct(StraussConfig $config, string $workingDir) |
|
32 | + { |
|
33 | + $this->filesystem = new Filesystem(new Local($workingDir)); |
|
34 | + |
|
35 | + $this->targetDirectory = $config->getTargetDirectory(); |
|
36 | + $this->namespacePrefix = $config->getNamespacePrefix(); |
|
37 | + $this->classmapPrefix = $config->getClassmapPrefix(); |
|
38 | + |
|
39 | + $this->excludePackageNamesFromPrefixing = $config->getExcludePackagesFromPrefixing(); |
|
40 | + $this->excludeNamespacesFromPrefixing = $config->getExcludeNamespacesFromPrefixing(); |
|
41 | + $this->excludeFilePatternsFromPrefixing = $config->getExcludeFilePatternsFromPrefixing(); |
|
42 | + } |
|
43 | + |
|
44 | + // Don't replace a classname if there's an import for a class with the same name. |
|
45 | + // but do replace \Classname always |
|
46 | + |
|
47 | + |
|
48 | + /** |
|
49 | + * @param array<string, string> $namespaceChanges |
|
50 | + * @param array<string, string> $classChanges |
|
51 | + * @param array<string, ComposerPackage> $phpFileList |
|
52 | + * @throws FileNotFoundException |
|
53 | + */ |
|
54 | + public function replaceInFiles(array $namespaceChanges, array $classChanges, array $phpFileList) |
|
55 | + { |
|
56 | + |
|
57 | + foreach ($phpFileList as $sourceRelativeFilePathFromVendor => $package) { |
|
58 | + // Skip excluded namespaces. |
|
59 | + if (in_array($package->getName(), $this->excludePackageNamesFromPrefixing)) { |
|
60 | + continue; |
|
61 | + } |
|
62 | + |
|
63 | + // Skip files whose filepath matches an excluded pattern. |
|
64 | + foreach ($this->excludeFilePatternsFromPrefixing as $excludePattern) { |
|
65 | + if (1 === preg_match($excludePattern, $sourceRelativeFilePathFromVendor)) { |
|
66 | + continue 2; |
|
67 | + } |
|
68 | + } |
|
69 | + |
|
70 | + $targetRelativeFilepathFromProject = |
|
71 | + $this->targetDirectory. $sourceRelativeFilePathFromVendor; |
|
72 | + |
|
73 | + // Throws an exception, but unlikely to happen. |
|
74 | + $contents = $this->filesystem->read($targetRelativeFilepathFromProject); |
|
75 | + |
|
76 | + $updatedContents = $this->replaceInString($namespaceChanges, $classChanges, $contents); |
|
77 | + |
|
78 | + if ($updatedContents !== $contents) { |
|
79 | + $this->changedFiles[$sourceRelativeFilePathFromVendor] = $package; |
|
80 | + $this->filesystem->put($targetRelativeFilepathFromProject, $updatedContents); |
|
81 | + } |
|
82 | + } |
|
83 | + } |
|
84 | + |
|
85 | + public function replaceInString(array $namespacesChanges, array $classes, string $contents): string |
|
86 | + { |
|
87 | + |
|
88 | + foreach ($namespacesChanges as $originalNamespace => $replacement) { |
|
89 | + if (in_array($originalNamespace, $this->excludeNamespacesFromPrefixing)) { |
|
90 | + continue; |
|
91 | + } |
|
92 | + |
|
93 | + $contents = $this->replaceNamespace($contents, $originalNamespace, $replacement); |
|
94 | + } |
|
95 | + |
|
96 | + foreach ($classes as $originalClassname) { |
|
97 | + $classmapPrefix = $this->classmapPrefix; |
|
98 | + |
|
99 | + $contents = $this->replaceClassname($contents, $originalClassname, $classmapPrefix); |
|
100 | + } |
|
101 | + |
|
102 | + return $contents; |
|
103 | + } |
|
104 | + |
|
105 | + /** |
|
106 | + * TODO: Test against traits. |
|
107 | + * |
|
108 | + * @param string $contents The text to make replacements in. |
|
109 | + * |
|
110 | + * @return string The updated text. |
|
111 | + */ |
|
112 | + public function replaceNamespace($contents, $originalNamespace, $replacement) |
|
113 | + { |
|
114 | + |
|
115 | + // When namespaces exist inside strings... |
|
116 | + $searchNamespace = |
|
117 | + preg_quote($originalNamespace, '/') |
|
118 | + . '|' |
|
119 | + . preg_quote(str_replace('\\', '\\\\', $originalNamespace), '/') |
|
120 | + . '|' |
|
121 | + . preg_quote('\\' . $originalNamespace, '/') |
|
122 | + . '|' |
|
123 | + . preg_quote('\\\\' . str_replace('\\', '\\\\', $originalNamespace), '/'); |
|
124 | + |
|
125 | + $pattern = " |
|
126 | 126 | / # Start the pattern |
127 | 127 | ( |
128 | 128 | ^\s* # start of the line |
@@ -156,62 +156,62 @@ discard block |
||
156 | 156 | ) |
157 | 157 | /Ux"; // U: Non-greedy matching, x: ignore whitespace in pattern. |
158 | 158 | |
159 | - $replacingFunction = function ($matches) use ($originalNamespace, $replacement) { |
|
160 | - |
|
161 | - $singleBackslash = '\\\\'; |
|
162 | - $doubleBackslash = '\\\\\\\\'; |
|
163 | - $originalNamespacePattern = str_replace('\\', '\\\\+', $originalNamespace); |
|
164 | - $beginsSingleBackslashPattern = "#{$singleBackslash}{$originalNamespacePattern}#"; |
|
165 | - $beginsDoubleBackslashPattern = "#{$doubleBackslash}{$originalNamespacePattern}#"; |
|
166 | - $containsDoubleBackslashPattern = "#{$doubleBackslash}#"; |
|
167 | - |
|
168 | - // Namespace begins with \\. |
|
169 | - if (preg_match($beginsDoubleBackslashPattern, $matches[2])) { |
|
170 | - $replacement = str_replace("\\", "\\\\", $replacement); |
|
171 | - $replacement = "\\\\" . $replacement; |
|
172 | - } elseif (preg_match($beginsSingleBackslashPattern, $matches[2])) { |
|
173 | - $replacement = "\\" . $replacement; |
|
174 | - } elseif (preg_match($containsDoubleBackslashPattern, $matches[2])) { |
|
175 | - $replacement = str_replace("\\", "\\\\", $replacement); |
|
176 | - } |
|
177 | - |
|
178 | - return $matches[1] . $replacement . $matches[3]; |
|
179 | - }; |
|
180 | - |
|
181 | - $result = preg_replace_callback($pattern, $replacingFunction, $contents); |
|
182 | - |
|
183 | - $matchingError = preg_last_error(); |
|
184 | - if (0 !== $matchingError) { |
|
185 | - $message = "Matching error {$matchingError}"; |
|
186 | - if (PREG_BACKTRACK_LIMIT_ERROR === $matchingError) { |
|
187 | - $message = 'Preg Backtrack limit was exhausted!'; |
|
188 | - } |
|
189 | - throw new Exception($message); |
|
190 | - } |
|
191 | - |
|
192 | - return $result; |
|
193 | - } |
|
194 | - |
|
195 | - /** |
|
196 | - * In a namespace: |
|
197 | - * * use \Classname; |
|
198 | - * * new \Classname() |
|
199 | - * |
|
200 | - * In a global namespace: |
|
201 | - * * new Classname() |
|
202 | - * |
|
203 | - * @param string $contents |
|
204 | - * @param string $originalClassname |
|
205 | - * @param string $classnamePrefix |
|
206 | - * @return array|string|string[]|null |
|
207 | - * @throws \Exception |
|
208 | - */ |
|
209 | - public function replaceClassname($contents, $originalClassname, $classnamePrefix) |
|
210 | - { |
|
211 | - $searchClassname = preg_quote($originalClassname, '/'); |
|
212 | - |
|
213 | - // This could be more specific if we could enumerate all preceeding and proceeding words ("new", "("...). |
|
214 | - $pattern = ' |
|
159 | + $replacingFunction = function ($matches) use ($originalNamespace, $replacement) { |
|
160 | + |
|
161 | + $singleBackslash = '\\\\'; |
|
162 | + $doubleBackslash = '\\\\\\\\'; |
|
163 | + $originalNamespacePattern = str_replace('\\', '\\\\+', $originalNamespace); |
|
164 | + $beginsSingleBackslashPattern = "#{$singleBackslash}{$originalNamespacePattern}#"; |
|
165 | + $beginsDoubleBackslashPattern = "#{$doubleBackslash}{$originalNamespacePattern}#"; |
|
166 | + $containsDoubleBackslashPattern = "#{$doubleBackslash}#"; |
|
167 | + |
|
168 | + // Namespace begins with \\. |
|
169 | + if (preg_match($beginsDoubleBackslashPattern, $matches[2])) { |
|
170 | + $replacement = str_replace("\\", "\\\\", $replacement); |
|
171 | + $replacement = "\\\\" . $replacement; |
|
172 | + } elseif (preg_match($beginsSingleBackslashPattern, $matches[2])) { |
|
173 | + $replacement = "\\" . $replacement; |
|
174 | + } elseif (preg_match($containsDoubleBackslashPattern, $matches[2])) { |
|
175 | + $replacement = str_replace("\\", "\\\\", $replacement); |
|
176 | + } |
|
177 | + |
|
178 | + return $matches[1] . $replacement . $matches[3]; |
|
179 | + }; |
|
180 | + |
|
181 | + $result = preg_replace_callback($pattern, $replacingFunction, $contents); |
|
182 | + |
|
183 | + $matchingError = preg_last_error(); |
|
184 | + if (0 !== $matchingError) { |
|
185 | + $message = "Matching error {$matchingError}"; |
|
186 | + if (PREG_BACKTRACK_LIMIT_ERROR === $matchingError) { |
|
187 | + $message = 'Preg Backtrack limit was exhausted!'; |
|
188 | + } |
|
189 | + throw new Exception($message); |
|
190 | + } |
|
191 | + |
|
192 | + return $result; |
|
193 | + } |
|
194 | + |
|
195 | + /** |
|
196 | + * In a namespace: |
|
197 | + * * use \Classname; |
|
198 | + * * new \Classname() |
|
199 | + * |
|
200 | + * In a global namespace: |
|
201 | + * * new Classname() |
|
202 | + * |
|
203 | + * @param string $contents |
|
204 | + * @param string $originalClassname |
|
205 | + * @param string $classnamePrefix |
|
206 | + * @return array|string|string[]|null |
|
207 | + * @throws \Exception |
|
208 | + */ |
|
209 | + public function replaceClassname($contents, $originalClassname, $classnamePrefix) |
|
210 | + { |
|
211 | + $searchClassname = preg_quote($originalClassname, '/'); |
|
212 | + |
|
213 | + // This could be more specific if we could enumerate all preceeding and proceeding words ("new", "("...). |
|
214 | + $pattern = ' |
|
215 | 215 | / # Start the pattern |
216 | 216 | namespace\s+([a-zA-Z0-9_\x7f-\xff\\\\]+).*?{.*?(namespace|\z) |
217 | 217 | # Look for a preceeding namespace declaration, up until a |
@@ -222,65 +222,65 @@ discard block |
||
222 | 222 | |
223 | 223 | /x'; // # x: ignore whitespace in regex. |
224 | 224 | |
225 | - $replacingFunction = function ($matches) use ($originalClassname, $classnamePrefix) { |
|
226 | - |
|
227 | - // If we're inside a namespace other than the global namespace: |
|
228 | - if (1 === preg_match('/^namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*/', $matches[0])) { |
|
229 | - $updated = $this->replaceGlobalClassInsideNamedNamespace( |
|
230 | - $matches[0], |
|
231 | - $originalClassname, |
|
232 | - $classnamePrefix |
|
233 | - ); |
|
234 | - |
|
235 | - return $updated; |
|
236 | - } |
|
237 | - |
|
238 | - return $matches[1] . $matches[2] . $matches[3] . $classnamePrefix . $originalClassname . $matches[5]; |
|
239 | - }; |
|
240 | - |
|
241 | - $result = preg_replace_callback($pattern, $replacingFunction, $contents); |
|
242 | - |
|
243 | - $matchingError = preg_last_error(); |
|
244 | - if (0 !== $matchingError) { |
|
245 | - $message = "Matching error {$matchingError}"; |
|
246 | - if (PREG_BACKTRACK_LIMIT_ERROR === $matchingError) { |
|
247 | - $message = 'Backtrack limit was exhausted!'; |
|
248 | - } |
|
249 | - throw new Exception($message); |
|
250 | - } |
|
251 | - |
|
252 | - return $result; |
|
253 | - } |
|
254 | - |
|
255 | - /** |
|
256 | - * Pass in a string and look for \Classname instances. |
|
257 | - * |
|
258 | - * @param string $contents |
|
259 | - * @param string $originalClassname |
|
260 | - * @param string $classnamePrefix |
|
261 | - * @return string |
|
262 | - */ |
|
263 | - protected function replaceGlobalClassInsideNamedNamespace($contents, $originalClassname, $classnamePrefix): string |
|
264 | - { |
|
265 | - |
|
266 | - return preg_replace_callback( |
|
267 | - '/([^a-zA-Z0-9_\x7f-\xff] # Not a class character |
|
225 | + $replacingFunction = function ($matches) use ($originalClassname, $classnamePrefix) { |
|
226 | + |
|
227 | + // If we're inside a namespace other than the global namespace: |
|
228 | + if (1 === preg_match('/^namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*/', $matches[0])) { |
|
229 | + $updated = $this->replaceGlobalClassInsideNamedNamespace( |
|
230 | + $matches[0], |
|
231 | + $originalClassname, |
|
232 | + $classnamePrefix |
|
233 | + ); |
|
234 | + |
|
235 | + return $updated; |
|
236 | + } |
|
237 | + |
|
238 | + return $matches[1] . $matches[2] . $matches[3] . $classnamePrefix . $originalClassname . $matches[5]; |
|
239 | + }; |
|
240 | + |
|
241 | + $result = preg_replace_callback($pattern, $replacingFunction, $contents); |
|
242 | + |
|
243 | + $matchingError = preg_last_error(); |
|
244 | + if (0 !== $matchingError) { |
|
245 | + $message = "Matching error {$matchingError}"; |
|
246 | + if (PREG_BACKTRACK_LIMIT_ERROR === $matchingError) { |
|
247 | + $message = 'Backtrack limit was exhausted!'; |
|
248 | + } |
|
249 | + throw new Exception($message); |
|
250 | + } |
|
251 | + |
|
252 | + return $result; |
|
253 | + } |
|
254 | + |
|
255 | + /** |
|
256 | + * Pass in a string and look for \Classname instances. |
|
257 | + * |
|
258 | + * @param string $contents |
|
259 | + * @param string $originalClassname |
|
260 | + * @param string $classnamePrefix |
|
261 | + * @return string |
|
262 | + */ |
|
263 | + protected function replaceGlobalClassInsideNamedNamespace($contents, $originalClassname, $classnamePrefix): string |
|
264 | + { |
|
265 | + |
|
266 | + return preg_replace_callback( |
|
267 | + '/([^a-zA-Z0-9_\x7f-\xff] # Not a class character |
|
268 | 268 | \\\) # Followed by a backslash to indicate global namespace |
269 | 269 | ('.$originalClassname.') # Followed by the classname |
270 | 270 | ([^\\\;]+) # Not a backslash or semicolon which might indicate a namespace |
271 | 271 | /x', // # x: ignore whitespace in regex. |
272 | - function ($matches) use ($originalClassname, $classnamePrefix) { |
|
273 | - return $matches[1] . $classnamePrefix . $originalClassname . $matches[3]; |
|
274 | - }, |
|
275 | - $contents |
|
276 | - ); |
|
277 | - } |
|
278 | - |
|
279 | - /** |
|
280 | - * @return array<string, ComposerPackage> |
|
281 | - */ |
|
282 | - public function getModifiedFiles(): array |
|
283 | - { |
|
284 | - return $this->changedFiles; |
|
285 | - } |
|
272 | + function ($matches) use ($originalClassname, $classnamePrefix) { |
|
273 | + return $matches[1] . $classnamePrefix . $originalClassname . $matches[3]; |
|
274 | + }, |
|
275 | + $contents |
|
276 | + ); |
|
277 | + } |
|
278 | + |
|
279 | + /** |
|
280 | + * @return array<string, ComposerPackage> |
|
281 | + */ |
|
282 | + public function getModifiedFiles(): array |
|
283 | + { |
|
284 | + return $this->changedFiles; |
|
285 | + } |
|
286 | 286 | } |