PluginProbe
MailerLite – WooCommerce integration / 3.1.25
MailerLite – WooCommerce integration v3.1.25
3.1.25 3.1.26 3.1.24 3.1.23 3.1.22 3.1.21 3.1.20 3.1.19 3.1.18 3.1.17 3.1.16 3.1.15 1.8.7 1.8.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 All 147 releases
woo-mailerlite / includes / common / WooMailerLiteQueryBuilder.php

WooMailerLiteQueryBuilder.php in MailerLite – WooCommerce integration 3.1.25, at includes/common/WooMailerLiteQueryBuilder.php

467 lines 15.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class WooMailerLiteQueryBuilder extends WooMailerLiteDBConnection
4 {
5 use WooMailerLiteResources;
6 protected $model;
7
8 private $select = "*";
9
10 protected $andWhere = false;
11
12 protected $withoutPrefix = false;
13 private $allowedOperators = ['=', '!=', '<>', '>', '<', '>=', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'IS', 'IS NOT'];
14
15 public function __construct($model)
16 {
17 $this->model = $model;
18 }
19
20 private function getOperation($operation)
21 {
22 $operation = strtoupper(trim($operation));
23 if (!in_array($operation, $this->allowedOperators, true)) {
24 return '=';
25 }
26
27 return $operation;
28 }
29
30 private function addPrefix($table) {
31 if (strpos($table, $this->db()->prefix) === 0) {
32 return $table;
33 }
34
35 return $this->db()->prefix . $table;
36 }
37
38 private function esc($value)
39 {
40 if (strpos($value, '.') !== false || strpos($value, $this->db()->prefix) === 0) {
41 return $value;
42 }
43
44 return esc_sql(preg_replace('/[^a-zA-Z0-9_-]/', '', $value));
45 }
46
47 private function prepareColumn($column)
48 {
49 if (strpos($column, '.') !== false) {
50 $parts = explode('.', $column, 2);
51 return $this->addPrefix($parts[0]) . '.' . $this->esc($parts[1]);
52 }
53
54 return $this->esc($column);
55 }
56
57 public function where($column, $operation = '=', $value = null)
58 {
59 if ($value === null) {
60 $value = $operation;
61 $operation = '=';
62 }
63 if ($this->model->isResource() || ((get_class($this->model) === 'WooMailerLiteCustomer') && !$this->customTableEnabled())) {
64 $this->set_resource(get_class($this->model));
65 $this->args[$column] = $value;
66 return $this;
67 }
68 $operation = $this->getOperation($operation);
69 $column = $this->prepareColumn($column);
70 if ($this->hasWhere) {
71 $this->andWhere($column, $operation, $value);
72 } else {
73 $this->query .= $this->db()->prepare(" WHERE {$column} {$operation} %s", $value);
74 }
75 $this->hasWhere = true;
76 return $this;
77 }
78
79 public function get(int $count = -1)
80 {
81 if ($count == -1 && (get_class($this->model) === 'WooMailerLiteCustomer')) {
82 return $this->buildQuery($count)->executeQuery();
83 }
84 if ($this->model->isResource() || ((get_class($this->model) === 'WooMailerLiteCustomer') && !$this->customTableEnabled())) {
85
86 $this->set_resource(get_class($this->model));
87 return $this->resource_get($count);
88 }
89 $collection = new WooMailerLiteCollection();
90
91 $data = $this->buildQuery($count)->executeQuery();
92 if ($this->countOnly && (get_class($this->model) === 'WooMailerLiteProduct')) {
93 return $data;
94 }
95 foreach ($data as $item) {
96 if ((get_class($this->model) === 'WooMailerLiteCustomer') && empty($item->email)) {
97 continue;
98 }
99 if (get_class($this->model) === 'WooMailerLiteProduct' && !$this->model->isResource()) {
100 $item = wc_get_product($item);
101 if (!$item) {
102 continue;
103 }
104 $itemData = $item->get_data();
105 $this->prepareResourceData(get_class($this->model), $itemData, $item->last_order_id ?? $item);
106 $item = $itemData;
107 }
108 $model = new $this->model();
109
110 if ($this->model->getCastsArray()) {
111 if ($this->model->isResource()) {
112 $this->prepareResourceData(get_class($this->model), $item, $item->last_order_id ?? $item);
113 }
114 $model->attributes = array_intersect_key((array)$item, array_flip($this->model->getCastsArray() ?? []));
115 } else {
116 $model->attributes = (array)$item;
117 }
118 if (!empty($this->model->getFormatArray())) {
119 foreach ($this->model->getFormatArray() as $key => $format) {
120 if (!isset($model->attributes[$key])) {
121 continue;
122 }
123 switch ($format) {
124 case 'array':
125 if (is_string($model->attributes[$key])){
126 $model->attributes[$key] = json_decode($model->attributes[$key], true);
127 }
128 break;
129 case 'boolean':
130 $model->attributes[$key] = (bool) $model->attributes[$key];
131 break;
132 case 'string':
133 $model->attributes[$key] = (string) $model->attributes[$key];
134 break;
135 }
136 }
137 }
138 if (!empty($this->model->getRemoveEmptyArray())) {
139 foreach ($this->model->getRemoveEmptyArray() as $key) {
140 if (isset($model->attributes[$key])) {
141 if (empty($model->attributes[$key]) || (is_string($model->attributes[$key]) && ctype_space($model->attributes[$key]))) {
142 unset($model->attributes[$key]);
143 }
144 }
145 }
146 }
147
148 $collection->collect($model);
149 }
150 return $collection;
151 }
152
153 public function buildQuery($count = -1)
154 {
155 $this->query = "SELECT " . $this->select . " from " . $this->addPrefix($this->esc($this->model->getTable())) . $this->query;
156 if ($count != -1) {
157 $this->query .= $this->db()->prepare(" LIMIT %d", absint($count));
158 }
159
160 $this->query .= ";" ;
161 return $this;
162 }
163
164 public function whereIn($column, $values)
165 {
166 if ($this->model->isResource()) {
167 $this->args[$column] = $values;
168 return $this;
169 }
170 $column = $this->prepareColumn($column);
171 $this->hasWhere = true;
172 if (empty($values)) {
173 $this->query .= " WHERE 1=0"; // Empty IN clause returns no results
174 return $this;
175 }
176 $placeholders = implode(',', array_fill(0, count($values), '%s'));
177 $this->query .= $this->db()->prepare(" WHERE {$column} IN ({$placeholders})", ...$values);
178 return $this;
179 }
180
181 public function groupBy($column)
182 {
183 $this->query .= " GROUP BY {$this->prepareColumn($column)}";
184 return $this;
185 }
186
187 public function orderBy($column)
188 {
189 $this->query .= " ORDER BY {$this->prepareColumn($column)}";
190 return $this;
191 }
192
193 public function join($table, $tableLeft = null, $tableRight = null, $alias = null)
194 {
195 if ($table instanceof WooMailerLiteQueryBuilder) {
196 $alias = $this->esc($alias ?? 'subquery');
197 $tableLeft = $this->esc($tableLeft);
198 $tableRight = $this->esc($tableRight);
199 $this->query .= " INNER JOIN (" . rtrim($table->buildQuery()->query, ';') . ") AS " . $this->addPrefix($alias) . " ON " . $this->addPrefix($tableLeft) . " = " . $this->addPrefix($tableRight);
200 return $this;
201 }
202
203 if (!$tableRight && is_array($tableLeft)) {
204 $operator = null;
205 $joins = [];
206 foreach ($tableLeft as $key => $value) {
207 $key = $this->esc($key);
208 if (is_string($value)) {
209 if (strpos($value, '.') === false) {
210 // String value - use prepare
211 $value = $this->db()->prepare('%s', $value);
212 } else {
213 // Column reference - escape
214 $value = $this->addPrefix($this->esc($value));
215 }
216 }
217
218 if (is_array($value) && is_string(array_keys($value)[0])) {
219 $originalKey = array_keys($value)[0];
220 $operator = $this->getOperation($originalKey);
221 if (empty($value[$originalKey])) {
222 $findin = "(1=0)"; // Empty IN clause
223 } else {
224 $placeholders = implode(',', array_fill(0, count($value[$originalKey]), '%s'));
225 $findin = $this->db()->prepare("({$placeholders})", ...$value[$originalKey]);
226 }
227 $value = " {$operator} {$findin}";
228 }
229 $joins[] = $this->addPrefix($key) . ($operator ? '' : ' = ') . $value;
230 }
231 $table = $this->esc($table);
232 $this->query .= " INNER JOIN " . $this->addPrefix($table) . " ON " . implode(' AND ', $joins);
233 return $this;
234 }
235 $table = $this->addPrefix($this->esc($table));
236 $tableLeft = $this->addPrefix($this->esc($tableLeft));
237 $tableRight = $this->addPrefix($this->esc($tableRight));
238 $this->query .= " INNER JOIN {$table} ON {$tableLeft} = {$tableRight}";
239 return $this;
240 }
241
242 public function from($table)
243 {
244 $this->model->setTable($this->esc($table));
245 return $this;
246 }
247
248 public function leftJoin(string $table, $tableLeft, string $tableRight = '')
249 {
250 if (!$tableRight && is_array($tableLeft)) {
251 // this condition is for join on key = value and another key = value
252 $joins = [];
253 foreach ($tableLeft as $key => $value) {
254 $key = $this->esc($key);
255 if (strpos($value, '.') === false) {
256 // String value - use prepare
257 $value = $this->db()->prepare('%s', $value);
258 } else {
259 // Column reference - escape
260 $value = $this->addPrefix($this->esc($value));
261 }
262 $joins[] = $this->addPrefix($key) . ' = ' . $value;
263 }
264 $table = $this->esc($table);
265 $this->query .= " LEFT JOIN " . $this->addPrefix($table) . " ON " . implode(' AND ', $joins);
266 return $this;
267 }
268 $table = $this->addPrefix($this->esc($table));
269 $tableLeft = $this->addPrefix($this->esc($tableLeft));
270 $tableRight = $this->addPrefix($this->esc($tableRight));
271 $this->query .= " LEFT JOIN {$table} ON {$tableLeft} = {$tableRight}";
272 return $this;
273 }
274
275 public function leftJoinAs(string $table, string $alias, array $tableLeft)
276 {
277 $tableSql = $this->addPrefix($this->esc($table));
278 $aliasSql = $this->addPrefix($this->esc($alias));
279 $joins = [];
280 foreach ($tableLeft as $key => $value) {
281 $key = $this->esc($key);
282 if (strpos($value, '.') === false) {
283 $value = $this->db()->prepare('%s', $value);
284 } else {
285 $value = $this->addPrefix($this->esc($value));
286 }
287 $joins[] = $this->addPrefix($key) . ' = ' . $value;
288 }
289 $this->query .= " LEFT JOIN {$tableSql} AS {$aliasSql} ON " . implode(' AND ', $joins);
290
291 return $this;
292 }
293
294 public function andWhere($column, $operation, $value)
295 {
296 $operation = $this->getOperation($operation);
297 $column = $this->prepareColumn($column);
298 if ($this->andWhere) {
299 $this->andWhere = false;
300 $this->query .= $this->db()->prepare(" {$column} {$operation} %s", $value);
301 return $this;
302 }
303 $this->query .= $this->db()->prepare(" AND {$column} {$operation} %s", $value);
304 return $this;
305 }
306
307 public function orWhere($column, $operation = '=', $value = null)
308 {
309 if ($value === null) {
310 $value = $operation;
311 $operation = '=';
312 }
313
314 $operation = $this->getOperation($operation);
315 if (!$this->withoutPrefix) {
316 $column = $this->addPrefix($column);
317 }
318 $column = $this->prepareColumn($column);
319 if ($value === null) {
320 $this->query .= " OR {$column} IS NULL";
321 } else {
322 $this->query .= $this->db()->prepare(" OR {$column} {$operation} %s", $value);
323 }
324 return $this;
325 }
326
327 public function withoutPrefix($callback)
328 {
329 $this->withoutPrefix = true;
330 $callback($this);
331 $this->withoutPrefix = false;
332 return $this;
333 }
334
335 public function andCombine($callback)
336 {
337 $this->andWhere = true;
338 $this->query .= ' AND (';
339 $callback($this);
340 $this->query .= ')';
341 return $this;
342 }
343
344 public function select($select)
345 {
346 $dangerous = [
347 ';',
348 '--',
349 '/*',
350 '*/',
351 ];
352
353 foreach ($dangerous as $pattern) {
354 $select = str_replace($pattern, '', $select);
355 }
356
357 $dangerousKeywords = ['INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE', 'ALTER', 'TRUNCATE', 'EXEC', 'EXECUTE'];
358 foreach ($dangerousKeywords as $keyword) {
359 $select = preg_replace('/\b' . $keyword . '\b/i', '', $select);
360 }
361
362 $this->select = $select;
363 return $this;
364 }
365
366 public function builder()
367 {
368 return new static($this->model);
369 }
370
371 public function all($arguments = [])
372 {
373 if ($this->model->isResource()) {
374 $this->set_resource(get_class($this->model));
375 return $this->resource_all();
376 } else {
377 return $this->get();
378 }
379 }
380
381 public function create($data)
382 {
383 return $this->prepareQuery('create', $data);
384 }
385
386 public function update($data)
387 {
388 return $this->prepareQuery('update', $data, $this->model);
389 }
390
391 public function delete()
392 {
393 return $this->prepareQuery('delete', [], $this->model);
394 }
395
396 public function first()
397 {
398 return $this->get(1)->items[0] ?? null;
399 }
400
401 public function firstOrCreate($where, $data)
402 {
403 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first();
404 if ($exists) {
405 return $exists;
406 }
407 $this->create(array_merge($where, $data));
408 $this->query = '';
409 $this->hasWhere = false;
410 return $this->where(array_key_first($where), $where[array_key_first($where)])->first();
411 }
412
413 public function updateOrCreate($where, $data)
414 {
415 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first();
416 if ($exists) {
417 $this->query = '';
418 $this->hasWhere = false;
419 $this->model = $exists;
420 $this->update($data);
421 return $exists;
422 }
423 $this->create(array_merge($where, $data));
424 $this->query = '';
425 $this->hasWhere = false;
426 return $this->where(array_key_first($where), $where[array_key_first($where)])->first();
427 }
428
429 protected function prepareQuery(string $action, array $data, $model = null)
430 {
431 switch ($action) {
432 case 'create':
433 foreach ($data as &$value) {
434 if (is_array($value)) {
435 $value = json_encode($value);
436 }
437 }
438 $this->db()->insert($this->addPrefix($this->model->getTable()), $data);
439 break;
440 case 'update':
441 foreach ($data as &$value) {
442 if (is_array($value)) {
443 $value = json_encode($value);
444 }
445 }
446 $this->db()->update(
447 $this->addPrefix($this->model->getTable()),
448 $data,
449 [
450 'id' => $model->id
451 ]
452 );
453 break;
454 case 'delete':
455 $this->db()->delete($this->addPrefix($this->model->getTable()), ['id' => $model->id] );
456 break;
457 }
458 return true;
459 }
460
461 public function getFromOrder()
462 {
463 $this->model->setResource();
464 return $this;
465 }
466 }
467