Completed
Push — master ( 3e48cd...87d066 )
by Lars
10:52 queued 04:05
created

Debug::checkForDev()   C

Complexity

Conditions 8
Paths 9

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8.125

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 30
ccs 14
cts 16
cp 0.875
rs 5.3846
cc 8
eloc 17
nc 9
nop 0
crap 8.125
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
  /**
59
   * Debug constructor.
60
   *
61
   * @param DB $db
62
   */
63 10
  public function __construct(DB $db)
64
  {
65 10
    $this->_db = $db;
66 10
  }
67
68
  /**
69
   * Check is the current user is a developer.
70
   *
71
   * INFO:
72
   * By default we will return "true" if the remote-ip-address is localhost or
73
   * if the script is called via CLI. But you can also overwrite this method or
74
   * you can implement a global "checkForDev()"-function.
75
   *
76
   * @return bool
77
   */
78 21
  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...
79
  {
80
    // init
81 21
    $return = false;
82
83 21
    if (function_exists('checkForDev')) {
84
      $return = checkForDev();
85
    } else {
86
87
      // for testing with dev-address
88 21
      $noDev = isset($_GET['noDev']) ? (int)$_GET['noDev'] : 0;
89 21
      $remoteIpAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
90
91
      if (
92
          $noDev != 1
93 21
          &&
94
          (
95
              $remoteIpAddress === '127.0.0.1'
96 21
              ||
97
              $remoteIpAddress === '::1'
98 21
              ||
99 21
              PHP_SAPI === 'cli'
100 21
          )
101 21
      ) {
102 21
        $return = true;
103 21
      }
104
    }
105
106 21
    return $return;
107
  }
108
109
  /**
110
   * Clear the errors in "$this->_errors".
111
   *
112
   * @return bool
113
   */
114 4
  public function clearErrors()
115
  {
116 4
    $this->_errors = array();
117
118 4
    return true;
119
  }
120
121
  /**
122
   * Display SQL-Errors or throw Exceptions (for dev).
123
   *
124
   * @param string       $error
125
   * @param null|boolean $force_exception_after_error
126
   *
127
   * @throws \Exception
128
   */
129 21
  public function displayError($error, $force_exception_after_error = null)
130
  {
131 21
    $fileInfo = $this->getFileAndLineFromSql();
132
133 21
    $this->logger(
134
        array(
135 21
            'error',
136 21
            '<strong>' . date(
137
                'd. m. Y G:i:s'
138 21
            ) . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . ') (sql-error):</strong> ' . $error . '<br>',
139
        )
140 21
    );
141
142 21
    $this->_errors[] = $error;
143
144 21
    if ($this->checkForDev() === true) {
145
146 21
      if ($this->echo_on_error) {
147 4
        $box_border = $this->css_mysql_box_border;
148 4
        $box_bg = $this->css_mysql_box_bg;
149
150
        echo '
151 4
        <div class="OBJ-mysql-box" style="border:' . $box_border . '; background:' . $box_bg . '; padding:10px; margin:10px;">
152
          <b style="font-size:14px;">MYSQL Error:</b>
153
          <code style="display:block;">
154 4
            file / line: ' . $fileInfo['file'] . ' / ' . $fileInfo['line'] . '
155 4
            ' . $error . '
156
          </code>
157
        </div>
158 4
        ';
159 4
      }
160
161 21
      if ($force_exception_after_error === true) {
162 4
        throw new \Exception($error);
163 17
      } 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...
164
        // nothing
165 17
      } elseif ($force_exception_after_error === null) {
166
        // default
167 14
        if ($this->exit_on_error === true) {
168 2
          throw new \Exception($error);
169
        }
170 12
      }
171 15
    }
172 15
  }
173
174
  /**
175
   * Get errors from "$this->_errors".
176
   *
177
   * @return array
178
   */
179 3
  public function getErrors()
180
  {
181 3
    return $this->_errors;
182
  }
183
184
  /**
185
   * Try to get the file & line from the current sql-query.
186
   *
187
   * @return array will return array['file'] and array['line']
188
   */
189 21
  private function getFileAndLineFromSql()
190
  {
191
    // init
192 21
    $return = array();
193 21
    $file = '';
194 21
    $line = '';
195
196 21
    if (Bootup::is_php('5.4') === true) {
197
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
198
    } else {
199 21
      $referrer = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
200
    }
201
202 21
    foreach ($referrer as $key => $ref) {
203
204
      if (
205 21
          $ref['function'] === 'execSQL'
206 21
          ||
207 21
          $ref['function'] === 'query'
208 21
          ||
209 21
          $ref['function'] === 'qry'
210 21
          ||
211 21
          $ref['function'] === 'execute'
212 21
          ||
213 21
          $ref['function'] === 'insert'
214 21
          ||
215 21
          $ref['function'] === 'update'
216 21
          ||
217 21
          $ref['function'] === 'replace'
218 21
          ||
219 21
          $ref['function'] === 'delete'
220 21
      ) {
221 16
        $file = $referrer[$key]['file'];
222 16
        $line = $referrer[$key]['line'];
223 16
      }
224
225 21
    }
226
227 21
    $return['file'] = $file;
228 21
    $return['line'] = $line;
229
230 21
    return $return;
231
  }
