-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInPlaceholders.php
More file actions
147 lines (136 loc) · 3.97 KB
/
Copy pathInPlaceholders.php
File metadata and controls
147 lines (136 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php
declare(strict_types=1);
namespace Bancer\NativeQueryMapper\Database;
use Cake\Database\StatementInterface;
use InvalidArgumentException;
use PDO;
/**
* Value object representing a set of named placeholders for use in SQL IN() clauses.
*
* This class encapsulates:
* - Placeholder generation (via __toString())
* - Safe binding of multiple scalar values to a prepared statement
* - Lazy inference of the appropriate PDO parameter type
*
* Example:
* ```php
* $statuses = new InPlaceholders('status', [1, 5, 9]);
* $sql = "SELECT email AS Users_email FROM users WHERE status_id IN ($statuses)";
* $stmt = $this->prepareNativeStatement($sql);
* $statuses->bindValuesToStatement($stmt);
* ```
*
* Resulting SQL:
* ```sql
* SELECT email AS Users_email FROM users WHERE status_id status_id IN (:status_0, :status_1, :status_2)
* ```
*/
class InPlaceholders
{
/**
* Placeholder name prefix (without colon).
*
* Example: "status" -> :status_0, :status_1, ...
*
* @var string
*/
private string $prefix;
/**
* Scalar values to be bound to the placeholders.
*
* @var list<scalar>
*/
private array $values;
/**
* PDO parameter type used when binding values.
*
* If not provided, the type is inferred from the first value.
*
* @var string|int|null
*/
private $pdoType;
/**
* Constructor.
*
* @param string $prefix Placeholder prefix (eg. "status").
* @param list<scalar> $values Values for the IN() clause
* @param string|int|null $pdoType PDO::PARAM_* constant or name of configured Type class.
* Same as `$type` parameter of \Cake\Database\StatementInterface::bindValue()
*/
public function __construct(string $prefix, array $values, $pdoType = null)
{
if ($prefix === '') {
throw new InvalidArgumentException('IN() placeholders cannot be constructed with an empty prefix');
}
if ($values === []) {
throw new InvalidArgumentException('IN() placeholders cannot be constructed with an empty value list');
}
$this->prefix = $prefix;
$this->values = $values;
$this->pdoType = $pdoType;
}
/**
* Bind all placeholder values to the prepared statement.
*
* Placeholders are bound using the pattern:
* :{prefix}_{index}
*
* @param \Cake\Database\StatementInterface $stmt Prepared statement.
* @return void
*/
public function bindValuesToStatement(StatementInterface $stmt): void
{
foreach ($this->values as $index => $value) {
$stmt->bindValue($this->prefix . '_' . $index, $value, $this->getPdoType());
}
}
/**
* Resolve the PDO parameter type.
*
* If the type was not provided in the constructor, it is inferred lazily
* from the first value in the list.
*
* @return string|int PDO::PARAM_* constant or name of configured Type class.
*/
private function getPdoType()
{
if (!isset($this->pdoType)) {
$this->pdoType = $this->inferPdoType();
}
return $this->pdoType;
}
/**
* Infer the PDO parameter type from the first value.
*
* @return int PDO::PARAM_* constant
*/
private function inferPdoType(): int
{
$first = $this->values[0];
if (is_int($first)) {
return PDO::PARAM_INT;
}
if (is_bool($first)) {
return PDO::PARAM_BOOL;
}
return PDO::PARAM_STR;
}
/**
* Generate the SQL placeholder list for use inside an IN() clause.
*
* Example output:
* ```sql
* :status_0, :status_1, :status_2
* ```
*
* @return string
*/
public function __toString(): string
{
$placeholders = [];
foreach ($this->values as $index => $_) {
$placeholders[] = ':' . $this->prefix . '_' . $index;
}
return implode(', ', $placeholders);
}
}