Passed
Push — master ( 34b8c4...58f43e )
by Sergei
26:51 queued 24:15
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 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 3
rs 10
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
     */
31
    private mixed $stream = null;
32
33
    /**
34
     * @var resource|Socket|string
35
     */
36
    private mixed $uri;
37
38
    private const SOCKET_PROTOCOLS = ['udp', 'udg', 'tcp', 'unix'];
39
40
    /**
41
     * @param mixed|resource|string $uri
42
     */
43 17
    public function __construct(
44
        mixed $uri = 'udp://127.0.0.1:8890'
45
    ) {
46 17
        if (!is_string($uri) && !is_resource($uri) && !$uri instanceof Socket) {
47 1
            throw new InvalidArgumentException(
48 1
                sprintf(
49 1
                    'Argument $uri must be either a string, a resource or a Socket instance, "%s" given.',
50 1
                    get_debug_type($uri)
51 1
                )
52 1
            );
53
        }
54 16
        $this->uri = $uri;
55
    }
56
57 16
    public function __destruct()
58
    {
59 16
        if (!is_string($this->uri) || !is_resource($this->stream)) {
60 14
            return;
61
        }
62 3
        fclose($this->stream);
63
    }
64
65
    /**
66
     * 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...
67
     */
68 15
    public function handle(mixed $variable, int $depth, bool $highlight = false): void
69
    {
70 15
        $data = ($this->encoder ?? '\json_encode')($variable);
71 15
        if (!is_string($data)) {
72 1
            throw new RuntimeException(
73 1
                sprintf(
74 1
                    'Encoder must return a string, "%s" returned.',
75 1
                    get_debug_type($data)
76 1
                )
77 1
            );
78
        }
79
80 14
        if (!is_resource($this->stream) && !$this->stream instanceof Socket) {
81 14
            $this->initializeStream();
82
        }
83
84 14
        if (!$this->writeToStream($data)) {
85 1
            $this->initializeStream();
86
87 1
            if (!$this->writeToStream($data)) {
88
                throw new RuntimeException('Cannot write a stream.');
89
            }
90
        }
91
    }
92
93
    /**
94
     * @param callable(mixed $variable): string $encoder Encoder that will be used to encode variable before sending it to the stream.
95
     */
96 3
    public function withEncoder(callable $encoder): static
97
    {
98 3
        $new = clone $this;
99 3
        $new->encoder = $encoder;
100 3
        return $new;
101
    }
102
103 14
    private function initializeStream(): void
104
    {
105 14
        if (!is_string($this->uri)) {
106 11
            $this->stream = $this->uri;
107
        } else {
108 3
            $uriHasSocketProtocol = false;
109 3
            foreach (self::SOCKET_PROTOCOLS as $protocol) {
110 3
                if (str_starts_with($this->uri, "$protocol://")) {
111 2
                    $uriHasSocketProtocol = true;
112 2
                    break;
113
                }
114
            }
115
116 3
            $this->stream = $uriHasSocketProtocol ? fsockopen($this->uri) : fopen($this->uri, 'wb+');
117
        }
118
119 14
        if (!is_resource($this->stream) && !$this->stream instanceof Socket) {
120 1
            throw new RuntimeException('Cannot initialize a stream.');
121
        }
122
    }
123
124 14
    private function writeToStream(string $data): bool
125
    {
126 14
        if ($this->stream === null) {
127
            return false;
128
        }
129
130 14
        if ($this->stream instanceof Socket) {
131 1
            socket_write($this->stream, $data, strlen($data));
132
133 1
            return true;
134
        }
135
136 13
        return @fwrite($this->stream, $data) !== false;
137
    }
138
}
139