232
233
  /**
234
   * @return string
235
   */
236
  public function getLoggerClassName()
237
  {
238
    return $this->logger_class_name;
239
  }
240
241
  /**
242
   * @return string
243
   */
244
  public function getLoggerLevel()
245
  {
246
    return $this->logger_level;
247
  }
248
249
  /**
250
   * @return boolean
251
   */
252
  public function isEchoOnError()
253
  {
254
    return $this->echo_on_error;
255
  }
256
257
  /**
258
   * @return boolean
259
   */
260
  public function isExitOnError()
261
  {
262
    return $this->exit_on_error;
263
  }
264
265
  /**
266
   * Log the current query via "$this->logger".
267
   *
268
   * @param string $sql     sql-query
269
   * @param int    $duration
270
   * @param int    $results result counter
271
   *
272
   * @return bool
273
   */
274 28
  public function logQuery($sql, $duration, $results)
275
  {
276 28
    $logLevelUse = strtolower($this->logger_level);
277
278 28
    if ($logLevelUse !== 'trace' && $logLevelUse !== 'debug') {
279 28
      return false;
280
    }
281
282 1
    $infoExtra = mysqli_info($this->_db->getLink());
283 1
    if ($infoExtra) {
284
      $infoExtra = ' | info => ' . $infoExtra;
285
    }
286
287 1
    $info = 'time => ' . round($duration, 5) . ' | results => ' . (int)$results . $infoExtra . ' | SQL => ' . UTF8::htmlentities($sql);
288 1
    $fileInfo = $this->getFileAndLineFromSql();
289
290 1
    $this->logger(
291
        array(
292 1
            'debug',
293 1
            '<strong>' . date('d. m. Y G:i:s') . ' (' . $fileInfo['file'] . ' line: ' . $fileInfo['line'] . '):</strong> ' . $info . '<br>',
294 1
            'sql',
295
        )
296 1
    );
297
298 1
    return true;
299
  }
300
301
  /**
302
   * Wrapper-Function for a "Logger"-Class.
303
   *
304
   * INFO:
305
   * The "Logger"-ClassName is set by "$this->logger_class_name",<br />
306
   * the "Logger"-Method is the [0] element from the "$log"-parameter,<br />
307
   * the text you want to log is the [1] element and<br />
308
   * the type you want to log is the next [2] element.
309
   *
310
   * @param string[] $log [method, text, type]<br />e.g.: array('error', 'this is a error', 'sql')
311
   */
312 22
  public function logger(array $log)
313
  {
314 22
    $logMethod = '';
315 22
    $logText = '';
316 22
    $logType = '';
317 22
    $logClass = $this->logger_class_name;
318
319 22
    if (isset($log[0])) {
320 22
      $logMethod = $log[0];
321 22
    }
322 22
    if (isset($log[1])) {
323 22
      $logText = $log[1];
324 22
    }
325 22
    if (isset($log[2])) {
326 1
      $logType = $log[2];
327 1
    }
328
329
    if (
330
        $logClass
331 22
        &&
332
        class_exists($logClass)
333 22
        &&
334
        method_exists($logClass, $logMethod)
335 22
    ) {
336
      $logClass::$logMethod($logText, $logType);
337
    }
338 22
  }
339
340
  /**
341
   * Send a error mail to the admin / dev.
342
   *
343
   * @param string $subject
344
   * @param string $htmlBody
345
   * @param int    $priority
346
   */
347 11
  public function mailToAdmin($subject, $htmlBody, $priority = 3)
348
  {
349 11
    if (function_exists('mailToAdmin')) {
350
      mailToAdmin($subject, $htmlBody, $priority);
351
    } else {
352
353 11
      if ($priority === 3) {
354 11
        $this->logger(
355
            array(
356 11
                'debug',
357 11
                $subject . ' | ' . $htmlBody,
358
            )
359 11
        );
360 11
      } elseif ($priority > 3) {
361
        $this->logger(
362
            array(
363
                'error',
364
                $subject . ' | ' . $htmlBody,
365
            )
366
        );
367
      } elseif ($priority < 3) {
368
        $this->logger(
369
            array(
370
                'info',
371
                $subject . ' | ' . $htmlBody,
372
            )
373
        );
374
      }
375
376
    }
377 11
  }
378
379
  /**
380
   * @param boolean $echo_on_error
381
   */
382 10
  public function setEchoOnError($echo_on_error)
383
  {
384 10
    $this->echo_on_error = (boolean)$echo_on_error;
385 10
  }
386
387
  /**
388
   * @param boolean $exit_on_error
389
   */
390 10
  public function setExitOnError($exit_on_error)
391
  {
392 10
    $this->exit_on_error = (boolean)$exit_on_error;
393 10
  }
394
395
  /**
396
   * @param string $logger_class_name
397
   */
398 10
  public function setLoggerClassName($logger_class_name)
399
  {
400 10
    $this->logger_class_name = (string)$logger_class_name;
401 10
  }
402
403
  /**
404
   * @param string $logger_level
405
   */
406 10
  public function setLoggerLevel($logger_level)
407
  {
408 10
    $this->logger_level = (string)$logger_level;
409 10
  }
410
411
}
412