Passed
Push — master ( 57f3ff...f2fe82 )
by Melech
04:07
created

Question   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 110
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 12
eloc 29
c 0
b 0
f 0
dl 0
loc 110
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A fopen() 0 3 1
A fgets() 0 3 1
A ask() 0 25 4
A getAnswer() 0 4 1
A __construct() 0 11 2
A withAnswer() 0 8 1
A getCallable() 0 4 1
A withCallable() 0 8 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Cli\Interaction\Message;
15
16
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use Valkyrja\Cli\Interaction\Exception\InvalidArgumentException;
18
use Valkyrja\Cli\Interaction\Formatter\Contract\Formatter;
19
use Valkyrja\Cli\Interaction\Formatter\QuestionFormatter;
20
use Valkyrja\Cli\Interaction\Message\Contract\Answer;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Valkyrja\Cli\Interaction\Message\Answer. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
21
use Valkyrja\Cli\Interaction\Message\Contract\Question as Contract;
22
use Valkyrja\Cli\Interaction\Output\Contract\Output;
23
24
use function fgets;
25
use function fopen;
26
use function is_callable;
27
28
/**
29
 * Class Question.
30
 *
31
 * @author Melech Mizrachi
32
 */
33
class Question extends Message implements Contract
34
{
35
    /**
36
     * @param non-empty-string                $text     The text
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
37
     * @param callable(Output, Answer):Output $callable The callable
38
     */
39
    public function __construct(
40
        string $text,
41
        protected $callable,
42
        protected Answer $answer,
43
        Formatter|null $formatter = new QuestionFormatter()
44
    ) {
45
        if (! is_callable($this->callable)) {
46
            throw new InvalidArgumentException('$callable must be a valid callable');
47
        }
48
49
        parent::__construct($text, $formatter);
50
    }
51
52
    /**
53
     * @inheritDoc
54
     */
55
    #[Override]
56
    public function getCallable(): callable
57
    {
58
        return $this->callable;
59
    }
60
61
    /**
62
     * @inheritDoc
63
     */
64
    #[Override]
65
    public function withCallable(callable $callable): static
66
    {
67
        $new = clone $this;
68
69
        $new->callable = $callable;
70
71
        return $new;
72
    }
73
74
    /**
75
     * @inheritDoc
76
     */
77
    #[Override]
78
    public function getAnswer(): Answer
79
    {
80
        return $this->answer;
81
    }
82
83
    /**
84
     * @inheritDoc
85
     */
86
    #[Override]
87
    public function withAnswer(Answer $answer): static
88
    {
89
        $new = clone $this;
90
91
        $new->answer = $answer;
92
93
        return $new;
94
    }
95
96
    /**
97
     * @inheritDoc
98
     */
99
    #[Override]
100
    public function ask(): Answer
101
    {
102
        $answer = $this->answer;
103
        $handle = $this->fopen(filename: 'php://stdin', mode: 'rb');
104
105
        if ($handle === false) {
106
            // TODO: Determine if we want to throw RuntimeException (UnhandledStreamQuestionException) here
107
            return $answer;
108
        }
109
110
        $line = $this->fgets($handle);
111
112
        if ($line === false) {
113
            // TODO: Determine if we want to throw RuntimeException (UnhandledLineQuestionException) here
114
            return $answer;
115
        }
116
117
        $response = trim($line);
118
119
        if ($response === '') {
120
            return $answer;
121
        }
122
123
        return $answer->withUserResponse($response);
124
    }
125
126
    /**
127
     * @param non-empty-string $filename The filename to open
0 ignored issues
show
Documentation Bug introduced by
The doc comment non-empty-string at position 0 could not be parsed: Unknown type name 'non-empty-string' at position 0 in non-empty-string.
Loading history...
128
     * @param non-empty-string $mode     The mode
129
     *
130
     * @return resource|false
131
     */
132
    protected function fopen(string $filename, string $mode)
133
    {
134
        return fopen(filename: $filename, mode: $mode);
135
    }
136
137
    /**
138
     * @param resource $stream The stream
139
     */
140
    protected function fgets($stream): string|false
141
    {
142
        return fgets($stream);
143
    }
144
}
145