Passed
Push — master ( 51f003...9bff90 )
by Php Easy Api
02:39
created

ErrorProvider   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 352
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 135
dl 0
loc 352
rs 8.72
c 0
b 0
f 0
wmc 46

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getStatusFromContext() 0 35 5
A handle() 0 20 1
A getEnvironmentStatus() 0 5 1
C setErrorHandler() 0 108 11
A inStactTrace() 0 8 5
A fatalErrorShutdownHandler() 0 25 4
A getAppException() 0 9 1
A getUncaughtProcess() 0 20 5
C getLangMessageForException() 0 46 13

How to fix   Complexity   

Complex Class

Complex classes like ErrorProvider often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ErrorProvider, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Resta\Exception;
4
5
use Resta\Support\Str;
6
use Resta\Support\Utils;
7
use Resta\Support\Dependencies;
8
use Resta\Support\ClosureDispatcher;
9
use Resta\Foundation\ApplicationProvider;
10
use Resta\Foundation\PathManager\StaticPathModel;
11
12
class ErrorProvider extends ApplicationProvider {
13
14
    /**
15
     * @var $lang
0 ignored issues
show
Documentation Bug introduced by
The doc comment $lang at position 0 could not be parsed: Unknown type name '$lang' at position 0 in $lang.
Loading history...
16
     */
17
    public $lang = null;
18
19
    /**
20
     * @var $exception
0 ignored issues
show
Documentation Bug introduced by
The doc comment $exception at position 0 could not be parsed: Unknown type name '$exception' at position 0 in $exception.
Loading history...
21
     */
22
    protected $exception;
23
24
    /**
25
     * @var $data array
0 ignored issues
show
Documentation Bug introduced by
The doc comment $data at position 0 could not be parsed: Unknown type name '$data' at position 0 in $data.
Loading history...
26
     */
27
    protected $data=array();
28
29
    /**
30
     * @return mixed|string
31
     */
32
    private function getEnvironmentStatus(){
33
34
        // application key, but if it has a null value
35
        // then we move the environment value to the production environment.
36
        return $this->app->environment();
37
    }
38
39
    /**
40
     * @return void|mixed
41
     */
42
    private function getStatusFromContext(){
43
44
        $exception=$this->exception;
45
46
        if(isset(core()->exceptiontrace))
47
        {
48
            $this->data['status'] = (int)core()->exceptiontrace['callNamespace']->getCode();
49
        }
50
        else {
51
52
            $this->data['status']=(int)$exception::exceptionTypeCodes($this->data['errType']);
53
        }
54
55
        $this->app->terminate('responseSuccess');
0 ignored issues
show
Bug introduced by
The method terminate() does not exist on Resta\Contracts\ApplicationContracts. Since it exists in all sub-types, consider adding an abstract or default implementation to Resta\Contracts\ApplicationContracts. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

55
        $this->app->/** @scrutinizer ignore-call */ terminate('responseSuccess');
Loading history...
56
        $this->app->terminate('responseStatus');
57
        $this->app->register('responseSuccess',(bool)false);
58
        $this->app->register('responseStatus',$this->data['status']);
59
60
61
        $optionalException=str_replace("\\","\\\\",$this->app->namespace()->exception());
62
63
        if(preg_match('@'.$optionalException.'@is',$this->data['errType'])){
64
65
            //linux test
66
            $trace=$this->data['errContext']['trace'];
67
            if(preg_match('@Stack trace:\n#0(.*)\n#1@is',$trace,$traceArray)){
68
                $traceFile=str_replace(root,'',$traceArray[1]);
69
                if(preg_match('@(.*)\((\d+)\)@is',$traceFile,$traceResolve)){
70
                    $this->data['errFile']=$traceResolve[1];
71
                    $this->data['errLine']=(int)$traceResolve[2];
72
                }
73
            }
74
75
76
            $this->data['errType']=class_basename($this->data['errType']);
77
        }
78
    }
79
80
    /**
81
     * @method handle
82
     * return void
83
     */
84
    public function handle()
85
    {
86
        //sets which php errors are reported
87
        error_reporting(0);
88
89
        // in general we will use the exception class
90
        // in the store/config directory to make it possible
91
        // to change the user-based exceptions.
92
        $this->exception=StaticPathModel::$store.'\Config\Exception';
93
94
        //This function can be used for defining your own way of handling errors during runtime,
95
        //for example in applications in which you need to do cleanup of data/files when a critical error happens,
96
        //or when you need to trigger an error under certain conditions (using trigger_error()).
97
        set_error_handler([$this,'setErrorHandler']);
98
99
        //Registers a callback to be executed after script execution finishes or exit() is called.
100
        //Multiple calls to register_shutdown_function() can be made, and each will be called in the same order as
101
        //they were registered. If you call exit() within one registered shutdown function,
102
        //processing will stop completely and no other registered shutdown functions will be called.
103
        register_shutdown_function([$this,'fatalErrorShutdownHandler']);
104
    }
105
106
    /**
107
     * @param null $errNo
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errNo is correct as it would always require null to be passed?
Loading history...
108
     * @param null $errStr
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errStr is correct as it would always require null to be passed?
Loading history...
109
     * @param null $errFile
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errFile is correct as it would always require null to be passed?
Loading history...
110
     * @param null $errLine
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errLine is correct as it would always require null to be passed?
Loading history...
111
     * @param null $errContext
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errContext is correct as it would always require null to be passed?
Loading history...
112
     */
113
    public function setErrorHandler($errNo=null, $errStr=null, $errFile=null, $errLine=null, $errContext=null)
114
    {
115
        // in case of a deficiency,
116
        // we need to boot our general needs to be needed for the exception.
117
        Dependencies::loadBootstrapperNeedsForException();
118
119
        // in general we will use the exception class
120
        // in the store/config directory to make it possible
121
        // to change the user-based exceptions.
122
        $this->data['exception'] = $this->exception;
123
124
        //constant object as default
125
        $this->data['errType']              = 'Undefined';
126
        $this->data['errStrReal']           = $errStr;
127
        $this->data['errorClassNamespace']  = null;
128
        $this->data['errFile']              = $errFile;
129
        $this->data['errLine']              = $errLine;
130
        $this->data['errNo']                = $errNo;
131
132
        // catch exception via preg match
133
        // and then clear the Uncaught statement from inside.
134
        $this->getUncaughtProcess();
135
136
        $this->getStatusFromContext();
137
138
        if(is_array($meta=config('response.meta'))){
139
140
            //set as the success object is false
141
            $this->data['appExceptionSuccess']=[];
142
        }
143
        else{
144
145
            //set as the success object is false
146
            $this->data['appExceptionSuccess']=['success'=>(bool)false,'status'=>$this->data['status']];
147
        }
148
149
        //get lang message for exception
150
        $this->getLangMessageForException();
151
152
        if(property_exists(core(),'exceptiontrace')){
153
154
            $customExceptionTrace=core()->exceptiontrace;
155
            $this->data['errFile']=$customExceptionTrace['file'];
156
            $this->data['errLine']=$customExceptionTrace['line'];
157
        }
158
159
        $environment = $this->getEnvironmentStatus();
160
161
        $vendorDirectory = str_replace(root.''.DIRECTORY_SEPARATOR.'','',$this->data['errFile']);
162
163
        if(Str::startsWith($vendorDirectory,'vendor')
164
            && Str::startsWith($vendorDirectory,'vendor/php-resta')===false)
165
        {
166
            $externalMessage = ($environment==="production") ?
167
                'An unexpected external error has occurred' :
168
                $this->data['errStrReal'];
169
170
            $appException=$this->getAppException($environment,$externalMessage);
171
172
173
            //Get or Set the HTTP response code
174
            http_response_code(500);
175
            $this->app->terminate('responseStatus');
176
            $this->app->register('responseStatus',500);
177
178
179
        }
180
        else{
181
182
            $appException=$this->getAppException($environment,$this->data['errStrReal']);
183
184
            //Get or Set the HTTP response code
185
            http_response_code($this->data['status']);
186
        }
187
188
189
        if($environment==="production"){
190
191
            $productionLogMessage = $this->getAppException('local',$this->data['errStrReal']);
192
            $this->app->register('productionLogMessage',core()->out->outputFormatter($productionLogMessage));
193
        }
194
195
        if(app()->has('requestExpected') && config('app.requestWithError')===true){
0 ignored issues
show
Bug introduced by
The method has() does not exist on Resta\Contracts\ApplicationHelpersContracts. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

195
        if(app()->/** @scrutinizer ignore-call */ has('requestExpected') && config('app.requestWithError')===true){

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
196
            $appException['request']['expected'] = app()->get('requestExpected');
0 ignored issues
show
Bug introduced by
The method get() does not exist on Resta\Contracts\ApplicationHelpersContracts. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

196
            $appException['request']['expected'] = app()->/** @scrutinizer ignore-call */ get('requestExpected');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
197
        }
198
199
200
        //set json app exception
201
        core()->routerResult=$appException;
202
203
        $restaOutHandle = null;
204
205
        if(!defined('responseApp')){
206
207
            $restaOutHandle=core()->out->handle();
208
        }
209
210
        if($restaOutHandle===null){
211
212
            //header set and symfony response call
213
            header('Content-type:application/json;charset=utf-8');
214
215
            echo json_encode(core()->out->outputFormatter($appException));
216
            exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
217
        }
218
        else{
219
            echo $restaOutHandle;
220
            exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
221
        }
222
223
    }
224
225
    /**
226
     * @param $environment
227
     * @return mixed
228
     */
229
    private function getAppException($environment,$message)
230
    {
231
        return $this->data['appExceptionSuccess']+$this->data['exception']::$environment(
232
                $this->data['errNo'],
233
                $message,
234
                $this->data['errFile'],
235
                $this->data['errLine'],
236
                $this->data['errType'],
237
                $this->data['lang']
238
            );
239
    }
240
241
    /**
242
     * @method fatalErrorShutdownHandler
243
     */
244
    public function fatalErrorShutdownHandler()
245
    {
246
        //get fatal error
247
        $last_error =error_get_last();
248
249
        $this->inStactTrace($last_error);
250
251
        if($last_error!==null){
252
253
            if(!defined('methodName')){
254
255
                define('methodName',null);
256
            }
257
258
            if(isset(core()->exceptionFile)){
259
                $last_error['file'] = core()->exceptionFile;
260
                $last_error['line'] = core()->exceptionLine;
261
            }
262
263
            $this->setErrorHandler(
264
                E_ERROR,
265
                $last_error['message'],
266
                $last_error['file'],
267
                $last_error['line'],
268
                []
269
            );
270
        }
271
    }
272
273
    /**
274
     * @param $error
275
     */
276
    public function inStactTrace($error)
277
    {
278
        if(isset(core()->urlComponent)){
279
            if(!preg_match('@'.core()->urlComponent['project'].'@',$error['file']) && !isset(core()->exceptionFile)){
280
                if(preg_match('@ in\s(.*?)\n@is',$error['message'],$result)){
281
                    $errorMessage = explode(":",$result[1]);
282
                    $this->app->register('exceptionFile',$errorMessage[0]);
283
                    $this->app->register('exceptionLine',$errorMessage[1]);
284
                }
285
            }
286
        }
287
    }
288
289
    /**
290
     * @return void|mixed
291
     */
292
    private function getLangMessageForException(){
293
294
        $clone = clone $this;
295
296
        if(property_exists(core(),'exceptionTranslate')){
297
298
            $langMessage=trans('exception.'.core()->exceptionTranslate);
299
300
            if(!is_null($langMessage) && property_exists(core(),'exceptionTranslateParams')){
301
302
                if(count(core()->exceptionTranslateParams[core()->exceptionTranslate])){
303
                    foreach (core()->exceptionTranslateParams[core()->exceptionTranslate] as $key=>$value){
304
                        $langMessage=preg_replace('@\('.$key.'\)@is',$value,$langMessage);
305
                    }
306
                }
307
            }
308
309
            if($langMessage!==null){
310
                $this->data['errStrReal']=$langMessage;
311
            }
312
        }
313
314
        if(class_exists($this->data['errorClassNamespace'])
315
            &&
316
            (Str::startsWith($this->data['errorClassNamespace'],'App')
317
                || Str::startsWith($this->data['errorClassNamespace'],__NAMESPACE__))){
318
319
            ClosureDispatcher::bind($this->data['errorClassNamespace'])->call(function() use ($clone) {
320
                if(property_exists($this,'lang')){
321
                    $clone->lang=$this->lang;
322
                }
323
            });
324
        }
325
326
        $this->data['lang']=$lang=$clone->lang;
327
328
        if($lang!==null){
329
            $langMessage=trans('exception.'.$lang);
330
        }
331
        else{
332
            $langMessage=null;
333
        }
334
335
336
        if($langMessage!==null){
337
            $this->data['errStrReal']=$langMessage;
338
        }
339
    }
340
341
    /**
342
     * @return void|mixed
343
     */
344
    private function getUncaughtProcess(){
345
346
        // catch exception via preg match
347
        // and then clear the Uncaught statement from inside.
348
        if(preg_match('@(.*?):@is',$this->data['errStrReal'],$errArr)){
349
350
            $this->data['errType']=trim(str_replace('Uncaught','',$errArr[1]));
351
            $this->data['errorClassNamespace']=$this->data['errType'];
352
        }
353
354
        if(preg_match('@Uncaught@is',$this->data['errStrReal'])
355
            && preg_match('@(.*?):(.*?)\sin\s@is',$this->data['errStrReal'],$errStrRealArray)){
356
            $this->data['errStrReal']=trim($errStrRealArray[2]);
357
        }
358
359
        if($this->data['errType']==="Undefined"){
360
            $this->data['errStrReal']=$this->data['errStrReal'];
361
        }
362
        else{
363
            $this->data['errContext']['trace']=$this->data['errStrReal'];
364
        }
365
    }
366
367
}