ErrorProvider::setErrorHandler()   F
last analyzed

Complexity

Conditions 15
Paths 512

Size

Total Lines 124
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 59
nc 512
nop 5
dl 0
loc 124
rs 2.4277
c 0
b 0
f 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 Resta\Exception;
4
5
use Resta\Support\Str;
6
use Resta\Support\Dependencies;
7
use Resta\Support\ClosureDispatcher;
8
use Resta\Foundation\ApplicationProvider;
9
use Resta\Foundation\PathManager\StaticPathModel;
10
11
class ErrorProvider extends ApplicationProvider
12
{
13
    /**
14
     * @var null|string
15
     */
16
    protected $lang;
17
18
    /**
19
     * @var null|object
20
     */
21
    protected $exception;
22
23
    /**
24
     * @var array
25
     */
26
    protected $data = array();
27
28
    /**
29
     * @var array
30
     */
31
    protected $result = [];
32
33
    /**
34
     * get status according to exception trace
35
     *
36
     * @return void
37
     */
38
    private function getStatusAccordingToExceptionTrace()
39
    {
40
        if($this->app->has('exceptiontrace')) {
41
            $this->data['status'] = (int)$this->app['exceptiontrace']['callNamespace']->getCode();
42
        }
43
        else {
44
            $this->data['status'] = (int)$this->exception::exceptionTypeCodes($this->data['errType']);
0 ignored issues
show
Bug introduced by
The method exceptionTypeCodes() does not exist on null. ( Ignorable by Annotation )

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

44
            $this->data['status'] = (int)$this->exception::/** @scrutinizer ignore-call */ exceptionTypeCodes($this->data['errType']);

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...
45
        }
46
    }
47
48
    /**
49
     * @return void|mixed
50
     */
51
    private function getStatusFromContext()
52
    {
53
        $this->getStatusAccordingToExceptionTrace();
54
55
        $this->app->terminate('responseSuccess');
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
            //trace pattern
66
            $trace = $this->data['errContext']['trace'];
67
            if(preg_match('@Stack trace:\n#0(.*)\n#1@is',$trace,$traceArray)){
68
69
                $traceFile = str_replace(root,'',$traceArray[1]);
70
71
                if(preg_match('@(.*)\((\d+)\)@is',$traceFile,$traceResolve)){
72
                    $this->data['errFile'] = $traceResolve[1];
73
                    $this->data['errLine'] = (int)$traceResolve[2];
74
                }
75
            }
76
77
78
            $this->data['errType'] = class_basename($this->data['errType']);
79
        }
80
81
        if(is_array($meta = config('response.meta'))){
82
83
            //set as the success object is false
84
            $this->data['appExceptionSuccess'] = [];
85
        }
86
        else{
87
88
            //set as the success object is false
89
            $this->data['appExceptionSuccess'] = ['success'=>(bool)false,'status'=>$this->data['status']];
90
        }
91
    }
92
93
    /**
94
     * error provider handle
95
     *
96
     * @return void
97
     */
98
    public function handle()
99
    {
100
        //sets which php errors are reported
101
        error_reporting(0);
102
103
        // in general we will use the exception class
104
        // in the store/config directory to make it possible
105
        // to change the user-based exceptions.
106
        $this->exception = StaticPathModel::$store.'\Config\Exception';
0 ignored issues
show
Documentation Bug introduced by
It seems like Resta\Foundation\PathMan...e . '\Config\Exception' of type string is incompatible with the declared type null|object of property $exception.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
107
108
        //This function can be used for defining your own way of handling errors during runtime,
109
        //for example in applications in which you need to do cleanup of data/files when a critical error happens,
110
        //or when you need to trigger an error under certain conditions (using trigger_error()).
111
        set_error_handler([$this,'setErrorHandler']);
112
113
        //Registers a callback to be executed after script execution finishes or exit() is called.
114
        //Multiple calls to register_shutdown_function() can be made, and each will be called in the same order as
115
        //they were registered. If you call exit() within one registered shutdown function,
116
        //processing will stop completely and no other registered shutdown functions will be called.
117
        register_shutdown_function([$this,'fatalErrorShutdownHandler']);
118
119
    }
120
121
    /**
122
     * set error handler
123
     *
124
     * @param null|string $errNo
125
     * @param null|string $errStr
126
     * @param null|string $errFile
127
     * @param null|string $errLine
128
     * @param null|string $errContext
129
     */
130
    public function setErrorHandler($errNo=null, $errStr=null, $errFile=null, $errLine=null, $errContext=null)
