PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.0
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Http / Request / Request.php

Request.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.1.0, at vendor/wpfluent/framework/src/WPFluent/Http/Request/Request.php

862 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\Framework\Http\Request;
4
5 use Closure;
6 use FluentBoards\Framework\Support\Arr;
7 use FluentBoards\Framework\Support\Helper;
8 use FluentBoards\Framework\Support\MacroableTrait;
9 use FluentBoards\Framework\Foundation\Application;
10 use FluentBoards\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 \FluentBoards\Framework\Foundation\Application
26 */
27 protected $app = null;
28
29 /**
30 * Validator instance.
31 *
32 * @var \FluentBoards\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 \FluentBoards\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 jar.
396 *
397 * Returns the raw cookie value, with one exception: if the value is a
398 * valid strict-base64 string AND the decoded bytes parse as JSON, the
399 * decoded value is returned instead. This keeps backwards compatibility
400 * with callers that stored base64(json(...)) payloads, while normal
401 * cookies (utm_*, UUIDs, hex hashes, JWTs, plain strings) pass through
402 * unchanged because they fail one of the two gates.
403 *
404 * @param string|null $key Cookie name, or null for the full array
405 * @param mixed $default Returned when $key is set but absent
406 * @return mixed
407 */
408 public function cookie($key = null, $default = null)
409 {
410 if ($key === null) {
411 return $this->cookie;
412 }
413
414 $raw = Arr::get($this->cookie, $key, $default);
415
416 // Pass through defaults, non-strings, and empty strings unchanged
417 if ($raw === $default || !is_string($raw) || $raw === '') {
418 return $raw;
419 }
420
421 // Auto-detect base64(json(...)) payloads. Both gates must pass;
422 // otherwise return the raw cookie. Strict mode rejects values
423 // containing characters outside the base64 alphabet (e.g. '-'),
424 // and most plain values fail the JSON parse on the decoded bytes.
425 $decoded = base64_decode($raw, true);
426 if ($decoded === false) {
427 return $raw;
428 }
429
430 $parsed = json_decode($decoded, true);
431 if (json_last_error() !== JSON_ERROR_NONE) {
432 return $raw;
433 }
434
435 return $parsed;
436 }
437
438 /**
439 * Get an item from the PHP $_GET array
440 * @param string $key
441 * @param mixed $default
442 * @return mixed
443 */
444 public function query($key = null, $default = null)
445 {
446 return $key ? Arr::get($this->get, $key, $default) : $this->get;
447 }
448
449 /**
450 * Get an item from the PHP $_POST array
451 * @param string $key
452 * @param mixed $default
453 * @return mixed
454 */
455 public function post($key = null, $default = null)
456 {
457 return $key ? Arr::get($this->post, $key, $default) : $this->post;
458 }
459
460 /**
461 * Return the only items given in the args
462 * @param array $keys
463 * @return array
464 */
465 public function only($keys)
466 {
467 $keys = is_array($keys) ? $keys : func_get_args();
468
469 return Arr::only($this->inputs(), $keys);
470 }
471
472 /**
473 * Return a subset of the request inputs except the given keys
474 * @param array $keys
475 * @return array
476 */
477 public function except($keys)
478 {
479 $keys = is_array($keys) ? $keys : func_get_args();
480
481 return Arr::except($this->inputs(), $keys);
482 }
483
484 /**
485 * Merge array with the request inputs
486 * @param array $data
487 * @return self
488 */
489 public function merge(array $data = [])
490 {
491 $this->request = array_replace($this->inputs(), $data);
492
493 return $this;
494 }
495
496 /**
497 * Merge array with the request inputs if the
498 * key(s) is missing from the request.
499 *
500 * @param array $data
501 * @return self
502 */
503 public function mergeIfMissing(array $data = [])
504 {
505 $all = $this->inputs();
506
507 $this->merge(Arr::mergeMissing($data, $all));
508
509 return $this;
510 }
511
512 /**
513 * Merge new input into the request's input, but only when
514 * that key is present in the request but value is missing.
515 *
516 * @param array $input
517 * @return $this
518 */
519 public function mergeMissing(array $input)
520 {
521 return $this->merge(Helper::collect($input)
522 ->filter(function($value, $key) {
523 return $this->missing($key);
524 })->toArray()
525 );
526 }
527
528 /**
529 * Returns the request body content.
530 *
531 * @return mixed
532 */
533 public function getContent()
534 {
535 if (!$this->content) {
536 $this->content = file_get_contents('php://input');
537 }
538
539 return $this->content;
540 }
541
542 /**
543 * Merges the input arrays from the WP_REST_Request.
544 *
545 * @param \WP_REST_Request $wpRestRequest
546 * @return void
547 */
548 public function mergeInputsFromRestRequest($wpRestRequest)
549 {
550 $this->request = array_merge(
551 $this->request, $wpRestRequest->get_params()
552 );
553
554 $this->post = array_merge(
555 $this->post, $wpRestRequest->get_body_params()
556 );
557
558 $this->get = array_merge(
559 $this->get, $wpRestRequest->get_query_params()
560 );
561
562 $this->mergerHeaders($wpRestRequest);
563
564 $this->wpRestRequest = true;
565 }
566
567 /**
568 * Merge the headers from the WP_REST_Request.
569 *
570 * @param \WP_REST_Request $wpRestRequest
571 * @return void
572 */
573 protected function mergerHeaders($wpRestRequest)
574 {
575 $headers = [];
576
577 foreach ($wpRestRequest->get_headers() as $key => $header) {
578 $headers[strtoupper($key)] = reset($header);
579 }
580
581 $this->headers = array_merge($this->headers, $headers);
582
583 if (
584 !isset($this->headers['CONTENT_TYPE']) || (
585 isset($this->headers['CONTENT_TYPE']) &&
586 $this->headers['CONTENT_TYPE'] !== true
587 )) {
588 if ($this->isJson()) {
589 $this->headers['CONTENT_TYPE'] = 'application/json';
590 }
591 }
592 }
593
594 /**
595 * Retrieve an input item from the request.
596 *
597 * @param string|null $key
598 * @param mixed $default
599 * @return mixed
600 */
601 public function input($key = null, $default = null)
602 {
603 return Arr::get($this->inputs(), $key, $default);
604 }
605
606 /**
607 * Remove a key(s) from the $request array
608 * @param mixed $key
609 * @return self
610 */
611 public function forget($key)
612 {
613 Arr::forget($this->request, $key);
614
615 return $this;
616 }
617
618 /**
619 * Get all inputs
620 *
621 * @return array
622 */
623 protected function inputs()
624 {
625 if (!$this->wpRestRequest) {
626 if ($this->app->bound('wprestrequest')) {
627 // @phpstan-ignore-next-line
628 $this->mergeInputsFromRestRequest($this->app->wprestrequest);
629 }
630 }
631
632 if (empty($this->json) && $json = $this->json()) {
633 $this->post = array_merge($this->post, $json);
634 $this->request = array_merge($this->request, $json);
635 }
636
637 return $this->safe === true ? $this->validated : $this->request;
638 }
639
640 /**
641 * To get item(s) from validated inputs
642 *
643 * @return self
644 */
645 public function safe()
646 {
647 $clone = clone $this;
648
649 $clone->safe = true;
650
651 return $clone;
652 }
653
654 /**
655 * Get the request method.
656 *
657 * @return string
658 */
659 public function method()
660 {
661 return $_SERVER['REQUEST_METHOD'];
662 }
663
664 /**
665 * Get the URL (no query string) for the request.
666 *
667 * @return string
668 */
669 public function url()
670 {
671 return preg_replace('/\?.*/', '', $this->getFullUrl());
672 }
673
674 /**
675 * Get the full URL for the request.
676 *
677 * @return string
678 */
679 public function getFullUrl()
680 {
681 return get_site_url() . rtrim($_SERVER['REQUEST_URI'], '/');
682 }
683
684 /**
685 * Validate the request.
686 *
687 * @param array $rules
688 * @param array $messages
689 * @return mixed
690 * @throws \FluentBoards\Framework\Validator\ValidationException
691 */
692 public function validate(array $rules, array $messages = [])
693 {
694 $this->validator = $this->app->make('validator')->make(
695 $data = $this->all(), $rules, $messages
696 );
697
698 if ($this->validator->validate()->fails()) {
699 throw new ValidationException(
700 'Unprocessable Entity!', 422, null, $this->validator->errors()
701 );
702 }
703
704 $this->validated = $this->validator->validated();
705
706 return $data;
707 }
708
709 /**
710 * Get the validator instance.
711 *
712 * @return \FluentBoards\Framework\Validator\Validator
713 */
714 public function getValidator()
715 {
716 return $this->validator;
717 }
718
719 /**
720 * Get the valid data after validation has been passed.
721 *
722 * @return array
723 */
724 public function validated($data = [])
725 {
726 if ($data) {
727 return $this->validated = $data;
728 }
729
730 return (array) $this->validated;
731 }
732
733 /**
734 * Abort the request.
735 *
736 * @param integer $status
737 * @param string $message
738 * @return \WP_REST_Response
739 */
740 public function abort($status = 403, $message = null)
741 {
742 $this->maybeThrowValidationException($status);
743
744 if (!$message && !is_numeric($status) && is_string($status)) {
745 $message = $status;
746 $status = 403;
747 }
748
749 $message = $message ?: "{$status} Request has been aborted.";
750
751 return new \WP_REST_Response(
752 is_array($message) ? $message : [
753 'message' => (string) $message
754 ], $status
755 );
756 }
757
758 /**
759 * Terminate the request.
760 *
761 * @param integer $status
762 * @param string $message
763 * @return \WP_REST_Response
764 */
765 public function terminate($status = 200, $message = null)
766 {
767 $this->maybeThrowValidationException($status);
768
769 if (!$message && !is_numeric($status) && is_string($status)) {
770 $message = $status;
771 $status = 403;
772 }
773
774 $message = $message
775 ?: "Request has benn terminated with status {$status}.";
776
777 return wp_send_json(
778 is_array($message) ? $message : ['message' => (string) $message], $status
779 );
780 }
781
782 /**
783 * Throw a validation exception if status is validation exception.
784 * @param mixed $status
785 * @return void
786 * @throws \FluentBoards\Framework\Validator\ValidationException
787 */
788 protected function maybeThrowValidationException($status)
789 {
790 if (is_object($status)) {
791 if (method_exists($status, 'errors')) {
792 throw new ValidationException(
793 'Unprocessable Entity!', 422, null, $status->errors()
794 );
795 }
796 }
797 }
798
799 /**
800 * Get an input element from the request.
801 *
802 * @param string $key
803 * @return mixed
804 */
805 public function __get($key)
806 {
807 if ($value = $this->get($key)) {
808 return $value;
809 }
810
811 return $this->file($key);
812 }
813
814 /**
815 * Retrieves the currently logged in user.
816 *
817 * @return \FluentBoards\Framework\Http\Request\WPUserProxy
818 */
819 public function user()
820 {
821 return $this->app->user();
822 }
823
824 /**
825 * Dynamyc method calls (specially for WP_rest_request)
826 * @param string $method
827 * @param array $params
828 * @return mixed
829 */
830 public function __call($method, $params = [])
831 {
832 if (static::hasMacro($method)) {
833 return $this->macroCall($method, $params);
834 }
835
836 if ($method == 'route') {
837 if ($params) {
838 // @phpstan-ignore-next-line
839 return $this->app->route->{$params[0]};
840 }
841 // @phpstan-ignore-next-line
842 return $this->app->route;
843 }
844
845 if ($this->app->bound('wprestrequest')) {
846 // @phpstan-ignore-next-line
847 if (!method_exists($this->app->wprestrequest, $method)) {
848 $method = strtolower(
849 preg_replace([
850 '/([a-z\d])([A-Z])/', '/([^_])([A-Z][a-z])/'
851 ], '$1_$2', $method)
852 );
853 }
854
855 return call_user_func_array([
856 // @phpstan-ignore-next-line
857 $this->app->wprestrequest, $method], $params
858 );
859 }
860 }
861 }
862