Completed
Push — 1.1 ( 47248b...510c04 )
by Tim
16s queued 13s
created

ErrorUtil::flatten()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 4
nop 2
dl 0
loc 12
ccs 0
cts 12
cp 0
crap 20
rs 9.9332
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
        $this->flattenExceptionBacktrace($e);
55
        return new Error(E_EXCEPTION, $e->__toString(), $e->getFile(), $e->getLine(), $e->getCode() ? $e->getCode() : 500, $e);
56
    }
57
58
    /**
59
     * Create's a new error instance with the values from the passed array.
60
     *
61
     * @param array $error The array containing the error information
62
     *
63
     * @return \AppserverIo\Appserver\Core\Utilities\ErrorInterface The error instance
64
     */
65
    public function fromArray(array $error)
66
    {
67
68
        // extract the array with the error information
69
        list ($type, $message, $file, $line) = array_values($error);
70
71
        // initialize and return the error instance
72
        return new Error($type, $message, $file, $line, ErrorUtil::singleton()->isFatal($type) ? 500 : 0);
73
    }
74
75
    /**
76
     * This method finally handles all PHP and user errors as well as the exceptions that
77
     * have been thrown through the servlet processing.
78
     *
79
     * @param \AppserverIo\Appserver\ServletEngine\RequestHandler        $requestHandler  The request handler instance
80
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface  $servletRequest  The actual request instance
81
     * @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse The actual request instance
82
     *
83
     * @return void
84
     */
85
    public function handleErrors(
86
        RequestHandler $requestHandler,
87
        HttpServletRequestInterface $servletRequest,
88
        HttpServletResponseInterface $servletResponse
89
    ) {
90
91
        // return immediately if we don't have any errors
92
        if (sizeof($errors = $requestHandler->getErrors()) === 0) {
93
            return;
94
        }
95
96
        // iterate over the errors to process each of them
97
        foreach ($errors as $error) {
98
            // prepare the error message
99
            $message = $this->prepareMessage($error);
100
101
            // query whether or not we have to log the error
102
            if (Boolean::valueOf(new String(ini_get('log_errors')))->booleanValue()) {
103
                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

103
                LoggerUtils::log(ErrorUtil::/** @scrutinizer ignore-call */ mapLogLevel($error), $message);
Loading history...
104
            }
105
106
            // we prepend the errors to the body stream if display_errors is on
107
            if (Boolean::valueOf(new String(ini_get('display_errors')))->booleanValue()) {
108
                $bodyContent = $servletResponse->getBodyContent();
109
                $servletResponse->resetBodyStream();
110
                $servletResponse->appendBodyStream(sprintf('%s<br/>%s', $message, $bodyContent));
111
            }
112
113
            // query whether or not, the error has an status code
114
            if ($statusCode = $error->getStatusCode()) {
115
                $servletResponse->setStatusCode($statusCode);
116
            }
117
        }
118
119
        // query whether or not we've a client or an server error
120
        if ($servletResponse->getStatusCode() > 399) {
121
            try {
122
                // create a local copy of the application
123
                $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

123
                /** @scrutinizer ignore-call */ 
124
                $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...
124
125
                // inject the application and servlet response
126
                $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

126
                $servletRequest->/** @scrutinizer ignore-call */ 
127
                                 injectResponse($servletResponse);
Loading history...
127
                $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

127
                $servletRequest->/** @scrutinizer ignore-call */ 
128
                                 injectContext($application);
Loading history...
128
129
                // load the servlet context instance
130
                $servletManager = $application->search(ServletContextInterface::IDENTIFIER);
131
132
                // initialize the request URI for the error page to be rendered
133
                $requestUri = null;
134
135
                // iterate over the configured error pages to find a matching one
136
                foreach ($servletManager->getErrorPages() as $errorCodePattern => $errorPage) {
137
                    // query whether or not we found an error page configured for the actual status code
138
                    if (fnmatch($errorCodePattern, $servletResponse->getStatusCode())) {
139
                        $requestUri = $errorPage;
140
                        break;
141
                    }
142
                }
143
144
                // query whether or not we've an found a configured error page
145
                if ($requestUri == null) {
146
                    throw new ServletException(
147
                        sprintf(
148
                            'Please configure an error page for status code %s',
149
                            $servletResponse->getStatusCode()
150
                        )
151
                    );
152
                }
153
154
                // initialize the request URI
155
                $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

155
                $servletRequest->/** @scrutinizer ignore-call */ 
156
                                 setRequestUri($requestUri);
Loading history...
156
                // prepare the request with the new data
157
                $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

157
                $servletRequest->/** @scrutinizer ignore-call */ 
158
                                 prepare();
Loading history...
158
                // reset the body stream to remove content, that has already been appended
159
                $servletResponse->resetBodyStream();
160
                // we add the filtered errors (status code > 399) to the servlet request
161
                $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

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