Completed
Push — master ( a59f0d...254be3 )
by Anton
17:12 queued 02:14
created

Application::hasLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Bluz Framework Component
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link https://github.com/bluzphp/framework
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Bluz\Application;
13
14
use Bluz\Application\Exception\ApplicationException;
15
use Bluz\Application\Exception\ForbiddenException;
16
use Bluz\Application\Exception\NotAcceptableException;
17
use Bluz\Application\Exception\NotAllowedException;
18
use Bluz\Application\Exception\RedirectException;
19
use Bluz\Application\Exception\ReloadException;
20
use Bluz\Auth\AbstractRowEntity;
21
use Bluz\Common;
22
use Bluz\Controller\Reflection;
23
use Bluz\Http;
24
use Bluz\Proxy\Acl;
25
use Bluz\Proxy\Cache;
26
use Bluz\Proxy\Config;
27
use Bluz\Proxy\Layout;
28
use Bluz\Proxy\Logger;
29
use Bluz\Proxy\Messages;
30
use Bluz\Proxy\Request;
31
use Bluz\Proxy\Response;
32
use Bluz\Proxy\Router;
33
use Bluz\Proxy\Session;
34
use Bluz\Proxy\Translator;
35
use Bluz\Request\RequestFactory;
36
use Bluz\Response\Response as ResponseInstance;
37
use Bluz\View\View;
38
39
/**
40
 * Application
41
 *
42
 * @package  Bluz\Application
43
 * @link     https://github.com/bluzphp/framework/wiki/Application *
44
 * @author   Anton Shevchuk
45
 * @created  06.07.11 16:25
46
 *
47
 * @method void denied()
48
 * @method void redirect(string $url)
49
 * @method void redirectTo(string $module, string $controller, array $params = array())
50
 * @method void reload()
51
 * @method AbstractRowEntity user()
52
 */
