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     * @return static
37     */
38    protected function addJsonOption(): static
39    {
40        return $this
41            ->addOption(name: 'json', mode: InputOption::VALUE_NONE, description: 'Emit structured JSON output.')
42            ->addOption(
43                name: 'pretty-json',
44                mode: InputOption::VALUE_NONE,
45                description: 'Emit structured JSON output using indentation for readability.',
46            );
47    }
48
49    /**
50     * Determines whether JSON output was requested.
51     *
52     * The pretty-json flag SHALL imply JSON output.
53     *
54     * @param InputInterface $input
55     */
56    protected function isJsonOutput(InputInterface $input): bool
57    {
58        if ($this->isPrettyJsonOutput($input)) {
59            return true;
60        }
61
62        if ($this->isOptionEnabled($input, 'json')) {
63            return true;
64        }
65
66        return $this->isImplicitJsonOutputEnabled();
67    }
68
69    /**
70     * Determines whether pretty JSON output was requested.
71     *
72     * @param InputInterface $input
73     */
74    protected function isPrettyJsonOutput(InputInterface $input): bool
75    {
76        return $this->isOptionEnabled($input, 'pretty-json');
77    }
78
79    /**
80     * Determines whether structured JSON output SHOULD be enabled implicitly.
81     *
82     * Commands MAY opt into runtime-environment-aware behavior by exposing a
83     * `$runtimeEnvironment` property. Commands that do not expose it SHALL fall
84     * back to the shared runtime-environment service from the DevTools container.
85     */
86    private function isImplicitJsonOutputEnabled(): bool
87    {
88        $runtimeEnvironment = $this->resolveRuntimeEnvironment();
89
90        if (! $runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
91            $runtimeEnvironment = ContainerFactory::get(RuntimeEnvironmentInterface::class);
92        }
93
94        if (! $runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
95            return false;
96        }
97
98        return $runtimeEnvironment->isAgentPresent() && ! $runtimeEnvironment->isComposerTestRun();
99    }
100
101    /**
102     * @return ?RuntimeEnvironmentInterface
103     */
104    private function resolveRuntimeEnvironment(): ?RuntimeEnvironmentInterface
105    {
106        if (! property_exists($this, 'runtimeEnvironment')) {
107            return null;
108        }
109
110        if (! $this->runtimeEnvironment instanceof RuntimeEnvironmentInterface) {
111            return null;
112        }
113
114        return $this->runtimeEnvironment;
115    }
116
117    /**
118     * Determines whether a boolean input option was enabled.
119     *
120     * @param InputInterface $input
121     * @param string $option
122     */
123    private function isOptionEnabled(InputInterface $input, string $option): bool
124    {
125        try {
126            return (bool) $input->getOption($option);
127        } catch (Throwable) {
128            return false;
129        }
130    }
131}