Passed
Pull Request — master (#95)
by Dmitriy
02:37
created

StreamHandler::__destruct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\VarDumper\Handler;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Socket;
10
use Yiisoft\VarDumper\HandlerInterface;
11
12
use function fsockopen;
13
use function fwrite;
14
use function get_debug_type;
15
use function is_resource;
16
use function is_string;
17
18
/**
19
 * Uses stream ({@link https://www.php.net/manual/en/intro.stream.php} for writing variable's data. Requires "sockets"
20
 * PHP extension when {@see StreamHandler::$uri} is a {@see Socket} instance.
21
 */
22
final class StreamHandler implements HandlerInterface
23
{
24
    /**
25
     * @var callable|null
26
     */
27
    private mixed $encoder = null;
28
    /**
29
     * @var resource|Socket|null
30
     * @psalm-suppress PropertyNotSetInConstructor
31
     */
32
    private mixed $stream = null;
33
34
    /**
35
     * @var resource|Socket|string
36
     */
37
    private mixed $uri;
38
39
    private const SOCKET_PROTOCOLS = ['udp', 'udg', 'tcp', 'unix'];
40
41
    /**
42
     * @param mixed|resource|string $uri
43
     */
44 17
    public function __construct(
45
        mixed $uri = 'udp://127.0.0.1:8890'
46
    ) {
47 17
        if (!is_string($uri) && !is_resource($uri) && !$uri instanceof Socket) {
48 1
            throw new InvalidArgumentException(
49 1
                sprintf(
50 1
                    'Argument $uri must be either a string, a resource or a Socket instance, "%s" given.',
51 1
                    get_debug_type($uri)
52 1
                )
53 1
            );
54
        }
55 16
        $this->uri = $uri;
56
    }
57
58 16
    public function __destruct()
59
    {
60 16
        if (!is_string($this->uri) || !is_resource($this->stream)) {
61 14
            return;
62
        }
63 3
        fclose($this->stream);
64
    }
65
66
    /**
67
     * Encodes {@param $variable} with {@see self::$encoder} and sends the result to the stream.
0 ignored issues
show
Bug introduced by
The type Yiisoft\VarDumper\Handler\with 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...
68
     */
69 15
    public function handle(mixed $variable, int $depth, bool $highlight = false): void
70
    {
71 15
        $data = ($this->encoder ?? '\json_encode')($variable);
72 15
        if (!is_string($data)) {
73 1
            throw new RuntimeException(
74 1
                sprintf(
75 1
                    'Encoder must return a string, "%s" returned.',
76 1
                    get_debug_type($data)
77 1
                )
78 1
            );
79
        }
80
81 14
        if (!is_resource($this->stream) && !$this->stream instanceof Socket) {
82 14
            $this->initializeStream();
83
        }
84
85 14
        if (!$this->writeToStream($data)) {
86 1
            $this->initializeStream();
87
88 1
            if (!$this->writeToStream($data)) {
89
                throw new RuntimeException('Cannot write a stream.');
90
            }
91
        }
92
    }
93
94 3
    public function withEncoder(callable $encoder): HandlerInterface
95
    {
96 3
        $new = clone $this;
97 3
        $new->encoder = $encoder;
98 3
        return $new;
99
    }
100
101 14
    private function initializeStream(): void
102
    {
103 14
        if (!is_string($this->uri)) {
104 11
            $this->stream = $this->uri;
105
        } else {
106 3
            $uriHasSocketProtocol = false;
107 3
            foreach (self::SOCKET_PROTOCOLS as $protocol) {
108 3
                if (str_starts_with($this->uri, "$protocol://")) {
109 2
                    $uriHasSocketProtocol = true;
110 2
                    break;
111
                }
112
            }
113
114 3
            $this->stream = $uriHasSocketProtocol ? fsockopen($this->uri) : fopen($this->uri, 'wb+');
115
        }
116
117 14
        if (!is_resource($this->stream) && !$this->stream instanceof Socket) {
118 1
            throw new RuntimeException('Cannot initialize a stream.');
119
        }
120
    }
121
122 14
    private function writeToStream(string $data): bool
123
    {
124 14
        if ($this->stream === null) {
125
            return false;
126
        }
127
128 14
        if ($this->stream instanceof Socket) {
129 1
            socket_write($this->stream, $data, strlen($data));
130
131 1
            return true;
132
        }
133
134 13
        return @fwrite($this->stream, $data) !== false;
135
    }
136
}
137