Passed
Pull Request — master (#32)
by Wilmer
14:54
created

ArrayParser::parseArray()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 18
nc 8
nop 2
dl 0
loc 26
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use function in_array;
8
use function strlen;
9
10
final class ArrayParser
11
{
12
    /**
13
     * @var string Character used in array
14
     */
15
    private string $delimiter = ',';
16
17
    /**
18
     * Convert array from PostgreSQL to PHP
19
     *
20
     * @param string $value string to be converted
21
     *
22
     * @return array|null
23
     */
24
    public function parse(?string $value): ?array
25
    {
26
        if ($value === null) {
27
            return null;
28
        }
29
30
        if ($value === '{}') {
31
            return [];
32
        }
33
34
        return $this->parseArray($value);
35
    }
36
37
    /**
38
     * Pares PgSQL array encoded in string.
39
     *
40
     * @param string $value
41
     * @param int $i parse starting position.
42
     *
43
     * @return array
44
     */
45
    private function parseArray(string $value, int &$i = 0): array
46
    {
47
        $result = [];
48
        $len = strlen($value);
49
50
        for (++$i; $i < $len; ++$i) {
51
            switch ($value[$i]) {
52
                case '{':
53
                    $result[] = $this->parseArray($value, $i);
54
                    break;
55
                case '}':
56
                    break 2;
57
                case $this->delimiter:
58
                    if (empty($result)) { /* `{}` case */
59
                        $result[] = null;
60
                    }
61
                    if (in_array($value[$i + 1], [$this->delimiter, '}'], true)) { /* `{,}` case */
62
                        $result[] = null;
63
                    }
64
                    break;
65
                default:
66
                    $result[] = $this->parseString($value, $i);
67
            }
68
        }
69
70
        return $result;
71
    }
72
73
    /**
74
     * Parses PgSQL encoded string.
75
     *
76
     * @param string $value
77
     * @param int $i parse starting position.
78
     *
79
     * @return string|null
80
     */
81
    private function parseString(string $value, int &$i): ?string
82
    {
83
        $isQuoted = $value[$i] === '"';
84
        $stringEndChars = $isQuoted ? ['"'] : [$this->delimiter, '}'];
85
        $result = '';
86
        $len = strlen($value);
87
88
        for ($i += $isQuoted ? 1 : 0; $i < $len; ++$i) {
89
            if (in_array($value[$i], ['\\', '"'], true) && in_array($value[$i + 1], [$value[$i], '"'], true)) {
90
                ++$i;
91
            } elseif (in_array($value[$i], $stringEndChars, true)) {
92
                break;
93
            }
94
95
            $result .= $value[$i];
96
        }
97
98
        $i -= $isQuoted ? 0 : 1;
99
100
        if (!$isQuoted && $result === 'NULL') {
101
            $result = null;
102
        }
103
104
        return $result;
105
    }
106
}
107