Completed
Push — master ( c9b7ea...c1fd74 )
by Mihail
02:38
created

App::run()   D

Complexity

Conditions 12
Paths 249

Size

Total Lines 75
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 75
rs 4.0189
cc 12
eloc 45
nc 249
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Ffcms\Core;
4
5
use Ffcms\Core\Arch\View;
6
use Ffcms\Core\Cache\MemoryObject;
7
use Ffcms\Core\Debug\DebugMeasure;
8
use Ffcms\Core\Debug\Manager as Debug;
9
use Ffcms\Core\Exception\ForbiddenException;
10
use Ffcms\Core\Exception\JsonException;
11
use Ffcms\Core\Exception\NativeException;
12
use Ffcms\Core\Exception\NotFoundException;
13
use Ffcms\Core\Exception\SyntaxException;
14
use Ffcms\Core\Exception\TemplateException;
15
use Ffcms\Core\Helper\Security;
16
use Ffcms\Core\Helper\Type\Any;
17
use Ffcms\Core\Helper\Type\Obj;
18
use Ffcms\Core\Helper\Type\Str;
19
use Ffcms\Core\I18n\Translate;
20
use Ffcms\Core\Managers\BootManager;
21
use Ffcms\Core\Managers\CronManager;
22
use Ffcms\Core\Managers\EventManager;
23
use Ffcms\Core\Network\Request;
24
use Ffcms\Core\Network\Response;
25
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
26
27
/**
28
 * Class App. Provide later static callbacks as entry point from any places of ffcms.
29
 * @package Ffcms\Core
30
 */