53
class Application
54
{
55
    use Common\Helper;
56
    use Common\Singleton;
57
58
    /**
59
     * @var string Application path
60
     */
61
    protected $path;
62
63
    /**
64
     * @var string Environment name
65
     */
66
    protected $environment = 'production';
67
68
    /**
69
     * @var \Exception
70
     */
71
    protected $exception;
72
73
    /**
74
     * @var bool Debug application flag
75
     */
76
    protected $debugFlag = false;
77
78
    /**
79
     * @var bool Layout flag
80
     */
81
    protected $layoutFlag = true;
82
83
    /**
84
     * @var array Stack of widgets closures
85
     */
86
    protected $widgets = array();
87
88
    /**
89
     * @var array Stack of API closures
90
     */
91
    protected $api = array();
92
93
    /**
94
     * Get application environment
95
     *
96
     * @return string
97
     */
98
    public function getEnvironment()
99
    {
100
        return $this->environment;
101
    }
102
103
    /**
104
     * Get path to Application
105
     *
106
     * @return string
107
     */
108 669
    public function getPath()
109
    {
110 669
        if (!$this->path) {
111
            if (defined('PATH_APPLICATION')) {
112
                $this->path = PATH_APPLICATION;
113
            } else {
114
                $reflection = new \ReflectionClass($this);
115
                // 3 level up
116
                $this->path = dirname(dirname(dirname($reflection->getFileName())));
117
            }
118
        }
119 669
        return $this->path;
120
    }
121
122
    /**
123
     * Check debug flag
124
     *
125
     * @return bool
126
     */
127 4
    public function isDebug()
128
    {
129 4
        return $this->debugFlag;
130
    }
131
132
    /**
133
     * Check Layout flag
134
     *
135
     * @return bool
136
     */
137 5
    public function hasLayout()
138
    {
139 5
        return $this->layoutFlag;
140
    }
141
142
    /**
143
     * Set Layout template and/or flag
144
     *
145
     * @param  bool|string $flag
146
     * @return void
147
     */
148 669
    public function useLayout($flag = true)
149
    {
150 669
        if (is_string($flag)) {
151
            Layout::setTemplate($flag);
152
            $this->layoutFlag = true;
153
        } else {
154 669
            $this->layoutFlag = $flag;
155
        }
156 669
    }
157
158
    /**
159
     * Set JSON presentation
160
     *
161
     * @return void
162
     */
163
    public function useJson()
164
    {
165
        // disable view and layout for JSON output
166
        $this->useLayout(false);
167
        Response::setHeader('Content-Type', 'application/json');
168
    }
169
170
    /**
171
     * Initialize process
172
     *
173
     * @param  string $environment
174
     * @throws ApplicationException
175
     * @return void
176
     */
177
    public function init($environment = 'production')
178
    {
179
        $this->environment = $environment;
180
181
        try {
182
            // initial default helper path
183
            $this->addHelperPath(dirname(__FILE__) . '/Helper/');
184
185
            // first log message
186
            Logger::info('app:init');
187
188
            // setup configuration for current environment
189
            if ($debug = Config::getData('debug')) {
190
                $this->debugFlag = (bool) $debug;
191
            }
192
193
            // initial php settings
194
            if ($ini = Config::getData('php')) {
195
                foreach ($ini as $key => $value) {
196
                    $result = ini_set($key, $value);
197
                    Logger::info('app:init:php:'.$key.':'.($result?:'---'));
198
                }
199
            }
200
201
            // init session, start inside class
202
            Session::getInstance();
203
204
            // init Messages
205
            Messages::getInstance();
206
207
            // init Translator
208
            Translator::getInstance();
209
210
            // init request
211
            $this->initRequest();
212
213
            // init response
214
            $this->initResponse();
215
216
            // init router
217
            $this->initRouter();
218
219
        } catch (\Exception $e) {
220
            throw new ApplicationException("Application can't be loaded: " . $e->getMessage());
221
        }
222
    }
223
224
    /**
225
     * Run application
226
     *
227
     * @return void
228
     */
229
    public function run()
230
    {
231
        $this->process();
232
        $this->render();
233
        $this->finish();
234
    }
235
236
    /**
237
     * Initial Request instance
238
     *
239
     * @return void
240
     */
241
    protected function initRequest()
242
    {
243
        $request = RequestFactory::fromGlobals();
244
245
        Request::setInstance($request);
246
    }
247
248
    /**
249
     * Initial Response instance
250
     *
251
     * @return void
252
     */
253 669
    protected function initResponse()
254
    {
255 669
        $response = new ResponseInstance();
256 669
257
        Response::setInstance($response);
258 669
    }
259 669
260
    /**
261
     * Initial Router instance
262
     *
263
     * @return void
264
     */
265
    protected function initRouter()
266
    {
267
        $router = new \Bluz\Router\Router();
268 2
        $router->setOptions(Config::getData('router'));
269
270
        Router::setInstance($router);
271 2
    }
272
273
    /**
274 2
     * Initial controller view
275
     *
276
     * @param  string $module
277 2
     * @param  string $controller
278
     * @return View
279
     */
280 2
    protected function initView($module, $controller)
281
    {
282
        // $view for use in closure
283 2
        $view = new View();
284
285 2
        // setup additional helper path
286
        $view->addHelperPath($this->getPath() . '/layouts/helpers');
287
288
        // setup additional partial path
289
        $view->addPartialPath($this->getPath() . '/layouts/partial');
290
291
        // setup default path
292
        $view->setPath($this->getPath() . '/modules/' . $module . '/views');
293
294
        // setup default template
295
        $view->setTemplate($controller . '.phtml');
296
297 2
        return $view;
298
    }
299 2
300
    /**
301 2
     * Process application
302 2
     *
303 2
     * Note:
304 2
     * - Why you don't use "X-" prefix for custom headers?
305
     * - Because it deprecated ({@link http://tools.ietf.org/html/rfc6648})
306
     *
307
     * @return void
308
     */
309
    public function process()
310
    {
311
        Logger::info('app:process');
312 2
313
        $this->preProcess();
314 2
        $this->doProcess();
315
        $this->postProcess();
316 2
    }
317
318
    /**
319 2
     * Pre process
320
     *
321
     * @return void
322 2
     * @throws ApplicationException
323
     */
324
    protected function preProcess()
325
    {
326
        Logger::info("app:process:pre");
327
328
        Router::process();
329 2
330
        // disable layout for AJAX requests
331 2
        if (Request::isXmlHttpRequest()) {
332
            $this->useLayout(false);
333 2
        }
334 2
    }
335 2
336
    /**
337
     * Do process
338
     *
339
     * @return void
340 2
     */
341 1
    protected function doProcess()
342
    {
343
        Logger::info("app:process:do");
344
345
        $module = Request::getModule();
346 1
        $controller = Request::getController();
347
        $params = Request::getParams();
348
349
        // try to dispatch controller
350
        try {
351 1
            // get reflection of requested controller
352
            $controllerFile = $this->getControllerFile($module, $controller);
353
            $reflection = $this->reflection($controllerFile);
354
355
            // check header "accept" for catch JSON requests, and switch response to JSON
356
            // it's some magic for AJAX and REST requests
357
            $acceptMap = [
358
                'HTML' => 'text/html',
359
                'JSON' => 'application/json'
360
            ];
361
362
            // some controller has @accept tag
363 1
            if ($allowAccept = $reflection->getAccept()) {
364
                // convert list of controller @accept to MIME types
365
                $allowAccept = array_filter(
366
                    $acceptMap,
367
                    function ($key) use ($allowAccept) {
368 1
                        return in_array($key, $allowAccept);
369
                    },
370
                    ARRAY_FILTER_USE_KEY
371
                );
372 1
                $allowAccept = array_values($allowAccept);
373
            } else {
374 1
                // by default allow just HTML output
375
                $allowAccept = ['text/html'];
376
            }
377
378
            // choose MIME type by browser accept header
379
            // filtered by controller @accept
380
            $accept = Request::getAccept($allowAccept);
381
382
            // switch statement for Accept header
383
            switch ($accept) {
384
                case 'text/html':
385
                    // HTML response with layout
386
                    break;
387
                case 'application/json':
388 1
                    $this->useJson();
389
                    break;
390
                default:
391
                    if (PHP_SAPI == 'cli') {
392
                        // all ok
393 1
                    } else {
394
                        // not acceptable MIME type
395
                        throw new NotAcceptableException();
396
                    }
397
            }
398
399
            // check call method(s)
400
            if ($reflection->getMethod() && !in_array(Request::getMethod(), $reflection->getMethod())) {
401
                throw new NotAllowedException(join(',', $reflection->getMethod()));
402 1
            }
403
404 1
            // check HTML cache
405
            if ($reflection->getCacheHtml() && Request::getMethod() == Request::METHOD_GET) {
406
                $htmlKey = 'html:' . $module . ':' . $controller . ':' . http_build_query($params);
407
                if ($cachedHtml = Cache::get($htmlKey)) {
408
                    Response::setBody($cachedHtml);
409
                    return;
410
                }
411
            }
412
413
            // dispatch controller
414
            $dispatchResult = $this->dispatch($module, $controller, $params);
415
416
        } catch (RedirectException $e) {
417
            $this->setException($e);
418
419 1
            Response::removeHeaders();
420
            Response::clearBody();
421
422
            if (Request::isXmlHttpRequest()) {
423
                Response::setStatusCode(204);
424
                Response::setHeader('Bluz-Redirect', $e->getMessage());
425
                return;
426
            } else {
427
                Response::setStatusCode(302);
428
                Response::setHeader('Location', $e->getMessage());
429
                return;
430
            }
431
        } catch (ReloadException $e) {
432
            $this->setException($e);
433
434 1
            Response::removeHeaders();
435 1
            Response::clearBody();
436
437 1
            if (Request::isXmlHttpRequest()) {
438 1
                Response::setStatusCode(204);
439
                Response::setHeader('Bluz-Reload', 'true');
440
                return;
441
            } else {
442 1
                Response::setStatusCode(302);
443 1
                Response::setHeader('Location', Request::getRequestUri());
444
                return;
445
            }
446 1
        } catch (\Exception $e) {
447 1
            $this->setException($e);
448 1
449
            Response::removeHeaders();
450 1
            Response::clearBody();
451 1
452
            // cast to valid HTTP error code
453
            // 500 - Internal Server Error
454
            $statusCode = (100 <= $e->getCode() && $e->getCode() <= 505) ? $e->getCode() : 500;
455
            Response::setStatusCode($statusCode);
456 2
457 2
            // TODO: if `error` controller consist error
458 2
            $dispatchResult = $this->dispatch(
459
                Router::getErrorModule(),
460
                Router::getErrorController(),
461 2
                array(
462
                    'code' => $e->getCode(),
463
                    'message' => $e->getMessage()
464
                )
465
            );
466
        }
467
468
        if ($this->hasLayout()) {
469 2
            Layout::setContent($dispatchResult);
470 2
            $dispatchResult = Layout::getInstance();
471
        }
472
473
        if (isset($htmlKey, $reflection)) {
474
            // @TODO: Added ETag header
475
            Cache::set($htmlKey, $dispatchResult(), $reflection->getCacheHtml());
476
            Cache::addTag($htmlKey, $module);
477 2
            Cache::addTag($htmlKey, 'html');
478
            Cache::addTag($htmlKey, 'html:' . $module);
479 2
            Cache::addTag($htmlKey, 'html:' . $module . ':' . $controller);
480 2
        }
481
        Response::setBody($dispatchResult);
482
    }
483
484
    /**
485
     * Post process
486
     *
487
     * @return void
488
     */
489
    protected function postProcess()
490
    {
491
        Logger::info("app:process:post");
492
    }
493
494 3
    /**
495
     * Dispatch controller with params
496 3
     *
497
     * Call dispatch from any \Bluz\Package
498 3
     *     Application::getInstance()->dispatch($module, $controller, array $params);
499 2
     *
500 2
     * @param  string $module
501
     * @param  string $controller
502 2
     * @param  array  $params
503
     * @return View|callable
504
     * @throws ApplicationException
505
     */
506
    public function dispatch($module, $controller, $params = array())
507
    {
508
        Logger::info("app:dispatch: " . $module . '/' . $controller);
509
510
        $this->preDispatch($module, $controller, $params);
511
        $result = $this->doDispatch($module, $controller, $params);
512
        $this->postDispatch($module, $controller, $params);
513 3
514
        return $result;
515 3
    }
516
517
    /**
518 3
     * Pre dispatch mount point
519
     *
520
     * @param  string $module
521 2
     * @param  string $controller
522
     * @param  array  $params
523
     * @return void
524
     */
525
    protected function preDispatch($module, $controller, $params = array())
526
    {
527
        Logger::info("---:dispatch:pre: " . $module . '/' . $controller);
528
529
        // check privilege before run controller
530
        if (!$this->isAllowed($module, $controller)) {
531
            $this->denied();
532 2
        }
533
    }
534 2
535 2
    /**
536 2
     * Do dispatch
537
     *
538 2
     * @param  string $module
539
     * @param  string $controller
540
     * @param  array  $params
541
     * @return View|callable
542
     * @throws ApplicationException
543
     */
544
    protected function doDispatch($module, $controller, $params = array())
545
    {
546 2
        Logger::info("---:dispatch:do: " . $module . '/' . $controller);
547
        $controllerFile = $this->getControllerFile($module, $controller);
548 2
        $reflection = $this->reflection($controllerFile);
549
550 2
        if ($reflection->getCache()) {
551
            $cacheKey = 'view:' . $module . ':' . $controller . ':' . http_build_query($params);
552
            if ($cachedView = Cache::get($cacheKey)) {
553
                return $cachedView;
554
            }
555
        }
556 2
557
        // process params
558
        $params = $reflection->params($params);
559 2
560
        $view = $this->initView($module, $controller);
561 2
562
        $bootstrapPath = $this->getPath() . '/modules/' . $module . '/bootstrap.php';
563
564
        /**
565
         * optional $bootstrap for use in closure
566 2
         * @var \closure $bootstrap
567
         */
568 2
        if (file_exists($bootstrapPath)) {
569
            $bootstrap = require $bootstrapPath;
570
        } else {
571
            $bootstrap = null;
572 2
        }
573
        unset($bootstrapPath);
574
575
        /**
576 2
         * @var \closure $controllerClosure
577
         */
578
        $controllerClosure = include $controllerFile;
579
580 2
        if (!is_callable($controllerClosure)) {
581 1
            throw new ApplicationException("Controller is not callable '$module/$controller'");
582
        }
583
584 1
        $result = $controllerClosure(...$params);
585 1
586
        // switch statement for result of Closure run
587
        switch (true) {
588
            case ($result === false):
589 1
                // return "false" is equal to disable view and layout
590
                $this->useLayout(false);
591
                return null;
592
            case is_callable($result):
593
            case is_object($result):
594
                // return callable structure (closure or object) for replace logic of controller
595 1
                // or return any class
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
596
                return $result;
597
            case is_string($result):
598
                // return string variable is equal to change view template
599
                $view->setTemplate($result);
600
                break;
601
            case is_array($result):
602
                // return associative array is equal to setup view data
603 1
                $view->setFromArray($result);
604
                break;
605
        }
606
607
        if (isset($cacheKey)) {
608
            Cache::set($cacheKey, $view, $reflection->getCache());
609
            Cache::addTag($cacheKey, $module);
610
            Cache::addTag($cacheKey, 'view');
611
            Cache::addTag($cacheKey, 'view:' . $module);
612
            Cache::addTag($cacheKey, 'view:' . $module . ':' . $controller);
613
        }
614 2
615
        return $view;
616 2
    }
617 2
618
    /**
619
     * Post dispatch mount point
620
     *
621
     * @param  string $module
622
     * @param  string $controller
623
     * @param  array  $params
624
     * @return void
625
     */
626
    protected function postDispatch($module, $controller, $params = array())
627
    {
628
        Logger::info("---:dispatch:post: " . $module . '/' . $controller);
629
    }
630
631
    /**
632
     * Render, is send Response
633
     *
634
     * @return void
635
     */
636 1
    public function render()
637
    {
638 1
        Logger::info('app:render');
639
640
        Response::send();
641
    }
642
643
    /**
644
     * Get Response instance
645
     *
646 1
     * @return \Bluz\Response\Response
647
     */
648 1
    public function getResponse()
649
    {
650
        return Response::getInstance();
651
    }
652
653
    /**
654
     * Get Request instance
655
     *
656
     * @return \Zend\Diactoros\ServerRequest
657 1
     */
658
    public function getRequest()
659 1
    {
660 1
        return Request::getInstance();
661
    }
662
663
    /**
664
     * setException
665
     *
666
     * @param \Exception $exception
667
     * @return void
668
     */
669
    public function setException($exception)
670
    {
671
        $this->exception = $exception;
672
    }
673
674
    /**
675
     * getException
676
     *
677
     * @return \Exception
678
     */
679
    public function getException()
680
    {
681
        return $this->exception;
682
    }
683
684
    /**
685
     * Widget call
686 1
     *
687
     * Call widget from any \Bluz\Package
688 1
     * <code>
689 1
     *     Application::getInstance()->widget($module, $widget, array $params);
690
     * </code>
691
     *
692
     * @param  string $module
693
     * @param  string $widget
694
     * @param  array  $params
695
     * @return \Closure
696
     * @throws ApplicationException
697
     */
698
    public function widget($module, $widget, $params = array())
699
    {
700
        Logger::info("app:widget: " . $module . '/' . $widget);
701
        $widgetFile = $this->getWidgetFile($module, $widget);
702
        $reflection = $this->reflection($widgetFile);
703
704
        // check acl
705
        if (!Acl::isAllowed($module, $reflection->getPrivilege())) {
706
            throw new ForbiddenException("Not enough permissions for call widget '$module/$widget'");
707
        }
708
709
        /**
710
         * Cachable widgets
711
         * @var \Closure $widgetClosure
712
         */
713
        if (isset($this->widgets[$module], $this->widgets[$module][$widget])) {
714
            $widgetClosure = $this->widgets[$module][$widget];
715
        } else {
716
            $widgetClosure = include $widgetFile;
717
718
            if (!isset($this->widgets[$module])) {
719
                $this->widgets[$module] = array();
720
            }
721
            $this->widgets[$module][$widget] = $widgetClosure;
722
        }
723
724
        if (!is_callable($widgetClosure)) {
725
            throw new ApplicationException("Widget is not callable '$module/$widget'");
726
        }
727
728
        return $widgetClosure;
729
    }
730
731
    /**
732 1
     * Api call
733
     *
734 1
     * Call API from any \Bluz\Package
735
     * <code>
736
     *     Application::getInstance()->api($module, $widget, array $params);
737
     * </code>
738
     *
739
     * @param  string $module
740 1
     * @param  string $method
741 1
     * @return \Closure
742
     * @throws ApplicationException
743
     */
744
    public function api($module, $method)
745 1
    {
746
        Logger::info("app:api: " . $module . '/' . $method);
747
748
        /**
749
         * Cachable APIs
750
         * @var \Closure $widgetClosure
751
         */
752
        if (isset($this->api[$module])
753
            && isset($this->api[$module][$method])
754
        ) {
755
            $apiClosure = $this->api[$module][$method];
756
        } else {
757
            $apiClosure = require $this->getApiFile($module, $method);
758
759
            if (!isset($this->api[$module])) {
760
                $this->api[$module] = array();
761
            }
762
            $this->api[$module][$method] = $apiClosure;
763
        }
764
765
        if (!is_callable($apiClosure)) {
766
            throw new ApplicationException("API is not callable '$module/$method'");
767 670
        }
768
769
        return $apiClosure;
770 670
    }
771 670
772 670
    /**
773
     * Retrieve reflection for anonymous function
774 670
     *
775 670
     * @param  string $file
776
     * @return Reflection
777 670
     * @throws ApplicationException
778
     */
779
    public function reflection($file)
780
    {
781
        // cache for reflection data
782
        if (!$reflection = Cache::get('reflection:' . $file)) {
783
            $reflection = new Reflection($file);
784
            $reflection->process();
785
786
            Cache::set('reflection:' . $file, $reflection);
787
            Cache::addTag('reflection:' . $file, 'reflection');
788 4
        }
789
        return $reflection;
790 4
    }
791 2
792
    /**
793 2
     * Is allowed controller/widget/etc
794
     *
795
     * @param  string $module
796 2
     * @param  string $controller
797
     * @return bool
798
     * @throws ApplicationException
799
     */
800
    public function isAllowed($module, $controller)
801
    {
802
        $file = $this->getControllerFile($module, $controller);
803
        $reflection = $this->reflection($file);
804
805
        if ($privilege = $reflection->getPrivilege()) {
806
            return Acl::isAllowed($module, $privilege);
807 4
        }
808
        return true;
809 4
    }
810 4
811
    /**
812 4
     * Get controller file
813 3
     *
814
     * @param  string $module
815
     * @param  string $controller
816 2
     * @return string
817
     * @throws ApplicationException
818
     */
819
    protected function getControllerFile($module, $controller)
820
    {
821
        $controllerPath = $this->getPath() . '/modules/' . $module
822
            . '/controllers/' . $controller . '.php';
823
824
        if (!file_exists($controllerPath)) {
825
            throw new ApplicationException("Controller file not found '$module/$controller'", 404);
826
        }
827 1
828
        return $controllerPath;
829 1
    }
830 1
831
    /**
832 1
     * Get widget file
833 1
     *
834
     * @param  string $module
835
     * @param  string $widget
836
     * @return string
837
     * @throws ApplicationException
838
     */
839
    protected function getWidgetFile($module, $widget)
840
    {
841
        $widgetPath = $this->getPath() . '/modules/' . $module
842
            . '/widgets/' . $widget . '.php';
843
844
        if (!file_exists($widgetPath)) {
845
            throw new ApplicationException("Widget file not found '$module/$widget'");
846
        }
847 1
848
        return $widgetPath;
849 1
    }
850 1
851
    /**
852 1
     * Get API file
853 1
     *
854
     * @param  string $module
855
     * @param  string $method
856
     * @return string
857
     * @throws ApplicationException
858
     */
859
    protected function getApiFile($module, $method)
860
    {
861
        $apiPath = $this->getPath() . '/modules/' . $module
862
            . '/api/' . $method . '.php';
863
864
        if (!file_exists($apiPath)) {
865
            throw new ApplicationException("API file not found '$module/$method'");
866
        }
867
868
        return $apiPath;
869
    }
870
    
871
    /**
872
     * Finally method
873
     *
874
     * @return void
875
     */
876
    public function finish()
877
    {
878
        Logger::info('app:finish');
879
    }
880
}
881