Completed
Push — master ( 2bf87e...513950 )
by Mihail
04:33
created

App::run()   F

Complexity

Conditions 18
Paths 804

Size

Total Lines 89
Code Lines 53

Duplication

Lines 10
Ratio 11.24 %

Importance

Changes 7
Bugs 2 Features 0
Metric Value
c 7
b 2
f 0
dl 10
loc 89
rs 2.3577
cc 18
eloc 53
nc 804
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\Exception\ForbiddenException;
6
use Ffcms\Core\Exception\JsonException;
7
use Ffcms\Core\Exception\NativeException;
8
use Ffcms\Core\Exception\NotFoundException;
9
use Ffcms\Core\Exception\SyntaxException;
10
use Ffcms\Core\Managers\BootManager;
11
use Ffcms\Core\Managers\CronManager;
12
use Ffcms\Core\Managers\EventManager;
13
use Ffcms\Core\Helper\Security;
14
use Ffcms\Core\Helper\Type\Obj;
15
use Ffcms\Core\Helper\Type\Str;
16
use Ffcms\Core\I18n\Translate;
17
use Ffcms\Core\Network\Request;
18
use Ffcms\Core\Network\Response;
19
use Ffcms\Core\Arch\View;
20
use Ffcms\Core\Debug\Manager as Debug;
21
use Ffcms\Core\Cache\MemoryObject;
22
23
/**
24
 * Class App. Provide later static callbacks as entry point from any places of ffcms.
25
 * @package Ffcms\Core
26
 */
