Completed
Pull Request — master (#11)
by Alexander
02:53
created

Params::unpackPayload()   B

Complexity

Conditions 8
Paths 64

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 37
c 0
b 0
f 0
ccs 18
cts 18
cp 1
rs 8.0835
cc 8
nc 64
nop 2
crap 8
1
<?php
2
/*
3
 * Protocol FCGI library
4
 *
5
 * @copyright Copyright 2021. Lisachenko Alexander <[email protected]>
6
 * This source file is subject to the license that is bundled
7
 * with this source code in the file LICENSE.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Lisachenko\Protocol\FCGI\Record;
13
14
use Lisachenko\Protocol\FCGI;
15
use Lisachenko\Protocol\FCGI\Record;
16
17
/**
18
 * Params request record
19
 *
20
 * @author Alexander.Lisachenko
21
 */
22
class Params extends Record
23
{
24
    /**
25
     * List of params
26
     *
27
     * @var string[]
28
     * @phpstan-var array<string, string>
29
     */
30 7
    protected array $values = [];
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ARRAY, expecting T_FUNCTION or T_CONST
Loading history...
31
32 7
    /**
33 7
     * Constructs a param request
34 7
     *
35 7
     * @phpstan-param array<string, string> $values
36
     */
37
    public function __construct(array $values = [])
38
    {
39
        $this->type   = FCGI::PARAMS;
40
        $this->values = $values;
41
        $this->setContentData($this->packPayload());
42 6
    }
43
44 6
    /**
45
     * Returns an associative list of parameters
46
     *
47
     * @phpstan-return array<string, string>
48
     */
49
    public function getValues(): array
50
    {
51
        return $this->values;
52
    }
53
54
    /**
55 3
     * {@inheritdoc}
56
     */
57 3
    protected static function unpackPayload($self, string $binaryData): void
58
    {
59 3
        assert($self instanceof self);
60 3
        $currentOffset = 0;
61 3
        do {
62
            /** @phpstan-var false|array{nameLengthHigh: int} */
63 3
            $payload = unpack('CnameLengthHigh', $binaryData);
64 3
            if ($payload === false) {
65 3
                throw new \RuntimeException('Can not unpack data from the binary buffer');
66
            }
67
            [$nameLengthHigh] = array_values($payload);
68 3
            $isLongName  = ($nameLengthHigh >> 7 == 1);
69 3
            $valueOffset = $isLongName ? 4 : 1;
70 3
71 3
            /** @phpstan-var false|array{valueLengthHigh: int} */
72
            $payload = unpack('CvalueLengthHigh', substr($binaryData, $valueOffset));
73
            if ($payload === false) {
74
                throw new \RuntimeException('Can not unpack data from the binary buffer');
75 3
            }
76
            [$valueLengthHigh] = array_values($payload);
77
            $isLongValue = ($valueLengthHigh >> 7 == 1);
78 3
            $dataOffset  = $valueOffset + ($isLongValue ? 4 : 1);
79 3
80
            $formatParts = [
81 3
                $isLongName ? 'NnameLength' : 'CnameLength',
82 3
                $isLongValue ? 'NvalueLength' : 'CvalueLength',
83 3
            ];
84 3
            $format      = join('/', $formatParts);
85
86 3
            /** @phpstan-var false|array{nameLength: int, valueLength: int} */
87
            $payload = unpack($format, $binaryData);
88 3
            if ($payload === false) {
89 3
                throw new \RuntimeException('Can not unpack data from the binary buffer');
90 3
            }
91 3
            [$nameLength, $valueLength] = array_values($payload);
92
93 3
            // Clear top bit for long record
94
            $nameLength  &= ($isLongName ? 0x7fffffff : 0x7f);
95
            $valueLength &= ($isLongValue ? 0x7fffffff : 0x7f);
96
97
            /** @phpstan-var false|array{nameData: string, valueData: string} */
98
            $payload = unpack(
99
                "a{$nameLength}nameData/a{$valueLength}valueData",
100
                substr($binaryData, $dataOffset)
101 7
            );
102
            if ($payload === false) {
103 7
                throw new \RuntimeException('Can not unpack data from the binary buffer');
104 7
            }
105 3
            [$nameData, $valueData] = array_values($payload);
106 3
107 3
            $self->values[$nameData] = $valueData;
108 3
109
            $keyValueLength = $dataOffset + $nameLength + $valueLength;
110 3
            $binaryData     = substr($binaryData, $keyValueLength);
111 3
            $currentOffset  += $keyValueLength;
112 3
        } while ($currentOffset < $self->getContentLength());
113 3
    }
114 3
115 3
    /**
116
     * {@inheritdoc}
117 3
     */
118 3
    protected function packPayload(): string
119 3
    {
120 3
        $payload = '';
121 3
        foreach ($this->values as $nameData => $valueData) {
122
            $nameLength  = strlen($nameData);
123 3
            $valueLength = strlen((string)$valueData);
124 7
            $isLongName  = $nameLength > 127;
125
            $isLongValue = $valueLength > 127;
126 7
            $formatParts = [
127
                $isLongName ? 'N' : 'C',
128
                $isLongValue ? 'N' : 'C',
129
                "a{$nameLength}",
130
                "a{$valueLength}",
131
            ];
132
133
            $format = join('', $formatParts);
134
135
            $payload .= pack(
136
                $format,
137
                $isLongName ? ($nameLength | 0x80000000) : $nameLength,
138
                $isLongValue ? ($valueLength | 0x80000000) : $valueLength,
139
                $nameData,
140
                $valueData
141
            );
142
        }
143
144
        return $payload;
145
    }
146
}
147