Passed
Pull Request — 2.x (#60)
by
unknown
17:12
created

Interpolator::resolveValue()   B

Complexity

Conditions 11
Paths 11

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 11.4763

Importance

Changes 0
Metric Value
cc 11
eloc 19
c 0
b 0
f 0
nc 11
nop 1
dl 0
loc 33
ccs 16
cts 19
cp 0.8421
crap 11.4763
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Query;
13
14
use Cycle\Database\Injection\ParameterInterface;
15
use DateTimeInterface;
16
17
/**
18
 * Simple helper class used to interpolate query with given values. To be used for profiling and
19
 * debug purposes only.
20
 */
21
final class Interpolator
22
{
23
    /**
24
     * Injects parameters into statement. For debug purposes only.
25
     *
26
     * @psalm-param non-empty-string $query
27
     *
28
     * @psalm-return non-empty-string
29
     */
30 3730
    public static function interpolate(string $query, iterable $parameters = []): string
31
    {
32 3730
        if ($parameters === []) {
33 3724
            return $query;
34
        }
35
36
        ['named' => $named, 'unnamed' => $unnamed] = self::normalizeParameters($parameters);
37 2026
        $params = self::findParams($query);
38 2026
39 20
        $caret = 0;
40 20
        $result = '';
41 20
        foreach ($params as $pos => $ph) {
42
            $result .= \substr($query, $caret, $pos - $caret);
43
            $caret = $pos + \strlen($ph);
44 20
            // find param
45
            $result .= match (true) {
46
                $ph === '?' && \count($unnamed) > 0 => self::resolveValue(\array_shift($unnamed)),
47 2022
                \array_key_exists($ph, $named) => self::resolveValue($named[$ph]),
48 2022
                default => $ph,
49 2022
            };
50
        }
51
        $result .= \substr($query, $caret);
52
53
        return $result;
54 2026
    }
55
56
    /**
57
     * Get parameter value.
58
     *
59
     * @psalm-return non-empty-string
60
     */
61
    private static function resolveValue(mixed $parameter): string
62 2026
    {
63
        if ($parameter instanceof ParameterInterface) {
64 2026
            return self::resolveValue($parameter->getValue());
65 432
        }
66
67
        switch (gettype($parameter)) {
68 2026
            case 'boolean':
69 2026
                return $parameter ? 'TRUE' : 'FALSE';
70
71
            case 'integer':
72 2026
                return (string)($parameter + 0);
73 590
74
            case 'NULL':
75 2026
                return 'NULL';
76 24
77
            case 'double':
78 2026
                return sprintf('%F', $parameter);
79
80
            case 'string':
81 2026
                return "'" . addcslashes($parameter, "'") . "'";
82 2026
83
            case 'object':
84 20
                if (method_exists($parameter, '__toString')) {
85 12
                    return "'" . addcslashes((string)$parameter, "'") . "'";
86
                }
87
88
                if ($parameter instanceof DateTimeInterface) {
89 12
                    return "'" . $parameter->format(DateTimeInterface::ATOM) . "'";
90 12
                }
91
        }
92
93
        return '[UNRESOLVED]';
94 8
    }
95
96
    /**
97
     * @return array<int, string>
98
     */
99
    private static function findParams(string $query): array
100
    {
101
        \preg_match_all(
102
            '/(?<dq>"(?:\\\\"|[^"])*")|(?<sq>\'[^\']*\')|(?<ph>\\?)|(?<named>:[a-z_]+)/',
103
            $query,
104
            $placeholders,
105
            PREG_OFFSET_CAPTURE|PREG_UNMATCHED_AS_NULL
106
        );
107
        $result = [];
108 2022
        foreach ([...$placeholders['named'], ...$placeholders['ph']] as $tuple) {
109
            if ($tuple[0] === null) {
110
                continue;
111
            }
112
            $result[$tuple[1]] = $tuple[0];
113 2022
        }
114 2022
        \ksort($result);
115 2022
116
        return $result;
117
    }
118
119
    /**
120
     * @return array{named: array, unnamed: array}
121
     */
122
    private static function normalizeParameters(iterable $parameters): array
123
    {
124
        $result = ['named' => [], 'unnamed' => []];
125
        foreach ($parameters as $k => $v) {
126
            if (\is_int($k)) {
127
                $result['unnamed'][$k] = $v;
128
            } else {
129
                $result['named'][':' . \ltrim($k, ':')] = $v;
130
            }
131
        }
132
        return $result;
133
    }
134
}
135