27
class App
28
{
29
    /** @var \Ffcms\Core\Network\Request */
30
    public static $Request;
31
32
    /** @var \Ffcms\Core\Properties */
33
    public static $Properties;
34
35
    /** @var \Ffcms\Core\Network\Response */
36
    public static $Response;
37
38
    /** @var \Ffcms\Core\Alias */
39
    public static $Alias;
40
41
    /** @var \Ffcms\Core\Arch\View */
42
    public static $View;
43
44
    /** @var \Ffcms\Core\Debug\Manager|null */
45
    public static $Debug;
46
47
    /** @var \Ffcms\Core\Helper\Security */
48
    public static $Security;
49
50
    /** @var \Ffcms\Core\I18n\Translate */
51
    public static $Translate;
52
53
    /** @var \Ffcms\Core\Interfaces\iUser */
54
    public static $User;
55
56
    /** @var \Symfony\Component\HttpFoundation\Session\Session */
57
    public static $Session;
58
59
    /** @var \Illuminate\Database\Capsule\Manager */
60
    public static $Database;
61
62
    /** @var \Ffcms\Core\Cache\MemoryObject */
63
    public static $Memory;
64
65
    /** @var \Swift_Mailer */
66
    public static $Mailer;
67
68
    /** @var \Ffcms\Core\Interfaces\iCaptcha */
69
    public static $Captcha;
70
71
    /** @var \phpFastCache\Drivers\files */
72
    public static $Cache;
73
74
    /** @var EventManager */
75
    public static $Event;
76
77
    /** @var CronManager */
78
    public static $Cron;
79
80
    private $_services;
81
    private $_loader;
82
83
    /**
84
     * App constructor. Build App entry-point instance
85
     * @param array|null $services
86
     * @param bool $loader
87
     * @throws \Ffcms\Core\Exception\NativeException
88
     * @throws \InvalidArgumentException
89
     */
90
    public function __construct(array $services = null, $loader = false)
91
    {
92
        // pass initialization data inside
93
        $this->_services = $services;
94
        $this->_loader = $loader;
95
        // initialize service links
96
        $this->nativeStaticLinks();
97
        $this->dynamicStaticLinks();
98
        // Initialize boot manager. This manager allow to auto-execute 'static boot()' methods in apps and widgets
99
        $bootManager = new BootManager($this->_loader);
100
        $bootManager->run();
101
    }
102
103
    /**
104
     * Factory method builder for app entry point
105
     * @param array|null $services
106
     * @param bool $loader
107
     * @return App
108
     * @throws \InvalidArgumentException
109
     * @throws \Ffcms\Core\Exception\NativeException
110
     */
111
    public static function factory(array $services = null, $loader = false)
112
    {
113
        return new self($services, $loader);
114
    }
115
116
    /**
117
     * Prepare static symbolic links for app services
118
     * @throws \InvalidArgumentException
119
     * @throws \Ffcms\Core\Exception\NativeException
120
     */
121
    private function nativeStaticLinks()
122
    {
123
        // initialize memory and properties controllers
124
        self::$Memory = MemoryObject::instance();
125
        self::$Properties = new Properties();
126
        // initialize debugger
127
        if (isset($this->_services['Debug']) && $this->_services['Debug'] === true && Debug::isEnabled()) {
128
            self::$Debug = new Debug();
129
        }
130
        // prepare request data
131
        self::$Request = Request::createFromGlobals();
132
        // initialize response, securty translate and other workers
133
        self::$Security = new Security();
134
        self::$Response = new Response();
135
        self::$View = new View();
136
        self::$Translate = new Translate();
137
        self::$Alias = new Alias();
138
        self::$Event = new EventManager();
139
        self::$Cron = new CronManager();
140
    }
141
142
    /**
143
     * Prepare dynamic static links from object configurations as anonymous functions
144
     * @throws NativeException
145
     */
146
    private function dynamicStaticLinks()
147
    {
148
        /** @var array $objects */
149
        $objects = App::$Properties->getAll('object');
150
        if (!Obj::isArray($objects)) {
151
            throw new NativeException('Object configurations is not loaded: /Private/Config/Object.php');
152
        }
153
154
        // each all objects as service_name => service_instance()
155
        foreach ($objects as $name => $instance) {
156
            // check if definition of object is exist and services list contains it or is null to auto build
157
            if (property_exists(get_called_class(), $name) && $instance instanceof \Closure && (isset($this->_services[$name]) || $this->_services === null)) {
158
                if ($this->_services[$name] === true || $this->_services === null) { // initialize from configs
159
                    self::${$name} = $instance();
160
                } elseif (is_callable($this->_services[$name])) { // raw initialization from App::run()
161
                    self::${$name} = $this->_services[$name]();
162
                }
163
            } elseif (Str::startsWith('_', $name)) { // just anonymous callback without entry-point
164
                @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...
165
            }
166
        }
167
    }
168
169
    /**
170
     * Run applications and display output
171
     * @throws \DebugBar\DebugBarException
172
     */
173
    public function run()
174
    {
175
        $html = null;
176
        // lets try to get html full content to page render
177
        try {
178
            /** @var \Ffcms\Core\Arch\Controller $callClass */
179
            $callClass = null;
180
            $callMethod = 'action' . self::$Request->getAction();
181
182
            // founded callback injection alias
183
            if (self::$Request->getCallbackAlias() !== false) {
184
                $cName = self::$Request->getCallbackAlias();
185 View Code Duplication
                if (class_exists($cName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
                    $callClass = new $cName;
187
                } else {
188
                    throw new NotFoundException('Callback alias of class "' . App::$Security->strip_tags($cName) . '" is not founded');
0 ignored issues
show
Bug introduced by
It seems like $cName defined by self::$Request->getCallbackAlias() on line 184 can also be of type boolean; however, Ffcms\Core\Helper\Security::strip_tags() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
189
                }
190
            } else { // typical parsing of native apps
191
                $cName = '\Apps\Controller\\' . env_name . '\\' . self::$Request->getController();
192
193
                // try to initialize class object
194 View Code Duplication
                if (class_exists($cName)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
                    $callClass = new $cName;
196
                } else {
197
                    throw new NotFoundException('Application can not be runned. Initialized class not founded: ' . App::$Security->strip_tags($cName));
198
                }
199
            }
200
201
            // try to call method of founded callback class
202
            if (method_exists($callClass, $callMethod)) {
203
                $actionQuery = [];
204
                // prepare action params for callback
205
                if (!Str::likeEmpty(self::$Request->getID())) {
206
                    $actionQuery[] = self::$Request->getID();
207
                    if (!Str::likeEmpty(self::$Request->getAdd())) {
208
                        $actionQuery[] = self::$Request->getAdd();
209
                    }
210
                }
211
212
                // get controller method arguments count
213
                $reflection = new \ReflectionMethod($callClass, $callMethod);
214
                $argumentCount = 0;
215
                foreach ($reflection->getParameters() as $arg) {
216
                    if (!$arg->isOptional()) {
217
                        $argumentCount++;
218
                    }
219
                }
220
221
                // check method arguments count and current request count to prevent warnings
222
                if (count($actionQuery) < $argumentCount) {
223
                    throw new NotFoundException(__('Arguments for method %method% is not enough. Expected: %required%, got: %current%.', [
224
                        'method' => $callMethod,
225
                        'required' => $argumentCount,
226
                        'current' => count($actionQuery)
227
                    ]));
228
                }
229
230
                // make callback call to action in controller and get response
231
                $actionResponse = call_user_func_array([$callClass, $callMethod], $actionQuery);
232
233
                if ($actionResponse !== null && !Str::likeEmpty($actionResponse)) {
234
                    // set response to controller property object
235
                    $callClass->setOutput($actionResponse);
236
                }
237
238
                // build full compiled output html data
239
                $html = $callClass->buildOutput();
240
            } else {
241
                throw new NotFoundException('Method "' . App::$Security->strip_tags($callMethod) . '()" not founded in "' . get_class($callClass) . '"');
242
            }
243
        } catch (NotFoundException $e) { // catch exceptions and set output
244
            $html = $e->display();
245
        } catch (ForbiddenException $e) {
246
            $html = $e->display();
247
        } catch (SyntaxException $e) {
248
            $html = $e->display();
249
        } catch (JsonException $e) {
250
            $html = $e->display();
251
        } catch (NativeException $e) {
252
            $html = $e->display();
253
        } catch (\Exception $e) { // catch all other exceptions
254
            $html = (new NativeException($e->getMessage()))->display();
255
        }
256
257
        // set full rendered content to response builder
258
        self::$Response->setContent($html);
259
        // echo full response to user via http foundation
260
        self::$Response->send();
261
    }
262
263
}