PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / trunk
Booking for Appointments and Events Calendar – Amelia vtrunk
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / src / Infrastructure / DB / WPDB / Statement.php
ameliabooking / src / Infrastructure / DB / WPDB Last commit date
Connection.php 4 months ago Statement.php 1 week ago
Statement.php
167 lines
1 <?php
2
3 namespace AmeliaBooking\Infrastructure\DB\WPDB;
4
5 use wpdb;
6
7 class Statement
8 {
9 public const FETCH_ASSOC = 'assoc';
10 public const FETCH_COLUMN = 'column';
11
12 /** @var wpdb */
13 private $wpdb;
14
15 /** @var string */
16 private $statement;
17
18 /** @var array */
19 private $bindings = [];
20
21 /** @var int */
22 private $fetchIndex = 0;
23
24 /** @var array|null Cached result set for fetch operations */
25 private $results = null;
26
27 /** @var int */
28 private $affectedRows = 0;
29
30 /**
31 * @param wpdb $wpdb
32 * @param string $statement
33 */
34 public function __construct($wpdb, $statement)
35 {
36 $this->wpdb = $wpdb;
37 $this->statement = $statement;
38 }
39
40 /**
41 * @param int|string $parameter
42 * @param mixed $variable
43 */
44 public function bindValue($parameter, $variable)
45 {
46 $this->bindings[$parameter] = $variable;
47 }
48
49 /**
50 * @param int|string $parameter
51 * @param mixed $variable
52 */
53 public function bindParam($parameter, $variable)
54 {
55 $this->bindValue($parameter, $variable);
56 }
57
58 /**
59 * Execute the statement.
60 * Accepts optional params array (e.g. [':id' => 5]).
61 * Repositories previously called execute($params); this now works.
62 *
63 * @param array $params
64 * @return void
65 */
66 public function execute(array $params = [])
67 {
68 // Merge passed params into bindings (allows both bindParam + execute($params))
69 if ($params) {
70 foreach ($params as $key => $value) {
71 $this->bindings[$key] = $value;
72 }
73 }
74
75 $query = $this->statement;
76
77 if ($this->bindings) {
78 // Sort bindings by key length, descending, to avoid matching ":p1" inside ":p10"
79 uksort($this->bindings, function ($a, $b) {
80 return strlen($b) - strlen($a);
81 });
82
83 $replacements = [];
84
85 foreach ($this->bindings as $placeholder => $value) {
86 if (strpos($this->statement, $placeholder) === false) {
87 continue;
88 }
89
90 $replacement = 'NULL';
91
92 if ($value !== null) {
93 $format = is_int($value) || is_bool($value) ? '%d' : (is_float($value) ? '%f' : '%s');
94 $replacement = $this->wpdb->prepare($format, $value);
95 }
96
97 $replacements[$placeholder] = $replacement;
98 }
99
100 if ($replacements) {
101 $query = strtr($this->statement, $replacements);
102 }
103 }
104
105 $this->fetchIndex = 0; // reset pointer for subsequent fetch() calls
106
107 // Execute using query() - this handles all query types (SELECT, INSERT, UPDATE, etc.)
108 // It populates $wpdb->last_result (for rows) and returns the count
109 $result = $this->wpdb->query($query);
110
111 if ($result === false) {
112 $this->results = [];
113 $this->affectedRows = 0;
114 throw new \RuntimeException($this->wpdb->last_error ?: 'Database query failed.');
115 }
116
117 // Capture affected rows immediately so it's stable even if other WP queries run later
118 $this->affectedRows = (int) $result;
119
120 // If the query produced results (SELECT, SHOW, etc.), wpdb stores them in last_result as objects
121 // Convert objects to arrays to match PDO::FETCH_ASSOC behavior
122 $this->results = array_map(function ($row) {
123 return (array) $row;
124 }, $this->wpdb->last_result ?? []);
125 }
126
127 /**
128 * @param string $fetchMode Fetch mode (FETCH_ASSOC or FETCH_COLUMN)
129 * @return array
130 */
131 public function fetchAll(string $fetchMode = self::FETCH_ASSOC)
132 {
133 if (!$this->results) {
134 return [];
135 }
136
137 // If FETCH_COLUMN, return only the first column from each row
138 if ($fetchMode === self::FETCH_COLUMN) {
139 return array_map(function ($row) {
140 return reset($row); // Get first value from associative array
141 }, $this->results);
142 }
143
144 return $this->results;
145 }
146
147 /**
148 * @return array|false
149 */
150 public function fetch()
151 {
152 if ($this->results && isset($this->results[$this->fetchIndex])) {
153 return $this->results[$this->fetchIndex++];
154 }
155
156 return false;
157 }
158
159 /**
160 * @return int
161 */
162 public function rowCount()
163 {
164 return $this->affectedRows;
165 }
166 }
167