Passed
Push — main ( b157b0...77200e )
by Peter
02:25
created

Expr::bindParams()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 12
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 6
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace QB\Generic\Expr;
6
7
use InvalidArgumentException;
8
use QB\Generic\IQueryPart;
9
use QB\Generic\Params\Params;
10
11
class Expr implements IQueryPart
12
{
13
    protected string $sql;
14
15
    protected bool $useNamedParams;
16
17
    protected Params $params;
18
19
    /** @var int Helps tracking the extensions done on the SQL originally received */
20
    protected int $extendedBy = 0;
21
22
    /**
23
     * Expr constructor.
24
     *
25
     * @param string|IQueryPart $sql
26
     * @param array             $params
27
     * @param int               $paramHandle
28
     */
29 68
    public function __construct(string|IQueryPart $sql, array $params = [], int $paramHandle = Params::ALL_AUTO)
30
    {
31 68
        $this->sql = (string)$sql;
32
33 68
        $this->useNamedParams = !array_key_exists(0, $params);
34
35 68
        $allParams = [];
36 68
        foreach ($params as $origKey => $origParam) {
37 47
            $fixedParam = $this->toParamArray($origKey, $origParam, $paramHandle);
38 46
            if (count($fixedParam) > 1) {
39 12
                if (!$this->useNamedParams) {
40 6
                    $this->expandSqlUnnamed($origKey, count($fixedParam));
41
                } else {
42 6
                    $this->expandSqlNamed($origKey, count($fixedParam));
43
                }
44
            }
45
46 46
            foreach ($fixedParam as $fixedKey => $var) {
47 46
                if ($this->useNamedParams) {
48 11
                    $allParams[$fixedKey] = $var;
49
                } else {
50 35
                    $allParams[] = $var;
51
                }
52
            }
53
        }
54
55 67
        $this->params = new Params($allParams, $paramHandle);
56 64
    }
57
58
    /**
59
     * @param string|int $origKey
60
     * @param            $param
61
     * @param int        $paramHandle
62
     *
63
     * @return array
64
     */
65 47
    protected function toParamArray(string|int $origKey, $param, int $paramHandle): array
66
    {
67 47
        if (!$this->isParamArray($param, $paramHandle)) {
68 40
            if ($this->useNamedParams) {
69 8
                return [$origKey => $param];
70
            }
71
72 32
            return [$param];
73
        }
74
75 15
        if (!$this->useNamedParams) {
76 9
            return $param;
77
        }
78
79 6
        $fixedParams = [];
80 6
        foreach ($param as $i => $p) {
81 6
            $fixedParams[$origKey . '_' . $i] = $p;
82
        }
83
84 6
        return $fixedParams;
85
    }
86
87
    /**
88
     * @param     $param
89
     * @param int $paramHandle
90
     *
91
     * @return bool
92
     */
93 47
    protected function isParamArray($param, int $paramHandle): bool
94
    {
95 47
        if (is_scalar($param) || is_null($param)) {
96 36
            return false;
97
        }
98
99 18
        if (!is_array($param)) {
100 1
            throw new InvalidArgumentException(sprintf('param must be scalar or array, %s received.', gettype($param)));
101
        }
102
103 17
        if (in_array($paramHandle, [Params::ALL_AUTO, Params::ALL_STRING], true)) {
104 13
            return true;
105
        }
106
107 4
        if (count($param) == 2 && is_scalar($param[0]) && is_int($param[1]) && $param[1] >= 0) {
108 4
            return false;
109
        }
110
111 2
        return true;
112
    }
113
114
    /**
115
     * @param int $origKey
116
     * @param int $fixedParamCount
117
     */
118 6
    protected function expandSqlUnnamed(int $origKey, int $fixedParamCount): void
119
    {
120 6
        $count = $fixedParamCount - 1;
121 6
        $pos   = $origKey + $this->extendedBy + $count;
122
123 6
        $parts = explode('?', $this->sql);
124 6
        $qs    = '?' . str_repeat(', ?', $count);
125 6
        $start = implode('?', array_slice($parts, 0, $pos));
126 6
        $end   = implode('?', array_slice($parts, $pos));
127
128 6
        $this->sql        = $start . $qs . $end;
129 6
        $this->extendedBy += $fixedParamCount - 1;
130 6
    }
131
132
    /**
133
     * @param string $origKey
134
     * @param int    $fixedParamCount
135
     */
136 6
    protected function expandSqlNamed(string $origKey, int $fixedParamCount): void
137
    {
138 6
        $searchKey = ':' . $origKey;
139 6
        $parts     = [];
140 6
        for ($i = 0; $i < $fixedParamCount; $i++) {
141 6
            $parts[] = $searchKey . '_' . $i;
142
        }
143 6
        $replace = implode(', ', $parts);
144
145 6
        $this->sql = str_replace($searchKey, $replace, $this->sql);
146 6
    }
147
148
    /**
149
     * @return string
150
     */
151 57
    public function __toString(): string
152
    {
153 57
        return $this->sql;
154
    }
155
156
    /**
157
     * @return array
158
     */
159 34
    public function getParams(): array
160
    {
161 34
        return $this->params->getAll();
162
    }
163
}
164