Completed
Pull Request — master (#14)
by Lars
03:00
created

Debug::getLoggerLevel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
namespace voku\db;
4
5
use voku\helper\Bootup;
6
use voku\helper\UTF8;
7
8
/**
9
 * Debug: this handles debug and error-logging.
10
 *
11
 * @package   voku\db
12
 */
13
class Debug
14
{
15
  /**
16
   * @var array
17
   */
18
  private $_errors = array();
19
20
  /**
21
   * @var bool
22
   */
23
  private $exit_on_error = true;
24
25
  /**
26
   * @var bool
27
   */
28
  private $echo_on_error = true;
29
30
  /**
31
   * @var string
32
   */
33
  private $css_mysql_box_border = '3px solid red';
34
35
  /**
36
   * @var string
37
   */
38
  private $css_mysql_box_bg = '#FFCCCC';
39
40
41
  /**
42
   * @var string
43
   */
44
  private $logger_class_name;
45
46
  /**
47
   * @var DB
48
   */
49
  private $_db;
50
51
  /**
52
   * @var string
53
   *
54
   * 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'
55
   */
56
  private $logger_level;
57
58 10
  public function __construct(DB $db)
59
  {
60 10
    $this->_db = $db;
61 10
  }
62
63
  /**
64
   * Check is the current user is a developer.
65
   *
66
   * INFO:
67
   * By default we will return "true" if the remote-ip-address is localhost or
68
   * if the script is called via CLI. But you can also overwrite this method or
69
   * you can implement a global "checkForDev()"-function.
70
   *
71
   * @return bool
72
   */
73 20
  public function checkForDev()
0 ignored issues
show
Coding Style introduced by
checkForDev uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
checkForDev uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
74
  {
75
    // init
76 20
    $return = false;
77
78 20
    if (function_exists('checkForDev')) {
79
      $return = checkForDev();
80
    } else {
81
82
      // for testing with dev-address
83 20
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
84 20
      $remoteIpAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
85
86
      if (
87
          $noDev != 1
88 20
          &&
89
          (
90
              $remoteIpAddress === '127.0.0.1'
91 20
              ||
92
              $remoteIpAddress === '::1'
93 20
              ||
94 20
              PHP_SAPI === 'cli'
95 20
          )
96 20
      ) {
97 20
        $return = true;
98 20
      }
99
    }
100
101 20
    return $return;
102
  }
103
104
  /**
105
   * Clear the errors in "$this->_errors".
106
   *
107
   * @return bool
108
   */
109 4
  public function clearErrors()
110
  {
111 4
    $this->_errors = array();
112
113 4
    return true;
114
  }
115
116
  /**
117
   * Display SQL-Errors or throw Exceptions (for dev).
118
   *
119
   * @param string       $error
120
   * @param null|boolean $force_exception_after_error
121
   *
122
   * @throws \Exception
123
   */
124 20
  public function displayError($error, $force_exception_after_error = null)
125
  {
126 20
    $fileInfo = $this->getFileAndLineFromSql();
127
128 20
    $this->logger(
129
        array(
130 20
            'error',
131 20
            '<strong>' . date(
132
                'd. m. Y G:i:s'
133 20
            ) . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . ') (sql-error):</strong> ' . $error . '<br>',
134
        )
135 20
    );
136
137 20
    $this->_errors[] = $error;
138
139 20
    if ($this->checkForDev() === true) {
140
141 20
      if ($this->echo_on_error) {
142 4
        $box_border = $this->css_mysql_box_border;
143 4
        $box_bg = $this->css_mysql_box_bg;
144
145
        echo '
146 4
        <div class="OBJ-mysql-box" style="border:' . $box_border . '; background:' . $box_bg . '; padding:10px; margin:10px;">
147
          <b style="font-size:14px;">MYSQL Error:</b>
148
          <code style="display:block;">
149 4
            file / line: ' . $fileInfo['file'] . ' / ' . $fileInfo['line'] . '
150 4
            ' . $error . '
151
          </code>
152
        </div>
153 4
        ';
154 4
      }
155
156 20
      if ($force_exception_after_error === true) {
157 4
        throw new \Exception($error);
158 16
      } elseif ($force_exception_after_error === false) {
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
159
        // nothing
160 16
      } elseif ($force_exception_after_error === null) {
161
        // default
162 13
        if ($this->exit_on_error === true) {
163 2
          throw new \Exception($error);
164
        }
165 11
      }
166 14
    }
167 14
  }
168
169
  /**
170
   * Get errors from "$this->_errors".
171
   *
172
   * @return array
173
   */
174 3
  public function getErrors()
175
  {
176 3
    return $this->_errors;
177
  }
178
179
  /**
180
   * Try to get the file & line from the current sql-query.
181
   *
182
   * @return array will return array['file'] and array['line']
183
   */
184 20
  private function getFileAndLineFromSql()
185
  {
186
    // init
187 20
    $return = array();
188 20
    $file = '';
189 20
    $line = '';
190
191 20
    if (Bootup::is_php('5.4') === true) {
192 20
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
193 20
    } else {
194
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
195
    }
196
197 20
    foreach ($referrer as $key => $ref) {
198
199
      if (
200 20
          $ref['function'] === 'execSQL'
201 20
          ||
202 20
          $ref['function'] === 'query'
203 20
          ||
204 20
          $ref['function'] === 'qry'
205 20
          ||
206 20
          $ref['function'] === 'insert'
207 20
          ||
208 20
          $ref['function'] === 'update'
209 20
          ||
210 20
          $ref['function'] === 'replace'
211 20
          ||
212 20
          $ref['function'] === 'delete'
213 20
      ) {
214 14
        $file = $referrer[$key]['file'];
215 14
        $line = $referrer[$key]['line'];
216 14
      }
217
218 20
    }
219
220 20
    $return['file'] = $file;
221 20
    $return['line'] = $line;
222
223 20
    return $return;
224
  }
225
226
  /**
227
   * @return string
228
   */
229
  public function getLoggerClassName()
230
  {
231
    return $this->logger_class_name;
232
  }
233
234
  /**
235
   * @return string
236
   */
237
  public function getLoggerLevel()
238
  {
239
    return $this->logger_level;
240
  }
241
242
  /**
243
   * @return boolean
244
   */
245
  public function isEchoOnError()
246
  {
247
    return $this->echo_on_error;
248
  }
249
250
  /**
251
   * @return boolean
252
   */
253
  public function isExitOnError()
254
  {
255
    return $this->exit_on_error;
256
  }
257
258
  /**
259
   * Log the current query via "$this->logger".
260
   *
261
   * @param string $sql     sql-query
262
   * @param int    $duration
263
   * @param int    $results result counter
264
   *
265
   * @return bool
266
   */
267 25
  public function logQuery($sql, $duration, $results)
268
  {
269 25
    $logLevelUse = strtolower($this->logger_level);
270
271 25
    if ($logLevelUse !== 'trace' && $logLevelUse !== 'debug') {
272 25
      return false;
273
    }
274
275 1
    $infoExtra = mysqli_info($this->_db->getLink());
276 1
    if ($infoExtra) {
277
      $infoExtra = ' | info => ' . $infoExtra;
278
    }
279
280 1
    $info = 'time => ' . round($duration, 5) . ' | results => ' . (int)$results . $infoExtra . ' | SQL => ' . UTF8::htmlentities($sql);
281 1
    $fileInfo = $this->getFileAndLineFromSql();
282
283 1
    $this->logger(
284
        array(
285 1
            'debug',
286 1
            '<strong>' . date('d. m. Y G:i:s') . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . '):</strong> ' . $info . '<br>',
287 1
            'sql',
288
        )
289 1
    );
290
291 1
    return true;
292
  }
293
294
  /**
295
   * Wrapper-Function for a "Logger"-Class.
296
   *
297
   * INFO:
298
   * The "Logger"-ClassName is set by "$this->logger_class_name",<br />
299
   * the "Logger"-Method is the [0] element from the "$log"-parameter,<br />
300
   * the text you want to log is the [1] element and<br />
301
   * the type you want to log is the next [2] element.
302
   *
303
   * @param string[] $log [method, text, type]<br />e.g.: array('error', 'this is a error', 'sql')
304
   */
305 21
  public function logger(array $log)
306
  {
307 21
    $logMethod = '';
308 21
    $logText = '';
309 21
    $logType = '';
310 21
    $logClass = $this->logger_class_name;
311
312 21
    if (isset($log[0])) {
313 21
      $logMethod = $log[0];
314 21
    }
315 21
    if (isset($log[1])) {
316 21
      $logText = $log[1];
317 21
    }
318 21
    if (isset($log[2])) {
319 1
      $logType = $log[2];
320 1
    }
321
322
    if (
323
        $logClass
324 21
        &&
325
        class_exists($logClass)
326 21
        &&
327
        method_exists($logClass, $logMethod)
328 21
    ) {
329
      $logClass::$logMethod($logText, $logType);
330
    }
331 21
  }
332
333
  /**
334
   * Send a error mail to the admin / dev.
335
   *
336
   * @param string $subject
337
   * @param string $htmlBody
338
   * @param int    $priority
339
   */
340 10
  public function mailToAdmin($subject, $htmlBody, $priority = 3)
341
  {
342 10
    if (function_exists('mailToAdmin')) {
343
      mailToAdmin($subject, $htmlBody, $priority);
344
    } else {
345
346 10
      if ($priority === 3) {
347 10
        $this->logger(
348
            array(
349 10
                'debug',
350 10
                $subject . ' | ' . $htmlBody,
351
            )
352 10
        );
353 10
      } elseif ($priority > 3) {
354
        $this->logger(
355
            array(
356
                'error',
357
                $subject . ' | ' . $htmlBody,
358
            )
359
        );
360
      } elseif ($priority < 3) {
361
        $this->logger(
362
            array(
363
                'info',
364
                $subject . ' | ' . $htmlBody,
365
            )
366
        );
367
      }
368
369
    }
370 10
  }
371
372
  /**
373
   * @param boolean $echo_on_error
374
   */
375 10
  public function setEchoOnError($echo_on_error)
376
  {
377 10
    $this->echo_on_error = (boolean)$echo_on_error;
378 10
  }
379
380
  /**
381
   * @param boolean $exit_on_error
382
   */
383 10
  public function setExitOnError($exit_on_error)
384
  {
385 10
    $this->exit_on_error = (boolean)$exit_on_error;
386 10
  }
387
388
  /**
389
   * @param string $logger_class_name
390
   */
391 10
  public function setLoggerClassName($logger_class_name)
392
  {
393 10
    $this->logger_class_name = (string)$logger_class_name;
394 10
  }
395
396
  /**
397
   * @param string $logger_level
398
   */
399 10
  public function setLoggerLevel($logger_level)
400
  {
401 10
    $this->logger_level = (string)$logger_level;
402 10
  }
403
404
}
405