Completed
Push — master ( 53b99e...05e097 )
by Basil
03:48
created

ErrorHandlerTrait   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 173
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 7
dl 0
loc 173
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A transferMessage() 0 8 1
A apiServerSendData() 0 17 2
A renderException() 0 8 3
F getExceptionArray() 0 50 15
A buildTrace() 0 14 6
1
<?php
2
3
namespace luya\traits;
4
5
use Yii;
6
use yii\helpers\Json;
7
use Curl\Curl;
8
use luya\helpers\Url;
9
use luya\helpers\ObjectHelper;
10
use luya\helpers\StringHelper;
11
use luya\helpers\ArrayHelper;
12
13
/**
14
 * ErrorHandler trait to extend the renderException method with an api call if enabled.
15
 *
16
 * @author Basil Suter <[email protected]>
17
 * @since 1.0.0
18
 */
19
trait ErrorHandlerTrait
20
{
21
    /**
22
     * @var string The url of the error api without trailing slash. Make sure you have installed the error api
23
     * module on the requested api url (https://luya.io/guide/module/luyadev---luya-module-errorapi).
24
     *
25
     * An example when using the erroapi module, the url could look like this `https://luya.io/errorapi`.
26
     */
27
    public $api;
28
29
    /**
30
     * @var boolean Enable the transfer of exceptions to the defined `$api` server.
31
     */
32
    public $transferException = false;
33
34
    /**
35
     * @var \Curl\Curl|null The curl object from the last error api call.
36
     * @since 1.0.5
37
     */
38
    public $lastTransferCall;
39
    
40
    /**
41
     * @var array An array of exceptions which are whitelisted and therefore NOT TRANSFERED.
42
     * @since 1.0.5
43
     */
44
    public $whitelist = ['yii\web\NotFoundHttpException'];
45
    
46
    /**
47
     * @var array
48
     * @since 1.0.6
49
     */
50
    public $sensitiveKeys = ['password', 'pwd', 'pass', 'passwort', 'pw', 'token', 'hash', 'authorization'];
51
    
52
    /**
53
     * Send a custom message to the api server event its not related to an exception.
54
     *
55
     * Sometimes you just want to pass informations from your application, this method allows you to transfer
56
     * a message to the error api server.
57
     *
58
     * Example of sending a message
59
     *
60
     * ```php
61
     * Yii::$app->errorHandler->transferMessage('Something went wrong here!', __FILE__, __LINE__);
62
     * ```
63
     *
64
     * @param string $message The message you want to send to the error api server.
65
     * @param string $file The you are currently send the message (use __FILE__)
66
     * @param string $line The line you want to submit (use __LINE__)
67
     * @return bool|null
68
     */
69
    public function transferMessage($message, $file = __FILE__, $line = __LINE__)
70
    {
71
        return $this->apiServerSendData($this->getExceptionArray([
72
            'message' => $message,
73
            'file' => $file,
74
            'line' => $line,
75
        ]));
76
    }
77
    
78
    /**
79
     * Send the array data to the api server.
80
     *
81
     * @param array $data The array to be sent to the server.
82
     * @return boolean|null true/false if data has been sent to the api successfull or not, null if the transfer is disabled.
83
     */
84
    private function apiServerSendData(array $data)
85
    {
86
        if ($this->transferException) {
87
            $curl = new Curl();
88
            $curl->setOpt(CURLOPT_CONNECTTIMEOUT, 2);
89
            $curl->setOpt(CURLOPT_TIMEOUT, 2);
90
            $curl->post(Url::ensureHttp(rtrim($this->api, '/')).'/create', [
91
                'error_json' => Json::encode($data),
92
            ]);
93
            
94
            $this->lastTransferCall = $curl;
95
            
96
            return $curl->isSuccess();
97
        }
98
        
99
        return null;
100
    }
101
    
102
    /**
103
     * @inheritdoc
104
     */
105
    public function renderException($exception)
106
    {
107
        if (!ObjectHelper::isInstanceOf($exception, $this->whitelist, false) && $this->transferException) {
108
            $this->apiServerSendData($this->getExceptionArray($exception));
109
        }
110
        
111
        return parent::renderException($exception);
112
    }
113
114
    /**
115
     * Get an readable array to transfer from an exception
116
     *
117
     * @param mixed $exception Exception object
118
     * @return array An array with transformed exception data
119
     */
120
    public function getExceptionArray($exception)
121
    {
122
        $_message = 'Uknonwn exception object, not instance of \Exception.';
123
        $_file = 'unknown';
124
        $_line = 0;
125
        $_trace = [];
126
        $_previousException = [];
127
        
128
        if (is_object($exception)) {
129
            $prev = $exception->getPrevious();
130
            
131
            if (!empty($prev)) {
132
                $_previousException = [
133
                    'message' => $prev->getMessage(),
134
                    'file' => $prev->getFile(),
135
                    'line' => $prev->getLine(),
136
                    'trace' => $this->buildTrace($prev),
137
                ];
138
            }
139
            
140
            $_trace = $this->buildTrace($exception);
141
            $_message = $exception->getMessage();
142
            $_file = $exception->getFile();
143
            $_line = $exception->getLine();
144
        } elseif (is_string($exception)) {
145
            $_message = 'exception string: ' . $exception;
146
        } elseif (is_array($exception)) {
147
            $_message = isset($exception['message']) ? $exception['message'] : 'exception array dump: ' . print_r($exception, true);
148
            $_file = isset($exception['file']) ? $exception['file'] : __FILE__;
149
            $_line = isset($exception['line']) ? $exception['line'] : __LINE__;
150
        }
151
152
        return [
153
            'message' => $_message,
154
            'file' => $_file,
155
            'line' => $_line,
156
            'requestUri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : null,
157
            'serverName' => isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null,
158
            'date' => date('d.m.Y H:i'),
159
            'trace' => $_trace,
160
            'previousException' => $_previousException,
161
            'ip' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
162
            'get' => isset($_GET) ? ArrayHelper::coverSensitiveValues($_GET, $this->sensitiveKeys) : [],
163
            'post' => isset($_POST) ? ArrayHelper::coverSensitiveValues($_POST, $this->sensitiveKeys) : [],
164
            'session' => isset($_SESSION) ? ArrayHelper::coverSensitiveValues($_SESSION, $this->sensitiveKeys) : [],
165
            'server' => isset($_SERVER) ? ArrayHelper::coverSensitiveValues($_SERVER, $this->sensitiveKeys) : [],
166
            'profiling' => Yii::getLogger()->profiling,
167
            'logger' => Yii::getLogger()->messages,
168
        ];
169
    }
170
    
171
    /**
172
     * Build trace array from exception.
173
     *
174
     * @param object $exception
175
     * @return array
176
     */
177
    private function buildTrace($exception)
178
    {
179
        $_trace = [];
180
        foreach ($exception->getTrace() as $key => $item) {
181
            $_trace[$key] = [
182
                'file' => isset($item['file']) ? $item['file'] : null,
183
                'line' => isset($item['line']) ? $item['line'] : null,
184
                'function' => isset($item['function']) ? $item['function'] : null,
185
                'class' => isset($item['class']) ? $item['class'] : null,
186
            ];
187
        }
188
        
189
        return $_trace;
190
    }
191
}
192