Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.19% covered (warning)
85.19%
23 / 27
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HasJsonOption
85.19% covered (warning)
85.19%
23 / 27
50.00% covered (danger)
50.00%
3 / 6
14.64
0.00% covered (danger)
0.00%
0 / 1
 addJsonOption
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 isJsonOutput
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 isPrettyJsonOutput
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isImplicitJsonOutputEnabled
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
4.07
 resolveRuntimeEnvironment
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 isOptionEnabled
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 FastForward\DevTools\Container\ContainerFactory;
23use FastForward\DevTools\Environment\RuntimeEnvironmentInterface;
24use Symfony\Component\Console\Input\InputInterface;
25use Symfony\Component\Console\Input\InputOption;
26use Throwable;
27
28/**
29 * Provides the standard JSON output options used by DevTools commands.
30 */
31trait HasJsonOption
32{
33    /**
34     * Adds the standard JSON output options to the current command.
35     */
36    protected function addJsonOption(): static
37    {
38        return $this
39            ->addOption(name: 'json', mode: InputOption::VALUE_NONE, description: 'Emit structured JSON output.')
40            ->addOption(
41                name: 'pretty-json',
42                mode: InputOption::VALUE_NONE,
43                description: 'Emit structured JSON output using indentation for readability.',
44            );
45    }
46
47    /**
48     * Determines whether JSON output was requested.
49     *
50     * The pretty-json flag SHALL imply JSON output.
51     *
52     * @param InputInterface $input
53     */
54    protected function isJsonOutput(InputInterface $input): bool
55    {
56        if ($this->isPrettyJsonOutput($input)) {
57            return true;
58        }
59
60        if ($this->isOptionEnabled($input, 'json')) {
61            return true;
62        }
63
64        return $this->isImplicitJsonOutputEnabled();
65    }
66
67    /**
68     * Determines whether pretty JSON output was requested.
69     *
70     * @param InputInterface $input
71     */
72    protected function isPrettyJsonOutput(InputInterface $input): bool
73    {
74        return $this->isOptionEnabled($input, 'pretty-json');
75    }
76
77    /**
78     * Determines whether structured JSON output SHOULD be enabled implicitly.
79     *
80     * Commands MAY opt into runtime-environment-aware behavior by exposing a
81     * `$runtimeEnvironment` property. Commands that do not expose it SHALL fall
82     * back to the shared runtime-environment service from the DevTools container.
83     */
84    private function isImplicitJsonOutputEnabled(): bool
85    {
86        $runtimeEnvironment = $this->resolveRuntimeEnvironment();
87
88        if (! $runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
89            $runtimeEnvironment = ContainerFactory::get(RuntimeEnvironmentInterface::class);
90        }
91
92        if (! $runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
93            return false;
94        }
95
96        return $runtimeEnvironment->isAgentPresent() && ! $runtimeEnvironment->isComposerTestRun();
97    }
98
99    /**
100     * @return ?RuntimeEnvironmentInterface
101     */
102    private function resolveRuntimeEnvironment(): ?RuntimeEnvironmentInterface
103    {
104        if (! property_exists($this, 'runtimeEnvironment')) {
105            return null;
106        }
107
108        if (! $this->runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
109            return null;
110        }
111
112        return $this->runtimeEnvironment;
113    }
114
115    /**
116     * Determines whether a boolean input option was enabled.
117     *
118     * @param InputInterface $input
119     * @param string $option
120     */
121    private function isOptionEnabled(InputInterface $input, string $option): bool
122    {
123        try {
124            return (bool) $input->getOption($option);
125        } catch (Throwable) {
126            return false;
127        }
128    }
129}