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