PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / libraries / adapter / mvc / controller.php

controller.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/mvc/controller.php

564 lines 11.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.mvc
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.mvc.view');
15 JLoader::import('adapter.mvc.model');
16
17 /**
18 * The main controller used by the MVC framework.
19 * This controller is used to dispatch the requested actions
20 * to the apposite views.
21 *
22 * @since 10.0
23 */
24 #[\AllowDynamicProperties]
25 abstract class JController
26 {
27 /**
28 * A list of controller instance.
29 *
30 * @var array
31 */
32 protected static $instances = array();
33
34 /**
35 * A list of excluded methods.
36 *
37 * @var array
38 */
39 protected $excludedMethods = array();
40
41 /**
42 * The controller prefix.
43 *
44 * @var string
45 */
46 protected $prefix;
47
48 /**
49 * The base path of the plugin.
50 *
51 * @var string
52 */
53 protected $basePath;
54
55 /**
56 * URL for redirection.
57 *
58 * @var string
59 * @since 10.1.30
60 */
61 protected $redirect;
62
63 /**
64 * Redirect message.
65 *
66 * @var string
67 * @since 10.1.30
68 */
69 protected $message;
70
71 /**
72 * Redirect message type.
73 *
74 * @var string
75 * @since 10.1.30
76 */
77 protected $messageType;
78
79 /**
80 * Array of class methods to call for a given task.
81 *
82 * @var array
83 * @since 10.1.30
84 */
85 protected $taskMap;
86
87 /**
88 * Class constructor.
89 *
90 * @param array $config An optional associative array of configuration settings.
91 */
92 public function __construct($config = array())
93 {
94 if (isset($config['prefix']))
95 {
96 $this->prefix = $config['prefix'];
97 }
98
99 if (isset($config['base']))
100 {
101 $this->basePath = $config['base'];
102 }
103
104 $reflect = new ReflectionClass('JController');
105
106 foreach ($reflect->getMethods() as $method)
107 {
108 $this->excludedMethods[] = $method->getName();
109 }
110
111 $this->registerTask('unpublish', 'publish');
112 }
113
114 /**
115 * Method to get a singleton controller instance.
116 *
117 * @param string $prefix The prefix for the controller.
118 * @param string $base The base path from which loading the controller.
119 *
120 * @return self A new controller instance.
121 */
122 public static function getInstance($prefix, $base)
123 {
124 if (!isset(static::$instances[$prefix]))
125 {
126 $app = JFactory::getApplication();
127 $input = $app->input;
128
129 $task = $input->get('task', '');
130 $cmd = $input->get('controller', '');
131
132 if (strpos($task, '.') !== false)
133 {
134 $split = explode('.', $task);
135
136 $cmd = array_shift($split);
137 $task = array_pop($split);
138 }
139
140 $folder = $app->isAdmin() ? 'admin' : 'site';
141
142 // load the main controller
143 if (!JLoader::import($folder . '.controller', $base))
144 {
145 wp_die(
146 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' .
147 '<p>' . JText::translate('CONTROLLER_FILE_NOT_FOUND_ERR') . '</p>',
148 404
149 );
150 }
151
152 $className = $prefix . 'Controller';
153
154 // check if the controller class exists
155 if (!class_exists($className))
156 {
157 wp_die(
158 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' .
159 '<p>' . JText::sprintf('CONTROLLER_CLASS_NOT_FOUND_ERR', $className) . '</p>',
160 404
161 );
162 }
163
164 // try to check if we should load a dedicated controller
165 if ($cmd)
166 {
167 /**
168 * Search also inside the libraries folder of the current plugin.
169 * Prioritize this folder to avoid conflicts with deprecated files.
170 *
171 * @since 10.1.35
172 */
173 if (JLoader::import('libraries.mvc.' . $folder . '.controllers.' . $cmd, $base) || JLoader::import($folder . '.controllers.' . $cmd, $base))
174 {
175 $childClass = $className . ucwords($cmd);
176
177 if (class_exists($childClass))
178 {
179 $className = $childClass;
180 }
181 }
182 }
183
184 // setup options array
185 $options = [
186 'prefix' => $prefix,
187 'base' => $base,
188 ];
189
190 // instantiate the controller
191 $controller = new $className($options);
192
193 // make sure the controller is a valid instance
194 if (!$controller instanceof JController)
195 {
196 wp_die(
197 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' .
198 '<p>' . JText::translate('CONTROLLER_INVALID_INSTANCE_ERR') . '</p>',
199 500
200 );
201 }
202
203 // cache the instance
204 static::$instances[$prefix] = $controller;
205 }
206
207 return static::$instances[$prefix];
208 }
209
210 /**
211 * Typical view method for MVC based architecture.
212 *
213 * This function is provided as a default implementation, in most cases
214 * you will need to override it in your own controllers.
215 *
216 * @return self This object to support chaining.
217 *
218 * @uses getView()
219 * @uses getModel()
220 */
221 public function display()
222 {
223 $input = JFactory::getApplication()->input;
224
225 // get view name
226 $action = $input->get('view', null);
227 $layout = $input->get('layout', null);
228
229 if ($action)
230 {
231 // try to obtain the view related to the specified action
232 $view = $this->getView($action);
233
234 if ($view)
235 {
236 // try to obtain the model related to the specified view
237 $model = $this->getModel($action);
238
239 if ($model)
240 {
241 // attach the model if exists
242 $view->setModel($model);
243 }
244
245 /**
246 * Inject the layout through the apposite setter instead of passing it
247 * as argument to the `JView::display()` method.
248 *
249 * @since 10.1.41
250 */
251 if ($layout)
252 {
253 $view->setLayout($layout);
254 }
255
256 /**
257 * Fires before the controller displays the view.
258 *
259 * @param JView $view The view instance.
260 *
261 * @since 10.1.16
262 */
263 do_action_ref_array(strtolower($this->prefix) . '_before_display_' . strtolower($action), array(&$view));
264
265 // display the view before to terminate
266 $view->display();
267
268 /**
269 * Fires after the controller displayed the view.
270 *
271 * @param JView $view The view instance.
272 *
273 * @since 10.1.16
274 */
275 do_action(strtolower($this->prefix) . '_after_display_' . strtolower($action), array($view));
276 }
277
278 }
279
280 return $this;
281 }
282
283 /**
284 * Execute a task by triggering a method in the derived class.
285 *
286 * @param string $task The task to perform. If no matching task is found,
287 * the default 'display' method is executed.
288 *
289 * @return mixed The value returned by the called method.
290 */
291 public function execute($task)
292 {
293 $task = (string) $task;
294
295 // get only the string after the dot, if any
296 if (strpos($task, '.') !== false)
297 {
298 $split = explode('.', $task);
299 $task = array_pop($split);
300
301 /**
302 * Reset the task without the controller context.
303 *
304 * @since 10.1.30
305 */
306 JFactory::getApplication()->input->set('task', $task);
307 }
308
309 // raise an error if we are trying to call reserved methods
310 if (in_array($task, $this->excludedMethods) && $task != 'display')
311 {
312 wp_die(
313 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>' .
314 '<p>' . JText::translate('CONTROLLER_PROTECTED_METHOD_ERR') . '</p>',
315 403
316 );
317 }
318
319 $reflect = new ReflectionClass(get_class($this));
320
321 // check if we should use a different task linked to the specified one
322 if (isset($this->taskMap[$task]))
323 {
324 $task = $this->taskMap[$task];
325 }
326
327 // check if the $task method is callable
328 if (!$reflect->hasMethod($task) || !$reflect->getMethod($task)->isPublic())
329 {
330 // otherwise use default 'display' method
331 $task = 'display';
332 }
333
334 try
335 {
336 // dispatch callback
337 $result = call_user_func(array($this, $task));
338 }
339 catch (Exception $e)
340 {
341 // We need to terminate the buffer here to avoid displaying
342 // the output printed by the views into the error screen.
343
344 while (ob_get_status())
345 {
346 // repeat until the buffer is empty
347 ob_end_clean();
348 }
349
350 if (!wp_doing_ajax())
351 {
352 /**
353 * Included exception backtrace within the document in case the DEBUG is turned on.
354 *
355 * @since 10.1.35
356 */
357 if (WP_DEBUG)
358 {
359 $trace = '<pre style="white-space:pre-wrap;">' . $e->getTraceAsString() . '</pre>';
360 }
361 else
362 {
363 $trace = '';
364 }
365
366 // raise an error in case an exception has been thrown
367 wp_die(
368 '<h1>' . JText::translate('FATAL_ERROR') . '</h1>'
369 . '<p>' . $e->getMessage() . '</p>'
370 . $trace,
371 ($code = $e->getCode()) ? $code : 500
372 );
373 }
374 else
375 {
376 /**
377 * Raise a minified error for AJAX requests.
378 *
379 * @since 10.1.21
380 */
381 wp_die(
382 $e->getMessage(),
383 ($code = $e->getCode()) ? $code : 500
384 );
385 }
386 }
387
388 return $result;
389 }
390
391 /**
392 * Set a URL for browser redirection.
393 *
394 * @param string $url URL to redirect to.
395 * @param string $msg Message to display on redirect.
396 * @param string $type Message type.
397 *
398 * @return self This object to support chaining.
399 *
400 * @since 10.1.30
401 */
402 public function setRedirect($url, $msg = null, $type = null)
403 {
404 // register redirection URL
405 $this->redirect = $url;
406
407 if ($msg !== null)
408 {
409 // controller may have set this directly
410 $this->message = $msg;
411 }
412
413 // ensure the type is not overwritten by a previous call to setMessage
414 if (empty($type))
415 {
416 if (empty($this->messageType))
417 {
418 $this->messageType = 'message';
419 }
420 }
421 // if the type is explicitly set, set it
422 else
423 {
424 $this->messageType = $type;
425 }
426
427 return $this;
428 }
429
430 /**
431 * Redirects the browser or returns false if no redirect is set.
432 *
433 * @return boolean False if no redirect exists.
434 *
435 * @since 10.1.30
436 */
437 public function redirect()
438 {
439 if ($this->redirect)
440 {
441 $app = JFactory::getApplication();
442
443 if ($this->message)
444 {
445 // enqueue the redirect message
446 $app->enqueueMessage($this->message, $this->messageType);
447 }
448
449 // execute the redirect
450 $app->redirect($this->redirect);
451 }
452
453 return false;
454 }
455
456 /**
457 * Register (map) a task to a method in the class.
458 *
459 * @param string $task The task.
460 * @param string $method The name of the method in the derived class to perform for this task.
461 *
462 * @return self This object to support chaining.
463 *
464 * @since 10.1.30
465 */
466 public function registerTask($task, $method)
467 {
468 $this->taskMap[strtolower($task)] = $method;
469
470 return $this;
471 }
472
473 /**
474 * Returns the view object related to the specified name.
475 *
476 * @param string $view The view name.
477 *
478 * @return mixed The view object if exists, otherwise false.
479 */
480 protected function getView($view)
481 {
482 $app = JFactory::getApplication();
483
484 $folder = $app->isAdmin() ? 'admin' : 'site';
485
486 $paths = [];
487
488 /**
489 * Search also inside the libraries folder of the current plugin.
490 * Prioritize this folder to avoid conflicts with deprecated views.
491 *
492 * @since 10.1.35
493 */
494 $paths[] = $this->basePath . '/libraries/mvc/' . $folder . '/views';
495
496 // get default view path
497 $paths[] = $this->basePath . '/' . $folder . '/views';
498
499 // find the matching path
500 $path = JPath::find($paths, $view);
501
502 if (!$path)
503 {
504 return false;
505 }
506
507 // view found, make sure the entry point exists
508 $path = JPath::clean($path . '/view.html.php');
509
510 // make sure the file path exists
511 if (!is_file($path))
512 {
513 return false;
514 }
515
516 // include the view file
517 require_once $path;
518
519 $className = $this->prefix . 'View' . ucwords($view);
520
521 // make sure the view class exists
522 if (!class_exists($className))
523 {
524 return false;
525 }
526
527 $obj = new $className(dirname($path));
528
529 // make sure the view is a valid instance
530 if (!$obj instanceof JView)
531 {
532 return false;
533 }
534
535 return $obj;
536 }
537
538 /**
539 * Method to get a model object.
540 *
541 * @param string $name The model name.
542 * @param string $prefix The class prefix.
543 * @param array $config Configuration array for model.
544 *
545 * @return mixed Model object on success; otherwise false on failure.
546 */
547 public function getModel($name = '', $prefix = '', $config = array())
548 {
549 if (!$name)
550 {
551 // obtain the model name from the classname of this controller
552 $name = strtolower(str_replace($this->prefix . 'Controller', '', get_class($this)));
553 }
554
555 if (!$prefix)
556 {
557 $prefix = $this->prefix;
558 }
559
560 // invoke parent
561 return JModel::getInstance($name, $prefix, $config);
562 }
563 }
564