Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.43% covered (warning)
80.43%
37 / 46
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
WarningLoggerTrait
80.43% covered (warning)
80.43%
37 / 46
28.57% covered (danger)
28.57%
2 / 7
15.47
0.00% covered (danger)
0.00%
0 / 1
 logLn
n/a
0 / 0
n/a
0 / 0
0
 warningHandler
85.71% covered (warning)
85.71%
30 / 35
0.00% covered (danger)
0.00%
0 / 1
8.19
 activateWarningLogger
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 deactivateWarningLogger
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 disableWarningsTemporarily
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 reenableWarnings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getWarningCount
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 resetWarningCount
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace WebPConvert\Convert\Converters\BaseTraits;
4
5/**
6 * Trait for handling warnings (by logging them)
7 *
8 * This trait is currently only used in the AbstractConverter class. It has been extracted into a
9 * trait in order to bundle the methods concerning options.
10 *
11 * @package    WebPConvert
12 * @author     Bjørn Rosell <it@rosell.dk>
13 * @since      Class available since Release 2.0.0
14 */
15trait WarningLoggerTrait
16{
17    abstract public function logLn($msg, $style = '');
18
19    /** @var string|array|null  Previous error handler (stored in order to be able pass warnings on) */
20    private $previousErrorHandler;
21
22    /** @var boolean  Suppress ALL warnings? (both from log and from bubbling up) */
23    private $suppressWarnings;
24
25    /** @var int  Count number of warnings */
26    private $warningCounter;
27
28    /**
29     *  Handle warnings and notices during conversion by logging them and passing them on.
30     *
31     *  The function is a callback used with "set_error_handler".
32     *  It is declared public because it needs to be accessible from the point where the warning is triggered.
33     *
34     *  PS: The fifth parameter ($errcontext) of an error handler is deprecated since PHP 7.2, however we have
35     *      it here to avoid calling another error handler with too few parameters (see #266)
36     *
37     *  @param  integer  $errno
38     *  @param  string   $errstr
39     *  @param  string   $errfile
40     *  @param  integer  $errline
41     *  @param  array    $errcontext
42     *
43     *  @return false|null|void
44     */
45    public function warningHandler($errno, $errstr, $errfile, $errline, $errcontext = null)
46    {
47        /*
48        We do NOT do the following (even though it is generally recommended):
49
50        if (!(error_reporting() & $errno)) {
51            // This error code is not included in error_reporting, so let it fall
52            // through to the standard PHP error handler
53            return false;
54        }
55
56        - Because we want to log all warnings and errors (also the ones that was suppressed with @)
57        https://secure.php.net/manual/en/language.operators.errorcontrol.php
58
59        If we were to decide suppressing the ones with @, I could do this:
60
61        if (error_reporting() == 0) {
62            /// @ sign temporary disabled error reporting
63            return;
64        }
65        [https://stackoverflow.com/questions/7380782/error-suppression-operator-and-set-error-handler]
66
67        However, that would also disable the warnings on systems with error reporting set to E_NONE.
68        And I really want the conversion log file to contain these warnings on all systems.
69
70        If it was possible to suppress the warnings with @ without suppressing warnings on systems
71        with error reporting set to E_NONE, I would do that.
72        */
73
74        $this->warningCounter++;
75        if ($this->suppressWarnings) {
76            return;
77        }
78
79        $errorTypes = [
80            E_WARNING =>             "Warning",
81            E_NOTICE =>              "Notice",
82            E_DEPRECATED =>          "Deprecated",
83            E_USER_DEPRECATED =>     "User Deprecated",
84
85            /*
86            The following can never be catched by a custom error handler:
87            E_PARSE, E_ERROR, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING
88
89            We do do not currently trigger the following:
90            E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
91
92            But we may want to do that at some point, like this:
93            trigger_error('Your version of Gd is very old', E_USER_WARNING);
94            in that case, remember to add them to this array
95            */
96        ];
97
98        if (PHP_VERSION_ID < 70400) {
99            $errorTypes[E_STRICT] = "Strict Notice";
100        }
101
102        if (isset($errorTypes[$errno])) {
103            $errType = $errorTypes[$errno];
104        } else {
105            $errType = "Unknown error/warning/notice ($errno)";
106        }
107
108        $msg = $errType . ': ' . $errstr . ' in ' . $errfile . ', line ' . $errline . ', PHP ' . PHP_VERSION .
109            ' (' . PHP_OS . ')';
110        $this->logLn('');
111        $this->logLn($msg, 'italic');
112        $this->logLn('');
113
114        if (!is_null($this->previousErrorHandler)) {
115            // If previousErrorHandler is this very error handler, exit to avoid recursion
116            // (this could happen if ::activateWarningLogger() were called twice)
117            if (is_array($this->previousErrorHandler) &&
118                isset($this->previousErrorHandler[0]) &&
119                ($this->previousErrorHandler[0] == $this)
120            ) {
121                return false;
122            } else {
123                return call_user_func($this->previousErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
124            }
125        } else {
126            return false;
127        }
128    }
129
130    /**
131     *  Activate warning logger.
132     *
133     *  Sets the error handler and stores the previous so our error handler can bubble up warnings
134     *
135     *  @return  void
136     */
137    protected function activateWarningLogger()
138    {
139        $this->suppressWarnings = false;
140        $this->warningCounter = 0;
141        $this->previousErrorHandler = set_error_handler(
142            array($this, "warningHandler"),
143            E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
144        );
145    }
146
147    /**
148     *  Deactivate warning logger.
149     *
150     *  Restores the previous error handler.
151     *
152     *  @return  void
153     */
154    protected function deactivateWarningLogger()
155    {
156        restore_error_handler();
157    }
158
159    protected function disableWarningsTemporarily()
160    {
161        $this->suppressWarnings = true;
162    }
163
164    protected function reenableWarnings()
165    {
166        $this->suppressWarnings = false;
167    }
168
169    protected function getWarningCount()
170    {
171        return $this->warningCounter;
172    }
173
174    protected function resetWarningCount()
175    {
176        $this->warningCounter = 0;
177    }
178}