Completed
Push — master ( 75a7c9...489ab6 )
by
unknown
26s queued 12s
created

RPC::handleBody()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 3
nop 2
dl 0
loc 17
rs 9.3888
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Dead simple, high performance, drop-in bridge to Golang RPC with zero dependencies
5
 *
6
 * @author Wolfy-J
7
 */
8
9
declare(strict_types=1);
10
11
namespace Spiral\Goridge;
12
13
use Spiral\Goridge\RelayInterface as Relay;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Spiral\Goridge\Relay.

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...
14
15
/**
16
 * RPC bridge to Golang net/rpc package over Goridge protocol.
17
 */
18
class RPC
19
{
20
    /** @var Relay */
21
    private $relay;
22
23
    /** @var int */
24
    private $seq = 0;
25
26
    /**
27
     * @param Relay $relay
28
     */
29
    public function __construct(Relay $relay)
30
    {
31
        $this->relay = $relay;
32
    }
33
34
    /**
35
     * @param string $method
36
     * @param mixed  $payload An binary data or array of arguments for complex types.
37
     * @param int    $flags   Payload control flags.
38
     *
39
     * @return mixed
40
     *
41
     * @throws Exceptions\RelayException
42
     * @throws Exceptions\ServiceException
43
     */
44
    public function call(string $method, $payload, int $flags = 0)
45
    {
46
        $header = $method . pack('P', $this->seq);
47
        if (!$this->relay instanceof SendPackageRelayInterface) {
48
            $this->relay->send($header, Relay::PAYLOAD_CONTROL | Relay::PAYLOAD_RAW);
49
        }
50
51
        if ($flags & Relay::PAYLOAD_RAW && is_scalar($payload)) {
52
            if (!$this->relay instanceof SendPackageRelayInterface) {
53
                $this->relay->send((string)$payload, $flags);
54
            } else {
55
                $this->relay->sendPackage(
56
                    $header,
57
                    Relay::PAYLOAD_CONTROL | Relay::PAYLOAD_RAW,
58
                    (string)$payload,
59
                    $flags
60
                );
61
            }
62
        } else {
63
            $body = json_encode($payload);
64
            if ($body === false) {
65
                throw new Exceptions\ServiceException(
66
                    sprintf(
67
                        'json encode: %s',
68
                        json_last_error_msg()
69
                    )
70
                );
71
            }
72
73
            if (!$this->relay instanceof SendPackageRelayInterface) {
74
                $this->relay->send($body);
75
            } else {
76
                $this->relay->sendPackage($header, Relay::PAYLOAD_CONTROL | Relay::PAYLOAD_RAW, $body);
77
            }
78
        }
79
80
        $body = (string)$this->relay->receiveSync($flags);
81
82
        if (!($flags & Relay::PAYLOAD_CONTROL)) {
83
            throw new Exceptions\TransportException('rpc response header is missing');
84
        }
85
86
        $rpc = unpack('Ps', substr($body, -8));
87
        $rpc['m'] = substr($body, 0, -8);
88
89
        if ($rpc['m'] !== $method || $rpc['s'] !== $this->seq) {
90
            throw new Exceptions\TransportException(
91
                sprintf(
92
                    'rpc method call, expected %s:%d, got %s%d',
93
                    $method,
94
                    $this->seq,
95
                    $rpc['m'],
96
                    $rpc['s']
97
                )
98
            );
99
        }
100
101
        // request id++
102
        $this->seq++;
103
104
        // wait for the response
105
        $body = (string)$this->relay->receiveSync($flags);
106
107
        return $this->handleBody($body, $flags);
108
    }
109
110
    /**
111
     * Handle response body.
112
     *
113
     * @param string $body
114
     * @param int    $flags
115
     *
116
     * @return mixed
117
     *
118
     * @throws Exceptions\ServiceException
119
     */
120
    protected function handleBody(string $body, int $flags)
121
    {
122
        if ($flags & Relay::PAYLOAD_ERROR && $flags & Relay::PAYLOAD_RAW) {
123
            throw new Exceptions\ServiceException(
124
                sprintf(
125
                    "error '$body' on '%s'",
126
                    $this->relay instanceof StringableRelayInterface ? (string)$this->relay : get_class($this->relay)
127
                )
128
            );
129
        }
130
131
        if ($flags & Relay::PAYLOAD_RAW) {
132
            return $body;
133
        }
134
135
        return json_decode($body, true);
136
    }
137
}
138