31
class App
32
{
33
    use DebugMeasure;
34
35
    /** @var \Ffcms\Core\Network\Request */
36
    public static $Request;
37
38
    /** @var \Ffcms\Core\Properties */
39
    public static $Properties;
40
41
    /** @var \Ffcms\Core\Network\Response */
42
    public static $Response;
43
44
    /** @var \Ffcms\Core\Alias */
45
    public static $Alias;
46
47
    /** @var \Ffcms\Core\Arch\View */
48
    public static $View;
49
50
    /** @var \Ffcms\Core\Debug\Manager|null */
51
    public static $Debug;
52
53
    /** @var \Ffcms\Core\Helper\Security */
54
    public static $Security;
55
56
    /** @var \Ffcms\Core\I18n\Translate */
57
    public static $Translate;
58
59
    /** @var \Ffcms\Core\Interfaces\iUser|\Apps\ActiveRecord\User */
60
    public static $User;
61
62
    /** @var \Symfony\Component\HttpFoundation\Session\Session */
63
    public static $Session;
64
65
    /** @var \Illuminate\Database\Capsule\Manager */
66
    public static $Database;
67
68
    /** @var \Ffcms\Core\Cache\MemoryObject */
69
    public static $Memory;
70
71
    /** @var \Swift_Mailer */
72
    public static $Mailer;
73
74
    /** @var \Ffcms\Core\Interfaces\iCaptcha */
75
    public static $Captcha;
76
77
    /** @var FilesystemAdapter */
78
    public static $Cache;
79
80
    /** @var EventManager */
81
    public static $Event;
82
83
    /** @var CronManager */
84
    public static $Cron;
85
86
    private $_services;
87
    private $_loader;
88
89
    /**
90
     * App constructor. Build App entry-point instance
91
     * @param array|null $services
92
     * @param bool $loader
93
     * @throws \Ffcms\Core\Exception\NativeException
94
     * @throws \InvalidArgumentException
95
     */
96
    public function __construct(array $services = null, $loader = false)
97
    {
98
        // pass initialization data inside
99
        $this->_services = $services;
100
        $this->_loader = $loader;
101
        // initialize service links
102
        $this->loadNativeServices();
103
        $this->loadDynamicServices();
104
        // Initialize boot manager. This manager allow to auto-execute 'static boot()' methods in apps and widgets
105
        $bootManager = new BootManager($this->_loader);
106
        $bootManager->run();
107
    }
108
109
    /**
110
     * Factory method builder for app entry point
111
     * @param array|null $services
112
     * @param bool $loader
113
     * @return App
114
     * @throws \InvalidArgumentException
115
     */
116
    public static function factory(array $services = null, $loader = false): self
117
    {
118
        return new self($services, $loader);
119
    }
120
121
    /**
122
     * Prepare native static symbolic links for app services
123
     * @throws \InvalidArgumentException
124
     */
125
    private function loadNativeServices(): void
126
    {
127
        // initialize memory and properties controllers
128
        self::$Memory = MemoryObject::instance();
129
        self::$Properties = new Properties();
130
        // initialize debugger
131
        if (isset($this->_services['Debug']) && $this->_services['Debug'] === true && Debug::isEnabled()) {
132
            self::$Debug = new Debug();
133
            $this->startMeasure(__METHOD__);
134
        }
135
        // prepare request data
136
        self::$Request = Request::createFromGlobals();
137
        // initialize response, securty translate and other workers
138
        self::$Security = new Security();
139
        self::$Response = new Response();
140
        self::$View = new View();
141
        self::$Translate = new Translate();
142
        self::$Alias = new Alias();
143
        self::$Event = new EventManager();
144
        self::$Cron = new CronManager();
145
        // stop debug timeline
146
        $this->stopMeasure(__METHOD__);
147
    }
148
149
    /**
150
     * Prepare dynamic static links from object configurations as anonymous functions
151
     * @throws NativeException
152
     */
153
    private function loadDynamicServices(): void
154
    {
155
        $this->startMeasure(__METHOD__);
156
157
        /** @var array $objects */
158
        $objects = App::$Properties->getAll('object');
159
        if (!Any::isArray($objects))
160
            throw new NativeException('Object configurations is not loaded: /Private/Config/Object.php');
161
162
        // each all objects as service_name => service_instance()
163
        foreach ($objects as $name => $instance) {
164
            // check if definition of object is exist and services list contains it or is null to auto build
165
            if (property_exists(get_called_class(), $name) && $instance instanceof \Closure && (isset($this->_services[$name]) || $this->_services === null)) {
166
                if ($this->_services[$name] === true || $this->_services === null) { // initialize from configs
167
                    self::${$name} = $instance();
168
                } elseif (is_callable($this->_services[$name])) { // raw initialization from App::run()
169
                    self::${$name} = $this->_services[$name]();
170
                }
171
            } elseif (Str::startsWith('_', $name)) { // just anonymous callback without entry-point
172
                @call_user_func($instance);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
173
            }
174
        }
175
176
        $this->stopMeasure(__METHOD__);
177
    }
178
179
    /**
180
     * Run applications and display output. Main entry point of system.
181
     * @return void
182
     */
183
    final public function run(): void
184
    {
185
        $html = null;
186
        // lets try to get html full content to page render
187
        try {
188
            $this->startMeasure(__METHOD__ . '::callback');
189
            /** @var \Ffcms\Core\Arch\Controller $callClass */
190
            $callClass = null;
191
            $callMethod = 'action' . self::$Request->getAction();
192
193
            // define callback class namespace/name full path
194
            $cName = (self::$Request->getCallbackAlias() ?? '\Apps\Controller\\' . env_name . '\\' . self::$Request->getController());
195
            if (!class_exists($cName))
196
                throw new NotFoundException('Callback class not found: ' . App::$Security->strip_tags($cName));
197
198
            $callClass = new $cName;
199
            // check if callback method (action) is exist in class object
200
            if (!method_exists($callClass, $callMethod))
201
                throw new NotFoundException('Method "' . App::$Security->strip_tags($callMethod) . '()" not founded in "' . get_class($callClass) . '"');
202
203
            $this->stopMeasure(__METHOD__ . '::callback');
204
            $params = [];
205
            if (!Str::likeEmpty(self::$Request->getID())) {
206
                $params[] = self::$Request->getID();
207
                if (!Str::likeEmpty(self::$Request->getAdd()))
208
                    $params[] = self::$Request->getAdd();
209
            }
210
211
            // get instance of callback object (class::method) as reflection
212
            $instance = new \ReflectionMethod($callClass, $callMethod);
213
            $argCount = 0;
214
            // calculate method defined arguments count
215
            foreach ($instance->getParameters() as $arg) {
216
                if (!$arg->isOptional())
217
                    $argCount++;
218
            }
219
            // compare method arg count with passed
220
            if (count($params) < $argCount)
221
                throw new NotFoundException(__('Arguments for method %method% is not enough. Expected: %required%, got: %current%.', [
222
                    'method' => $callMethod,
223
                    'required' => $argCount,
224
                    'current' => count($params)
225
                ]));
226
227
            $this->startMeasure($cName . '::' . $callMethod);
228
            // make callback call to action in controller and get response
229
            $actionResponse = call_user_func_array([$callClass, $callMethod], $params);
230
            $this->stopMeasure($cName . '::' . $callMethod);
231
232
            // set response to controller attribute
233
            if (!Str::likeEmpty($actionResponse))
234
                $callClass->setOutput($actionResponse);
235
236
            // build full compiled output html data with default layout and widgets
237
            $html = $callClass->buildOutput();
238
        } catch (\Exception $e) {
239
            // check if exception is system-based throw
240
            if ($e instanceof TemplateException) {
241
                $html = $e->display();
242
            } else { // or hook exception to system based :)))
243
                if (App::$Debug) {
244
                    $msg = $e->getMessage() . $e->getTraceAsString();
245
                    $html = (new NativeException($msg))->display();
246
                } else {
247
                    $html = (new NativeException($e->getMessage()))->display();
248
                }
249
250
            }
251
        }
252
253
        // set full rendered content to response builder
254
        self::$Response->setContent($html);
255
        // echo full response to user via symfony http foundation
256
        self::$Response->send();
257
    }
258
259
}