131
    {
132
        // in case of a deficiency,
133
        // we need to boot our general needs to be needed for the exception.
134
        Dependencies::loadBootstrapperNeedsForException();
135
136
        // in general we will use the exception class
137
        // in the store/config directory to make it possible
138
        // to change the user-based exceptions.
139
        $this->data['exception']            = $this->exception;
140
        $this->data['errType']              = 'Undefined';
141
        $this->data['errStrReal']           = $errStr;
142
        $this->data['errorClassNamespace']  = null;
143
        $this->data['errFile']              = $errFile;
144
        $this->data['errLine']              = $errLine;
145
        $this->data['errNo']                = $errNo;
146
147
        // catch exception via preg match
148
        // and then clear the Uncaught statement from inside.
149
        $this->getUncaughtProcess();
150
151
        //get status from context
152
        $this->getStatusFromContext();
153
154
        //get lang message for exception
155
        $this->getLangMessageForException();
156
157
        if($this->app->has('exceptiontrace')){
158
159
            $customExceptionTrace   = $this->app['exceptiontrace'];
160
            $this->data['errFile']  = $customExceptionTrace['file'];
161
            $this->data['errLine']  = $customExceptionTrace['line'];
162
        }
163
164
        $environment = $this->app->environment();
165
166
        $vendorDirectory = str_replace(root.''.DIRECTORY_SEPARATOR.'','',$this->data['errFile']);
167
168
        if(preg_match('@vendor.*\.php@is',$vendorDirectory,$vd)){
169
            $vendorDirectory = $vd[0];
170
        }
171
172
        if(Str::startsWith($vendorDirectory,'vendor')
173
            && Str::startsWith($vendorDirectory,'vendor/php-resta')===false)
174
        {
175
176
            $externalMessage = ($environment==="production") ?
177
                'An unexpected external error has occurred' :
178
                $this->data['errStrReal'];
179
180
            $this->result = $this->getAppException($environment,$externalMessage);
181
182
183
            //Get or Set the HTTP response code
184
            http_response_code(500);
185
            $this->app->terminate('responseStatus');
186
            $this->app->register('responseStatus',500);
187
188
189
        }
190
        else{
191
192
            if($this->data['status']=='500' && $environment=='production'){
193
                $externalMessage = 'An unexpected external error has occurred';
194
            }
195
            else{
196
                $externalMessage = $this->data['errStrReal'];
197
            }
198
199
            $this->result = $this->getAppException($environment,$externalMessage);
200
201
            //Get or Set the HTTP response code
202
            http_response_code($this->data['status']);
203
        }
204
205
        // exception extender The exception information
206
        // that is presented as an extra to the exception result set.
207
        $this->result = $this->getExceptionExtender();
208
209
        if($environment==="production"){
210
            $productionLogMessage = $this->getAppException('local',$this->data['errStrReal']);
211
            $productionLogMessage = array_merge($this->result,$productionLogMessage);
212
            $this->app->register('productionLogMessage',$this->app->get('out')->outputFormatter($productionLogMessage));
213
        }
214
215
        //set json app exception
216
        $this->app->register('routerResult',$this->result);
217
218
        if($this->app->has('exceptionResponse')){
219
            $exceptionResponse = $this->app->get('exceptionResponse');
220
            $exceptionResponse((isset($productionLogMessage)) ? $productionLogMessage : $this->result,$this->data['status']);
221
        }
222
223
        $restaOutHandle = null;
224
225
        if(!defined('responseApp')){
226
            $restaOutHandle = $this->app->get('out')->handle();
227
        }
228
229
        header("HTTP/1.1 ".$this->data['status']);
230
231
        if($restaOutHandle===null){
232
233
            //header set and symfony response call
234
            $lastResult = $this->app->get('out')->outputFormatter($this->result);
235
236
            if($this->app->has('clientResponseType')){
237
238
                $responseType = $this->app->get('clientResponseType');
239
                echo app()->resolve($this->app->get('out')->formatter())->{$responseType}($lastResult);
240
            }
241
            else{
242
243
                $defaultResponseType = (is_null(config('app.response'))) ? 'json' : config('app.response');
244
                echo app()->resolve($this->app->get('out')->formatter())->{$defaultResponseType}($lastResult);
245
            }
246
247
248
        }
249
        else{
250
            echo $restaOutHandle;
251
        }
252
253
        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...
254
    }
255
256
    /**
257
     * @param $environment
258
     * @return mixed
259
     */
260
    private function getAppException($environment,$message)
261
    {
262
        return $this->data['appExceptionSuccess']+$this->data['exception']::$environment(
263
                $this->data['errNo'],
264
                $message,
265
                $this->data['errFile'],
266
                $this->data['errLine'],
267
                $this->data['errType'],
268
                $this->data['lang']
269
            );
270
    }
271
272
    /**
273
     * get exception extender object
274
     *
275
     * @return mixed
276
     */
277
    private function getExceptionExtender()
278
    {
279
        return  $this->app->resolve(
280
            $this->app->get('macro')->call('exceptionExtender',function(){
281
                return ExceptionExtender::class;
282
            }),
283
            ['result'=>$this->result])->getResult();
284
    }
