Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
70.27% covered (warning)
70.27%
26 / 37
14.29% covered (danger)
14.29%
1 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
HasCacheOption
70.27% covered (warning)
70.27%
26 / 37
14.29% covered (danger)
14.29%
1 / 7
30.51
0.00% covered (danger)
0.00%
0 / 1
 addCacheOption
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 addCacheDirOption
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
 isCacheEnabled
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 resolveCacheArgument
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 resolveCacheDirArgument
60.00% covered (warning)
60.00%
6 / 10
0.00% covered (danger)
0.00%
0 / 1
8.30
 hasExplicitCacheDirArgument
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
3.19
 isNoCacheRequested
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
3.19
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\Console\Input;
21
22use Throwable;
23use InvalidArgumentException;
24use Symfony\Component\Console\Input\InputInterface;
25use Symfony\Component\Console\Input\InputOption;
26use Symfony\Component\Filesystem\Path;
27
28/**
29 * Provides the standard cache option used by cache-writing commands.
30 */
31trait HasCacheOption
32{
33    /**
34     * Adds the standard cache control option to the current command.
35     *
36     * @param string $description the cache option description
37     */
38    protected function addCacheOption(string $description): static
39    {
40        if ($this->getDefinition()->hasOption('cache')) {
41            return $this;
42        }
43
44        return $this->addOption(name: 'cache', mode: InputOption::VALUE_NONE, description: $description);
45    }
46
47    /**
48     * Adds the standard cache directory option to the current command.
49     *
50     * @param string $description the cache directory option description
51     * @param string $default the command-specific default cache directory
52     */
53    protected function addCacheDirOption(string $description, string $default): static
54    {
55        if ($this->getDefinition()->hasOption('cache-dir')) {
56            return $this;
57        }
58
59        return $this->addOption(
60            name: 'cache-dir',
61            mode: InputOption::VALUE_OPTIONAL,
62            description: $description,
63            default: $default,
64        );
65    }
66
67    /**
68     * Resolves whether cache writes SHOULD be enabled for the current invocation.
69     *
70     * @param InputInterface $input the current command input
71     * @param bool $default the command-specific default cache behavior when the option is omitted
72     */
73    protected function isCacheEnabled(InputInterface $input, bool $default = true): bool
74    {
75        if ((bool) $input->getOption('cache')) {
76            return true;
77        }
78
79        if ($this->isNoCacheRequested($input)) {
80            return false;
81        }
82
83        return $default;
84    }
85
86    /**
87     * Returns the explicit cache flag that SHOULD be forwarded to nested commands.
88     *
89     * @param InputInterface $input the current command input
90     */
91    protected function resolveCacheArgument(InputInterface $input): ?string
92    {
93        if ((bool) $input->getOption('cache')) {
94            return '--cache';
95        }
96
97        if ($this->isNoCacheRequested($input)) {
98            return '--no-cache';
99        }
100
101        return null;
102    }
103
104    /**
105     * Resolves a nested cache directory for a child command.
106     *
107     * @param InputInterface $input the current command input
108     * @param string $path the child cache path relative to the current command cache root
109     */
110    protected function resolveCacheDirArgument(InputInterface $input, string $path = ''): ?string
111    {
112        if (! $this->hasExplicitCacheDirArgument($input)) {
113            return null;
114        }
115
116        try {
117            $cacheDir = $input->getOption('cache-dir');
118        } catch (InvalidArgumentException) {
119            return null;
120        }
121
122        if (! \is_string($cacheDir) || '' === $cacheDir) {
123            return null;
124        }
125
126        return '' === $path
127            ? $cacheDir
128            : Path::join($cacheDir, $path);
129    }
130
131    /**
132     * Determines whether the current invocation explicitly passed `--cache-dir`.
133     *
134     * @param InputInterface $input the current command input
135     */
136    private function hasExplicitCacheDirArgument(InputInterface $input): bool
137    {
138        try {
139            return $input->hasParameterOption('--cache-dir', true);
140        } catch (Throwable) {
141            return false;
142        }
143    }
144
145    /**
146     * Determines whether cache writes were explicitly disabled for the current invocation.
147     *
148     * The Composer application already provides a global `--no-cache` flag, so commands
149     * SHALL reuse that switch instead of redefining a local negated variant.
150     *
151     * @param InputInterface $input the current command input
152     */
153    private function isNoCacheRequested(InputInterface $input): bool
154    {
155        try {
156            return true === $input->getOption('no-cache');
157        } catch (InvalidArgumentException) {
158            return false;
159        }
160    }
161}