Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.25% covered (warning)
76.25%
122 / 160
52.08% covered (warning)
52.08%
25 / 48
CRAP
0.00% covered (danger)
0.00%
0 / 1
StraussConfig
76.25% covered (warning)
76.25%
122 / 160
52.08% covered (warning)
52.08%
25 / 48
248.90
0.00% covered (danger)
0.00%
0 / 1
 __construct
93.75% covered (success)
93.75%
75 / 80
0.00% covered (danger)
0.00%
0 / 1
28.19
 getTargetDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setTargetDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getVendorDirectory
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setVendorDirectory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespacePrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 setNamespacePrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getClassmapPrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setClassmapPrefix
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getConstantsPrefix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setConstantsPrefix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUpdateCallSites
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setUpdateCallSites
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
7
 setExcludeFromCopy
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getExcludePackagesFromCopy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExcludeNamespacesFromCopy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExcludeFilePatternsFromCopy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setExcludeFromPrefix
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
4.59
 getExcludePackagesFromPrefixing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setExcludePackagesFromPrefixing
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExcludeNamespacesFromPrefixing
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExcludeFilePatternsFromPrefixing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOverrideAutoload
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setOverrideAutoload
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDeleteVendorFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDeleteVendorPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDeleteVendorFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDeleteVendorPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPackagesToCopy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPackagesToCopy
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getPackagesToPrefix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPackagesToPrefix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isClassmapOutput
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setClassmapOutput
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setExcludePackages
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaceReplacementPatterns
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNamespaceReplacementPatterns
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isIncludeModifiedDate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setIncludeModifiedDate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isIncludeAuthor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setIncludeAuthor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDryRun
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDryRun
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateFromCli
55.00% covered (warning)
55.00%
11 / 20
0.00% covered (danger)
0.00%
0 / 1
35.50
 isCreateAliases
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 getProjectDirectory
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
1<?php
2/**
3 * The extra/strauss key in composer.json.
4 */
5
6namespace BrianHenryIE\Strauss\Composer\Extra;
7
8use BrianHenryIE\Strauss\Composer\ComposerPackage;
9use BrianHenryIE\Strauss\Config\AliasesConfigInterface;
10use BrianHenryIE\Strauss\Config\AutoloadConfigInterface;
11use BrianHenryIE\Strauss\Config\ChangeEnumeratorConfigInterface;
12use BrianHenryIE\Strauss\Config\CleanupConfigInterface;
13use BrianHenryIE\Strauss\Config\FileCopyScannerConfigInterface;
14use BrianHenryIE\Strauss\Config\FileEnumeratorConfig;
15use BrianHenryIE\Strauss\Config\FileSymbolScannerConfigInterface;
16use BrianHenryIE\Strauss\Config\PrefixerConfigInterface;
17use BrianHenryIE\Strauss\Console\Commands\DependenciesCommand;
18use BrianHenryIE\Strauss\Pipeline\Autoload\DumpAutoload;
19use Composer\Composer;
20use Composer\Factory;
21use Exception;
22use JsonMapper\JsonMapperFactory;
23use JsonMapper\Middleware\Rename\Rename;
24use Symfony\Component\Console\Input\InputInterface;
25
26class StraussConfig implements
27    AliasesConfigInterface,
28    AutoloadConfigInterface,
29    ChangeEnumeratorConfigInterface,
30    CleanupConfigInterface,
31    FileSymbolScannerConfigInterface,
32    FileEnumeratorConfig,
33    FileCopyScannerConfigInterface,
34    PrefixerConfigInterface,
35    ReplaceConfigInterface
36{
37    /**
38     * The directory containing `composer.json`. Probably `cwd()`.
39     */
40    protected string $projectDirectory;
41
42    /**
43     * The output directory.
44     */
45    protected string $targetDirectory = 'vendor-prefixed';
46
47    /**
48     * The vendor directory.
49     *
50     * Probably 'vendor/'
51     */
52    protected string $vendorDirectory = 'vendor';
53
54    /**
55     * `namespacePrefix` is the prefix to be given to any namespaces.
56     * Presumably this will take the form `My_Project_Namespace\dep_directory`.
57     *
58     * @link https://www.php-fig.org/psr/psr-4/
59     */
60    protected ?string $namespacePrefix = null;
61
62    /**
63     * @var string
64     */
65    protected ?string $classmapPrefix = null;
66
67    /**
68     * @var ?string
69     */
70    protected ?string $constantsPrefix = null;
71
72    /**
73     * Should replacements be performed in project files?
74     *
75     * When null, files in the project's `autoload` key are scanned and changes which have been performed on the
76     * vendor packages are reflected in the project files.
77     *
78     * When an array of relative file paths are provided, the files in those directories are updated.
79     *
80     * An empty array disables updating project files.
81     *
82     * @var ?string[]
83     */
84    protected ?array $updateCallSites = array();
85
86    /**
87     * Packages to copy and (maybe) prefix.
88     *
89     * If this is empty, the "requires" list in the project composer.json is used.
90     *
91     * @var string[]
92     */
93    protected array $packages = [];
94
95    /**
96     *
97     * @var array<string,ComposerPackage>
98     */
99    protected array $packagesToCopy = [];
100
101    /**
102     *
103     * @var array<string,ComposerPackage>
104     */
105    protected array $packagesToPrefix = [];
106
107    /**
108     * Back-compatibility with Mozart.
109     *
110     * @var string[]
111     */
112    private array $excludePackages;
113
114    /**
115     * 'exclude_from_copy' in composer/extra config.
116     *
117     * @var array{packages: string[], namespaces: string[], file_patterns: string[]}
118     */
119    protected array $excludeFromCopy = array('file_patterns'=>array(),'namespaces'=>array(),'packages'=>array());
120
121    /**
122     * @var array{packages: string[], namespaces: string[], file_patterns: string[]}
123     */
124    protected array $excludeFromPrefix = array('file_patterns'=>array(),'namespaces'=>array(),'packages'=>array());
125
126    /**
127     * An array of autoload keys to replace packages' existing autoload key.
128     *
129     * e.g. when
130     * * A package has no autoloader
131     * * A package specified both a PSR-4 and a classmap but only needs one
132     * ...
133     *
134     * @var array<string, array{files?:array<string>,classmap?:array<string>,"psr-4":array<string|array<string>>}>|array{} $overrideAutoload
135     */
136    protected array $overrideAutoload = [];
137
138    /**
139     * After completing prefixing should the source files be deleted?
140     * This does not affect symlinked directories.
141     */
142    protected bool $deleteVendorFiles = false;
143
144    /**
145     * After completing prefixing should the source packages be deleted?
146     * This does not affect symlinked directories.
147     */
148    protected bool $deleteVendorPackages = false;
149
150    protected bool $classmapOutput;
151
152    /**
153     * A dictionary of regex captures => regex replacements.
154     *
155     * E.g. used to avoid repetition of the plugin vendor name in namespaces.
156     * `"~BrianHenryIE\\\\(.*)~" : "BrianHenryIE\\WC_Cash_App_Gateway\\\\$1"`.
157     *
158     * @var array<string, string> $namespaceReplacementPatterns
159     */
160    protected array $namespaceReplacementPatterns = array();
161
162    /**
163     * Should a modified date be included in the header for modified files?
164     *
165     * @var bool
166     */
167    protected $includeModifiedDate = true;
168
169    /**
170     * Should the author name be included in the header for modified files?
171     *
172     * @var bool
173     */
174    protected $includeAuthor = true;
175
176    /**
177     * Should the changes be printed to console rather than files modified?
178     */
179    protected bool $dryRun = false;
180
181    /**
182     * Read any existing Mozart config.
183     * Overwrite it with any Strauss config.
184     * Provide sensible defaults.
185     *
186     * @param Composer $composer
187     *
188     * @throws Exception
189     */
190    public function __construct(?Composer $composer = null)
191    {
192
193        $configExtraSettings = null;
194
195        // Backwards compatibility with Mozart.
196        if (isset($composer, $composer->getPackage()->getExtra()['mozart'])) {
197            $configExtraSettings = (object)$composer->getPackage()->getExtra()['mozart'];
198
199            // Default setting for Mozart.
200            $this->setDeleteVendorFiles(true);
201        }
202
203        if (isset($composer, $composer->getPackage()->getExtra()['strauss'])) {
204            $configExtraSettings = (object)$composer->getPackage()->getExtra()['strauss'];
205        }
206
207        if (!is_null($configExtraSettings)) {
208            $mapper = (new JsonMapperFactory())->bestFit();
209
210            $rename = new Rename();
211            $rename->addMapping(StraussConfig::class, 'dep_directory', 'targetDirectory');
212            $rename->addMapping(StraussConfig::class, 'dep_namespace', 'namespacePrefix');
213
214            $rename->addMapping(StraussConfig::class, 'exclude_packages', 'excludePackages');
215            $rename->addMapping(StraussConfig::class, 'delete_vendor_files', 'deleteVendorFiles');
216            $rename->addMapping(StraussConfig::class, 'delete_vendor_packages', 'deleteVendorPackages');
217
218            $rename->addMapping(StraussConfig::class, 'exclude_prefix_packages', 'excludePackagesFromPrefixing');
219
220            $mapper->unshift($rename);
221            $mapper->push(new \JsonMapper\Middleware\CaseConversion(
222                \JsonMapper\Enums\TextNotation::UNDERSCORE(),
223                \JsonMapper\Enums\TextNotation::CAMEL_CASE()
224            ));
225
226            $mapper->mapObject($configExtraSettings, $this);
227        }
228
229        // Defaults.
230        // * Use PSR-4 autoloader key
231        // * Use PSR-0 autoloader key
232        // * Use the package name
233        if (! isset($this->namespacePrefix)) {
234            if (isset($composer, $composer->getPackage()->getAutoload()['psr-4'])) {
235                $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-4']));
236            } elseif (isset($composer, $composer->getPackage()->getAutoload()['psr-0'])) {
237                $this->setNamespacePrefix(array_key_first($composer->getPackage()->getAutoload()['psr-0']));
238            } elseif (isset($composer) && '__root__' !== $composer->getPackage()->getName()) {
239                $packageName = $composer->getPackage()->getName();
240                $namespacePrefix = preg_replace('/[^\w\/]+/', '_', $packageName);
241                $namespacePrefix = str_replace('/', '\\', $namespacePrefix) . '\\';
242                $namespacePrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) {
243                    return strtoupper($match[0]);
244                }, $namespacePrefix);
245                $this->setNamespacePrefix($namespacePrefix);
246            } elseif (isset($this->classmapPrefix)) {
247                $namespacePrefix = rtrim($this->getClassmapPrefix(), '_');
248                $this->setNamespacePrefix($namespacePrefix);
249            }
250        }
251
252        if (! isset($this->classmapPrefix)) {
253            if (isset($composer, $composer->getPackage()->getAutoload()['psr-4'])) {
254                $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-4']);
255                $classmapPrefix = str_replace("\\", "_", $autoloadKey);
256                $this->setClassmapPrefix($classmapPrefix);
257            } elseif (isset($composer, $composer->getPackage()->getAutoload()['psr-0'])) {
258                $autoloadKey = array_key_first($composer->getPackage()->getAutoload()['psr-0']);
259                $classmapPrefix = str_replace("\\", "_", $autoloadKey);
260                $this->setClassmapPrefix($classmapPrefix);
261            } elseif (isset($composer) && '__root__' !== $composer->getPackage()->getName()) {
262                $packageName = $composer->getPackage()->getName();
263                $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $packageName);
264                $classmapPrefix = str_replace('/', '\\', $classmapPrefix);
265                // Uppercase the first letter of each word.
266                $classmapPrefix = preg_replace_callback('/(?<=^|_|\\\\)[a-z]/', function ($match) {
267                    return strtoupper($match[0]);
268                }, $classmapPrefix);
269                $classmapPrefix = str_replace("\\", "_", $classmapPrefix);
270                $this->setClassmapPrefix($classmapPrefix);
271            } elseif (isset($this->namespacePrefix)) {
272                $classmapPrefix = preg_replace('/[^\w\/]+/', '_', $this->getNamespacePrefix());
273                $classmapPrefix = rtrim($classmapPrefix, '_') . '_';
274                $this->setClassmapPrefix($classmapPrefix);
275            }
276        }
277
278//        if (!isset($this->namespacePrefix) || !isset($this->classmapPrefix)) {
279//            throw new Exception('Prefix not set. Please set `namespace_prefix`, `classmap_prefix` in composer.json/extra/strauss.');
280//        }
281
282        if (isset($composer) && empty($this->packages)) {
283            $this->packages = array_map(function (\Composer\Package\Link $element) {
284                return $element->getTarget();
285            }, $composer->getPackage()->getRequires());
286        }
287
288        // If the bool flag for classmapOutput wasn't set in the Json config.
289        if (!isset($this->classmapOutput)) {
290            $this->classmapOutput = true;
291            // Check each autoloader.
292            if (isset($composer)) {
293                foreach ($composer->getPackage()->getAutoload() as $autoload) {
294                    // To see if one of its paths.
295                    foreach ($autoload as $entry) {
296                        $paths = (array) $entry;
297                        foreach ($paths as $path) {
298                            // Matches the target directory.
299                            if (trim($path, '\\/') . '/' === $this->getTargetDirectory()) {
300                                $this->classmapOutput = false;
301                                break 3;
302                            }
303                        }
304                    }
305                }
306            }
307        }
308
309        // TODO: Throw an exception if any regex patterns in config are invalid.
310        // https://stackoverflow.com/questions/4440626/how-can-i-validate-regex
311        // preg_match('~Valid(Regular)Expression~', null) === false);
312
313        if (isset($configExtraSettings, $configExtraSettings->updateCallSites)) {
314            if (true === $configExtraSettings->updateCallSites) {
315                $this->updateCallSites = null;
316            } elseif (false === $configExtraSettings->updateCallSites) {
317                $this->updateCallSites = array();
318            } elseif (is_array($configExtraSettings->updateCallSites)) {
319                $this->updateCallSites = $configExtraSettings->updateCallSites;
320            } else {
321                // uh oh.
322            }
323        }
324    }
325
326    /**
327     * `target_directory` will always be returned without a leading slash and with a trailing slash.
328     *
329     * @return string
330     */
331    public function getTargetDirectory(): string
332    {
333        return $this->getProjectDirectory() . trim($this->targetDirectory, '\\/') . '/';
334    }
335
336    /**
337     * @param string $targetDirectory
338     */
339    public function setTargetDirectory(string $targetDirectory): void
340    {
341        $this->targetDirectory = $targetDirectory;
342    }
343
344    /**
345     * @return string
346     */
347    public function getVendorDirectory(): string
348    {
349        return $this->getProjectDirectory() . trim($this->vendorDirectory, '\\/') . '/';
350    }
351
352    /**
353     * @param string $vendorDirectory
354     */
355    public function setVendorDirectory(string $vendorDirectory): void
356    {
357        $this->vendorDirectory = $vendorDirectory;
358    }
359
360    public function getNamespacePrefix(): ?string
361    {
362        return !isset($this->namespacePrefix) ? null :trim($this->namespacePrefix, '\\');
363    }
364
365    /**
366     * @param string $namespacePrefix
367     */
368    public function setNamespacePrefix(string $namespacePrefix): void
369    {
370        $this->namespacePrefix = $namespacePrefix;
371    }
372
373    /**
374     * @return string
375     */
376    public function getClassmapPrefix(): ?string
377    {
378        return $this->classmapPrefix;
379    }
380
381    /**
382     * @param string $classmapPrefix
383     */
384    public function setClassmapPrefix(string $classmapPrefix): void
385    {
386        $this->classmapPrefix = $classmapPrefix;
387    }
388
389    /**
390     * @return string
391     */
392    public function getConstantsPrefix(): ?string
393    {
394        return $this->constantsPrefix;
395    }
396
397    /**
398     * @param string $constantsPrefix
399     */
400    public function setConstantsPrefix(string $constantsPrefix): void
401    {
402        $this->constantsPrefix = $constantsPrefix;
403    }
404
405    /**
406     * List of files and directories to update call sites in. Empty to disable. Null infers from the project's autoload key.
407     *
408     * @return string[]|null
409     */
410    public function getUpdateCallSites(): ?array
411    {
412        return $this->updateCallSites;
413    }
414
415    /**
416     * @param string[]|null $updateCallSites
417     */
418    public function setUpdateCallSites($updateCallSites): void
419    {
420        if (is_array($updateCallSites) && count($updateCallSites) === 1 && $updateCallSites[0] === true) {
421            // Setting `null` instructs Strauss to update call sites in the project's autoload key.
422            $this->updateCallSites = null;
423        } elseif (is_array($updateCallSites) && count($updateCallSites) === 1 && $updateCallSites[0] === false) {
424            $this->updateCallSites = array();
425        } else {
426            $this->updateCallSites = $updateCallSites;
427        }
428    }
429
430    /**
431     * @param array{packages?:array<string>, namespaces?:array<string>, file_patterns?:array<string>} $excludeFromCopy
432     */
433    public function setExcludeFromCopy(array $excludeFromCopy): void
434    {
435        foreach (array( 'packages', 'namespaces', 'file_patterns' ) as $key) {
436            if (isset($excludeFromCopy[$key])) {
437                $this->excludeFromCopy[$key] = $excludeFromCopy[$key];
438            }
439        }
440    }
441
442    /**
443     * @return string[]
444     */
445    public function getExcludePackagesFromCopy(): array
446    {
447        return $this->excludeFromCopy['packages'] ?? array();
448    }
449
450    /**
451     * @return string[]
452     */
453    public function getExcludeNamespacesFromCopy(): array
454    {
455        return $this->excludeFromCopy['namespaces'] ?? array();
456    }
457
458    /**
459     * @return string[]
460     */
461    public function getExcludeFilePatternsFromCopy(): array
462    {
463        return $this->excludeFromCopy['file_patterns'] ?? array();
464    }
465
466    /**
467     * @param array{packages?:array<string>, namespaces?:array<string>, file_patterns?:array<string>} $excludeFromPrefix
468     */
469    public function setExcludeFromPrefix(array $excludeFromPrefix): void
470    {
471        if (isset($excludeFromPrefix['packages'])) {
472            $this->excludeFromPrefix['packages'] = $excludeFromPrefix['packages'];
473        }
474        if (isset($excludeFromPrefix['namespaces'])) {
475            $this->excludeFromPrefix['namespaces'] = $excludeFromPrefix['namespaces'];
476        }
477        if (isset($excludeFromPrefix['file_patterns'])) {
478            $this->excludeFromPrefix['file_patterns'] = $excludeFromPrefix['file_patterns'];
479        }
480    }
481
482    /**
483     * When prefixing, do not prefix these packages (which have been copied).
484     *
485     * @return string[]
486     */
487    public function getExcludePackagesFromPrefixing(): array
488    {
489        return $this->excludeFromPrefix['packages'] ?? array();
490    }
491
492    /**
493     * @param string[] $excludePackagesFromPrefixing
494     */
495    public function setExcludePackagesFromPrefixing(array $excludePackagesFromPrefixing): void
496    {
497        $this->excludeFromPrefix['packages'] = $excludePackagesFromPrefixing;
498    }
499
500    /**
501     * @return string[]
502     */
503    public function getExcludeNamespacesFromPrefixing(): array
504    {
505        return $this->excludeFromPrefix['namespaces'] ?? array();
506    }
507
508    /**
509     * @return string[]
510     */
511    public function getExcludeFilePatternsFromPrefixing(): array
512    {
513        return $this->excludeFromPrefix['file_patterns'] ?? array();
514    }
515
516
517    /**
518     * @return array{}|array<string, array{files?:array<string>,classmap?:array<string>,"psr-4":array<string|array<string>>}> $overrideAutoload Dictionary of package name: autoload rules.
519     */
520    public function getOverrideAutoload(): array
521    {
522        return $this->overrideAutoload;
523    }
524
525    /**
526     * @param array<string, array{files?:array<string>,classmap?:array<string>,"psr-4":array<string|array<string>>}> $overrideAutoload Dictionary of package name: autoload rules.
527     */
528    public function setOverrideAutoload(array $overrideAutoload): void
529    {
530        $this->overrideAutoload = $overrideAutoload;
531    }
532
533    /**
534     * @return bool
535     */
536    public function isDeleteVendorFiles(): bool
537    {
538        return $this->deleteVendorFiles;
539    }
540
541    /**
542     * @return bool
543     */
544    public function isDeleteVendorPackages(): bool
545    {
546        return $this->deleteVendorPackages;
547    }
548
549    /**
550     * @param bool $deleteVendorFiles
551     */
552    public function setDeleteVendorFiles(bool $deleteVendorFiles): void
553    {
554        $this->deleteVendorFiles = $deleteVendorFiles;
555    }
556
557    /**
558     * @param bool $deleteVendorPackages
559     */
560    public function setDeleteVendorPackages(bool $deleteVendorPackages): void
561    {
562        $this->deleteVendorPackages = $deleteVendorPackages;
563    }
564
565    /**
566     * @return string[]
567     */
568    public function getPackages(): array
569    {
570        return $this->packages;
571    }
572
573    /**
574     * @param string[] $packages
575     */
576    public function setPackages(array $packages): void
577    {
578        $this->packages = $packages;
579    }
580
581    /**
582     * @used-by DumpAutoload::createInstalledVersionsFiles()
583     * @return array<string,ComposerPackage>
584     */
585    public function getPackagesToCopy(): array
586    {
587        return $this->packagesToCopy;
588    }
589
590    /**
591     * @used-by DependenciesCommand::buildDependencyList()
592     */
593    public function setPackagesToCopy(array $packagesToCopy): void
594    {
595        $this->packagesToCopy = $packagesToCopy;
596    }
597
598    public function getPackagesToPrefix(): array
599    {
600        return $this->packagesToPrefix;
601    }
602
603    public function setPackagesToPrefix(array $packagesToPrefix): void
604    {
605        $this->packagesToPrefix = $packagesToPrefix;
606    }
607    /**
608     * @return bool
609     */
610    public function isClassmapOutput(): bool
611    {
612        return $this->classmapOutput;
613    }
614
615    /**
616     * @param bool $classmapOutput
617     */
618    public function setClassmapOutput(bool $classmapOutput): void
619    {
620        $this->classmapOutput = $classmapOutput;
621    }
622
623    /**
624     * Backwards compatibility with Mozart.
625     *
626     * @param string[] $excludePackages
627     */
628    public function setExcludePackages(array $excludePackages): void
629    {
630        $this->excludeFromPrefix['packages'] = $excludePackages;
631    }
632
633    /**
634     * @return array<string,string>
635     */
636    public function getNamespaceReplacementPatterns(): array
637    {
638        return $this->namespaceReplacementPatterns;
639    }
640
641    /**
642     * @param array<string,string> $namespaceReplacementPatterns
643     */
644    public function setNamespaceReplacementPatterns(array $namespaceReplacementPatterns): void
645    {
646        $this->namespaceReplacementPatterns = $namespaceReplacementPatterns;
647    }
648
649    /**
650     * @return bool
651     */
652    public function isIncludeModifiedDate(): bool
653    {
654        return $this->includeModifiedDate;
655    }
656
657    /**
658     * @param bool $includeModifiedDate
659     */
660    public function setIncludeModifiedDate(bool $includeModifiedDate): void
661    {
662        $this->includeModifiedDate = $includeModifiedDate;
663    }
664
665
666    /**
667     * @return bool
668     */
669    public function isIncludeAuthor(): bool
670    {
671        return $this->includeAuthor;
672    }
673
674    /**
675     * @param bool $includeAuthor
676     */
677    public function setIncludeAuthor(bool $includeAuthor): void
678    {
679        $this->includeAuthor = $includeAuthor;
680    }
681
682    /**
683     * Should expected changes be printed to console rather than files modified?
684     */
685    public function isDryRun(): bool
686    {
687        return $this->dryRun;
688    }
689
690    /**
691     * Disable making changes to files; output changes to console instead.
692     */
693    public function setDryRun(bool $dryRun): void
694    {
695        $this->dryRun = $dryRun;
696    }
697
698    /**
699     * @param InputInterface $input To access the command line options.
700     */
701    public function updateFromCli(InputInterface $input): void
702    {
703
704        // strauss --updateCallSites=false (default)
705        // strauss --updateCallSites=true
706        // strauss --updateCallSites=src,input,extra
707
708        if ($input->hasOption('updateCallSites') && $input->getOption('updateCallSites') !== null) {
709            $updateCallSitesInput = $input->getOption('updateCallSites');
710
711            if ('false' === $updateCallSitesInput) {
712                $this->updateCallSites = array();
713            } elseif ('true' === $updateCallSitesInput) {
714                $this->updateCallSites = null;
715            } elseif (! is_null($updateCallSitesInput)) {
716                $this->updateCallSites = explode(',', $updateCallSitesInput);
717            }
718        }
719
720        if ($input->hasOption('deleteVendorPackages')  && $input->getOption('deleteVendorPackages') !== false) {
721            $isDeleteVendorPackagesCommandLine = $input->getOption('deleteVendorPackages') === 'true'
722                || $input->getOption('deleteVendorPackages') === null;
723            $this->setDeleteVendorPackages($isDeleteVendorPackagesCommandLine);
724        } elseif ($input->hasOption('delete_vendor_packages') && $input->getOption('delete_vendor_packages') !== false) {
725            $isDeleteVendorPackagesCommandLine = $input->getOption('delete_vendor_packages') === 'true'
726                || $input->getOption('delete_vendor_packages') === null;
727            $this->setDeleteVendorPackages($isDeleteVendorPackagesCommandLine);
728        }
729
730        if ($input->hasOption('dry-run') && $input->getOption('dry-run') !== false) {
731            // If we're here, the parameter was passed in the CLI command.
732            $this->dryRun = empty($input->getOption('dry-run'))
733                ? true
734                : filter_var($input->getOption('dry-run'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
735        }
736    }
737
738    /**
739     * Should we create the `autoload_aliases.php` file in `vendor/composer`?
740     *
741     * TODO:
742     * [x] YES when we are deleting vendor packages or files
743     * [ ] NO when we are running composer install `--no-dev`
744     * [ ] SOMETIMES: see https://github.com/BrianHenryIE/strauss/issues/144
745     * [ ] Add `aliases` to `extra` in `composer.json`
746     * [ ] Add `--aliases=true` CLI option
747     */
748    public function isCreateAliases(): bool
749    {
750        return $this->deleteVendorPackages || $this->deleteVendorFiles || $this->targetDirectory === 'vendor';
751    }
752
753    public function getProjectDirectory(): string
754    {
755        $projectDirectory = $this->projectDirectory ?? getcwd() . '/';
756
757        return $this->isDryRun()
758            ? 'mem:/' . $projectDirectory
759            : $projectDirectory;
760    }
761}