PluginProbe
MailerLite – WooCommerce integration / 3.1.16
MailerLite – WooCommerce integration v3.1.16
3.1.27 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 All 148 releases
woo-mailerlite / includes / common / WooMailerLiteQueryBuilder.php

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

448 lines 15.1 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 andWhere($column, $operation, $value)
276 {
277 $operation = $this->getOperation($operation);
278 $column = $this->prepareColumn($column);
279 if ($this->andWhere) {
280 $this->andWhere = false;
281 $this->query .= $this->db()->prepare(" {$column} {$operation} %s", $value);
282 return $this;
283 }
284 $this->query .= $this->db()->prepare(" AND {$column} {$operation} %s", $value);
285 return $this;
286 }
287
288 public function orWhere($column, $operation = '=', $value = null)
289 {
290 if ($value === null) {
291 $value = $operation;
292 $operation = '=';
293 }
294
295 $operation = $this->getOperation($operation);
296 if (!$this->withoutPrefix) {
297 $column = $this->addPrefix($column);
298 }
299 $column = $this->prepareColumn($column);
300 if ($value === null) {
301 $this->query .= " OR {$column} IS NULL";
302 } else {
303 $this->query .= $this->db()->prepare(" OR {$column} {$operation} %s", $value);
304 }
305 return $this;
306 }
307
308 public function withoutPrefix($callback)
309 {
310 $this->withoutPrefix = true;
311 $callback($this);
312 $this->withoutPrefix = false;
313 return $this;
314 }
315
316 public function andCombine($callback)
317 {
318 $this->andWhere = true;
319 $this->query .= ' AND (';
320 $callback($this);
321 $this->query .= ')';
322 return $this;
323 }
324
325 public function select($select)
326 {
327 $dangerous = [
328 ';',
329 '--',
330 '/*',
331 '*/',
332 ];
333
334 foreach ($dangerous as $pattern) {
335 $select = str_replace($pattern, '', $select);
336 }
337
338 $dangerousKeywords = ['INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE', 'ALTER', 'TRUNCATE', 'EXEC', 'EXECUTE'];
339 foreach ($dangerousKeywords as $keyword) {
340 $select = preg_replace('/\b' . $keyword . '\b/i', '', $select);
341 }
342
343 $this->select = $select;
344 return $this;
345 }
346
347 public function builder()
348 {
349 return new static($this->model);
350 }
351
352 public function all($arguments = [])
353 {
354 if ($this->model->isResource()) {
355 $this->set_resource(get_class($this->model));
356 return $this->resource_all();
357 } else {
358 return $this->get();
359 }
360 }
361
362 public function create($data)
363 {
364 return $this->prepareQuery('create', $data);
365 }
366
367 public function update($data)
368 {
369 return $this->prepareQuery('update', $data, $this->model);
370 }
371
372 public function delete()
373 {
374 return $this->prepareQuery('delete', [], $this->model);
375 }
376
377 public function first()
378 {
379 return $this->get(1)->items[0] ?? null;
380 }
381
382 public function firstOrCreate($where, $data)
383 {
384 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first();
385 if ($exists) {
386 return $exists;
387 }
388 $this->create(array_merge($where, $data));
389 $this->query = '';
390 $this->hasWhere = false;
391 return $this->where(array_key_first($where), $where[array_key_first($where)])->first();
392 }
393
394 public function updateOrCreate($where, $data)
395 {
396 $exists = $this->where(array_key_first($where), $where[array_key_first($where)])->first();
397 if ($exists) {
398 $this->query = '';
399 $this->hasWhere = false;
400 $this->model = $exists;
401 $this->update($data);
402 return $exists;
403 }
404 $this->create(array_merge($where, $data));
405 $this->query = '';
406 $this->hasWhere = false;
407 return $this->where(array_key_first($where), $where[array_key_first($where)])->first();
408 }
409
410 protected function prepareQuery(string $action, array $data, $model = null)
411 {
412 switch ($action) {
413 case 'create':
414 foreach ($data as &$value) {
415 if (is_array($value)) {
416 $value = json_encode($value);
417 }
418 }
419 $this->db()->insert($this->addPrefix($this->model->getTable()), $data);
420 break;
421 case 'update':
422 foreach ($data as &$value) {
423 if (is_array($value)) {
424 $value = json_encode($value);
425 }
426 }
427 $this->db()->update(
428 $this->addPrefix($this->model->getTable()),
429 $data,
430 [
431 'id' => $model->id
432 ]
433 );
434 break;
435 case 'delete':
436 $this->db()->delete($this->addPrefix($this->model->getTable()), ['id' => $model->id] );
437 break;
438 }
439 return true;
440 }
441
442 public function getFromOrder()
443 {
444 $this->model->setResource();
445 return $this;
446 }
447 }
448