Passed
Pull Request — master (#225)
by Dmitriy
13:44
created

Connection::broadcast()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 17
rs 9.8333
1
<?php
2
3
/** @noinspection PhpComposerExtensionStubsInspection */
4
5
declare(strict_types=1);
6
7
namespace Yiisoft\Yii\Debug\DevServer;
8
9
use Closure;
10
use RuntimeException;
11
use Socket;
12
use Throwable;
13
14
final class Connection
15
{
16
    public const DEFAULT_SOCKET_DIR = '/tmp/var-dumper';
17
    public const DEFAULT_SOCKET_URL = '/tmp/var-dumper-%d.sock';
18
19
    public function __construct(
20
        private Socket $socket,
21
    ) {
22
    }
23
24
    public static function create(): self
25
    {
26
        $socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
27
28
        return new self(
29
            $socket,
0 ignored issues
show
Bug introduced by
It seems like $socket can also be of type resource; however, parameter $socket of Yiisoft\Yii\Debug\DevSer...nnection::__construct() does only seem to accept Socket, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

29
            /** @scrutinizer ignore-type */ $socket,
Loading history...
30
        );
31
    }
32
33
    public function bind(): void
34
    {
35
        $n = random_int(0, PHP_INT_MAX);
36
        $file = sprintf(self::DEFAULT_SOCKET_URL, $n);
37
        if (!socket_bind($this->socket, $file)) {
38
            $socket_last_error = socket_last_error($this->socket);
39
40
            throw new RuntimeException(
41
                sprintf(
42
                    'An error occurred while reading the socket. "socket_last_error" returned %d: "%s".',
43
                    $socket_last_error,
44
                    socket_strerror($socket_last_error),
45
                ),
46
            );
47
        }
48
    }
49
50
    public function broadcast(string $data): void
51
    {
52
        $files = glob(self::DEFAULT_SOCKET_DIR . '-*.sock', GLOB_NOSORT);
53
        //echo 'Files: ' . implode(', ', $files) . "\n";
54
        foreach ($files as $file) {
55
            $socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
56
            if (!@socket_connect($socket, $file)) {
57
                @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

57
                /** @scrutinizer ignore-unhandled */ @unlink($file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
58
                continue;
59
            }
60
            try {
61
                socket_send($socket, $data, strlen($data), 0);
62
            } catch (Throwable $e) {
63
                unlink($file);
64
                throw $e;
65
            } finally {
66
                socket_close($socket);
67
            }
68
        }
69
    }
70
71
    public function close(): void
72
    {
73
        @socket_getsockname($this->socket, $path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for socket_getsockname(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

73
        /** @scrutinizer ignore-unhandled */ @socket_getsockname($this->socket, $path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
74
        @socket_close($this->socket);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for socket_close(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

74
        /** @scrutinizer ignore-unhandled */ @socket_close($this->socket);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Bug introduced by
Are you sure the usage of socket_close($this->socket) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
75
        @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

75
        /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
76
    }
77
78
    public function read(Closure $onSuccess, ?Closure $onError = null): \Generator
79
    {
80
        while (true) {
81
            if (!socket_recvfrom($this->socket, $buffer, 32768, MSG_DONTWAIT, $ip, $port)) {
82
                $socket_last_error = socket_last_error($this->socket);
83
                if ($socket_last_error === 35) {
84
                    continue;
85
                }
86
                $this->close();
87
                if ($onError !== null) {
88
                    yield $onError($socket_last_error);
89
                    break;
90
                }
91
                throw new RuntimeException(
92
                    sprintf(
93
                        'An error occurred while reading the socket. socket_last_error returned %d: "%s".',
94
                        $socket_last_error,
95
                        socket_strerror($socket_last_error)
96
                    ),
97
                );
98
            }
99
            yield $onSuccess($buffer, $ip, $port);
100
        }
101
    }
102
}
103