PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.23
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.23
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / vendor / wpfluent / framework / src / WPFluent / Http / Request / Request.php

Request.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.23, at vendor/wpfluent/framework/src/WPFluent/Http/Request/Request.php

833 lines 19.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\Framework\Http\Request;
4
5 use Closure;
6 use FluentCart\Framework\Support\Arr;
7 use FluentCart\Framework\Support\Helper;
8 use FluentCart\Framework\Support\MacroableTrait;
9 use FluentCart\Framework\Foundation\Application;
10 use FluentCart\Framework\Validator\ValidationException;
11
12 class Request
13 {
14 use InteractsWithCleaningTrait,
15 InteractsWithHeadersTrait,
16 InputHelperMethodsTrait,
17 InteractsWithFilesTrait,
18 InteractsWithIPTrait,
19 MacroableTrait {
20 __call as macroCall;
21 }
22
23 /**
24 * The application instance
25 * @var \FluentCart\Framework\Foundation\Application
26 */
27 protected $app = null;
28
29 /**
30 * Validator instance.
31 *
32 * @var \FluentCart\Framework\Validator\Validator
33 */
34 protected $validator = null;
35
36 /**
37 * PHP header variables
38 * @var array
39 */
40 protected $headers = [];
41
42 /**
43 * PHP server variables
44 * @var array
45 */
46 protected $server = [];
47
48 /**
49 * PHP cookie variables
50 * @var array
51 */
52 protected $cookie = [];
53
54 /**
55 * The content of the request
56 * @var mixed
57 */
58 protected $content = null;
59
60 /**
61 * The JSON payload of the request
62 * @var array
63 */
64 protected $json = [];
65
66 /**
67 * PHP $_GET Superglobal
68 * @var array
69 */
70 protected $get = [];
71
72
73 /**
74 * PHP $_POST Superglobal
75 * @var array
76 */
77 protected $post = [];
78
79 /**
80 * PHP $_GET and $_POST Superglobals
81 * @var array
82 */
83 protected $request = [];
84
85 /**
86 * WP_REST_Request instance
87 * @var \WP_REST_Request
88 */
89 protected $wpRestRequest = false;
90
91 /**
92 * Validated data after validation has been passed
93 * @var array
94 */
95 protected $validated = [];
96
97 /**
98 * $safe Determines the input source when data retrieval methods get called.
99 * If true, the data will be returned from the $validated array.
100 * If false, the data will be returned from the $request array.
101 *
102 * @var boolean
103 */
104 protected $safe = false;
105
106 /**
107 * Construct the request instance
108 * @param \FluentCart\Framework\Foundation\Application $app
109 * @param $_GET $get
110 * @param $_POST $post
111 */
112 public function __construct(Application $app, $get, $post)
113 {
114 $this->app = $app;
115 $this->server = $_SERVER;
116 $this->cookie = $_COOKIE;
117
118 $this->request = array_merge(
119 $this->get = $this->clean($get),
120 $this->post = $this->clean($post)
121 );
122 }
123
124 /**
125 * Variable exists
126 * @param string $key
127 * @return bool
128 */
129 public function exists($key)
130 {
131 return Arr::has($this->inputs(), $key);
132 }
133
134 /**
135 * Variable exists and has truthy value
136 * @param string $key
137 * @return bool
138 */
139 public function has($key)
140 {
141 $inputs = $this->inputs();
142
143 return isset($inputs[$key]) && !empty($inputs[$key]);
144 }
145
146 /**
147 * Any variable exists and has truthy value
148 * @param array|string $keys
149 * @return bool
150 */
151 public function hasAny($keys)
152 {
153 $keys = is_array($keys) ? $keys : func_get_args();
154
155 if ($data = $this->only($keys)) {
156 return (bool) count(array_filter($data));
157 }
158
159 return false;
160 }
161
162 /**
163 * Calls a callback if has value, otherwise
164 * calls another/second callback if given.
165 *
166 * @param string $key
167 * @param \Closure $has
168 * @param \Closure|null $hasnot
169 * @return mixed
170 */
171 public function whenHas($key, Closure $has, ?Closure $hasnot = null)
172 {
173 if ($this->has($key)) {
174 return $has($key, $this->get($key));
175 }
176
177 return ($hasnot ? $hasnot($key) : null);
178 }
179
180 /**
181 * Checks if a key is missing in the request.
182 *
183 * @param string $key
184 * @return bool
185 */
186 public function missing($key)
187 {
188 return !$this->has($key);
189 }
190
191 /**
192 * Calls the given callback if the provided key is missing.
193 *
194 * @param string $key
195 * @param \Closure $callback
196 * @return mixed
197 */
198 public function whenMissing($key, Closure $callback)
199 {
200 if ($this->missing($key)) {
201 return $callback($key, $this);
202 }
203
204 return $this;
205 }
206
207 /**
208 * Set an item into the request inputs
209 * @param string $key
210 * @param mixed $value
211 * @return self
212 */
213 public function set($key, $value)
214 {
215 Arr::set($this->request, $key, $value);
216
217 return $this;
218 }
219
220 /**
221 * Retrive all the items from the request inputs
222 * @return array
223 */
224 public function all()
225 {
226 return $this->get();
227 }
228
229 /**
230 * Retrieve an item from the request inputs
231 * @param string|null $key
232 * @param mixed $default
233 * @return mixed
234 */
235 public function get($key = null, $default = null)
236 {
237 return Helper::dataGet($this->inputs(), $key, $default);
238 }
239
240 /**
241 * Check the content-type for JSON
242 *
243 * @return boolean
244 */
245 public function isJson()
246 {
247 if (!($isJson = $this->is_json_content_type())) {
248 if (!$isJson) {
249 if ($body = $this->get_body()) {
250 json_decode($body);
251 if (json_last_error() === JSON_ERROR_NONE) {
252 $isJson = true;
253 }
254 } elseif ($this->isRest()) {
255 $isJson = true;
256 }
257 }
258 }
259
260 if (
261 !$isJson &&
262 isset($_SERVER['CONTENT_TYPE']) &&
263 strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false
264 ) {
265 $requestBody = file_get_contents('php://input');
266
267 if (!empty($requestBody)) {
268 $this->json = json_decode($requestBody, true);
269 $isJson = json_last_error() === JSON_ERROR_NONE;
270 }
271 }
272
273 if (!$isJson) {
274 $requestBody = file_get_contents('php://input');
275 if (!empty($requestBody)) {
276 json_decode($requestBody);
277 $isJson = json_last_error() === JSON_ERROR_NONE;
278 }
279 }
280
281 return $isJson;
282 }
283
284 /**
285 * Check if current request wants JSON response.
286 *
287 * @return boolean
288 */
289 public function wantsJson()
290 {
291 $wants = $this->header('accept');
292
293 if ($wants === '*/*') {
294 return $this->isJson();
295 }
296
297 return $wants === 'application/json';
298 }
299
300 /**
301 * Check if current request is a Rest request
302 *
303 * @return boolean
304 */
305 public function isRest()
306 {
307 $isRest = false;
308
309 if ($this->app->isUnitTesting()) {
310 return $isRest;
311 }
312
313 $url = $this->url();
314
315 $niddle = $this->app->config->get(
316 'app.rest_namespace'
317 ).'/__endpoints';
318
319 if (str_contains($url, $niddle)) {
320 return $isRest;
321 }
322
323 $isRest = defined('REST_REQUEST') && REST_REQUEST;
324
325 if (!$isRest) {
326 if (!get_option('permalink_structure')) {
327 $isRest = $this->query('rest_route', false);
328 } else {
329 $parsed = parse_url($url);
330 $path = isset($parsed['path']) ? $parsed['path'] : '';
331 $isRest = str_starts_with($path, '/wp-json');
332 }
333 }
334
335 return $isRest;
336 }
337
338 /**
339 * Determine if the request is initiated by WordPress.
340 *
341 * @return boolean
342 */
343 public function isInternal()
344 {
345 return $GLOBALS['wp_rest_server']->is_dispatching();
346 }
347
348 /**
349 * Retrieve an item from the json payload of the request.
350 *
351 * @param string $key
352 * @param string $default
353 * @return mixed
354 */
355 public function json($key = null, $default = null)
356 {
357 if (!$this->isJson()) {
358 return [];
359 }
360
361 if (empty($this->json)) {
362 $json = $this->get_json_params() ?: $this->getContent();
363
364 if (!is_array($json)) {
365 $decoded = json_decode($json, true);
366 if (json_last_error() !== JSON_ERROR_NONE) {
367 $this->json = [];
368 } else {
369 $this->json = (array) $decoded;
370 }
371 } else {
372 $this->json = $json;
373 }
374 }
375
376 if (is_null($key)) {
377 return $this->json;
378 }
379
380 return Helper::dataGet($this->json, $key, $default);
381 }
382
383 /**
384 * Retrieve an item from the PHP $_SERVER array
385 * @param string $key
386 * @param string $default
387 * @return mixed
388 */
389 public function server($key = null, $default = null)
390 {
391 return $key ? Arr::get($this->server, $key, $default) : $this->server;
392 }
393
394 /**
395 * Retrieve an item from the cookie
396 * @param string $key
397 * @param mixed $default
398 * @return mixed
399 */
400 public function cookie($key = null, $default = null)
401 {
402 $cookie = $key ? Arr::get(
403 $this->cookie, $key, $default
404 ) : $this->cookie;
405
406 return json_decode(base64_decode($cookie, true));
407 }
408
409 /**
410 * Get an item from the PHP $_GET array
411 * @param string $key
412 * @param mixed $default
413 * @return mixed
414 */
415 public function query($key = null, $default = null)
416 {
417 return $key ? Arr::get($this->get, $key, $default) : $this->get;
418 }
419
420 /**
421 * Get an item from the PHP $_POST array
422 * @param string $key
423 * @param mixed $default
424 * @return mixed
425 */
426 public function post($key = null, $default = null)
427 {
428 return $key ? Arr::get($this->post, $key, $default) : $this->post;
429 }
430
431 /**
432 * Return the only items given in the args
433 * @param array $keys
434 * @return array
435 */
436 public function only($keys)
437 {
438 $keys = is_array($keys) ? $keys : func_get_args();
439
440 return Arr::only($this->inputs(), $keys);
441 }
442
443 /**
444 * Return a subset of the request inputs except the given keys
445 * @param array $keys
446 * @return array
447 */
448 public function except($keys)
449 {
450 $keys = is_array($keys) ? $keys : func_get_args();
451
452 return Arr::except($this->inputs(), $keys);
453 }
454
455 /**
456 * Merge array with the request inputs
457 * @param array $data
458 * @return self
459 */
460 public function merge(array $data = [])
461 {
462 $this->request = array_replace($this->inputs(), $data);
463
464 return $this;
465 }
466
467 /**
468 * Merge array with the request inputs if the
469 * key(s) is missing from the request.
470 *
471 * @param array $data
472 * @return self
473 */
474 public function mergeIfMissing(array $data = [])
475 {
476 $all = $this->inputs();
477
478 $this->merge(Arr::mergeMissing($data, $all));
479
480 return $this;
481 }
482
483 /**
484 * Merge new input into the request's input, but only when
485 * that key is present in the request but value is missing.
486 *
487 * @param array $input
488 * @return $this
489 */
490 public function mergeMissing(array $input)
491 {
492 return $this->merge(Helper::collect($input)
493 ->filter(function($value, $key) {
494 return $this->missing($key);
495 })->toArray()
496 );
497 }
498
499 /**
500 * Returns the request body content.
501 *
502 * @return mixed
503 */
504 public function getContent()
505 {
506 if (!$this->content) {
507 $this->content = file_get_contents('php://input');
508 }
509
510 return $this->content;
511 }
512
513 /**
514 * Merges the input arrays from the WP_REST_Request.
515 *
516 * @param \WP_REST_Request $wpRestRequest
517 * @return void
518 */
519 public function mergeInputsFromRestRequest($wpRestRequest)
520 {
521 $this->request = array_merge(
522 $this->request, $wpRestRequest->get_params()
523 );
524
525 $this->post = array_merge(
526 $this->post, $wpRestRequest->get_body_params()
527 );
528
529 $this->get = array_merge(
530 $this->get, $wpRestRequest->get_query_params()
531 );
532
533 $this->mergerHeaders($wpRestRequest);
534
535 $this->wpRestRequest = true;
536 }
537
538 /**
539 * Merge the headers from the WP_REST_Request.
540 *
541 * @param \WP_REST_Request $wpRestRequest
542 * @return void
543 */
544 protected function mergerHeaders($wpRestRequest)
545 {
546 $headers = [];
547
548 foreach ($wpRestRequest->get_headers() as $key => $header) {
549 $headers[strtoupper($key)] = reset($header);
550 }
551
552 $this->headers = array_merge($this->headers, $headers);
553
554 if (
555 !isset($this->headers['CONTENT_TYPE']) || (
556 isset($this->headers['CONTENT_TYPE']) &&
557 $this->headers['CONTENT_TYPE'] !== true
558 )) {
559 if ($this->isJson()) {
560 $this->headers['CONTENT_TYPE'] = 'application/json';
561 }
562 }
563 }
564
565 /**
566 * Retrieve an input item from the request.
567 *
568 * @param string|null $key
569 * @param mixed $default
570 * @return mixed
571 */
572 public function input($key = null, $default = null)
573 {
574 return Arr::get($this->inputs(), $key, $default);
575 }
576
577 /**
578 * Remove a key(s) from the $request array
579 * @param mixed $key
580 * @return self
581 */
582 public function forget($key)
583 {
584 Arr::forget($this->request, $key);
585
586 return $this;
587 }
588
589 /**
590 * Get all inputs
591 *
592 * @return array
593 */
594 protected function inputs()
595 {
596 if (!$this->wpRestRequest) {
597 if ($this->app->bound('wprestrequest')) {
598 // @phpstan-ignore-next-line
599 $this->mergeInputsFromRestRequest($this->app->wprestrequest);
600 }
601 }
602
603 if (empty($this->json) && $json = $this->json()) {
604 $this->post = array_merge($this->post, $json);
605 $this->request = array_merge($this->request, $json);
606 }
607
608 return $this->safe === true ? $this->validated : $this->request;
609 }
610
611 /**
612 * To get item(s) from validated inputs
613 *
614 * @return self
615 */
616 public function safe()
617 {
618 $clone = clone $this;
619
620 $clone->safe = true;
621
622 return $clone;
623 }
624
625 /**
626 * Get the request method.
627 *
628 * @return string
629 */
630 public function method()
631 {
632 return $_SERVER['REQUEST_METHOD'];
633 }
634
635 /**
636 * Get the URL (no query string) for the request.
637 *
638 * @return string
639 */
640 public function url()
641 {
642 return preg_replace('/\?.*/', '', $this->getFullUrl());
643 }
644
645 /**
646 * Get the full URL for the request.
647 *
648 * @return string
649 */
650 public function getFullUrl()
651 {
652 return get_site_url() . rtrim($_SERVER['REQUEST_URI'], '/');
653 }
654
655 /**
656 * Validate the request.
657 *
658 * @param array $rules
659 * @param array $messages
660 * @return mixed
661 * @throws \FluentCart\Framework\Validator\ValidationException
662 */
663 public function validate(array $rules, array $messages = [])
664 {
665 $this->validator = $this->app->make('validator')->make(
666 $data = $this->all(), $rules, $messages
667 );
668
669 if ($this->validator->validate()->fails()) {
670 throw new ValidationException(
671 'Unprocessable Entity!', 422, null, $this->validator->errors()
672 );
673 }
674
675 $this->validated = $this->validator->validated();
676
677 return $data;
678 }
679
680 /**
681 * Get the validator instance.
682 *
683 * @return \FluentCart\Framework\Validator\Validator
684 */
685 public function getValidator()
686 {
687 return $this->validator;
688 }
689
690 /**
691 * Get the valid data after validation has been passed.
692 *
693 * @return array
694 */
695 public function validated($data = [])
696 {
697 if ($data) {
698 return $this->validated = $data;
699 }
700
701 return (array) $this->validated;
702 }
703
704 /**
705 * Abort the request.
706 *
707 * @param integer $status
708 * @param string $message
709 * @return \WP_REST_Response
710 */
711 public function abort($status = 403, $message = null)
712 {
713 $this->maybeThrowValidationException($status);
714
715 if (!$message && !is_numeric($status) && is_string($status)) {
716 $message = $status;
717 $status = 403;
718 }
719
720 $message = $message ?: "{$status} Request has been aborted.";
721
722 return new \WP_REST_Response(
723 is_array($message) ? $message : [
724 'message' => (string) $message
725 ], $status
726 );
727 }
728
729 /**
730 * Terminate the request.
731 *
732 * @param integer $status
733 * @param string $message
734 * @return \WP_REST_Response
735 */
736 public function terminate($status = 200, $message = null)
737 {
738 $this->maybeThrowValidationException($status);
739
740 if (!$message && !is_numeric($status) && is_string($status)) {
741 $message = $status;
742 $status = 403;
743 }
744
745 $message = $message
746 ?: "Request has benn terminated with status {$status}.";
747
748 return wp_send_json(
749 is_array($message) ? $message : ['message' => (string) $message], $status
750 );
751 }
752
753 /**
754 * Throw a validation exception if status is validation exception.
755 * @param mixed $status
756 * @return void
757 * @throws \FluentCart\Framework\Validator\ValidationException
758 */
759 protected function maybeThrowValidationException($status)
760 {
761 if (is_object($status)) {
762 if (method_exists($status, 'errors')) {
763 throw new ValidationException(
764 'Unprocessable Entity!', 422, null, $status->errors()
765 );
766 }
767 }
768 }
769
770 /**
771 * Get an input element from the request.
772 *
773 * @param string $key
774 * @return mixed
775 */
776 public function __get($key)
777 {
778 if ($value = $this->get($key)) {
779 return $value;
780 }
781
782 return $this->file($key);
783 }
784
785 /**
786 * Retrieves the currently logged in user.
787 *
788 * @return \FluentCart\Framework\Http\Request\WPUserProxy
789 */
790 public function user()
791 {
792 return $this->app->user();
793 }
794
795 /**
796 * Dynamyc method calls (specially for WP_rest_request)
797 * @param string $method
798 * @param array $params
799 * @return mixed
800 */
801 public function __call($method, $params = [])
802 {
803 if (static::hasMacro($method)) {
804 return $this->macroCall($method, $params);
805 }
806
807 if ($method == 'route') {
808 if ($params) {
809 // @phpstan-ignore-next-line
810 return $this->app->route->{$params[0]};
811 }
812 // @phpstan-ignore-next-line
813 return $this->app->route;
814 }
815
816 if ($this->app->bound('wprestrequest')) {
817 // @phpstan-ignore-next-line
818 if (!method_exists($this->app->wprestrequest, $method)) {
819 $method = strtolower(
820 preg_replace([
821 '/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'
822 ], '$1_$2', $method)
823 );
824 }
825
826 return call_user_func_array([
827 // @phpstan-ignore-next-line
828 $this->app->wprestrequest, $method], $params
829 );
830 }
831 }
832 }
833