Completed
Pull Request — 1.1 (#1128)
by
unknown
42:36
created

ErrorUtil::flattenExceptionBacktrace()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 2
nop 1
dl 0
loc 12
ccs 0
cts 0
cp 0
crap 12
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * AppserverIo\Appserver\ServletEngine\Utils\ErrorUtil
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/appserver
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Appserver\ServletEngine\Utils;
22
23
use AppserverIo\Lang\String;
24
use AppserverIo\Lang\Boolean;
25
use AppserverIo\Psr\Servlet\ServletException;
26
use AppserverIo\Psr\Servlet\ServletContextInterface;
27
use AppserverIo\Psr\Servlet\Utils\RequestHandlerKeys;
28
use AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface;
29
use AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface;
30
use AppserverIo\Appserver\ServletEngine\RequestHandler;
31
use AppserverIo\Appserver\Core\Utilities\LoggerUtils;
32
33
/**
34
 * Utility class that providing functionality to handle PHP errors.
35
 *
36
 * @author    Tim Wagner <[email protected]>
37
 * @copyright 2015 TechDivision GmbH <[email protected]>
38
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
39
 * @link      https://github.com/appserver-io/appserver
40
 * @link      http://www.appserver.io
41
 */
42
class ErrorUtil extends \AppserverIo\Appserver\Core\Utilities\ErrorUtil
43
{
44
45
    /**
46
     * Create's a new error instance from the passed exception.
47
     *
48
     * @param \Exception $e The exception to create the error instance from
49
     *
50
     * @return \AppserverIo\Appserver\Core\Utilities\ErrorInterface The error instance
51
     */
52
    public function fromException(\Exception $e)
53
    {
54
        return new Error(E_EXCEPTION, $e->__toString(), $e->getFile(), $e->getLine(), $e->getCode() ? $e->getCode() : 500, $e);
55
    }
56
57
    /**
58
     * Create's a new error instance with the values from the passed array.
59
     *
60
     * @param array $error The array containing the error information
61
     *
62
     * @return \AppserverIo\Appserver\Core\Utilities\ErrorInterface The error instance
63
     */
64
    public function fromArray(array $error)
65
    {
66
67
        // extract the array with the error information
68
        list ($type, $message, $file, $line) = array_values($error);
69
70
        // initialize and return the error instance
71
        return new Error($type, $message, $file, $line, ErrorUtil::singleton()->isFatal($type) ? 500 : 0);
72
    }
73
74
    /**
75
     * This method finally handles all PHP and user errors as well as the exceptions that
76
     * have been thrown through the servlet processing.
77
     *
78
     * @param \AppserverIo\Appserver\ServletEngine\RequestHandler        $requestHandler  The request handler instance
79
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The actual request instance
80
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The actual request instance
81
     *
82
     * @return void
83
     */
84
    public function handleErrors(
85
        RequestHandler $requestHandler,
86
        HttpServletRequestInterface $servletRequest,
87
        HttpServletResponseInterface $servletResponse
88
    ) {
89
90
        // return immediately if we don't have any errors
91
        if (sizeof($errors = $requestHandler->getErrors()) === 0) {
92
            return;
93
        }
94
95
        // iterate over the errors to process each of them
96
        foreach ($errors as $error) {
97
            // prepare the error message
98
            $message = $this->prepareMessage($error);
99
100
            // query whether or not we have to log the error
101
            if (Boolean::valueOf(new String(ini_get('log_errors')))->booleanValue()) {
102
                LoggerUtils::log(ErrorUtil::mapLogLevel($error), $message);
0 ignored issues
show
Bug Best Practice introduced by
The method AppserverIo\Appserver\Co...rrorUtil::mapLogLevel() is not static, but was called statically. ( Ignorable by Annotation )

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

102
                LoggerUtils::log(ErrorUtil::/** @scrutinizer ignore-call */ mapLogLevel($error), $message);
Loading history...
103
            }
104
105
            // we prepend the errors to the body stream if display_errors is on
106
            if (Boolean::valueOf(new String(ini_get('display_errors')))->booleanValue()) {
107
                $bodyContent = $servletResponse->getBodyContent();
108
                $servletResponse->resetBodyStream();
109
                $servletResponse->appendBodyStream(sprintf('%s<br/>%s', $message, $bodyContent));
110
            }
111
112
            // query whether or not, the error has an status code
113
            if ($statusCode = $error->getStatusCode()) {
114
                $servletResponse->setStatusCode($statusCode);
115
            }
116
        }
117
118
        // query whether or not we've a client or an server error
119
        if ($servletResponse->getStatusCode() > 399) {
120
            try {
121
                // create a local copy of the application
122
                $application = $servletRequest->getContext();
0 ignored issues
show
Bug introduced by
The method getContext() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. Did you maybe mean getContextPath()? ( Ignorable by Annotation )

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

122
                /** @scrutinizer ignore-call */ 
123
                $application = $servletRequest->getContext();

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...
123
124
                // inject the application and servlet response
125
                $servletRequest->injectResponse($servletResponse);
0 ignored issues
show
Bug introduced by
The method injectResponse() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Appserver\ServletEngine\Http\Request. ( Ignorable by Annotation )

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

125
                $servletRequest->/** @scrutinizer ignore-call */ 
126
                                 injectResponse($servletResponse);
Loading history...
126
                $servletRequest->injectContext($application);
0 ignored issues
show
Bug introduced by
The method injectContext() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Appserver\ServletEngine\Http\Request. ( Ignorable by Annotation )

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

126
                $servletRequest->/** @scrutinizer ignore-call */ 
127
                                 injectContext($application);
Loading history...
127
128
                // load the servlet context instance
129
                $servletManager = $application->search(ServletContextInterface::IDENTIFIER);
130
131
                // initialize the request URI for the error page to be rendered
132
                $requestUri = null;
133
134
                // iterate over the configured error pages to find a matching one
135
                foreach ($servletManager->getErrorPages() as $errorCodePattern => $errorPage) {
136
                    // query whether or not we found an error page configured for the actual status code
137
                    if (fnmatch($errorCodePattern, $servletResponse->getStatusCode())) {
138
                        $requestUri = $errorPage;
139
                        break;
140
                    }
141
                }
142
143
                // query whether or not we've an found a configured error page
144
                if ($requestUri == null) {
145
                    throw new ServletException(
146
                        sprintf(
147
                            'Please configure an error page for status code %s',
148
                            $servletResponse->getStatusCode()
149
                        )
150
                    );
151
                }
152
153
                // initialize the request URI
154
                $servletRequest->setRequestUri($requestUri);
0 ignored issues
show
Bug introduced by
The method setRequestUri() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Appserver\ServletEngine\Http\Request. ( Ignorable by Annotation )

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

154
                $servletRequest->/** @scrutinizer ignore-call */ 
155
                                 setRequestUri($requestUri);
Loading history...
155
                // prepare the request with the new data
156
                $servletRequest->prepare();
0 ignored issues
show
Bug introduced by
The method prepare() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Appserver\ServletEngine\Http\Request. ( Ignorable by Annotation )

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

156
                $servletRequest->/** @scrutinizer ignore-call */ 
157
                                 prepare();
Loading history...
157
                // reset the body stream to remove content, that has already been appended
158
                $servletResponse->resetBodyStream();
159
                // we add the filtered errors (status code > 399) to the servlet request
160
                $servletRequest->setAttribute(
0 ignored issues
show
Bug introduced by
The method setAttribute() does not exist on AppserverIo\Psr\Servlet\...ServletRequestInterface. It seems like you code against a sub-type of AppserverIo\Psr\Servlet\...ServletRequestInterface such as AppserverIo\Appserver\ServletEngine\Http\Request. ( Ignorable by Annotation )

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

160
                $servletRequest->/** @scrutinizer ignore-call */ 
161
                                 setAttribute(
Loading history...
161
                    RequestHandlerKeys::ERROR_MESSAGES,
162
                    array_filter($errors, function ($message) {
163
                        return $message->getStatusCode() > 399;
164
                    })
165
                );
166
                // load the servlet path and session-ID
167
                $servletPath = $servletRequest->getServletPath();
168
                $sessionId = $servletRequest->getProposedSessionId();
169
                // load and process the servlet
170
                $servlet = $servletManager->lookup($servletPath, $sessionId);
171
                $servlet->service($servletRequest, $servletResponse);
172
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
173
            } catch (\Exception $e) {
174
                // finally log the exception
175
                $application->getInitialContext()->getSystemLogger()->critical($e->__toString());
176
                // append the exception message to the body stream
177
                $servletResponse->appendBodyStream($e->__toString());
178
            }
179
        }
180
    }
181
182
    /**
183
     * Flattens an exception in order to enable serialization.
184
     *
185
     * @param \Exception $exception The exception to flatten
186
     *
187
     * @return void
188
     * @throws \ReflectionException
189
     */
190
    public function flattenExceptionBacktrace(\Exception $exception)
191
    {
192
        $traceProperty = (new \ReflectionClass('Exception'))->getProperty('trace');
193
        $traceProperty->setAccessible(true);
194
        do {
195
            $trace = $traceProperty->getValue($exception);
196
            foreach ($trace as &$call) {
197
                array_walk_recursive($call['args'], array($this, 'flatten'));
198
            }
199
            $traceProperty->setValue($exception, $trace);
200
        } while ($exception = $exception->getPrevious());
201
        $traceProperty->setAccessible(false);
202
    }
203
204
    /**
205
     * Flattens a value reference.
206
     *
207
     * @param mixed $value The reference of the value to flatten
208
     * @param mixed $key   The key parameter
209
     *
210
     * @return void
211
     * @throws \ReflectionException
212
     */
213
    public function flatten(&$value, $key) {
214
        if ($value instanceof \Closure) {
215
            $closureReflection = new \ReflectionFunction($value);
216
            $value = sprintf(
217
                '(Closure at %s:%s)',
218
                $closureReflection->getFileName(),
219
                $closureReflection->getStartLine()
220
            );
221
        } elseif (is_object($value)) {
222
            $value = sprintf('object(%s)', get_class($value));
223
        } elseif (is_resource($value)) {
224
            $value = sprintf('resource(%s)', get_resource_type($value));
225
        }
226
    }
227
}
228