PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.2.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.2.0
2.2.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 All 43 releases
← All changes | vendor/wpfluent/framework/src/WPFluent/Foundation/Application.php +179 -22 1.32 → 2.2.0 View file →
@@ -1,23 +1,58 @@
1 1 <?php
2 +/**
3 + * @package WPFluent
4 + * @author Sheikh Heera <[email protected]> (https://heera.it)
5 + * @author Sheikh Heera <[email protected]>
6 + * @author Sheikh Heera <[email protected]>
7 + * @link https://github.com/wpfluent/framework2x
8 + * @license MIT https://opensource.org/licenses/MIT
9 + * @license GPL-2.0-or-later https://www.gnu.org/licenses/gpl-2.0.html
10 + */
2 11
3 12 namespace FluentBoards\Framework\Foundation;
4 13
5 14 use InvalidArgumentException;
6 15 use FluentBoards\Framework\Support\Arr;
16 +use FluentBoards\Framework\Support\Env;
17 +use FluentBoards\Framework\Http\Client;
7 18 use FluentBoards\Framework\Foundation\Config;
8 19 use FluentBoards\Framework\Container\Container;
9 20 use FluentBoards\Framework\Foundation\ComponentBinder;
10 -use FluentBoards\Framework\Foundation\FoundationTrait;
11 -use FluentBoards\Framework\Foundation\AsyncRequestTrait;
21 +use FluentBoards\Framework\Foundation\Concerns\FoundationTrait;
12 22
23 +/**
24 + * Application — service container with magic property access via __get.
25 + *
26 + * Properties below are bound in {@see ComponentBinder::bindComponents()} and
27 + * {@see Application::init()}/{@see Application::setAppLevelNamespace()}. Each
28 + * `@property` declaration teaches PHPStan about a runtime container binding so
29 + * `$app->view->render(...)` and similar access can be type-checked.
30 + *
31 + * @property \FluentBoards\Framework\Foundation\Config $config
32 + * @property \FluentBoards\Framework\View\View $view
33 + * @property \FluentBoards\Framework\Cache\Cache $cache
34 + * @property \FluentBoards\Framework\Http\Router\Router $router
35 + * @property \FluentBoards\Framework\Http\Request\Request $request
36 + * @property \FluentBoards\Framework\Http\Response\Response $response
37 + * @property \FluentBoards\Framework\Validator\Validator $validator
38 + * @property \FluentBoards\Framework\Events\Dispatcher $events
39 + * @property \FluentBoards\Framework\Encryption\Encrypter $encrypter
40 + * @property \FluentBoards\Framework\Encryption\Encrypter $crypt
41 + * @property \FluentBoards\Framework\Database\DatabaseManager $db
42 + * @property \FluentBoards\Framework\Http\URL $url
43 + * @property \FluentBoards\Framework\Support\Mail $mail
44 + * @property \FluentBoards\Framework\Support\Pipeline $pipeline
45 + * @property string $__pluginfile__
46 + * @property string $__namespace__
47 + */
13 48 class Application extends Container
14 49 {
15 - use FoundationTrait,
16 - AsyncRequestTrait;
50 + use FoundationTrait;
17 51
18 52 /**
19 53 * Main plugin file's absolute path
54 + *
20 55 * @var string
21 56 */
22 57 protected $file = null;
23 58
@@ -22,8 +57,9 @@
22 57 protected $file = null;
23 58
24 59 /**
25 60 * Plugin's base url
61 + *
26 62 * @var string
27 63 */
28 64 protected $baseUrl = null;
29 65
@@ -28,8 +64,9 @@
28 64 protected $baseUrl = null;
29 65
30 66 /**
31 67 * Plugin's base path
68 + *
32 69 * @var string
33 70 */
34 71 protected $basePath = null;
35 72
@@ -34,8 +71,9 @@
34 71 protected $basePath = null;
35 72
36 73 /**
37 74 * Default namespace for hook's handlers
75 + *
38 76 * @var string
39 77 */
40 78 protected $handlerNamespace = null;
41 79
@@ -40,8 +78,9 @@
40 78 protected $handlerNamespace = null;
41 79
42 80 /**
43 81 * Default namespace for controllers
82 + *
44 83 * @var string
45 84 */
46 85 protected $controllerNamespace = null;
47 86
@@ -46,19 +85,35 @@
46 85 protected $controllerNamespace = null;
47 86
48 87 /**
49 88 * Default namespace for policy handlers
89 + *
50 90 * @var string
51 91 */
52 - protected $permissionNamespace = null;
92 + protected $policyNamespace = null;
53 93
54 94 /**
55 95 * Composer JSON
96 + *
56 97 * @var null|array
57 98 */
58 99 protected static $composer = null;
59 100
60 101 /**
102 + * Ready event handlers
103 + *
104 + * @var array
105 + */
106 + protected $onReady = [];
107 +
108 + /**
109 + * Flag to check if components are bound.
110 + *
111 + * @var boolean
112 + */
113 + protected $componentsBound = false;
114 +
115 + /**
61 116 * Construct the application instance
62 117 *
63 118 * @param string $file The main plugin file's absolute path
64 119 * @return null
@@ -65,10 +120,12 @@
65 120 */
66 121 public function __construct($file = null)
67 122 {
68 123 $this->init($file);
124 + $this->loadEnvironmentVars();
69 125 $this->setAppLevelNamespace();
70 126 $this->bootstrapApplication();
127 + $this->callPluginReadyCallbacks();
71 128 }
72 129
73 130 /**
74 131 * Init the application instance
@@ -84,8 +141,21 @@
84 141 $this->baseUrl = plugin_dir_url($this->file);
85 142 }
86 143
87 144 /**
145 + * Load the environment bariables from .env file.
146 + *
147 + * @param string $path
148 + * @return void
149 + */
150 + protected function loadEnvironmentVars($path = null)
151 + {
152 + $path = $path ?: $this->basePath . '.env';
153 +
154 + is_readable($path) && Env::load($path);
155 + }
156 +
157 + /**
88 158 * Set the default application level namespaces to resolve
89 159 * the controllers, policies and various hook handlers.
90 160 *
91 161 * @return null
@@ -119,9 +189,11 @@
119 189 file_get_contents($this->basePath . 'composer.json'), true
120 190 );
121 191 }
122 192
123 - return $section ? Arr::get(static::$composer, $section) : static::$composer;
193 + return $section ? Arr::get(
194 + static::$composer, $section
195 + ) : static::$composer;
124 196 }
125 197
126 198 /**
127 199 * Bootstrap the application.
@@ -135,9 +207,8 @@
135 207 $this->loadConfigIfExists();
136 208 $this->registerTextdomain();
137 209 $this->bindCoreComponents();
138 210 $this->requireCommonFiles($this);
139 - $this->registerAsyncActions();
140 211 $this->addRestApiInitAction($this);
141 212 }
142 213
143 214 /**
@@ -211,8 +282,30 @@
211 282 $this->instance('config', new Config($data));
212 283 }
213 284
214 285 /**
286 + * Resolve the given type from the container.
287 + * This method is required for unit testing.
288 + *
289 + * @param string $abstract
290 + * @param array $parameters
291 + * @return mixed
292 + */
293 + public function make($abstract, $parameters = [])
294 + {
295 + if (str_starts_with($abstract, '_NS')) {
296 +
297 + $namespace = $this->getComposer(
298 + 'extra.wpfluent.namespace.current'
299 + );
300 +
301 + $abstract = str_replace('_NS', $namespace, $abstract);
302 + }
303 +
304 + return parent::make($abstract, $parameters);
305 + }
306 +
307 + /**
215 308 * Register plugin's text domain
216 309 *
217 310 * @return null
218 311 */
@@ -219,9 +312,11 @@
219 312 protected function registerTextdomain()
220 313 {
221 314 $this->addAction('init', function() {
222 315 load_plugin_textdomain(
223 - $this->config->get('app.text_domain'), false, $this->textDomainPath()
316 + $this->config->get(
317 + 'app.text_domain'
318 + ), false, $this->textDomainPath()
224 319 );
225 320 });
226 321 }
227 322
@@ -242,9 +337,12 @@
242 337 * @return null
243 338 */
244 339 protected function bindCoreComponents()
245 340 {
246 - (new ComponentBinder($this))->bindComponents();
341 + if (!$this->componentsBound) {
342 + $this->componentsBound = true;
343 + (new ComponentBinder($this))->bindComponents();
344 + }
247 345 }
248 346
249 347 /**
250 348 * Load (include) the files where hooks are registered.
@@ -260,11 +358,11 @@
260 358 );
261 359
262 360 require_once $this->basePath . 'app/Hooks/actions.php';
263 361 require_once $this->basePath . 'app/Hooks/filters.php';
264 -
265 - if (file_exists($includes = $this->basePath . 'app/Hooks/includes.php')) {
266 - require_once $includes;
362 +
363 + if (file_exists($f = $this->basePath . 'app/Hooks/includes.php')) {
364 + require_once $f;
267 365 }
268 366 }
269 367
270 368 /**
@@ -270,11 +368,11 @@
270 368 /**
271 369 * Handler for rest_pre_serve_request filter.
272 370 *
273 371 * @param bool $served (default: false)
274 - * @param \WP_Rest_Response $result
275 - * @param \WP_Rest_Request $request
276 - * @param \WP_Rest_Server $server
372 + * @param \WP_REST_Response $result
373 + * @param \WP_REST_Request $request
374 + * @param \WP_REST_Server $server
277 375 * @return bool (false to intercept, otherwise true)
278 376 */
279 377 public function preServeRequest($served, $result, $request, $server)
280 378 {
@@ -285,8 +383,9 @@
285 383 if ($this->isRequestOfPlugin($route, $slug)) {
286 384 if ($this->isRequestForEndpoints($route)) {
287 385 status_header(200);
288 386 $result->set_status(200);
387 + // @phpstan-ignore-next-line
289 388 $result->set_data($this->endpoints);
290 389 } else {
291 390 $result->set_data(
292 391 $this->customizeNotFoundResponse(
@@ -308,12 +407,35 @@
308 407 * @return bool
309 408 */
310 409 public function isRequestOfPlugin($route = '', $slug = '')
311 410 {
312 - return str_contains(
313 - $route ?: $this->request->url(),
314 - $slug ?: $this->config->get('app.slug')
315 - );
411 + $slug = $slug ?: $this->config->get('app.slug');
412 +
413 + if (!$route) {
414 + if (get_option('permalink_structure')) {
415 + $route = $this->request->url();
416 + } else {
417 + $route = $this->request->query('rest_route');
418 + }
419 + }
420 +
421 + // For web routing (If web-routing is installed)
422 + if (!$route && !$this->request->isRest()) {
423 + $route = $this->request->url();
424 + }
425 +
426 + $parsedUrl = parse_url($route ?? '');
427 +
428 + $path = str_replace('/wp-json', '', $parsedUrl['path'] ?? '');
429 +
430 + if (is_admin()) {
431 + $page = $this->request->query('page');
432 + if ($slug === $page) {
433 + $path = $page;
434 + }
435 + }
436 +
437 + return str_starts_with(ltrim($path, '/'), $slug);
316 438 }
317 439
318 440 /**
319 441 * Determines if the request is made for endpoints.
@@ -329,10 +451,10 @@
329 451
330 452 /**
331 453 * Prepare a custom not found response.
332 454 *
333 - * @param \WP_Rest_Response $result
334 - * @param \WP_Rest_Request $request
455 + * @param \WP_REST_Response $result
456 + * @param \WP_REST_Request $request
335 457 *
336 458 * @return array
337 459 */
338 460 public function customizeNotFoundResponse($result, $request)
@@ -356,8 +478,17 @@
356 478
357 479 return $response;
358 480 }
359 481
482 + /**
483 + * Check if running unit test.
484 + *
485 + * @return boolean
486 + */
487 + public function isUnitTesting()
488 + {
489 + return getenv('ENV') === 'testing';
490 + }
360 491
361 492 /**
362 493 * Register the rest api init actions and routes
363 494 *
@@ -380,12 +511,13 @@
380 511 * Register rest routes.
381 512 *
382 513 * @param \FluentBoards\Framework\Http\Router $router
383 514 *
384 - * @return null
515 + * @return void
385 516 */
386 517 protected function registerRestRoutes($router)
387 518 {
519 + // @phpstan-ignore-next-line
388 520 $router->registerRoutes(
389 521 $this->requireRouteFile($router)
390 522 );
391 523 }
@@ -398,6 +530,31 @@
398 530 */
399 531 protected function requireRouteFile($router)
400 532 {
401 533 require_once $this['path.http'] . 'Routes/routes.php';
534 + }
535 +
536 + /**
537 + * Register plugin ready (booted) callbacks.
538 + *
539 + * @param callable $callback
540 + * @return void
541 + */
542 + public function ready(callable $callback)
543 + {
544 + $this->onReady[] = $callback;
545 + }
546 +
547 + /**
548 + * Execute plugin booted callbacks.
549 + *
550 + * @return void
551 + */
552 + protected function callPluginReadyCallbacks()
553 + {
554 + $this->addAction('init', function() {
555 + while ($callback = array_pop($this->onReady)) {
556 + $callback($this);
557 + }
558 + });
402 559 }
403 560 }