PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.41
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.41
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 / Foundation / Application.php

Application.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.41, at vendor/wpfluent/framework/src/WPFluent/Foundation/Application.php

518 lines 12.6 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\Foundation;
4
5 use InvalidArgumentException;
6 use FluentBoards\Framework\Support\Arr;
7 use FluentBoards\Framework\Support\Env;
8 use FluentBoards\Framework\Http\Client;
9 use FluentBoards\Framework\Foundation\Config;
10 use FluentBoards\Framework\Container\Container;
11 use FluentBoards\Framework\Foundation\ComponentBinder;
12 use FluentBoards\Framework\Foundation\FoundationTrait;
13
14 class Application extends Container
15 {
16 use FoundationTrait;
17
18 /**
19 * Main plugin file's absolute path
20 *
21 * @var string
22 */
23 protected $file = null;
24
25 /**
26 * Plugin's base url
27 *
28 * @var string
29 */
30 protected $baseUrl = null;
31
32 /**
33 * Plugin's base path
34 *
35 * @var string
36 */
37 protected $basePath = null;
38
39 /**
40 * Default namespace for hook's handlers
41 *
42 * @var string
43 */
44 protected $handlerNamespace = null;
45
46 /**
47 * Default namespace for controllers
48 *
49 * @var string
50 */
51 protected $controllerNamespace = null;
52
53 /**
54 * Default namespace for policy handlers
55 *
56 * @var string
57 */
58 protected $permissionNamespace = null;
59
60 /**
61 * Composer JSON
62 *
63 * @var null|array
64 */
65 protected static $composer = null;
66
67 /**
68 * Ready event handlers
69 *
70 * @var array
71 */
72 protected $onReady = [];
73
74 /**
75 * Construct the application instance
76 *
77 * @param string $file The main plugin file's absolute path
78 * @return null
79 */
80 public function __construct($file = null)
81 {
82 $this->init($file);
83 $this->loadEnvironmentVars();
84 $this->setAppLevelNamespace();
85 $this->bootstrapApplication();
86 $this->callPluginReadyCallbacks();
87 }
88
89 /**
90 * Init the application instance
91 *
92 * @param string $file The main plugin file's absolute path
93 *
94 * @return null
95 */
96 protected function init($file)
97 {
98 $this['__pluginfile__'] = $this->file = $file;
99 $this->basePath = plugin_dir_path($this->file);
100 $this->baseUrl = plugin_dir_url($this->file);
101 }
102
103 protected function loadEnvironmentVars($path = null)
104 {
105 $path = $path ?: $this->basePath . '.env';
106
107 is_readable($path) && Env::load($path);
108 }
109
110 /**
111 * Set the default application level namespaces to resolve
112 * the controllers, policies and various hook handlers.
113 *
114 * @return null
115 */
116 protected function setAppLevelNamespace()
117 {
118 $composer = $this->getComposer();
119
120 $psr4 = array_flip($composer['autoload']['psr-4']);
121
122 $this->policyNamespace = $psr4['app/'] . 'Http\Policies';
123
124 $this->handlerNamespace = $psr4['app/'] . 'Hooks\Handlers';
125
126 $this->controllerNamespace = $psr4['app/'] . 'Http\Controllers';
127
128 $this['__namespace__'] = $composer['extra']['wpfluent']['namespace']['current'];
129 }
130
131 /**
132 * Get the composer data as an array
133 *
134 * @param string $section Specific key
135 *
136 * @return array partial or full composer data array
137 */
138 public function getComposer($section = null)
139 {
140 if (is_null(static::$composer)) {
141 static::$composer = json_decode(
142 file_get_contents($this->basePath . 'composer.json'), true
143 );
144 }
145
146 return $section ? Arr::get(
147 static::$composer, $section
148 ) : static::$composer;
149 }
150
151 /**
152 * Bootstrap the application.
153 *
154 * @return null
155 */
156 protected function bootstrapApplication()
157 {
158 $this->bindAppInstance();
159 $this->bindPathsAndUrls();
160 $this->loadConfigIfExists();
161 $this->registerTextdomain();
162 $this->bindCoreComponents();
163 $this->registerAsyncActions();
164 $this->requireCommonFiles($this);
165 $this->addRestApiInitAction($this);
166 }
167
168 /**
169 * Bind application instance in the container.
170 *
171 * @return null
172 */
173 protected function bindAppInstance()
174 {
175 App::setInstance($this);
176 $this->instance('app', $this);
177 $this->instance(__CLASS__, $this);
178 $this->instance('endpoints', []);
179 }
180
181 /**
182 * Bind the paths and urls
183 *
184 * @return null
185 */
186 protected function bindPathsAndUrls()
187 {
188 $this->bindUrls();
189 $this->basePaths();
190 }
191
192 /**
193 * Bind urls
194 *
195 * @return null
196 */
197 protected function bindUrls()
198 {
199 $this['url.assets'] = $this->baseUrl . 'assets/';
200 }
201
202 /**
203 * Bind paths
204 *
205 * @return null
206 */
207 protected function basePaths()
208 {
209 $this['path'] = $this->basePath;
210 $this['path.app'] = $this->basePath . 'app/';
211 $this['path.hooks'] = $this['path.app'] . 'Hooks/';
212 $this['path.http'] = $this['path.app'] . 'Http/';
213 $this['path.controllers'] = $this['path.http'] . 'Controllers/';
214 $this['path.config'] = $this->basePath . 'config/';
215 $this['path.assets'] = $this->basePath . 'assets/';
216 $this['path.resources'] = $this->basePath . 'resources/';
217 $this['path.views'] = $this['path.app'] . 'Views/';
218 }
219
220 /**
221 * Load application's config and set
222 * the data in the Config instance.
223 *
224 * @return null
225 */
226 protected function loadConfigIfExists()
227 {
228 $data = [];
229
230 if (is_dir($this['path.config'])) {
231 foreach (glob($this['path.config'] . '*.php') as $file) {
232 $data[basename($file, '.php')] = require($file);
233 }
234 }
235
236 $this->instance('config', new Config($data));
237 }
238
239 /**
240 * Resolve the given type from the container.
241 *
242 * @param string $abstract
243 * @param array $parameters
244 * @return mixed
245 */
246 public function make($abstract, $parameters = [])
247 {
248 if (str_starts_with($abstract, '_NS')) {
249
250 $namespace = $this->getComposer(
251 'extra.wpfluent.namespace.current'
252 );
253
254 $abstract = str_replace('_NS', $namespace, $abstract);
255 }
256
257 return parent::make($abstract, $parameters);
258 }
259
260 /**
261 * Register plugin's text domain
262 *
263 * @return null
264 */
265 protected function registerTextdomain()
266 {
267 $this->addAction('init', function() {
268 load_plugin_textdomain(
269 $this->config->get(
270 'app.text_domain'
271 ), false, $this->textDomainPath()
272 );
273 });
274 }
275
276 /**
277 * Resolve the text domain path.
278 *
279 * @return null
280 */
281 protected function textDomainPath()
282 {
283 return basename($this['path']) . $this->config->get('app.domain_path');
284 }
285
286 /**
287 * Bind the components of the framework into the container so
288 * they'll be available throughout the application life cycle.
289 *
290 * @return null
291 */
292 protected function bindCoreComponents()
293 {
294 (new ComponentBinder($this))->bindComponents();
295 }
296
297 /**
298 * Load (include) the files where hooks are registered.
299 *
300 * @param self $app
301 *
302 * @return null
303 */
304 protected function requireCommonFiles($app)
305 {
306 $this->addFilter(
307 'rest_pre_serve_request', [$this, 'preServeRequest'], 10, 4
308 );
309
310 require_once $this->basePath . 'app/Hooks/actions.php';
311 require_once $this->basePath . 'app/Hooks/filters.php';
312
313 if (file_exists($includes = $this->basePath . 'app/Hooks/includes.php')) {
314 require_once $includes;
315 }
316 }
317
318 /**
319 * Handler for rest_pre_serve_request filter.
320 *
321 * @param bool $served (default: false)
322 * @param \WP_Rest_Response $result
323 * @param \WP_Rest_Request $request
324 * @param \WP_Rest_Server $server
325 * @return bool (false to intercept, otherwise true)
326 */
327 public function preServeRequest($served, $result, $request, $server)
328 {
329 if ($result->get_status() === 404) {
330 $route = $request->get_route();
331 $slug = $this->config->get('app.slug');
332
333 if ($this->isRequestOfPlugin($route, $slug)) {
334 if ($this->isRequestForEndpoints($route)) {
335 status_header(200);
336 $result->set_status(200);
337 $result->set_data($this->endpoints);
338 } else {
339 $result->set_data(
340 $this->customizeNotFoundResponse(
341 $result, $request
342 )
343 );
344 }
345 }
346 }
347
348 return $served;
349 }
350
351 /**
352 * Determines whether the request is madse by plugin.
353 *
354 * @param string $route (Rest route|Full URL)
355 * @param string $slug (Plugin's slug)
356 * @return bool
357 */
358 public function isRequestOfPlugin($route = '', $slug = '')
359 {
360 $slug = $slug ?: $this->config->get('app.slug');
361
362 if (!$route) {
363 if (get_option('permalink_structure')) {
364 $route = $this->request->url();
365 } else {
366 $route = $this->request->query('rest_route');
367 }
368 }
369
370 // For web routing (If web-routing is installed)
371 if (!$route && !$this->request->isRest()) {
372 $route = $this->request->url();
373 }
374
375 $parsedUrl = parse_url($route ?? '');
376
377 $path = str_replace('/wp-json', '', $parsedUrl['path'] ?? '');
378
379 if (is_admin()) {
380 $page = $this->request->query('page');
381 if ($slug === $page) {
382 $path = $page;
383 }
384 }
385
386 return str_starts_with(ltrim($path, '/'), $slug);
387 }
388
389 /**
390 * Determines if the request is made for endpoints.
391 *
392 * @param string $route (Rest route|Full URL)
393
394 * @return bool
395 */
396 protected function isRequestForEndpoints($route)
397 {
398 return str_ends_with($route, '__endpoints');
399 }
400
401 /**
402 * Prepare a custom not found response.
403 *
404 * @param \WP_Rest_Response $result
405 * @param \WP_Rest_Request $request
406 *
407 * @return array
408 */
409 public function customizeNotFoundResponse($result, $request)
410 {
411 $response = $result->get_data();
412
413 if ($this->env() === 'dev') {
414 $response['data']['wpfluent'] = [
415 'env' => $this->env(),
416 'method' => $request->get_method(),
417 'request_url' => $this->request->url(),
418 'route_params' => $request->get_url_params(),
419 'query_params' => $request->get_query_params(),
420 'body_params' => $request->get_body_params(),
421 ];
422 } else {
423 $response['data']['wpfluent'] = [
424 'env' => $this->env()
425 ];
426 }
427
428 return $response;
429 }
430
431 /**
432 * Check if running unit test.
433 *
434 * @return boolean
435 */
436 public function isUnitTesting()
437 {
438 return getenv('ENV') === 'testing';
439 }
440
441 /**
442 * Register the rest api init actions and routes
443 *
444 * @param self $app
445 */
446 protected function addRestApiInitAction($app)
447 {
448 $this->addAction('rest_api_init', function($wpRestServer) use ($app) {
449 try {
450 $this->registerRestRoutes($app->router);
451 } catch (InvalidArgumentException $e) {
452 return $app->response->json([
453 'message' => $e->getMessage()
454 ], $e->getCode() ?: 500);
455 }
456 });
457 }
458
459 /**
460 * Register rest routes.
461 *
462 * @param \FluentBoards\Framework\Http\Router $router
463 *
464 * @return null
465 */
466 protected function registerRestRoutes($router)
467 {
468 $router->registerRoutes(
469 $this->requireRouteFile($router)
470 );
471 }
472
473 /**
474 * Load (include) routes
475 *
476 * @param \FluentBoards\Framework\Http\Router $router
477 * @return null
478 */
479 protected function requireRouteFile($router)
480 {
481 require_once $this['path.http'] . 'Routes/routes.php';
482 }
483
484 /**
485 * Register plugin booted callbacks.
486 *
487 * @param callable $callback
488 * @return void
489 */
490 protected function ready(callable $callback)
491 {
492 $this->onReady[] = $callback;
493 }
494
495 /**
496 * Register Async Actions.
497 *
498 * @return void
499 */
500 protected function registerAsyncActions()
501 {
502 Client::registerAsyncRequestHandler();
503 }
504
505 /**
506 * Execute plugin booted callbacks.
507 *
508 * @param callable $callback
509 * @return void
510 */
511 protected function callPluginReadyCallbacks()
512 {
513 while ($callback = array_shift($this->onReady)) {
514 $callback($this);
515 }
516 }
517 }
518