285
286
    /**
287
     * fatal error shutdown handler
288
     *
289
     * @return mixed
290
     */
291
    public function fatalErrorShutdownHandler()
292
    {
293
        //get fatal error
294
        $last_error = error_get_last();
295
296
        $this->inStackTrace($last_error);
297
298
        if(!is_null($last_error)){
299
300
            if(!defined('methodName')){
301
                define('methodName',null);
302
            }
303
304
            if($this->app->has('exceptionFile')){
305
                $last_error['file'] = $this->app['exceptionFile'];
306
                $last_error['line'] = $this->app['exceptionLine'];
307
            }
308
309
            $this->setErrorHandler(
310
                E_ERROR,
311
                $last_error['message'],
312
                $last_error['file'],
313
                $last_error['line'],
314
                []
0 ignored issues
show
Bug introduced by
array() of type array is incompatible with the type null|string expected by parameter $errContext of Resta\Exception\ErrorProvider::setErrorHandler(). ( Ignorable by Annotation )

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

314
                /** @scrutinizer ignore-type */ []
Loading history...
315
            );
316
        }
317
    }
318
319
    /**
320
     * @param $error
321
     */
322
    public function inStackTrace($error)
323
    {
324
        if($this->app->has('urlComponent') && isset($error['file'])){
325
            if(!preg_match('@'.$this->app['urlComponent']['project'].'@',$error['file'])
326
                && !$this->app->has('exceptionFile')){
327
                if(preg_match('@ in\s(.*?)\n@is',$error['message'],$result)){
328
                    $errorMessage = explode(":",$result[1]);
329
                    $this->app->register('exceptionFile',$errorMessage[0]);
330
                    $this->app->register('exceptionLine',$errorMessage[1]);
331
                }
332
            }
333
        }
334
    }
335
336
    /**
337
     * @return void|mixed
338
     */
339
    private function getLangMessageForException()
340
    {
341
        $clone = clone $this;
342
343
        if($this->app->has('exceptionTranslate')){
344
345
            $langMessage = trans('exception.'.$this->app->get('exceptionTranslate'));
346
347
            if(!is_null($langMessage) && $this->app->has('exceptionTranslateParams')){
348
349
                if(count($this->app['exceptionTranslateParams'][$this->app['exceptionTranslate']])){
350
                    foreach ($this->app['exceptionTranslateParams'][$this->app['exceptionTranslate']] as $key=>$value){
351
352
                        $valueLangName = !is_null(trans('default.'.$value)) ? trans('default.'.$value) : $value;
353
                        $langMessage = preg_replace('@\('.$key.'\)@is',$valueLangName,$langMessage);
354
                    }
355
                }
356
            }
357
358
            if($langMessage!==null){
359
                $this->data['errStrReal'] = $langMessage;
360
            }
361
        }
362
363
        if(class_exists($this->data['errorClassNamespace'])
364
            &&
365
            (Str::startsWith($this->data['errorClassNamespace'],'App')
366
                || Str::startsWith($this->data['errorClassNamespace'],__NAMESPACE__))){
367
368
            ClosureDispatcher::bind($this->data['errorClassNamespace'])->call(function() use ($clone) {
369
                if(property_exists($this,'lang')){
370
                    $clone->setLang($this->lang);
371
                }
372
            });
373
        }
374
375
        $this->data['lang'] = $clone->lang;
376
377
        $langMessage = (!is_null($this->data['lang'])) ? trans('exception.'.$this->data['lang']) : null;
378
379
        if($langMessage!==null){
380
            $this->data['errStrReal'] = $langMessage;
381
        }
382
    }
383
384
    /**
385
     * get uncaught process
386
     *
387
     * @return void|mixed
388
     */
389
    private function getUncaughtProcess()
390
    {
391
        // catch exception via preg match
392
        // and then clear the Uncaught statement from inside.
393
        if(preg_match('@(.*?):@is',$this->data['errStrReal'],$errArr)){
394
395
            $this->data['errType'] = trim(str_replace('Uncaught','',$errArr[1]));
396
            $this->data['errorClassNamespace'] = $this->data['errType'];
397
        }
398
399
        if(preg_match('@Uncaught@is',$this->data['errStrReal'])
400
            && preg_match('@(.*?):(.*?)\sin\s@is',$this->data['errStrReal'],$errStrRealArray)){
401
            $this->data['errStrReal'] = trim($errStrRealArray[2]);
402
        }
403
404
        $this->data['errContext']['trace'] = $this->data['errStrReal'];
405
    }
406
407
    /**
408
     * get result for exception
409
     *
410
     * @return array
411
     */
412
    public function getResult()
413
    {
414
        return $this->result;
415
    }
416
417
    /**
418
     * @param null|string $lang
419
     */
420
    public function setLang($lang=null)
421
    {
422
        $this->lang = $lang;
423
    }
424
425
}