Completed
Push — develop ( b6f561...b77323 )
by Neomerx
01:59
created

FluteThrowableHandler::createResponse()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 17
nc 3
nop 2
1
<?php namespace Limoncello\Flute\Http\Errors;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Exception;
20
use Limoncello\Contracts\Exceptions\ThrowableHandlerInterface;
21
use Limoncello\Contracts\Http\ThrowableResponseInterface;
22
use Limoncello\Flute\Contracts\Encoder\EncoderInterface;
23
use Limoncello\Flute\Http\JsonApiResponse;
24
use Neomerx\JsonApi\Document\Error;
25
use Neomerx\JsonApi\Exceptions\ErrorCollection;
26
use Neomerx\JsonApi\Exceptions\JsonApiException;
27
use Psr\Container\ContainerInterface;
28
use Psr\Log\LoggerAwareTrait;
29
use Throwable;
30
31
/**
32
 * @package Limoncello\Flute
33
 *
34
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35
 */
36
class FluteThrowableHandler implements ThrowableHandlerInterface
37
{
38
    use LoggerAwareTrait;
39
40
    /**
41
     * Those classes will not be logged. Note that classes are expected to be keys but not values.
42
     *
43
     * @var array
44
     */
45
    private $doNotLogClassesAsKeys;
46
47
    /**
48
     * @var int
49
     */
50
    private $httpCodeForUnexpected;
51
52
    /**
53
     * @var bool
54
     */
55
    private $isDebug;
56
57
    /**
58
     * @var EncoderInterface
59
     */
60
    private $encoder;
61
62
    /**
63
     * @param EncoderInterface $encoder
64
     * @param array            $doNotLogClassesAsKeys
65
     * @param int              $httpCodeForUnexpected
66
     * @param bool             $isDebug
67
     */
68
    public function __construct(
69
        EncoderInterface $encoder,
70
        array $doNotLogClassesAsKeys,
71
        int $httpCodeForUnexpected,
72
        bool $isDebug
73
    ) {
74
        $this->doNotLogClassesAsKeys = $doNotLogClassesAsKeys;
75
        $this->httpCodeForUnexpected = $httpCodeForUnexpected;
76
        $this->isDebug               = $isDebug;
77
        $this->encoder               = $encoder;
78
    }
79
80
    /**
81
     * @inheritdoc
82
     */
83
    public function createResponse(Throwable $throwable, ContainerInterface $container): ThrowableResponseInterface
84
    {
85
        unset($container);
86
87
        $message = 'Internal Server Error';
88
89
        $this->logError($throwable, $message);
90
91
        // compose JSON API Error with appropriate level of details
92
        if ($throwable instanceof JsonApiException) {
93
            /** @var JsonApiException $throwable */
94
            $errors   = $throwable->getErrors();
95
            $httpCode = $throwable->getHttpCode();
96
        } else {
97
            $errors   = new ErrorCollection();
98
            $httpCode = $this->getHttpCodeForUnexpectedThrowable();
99
            $details  = null;
100
            if ($this->isDebug === true) {
101
                $message = $throwable->getMessage();
102
                $details = (string)$throwable;
103
            }
104
            $errors->add(new Error(null, null, $httpCode, null, $message, $details));
105
        }
106
107
        // encode the error and send to client
108
        $content = $this->encoder->encodeErrors($errors);
109
110
        return $this->createThrowableJsonApiResponse($throwable, $content, $httpCode);
111
    }
112
113
    /**
114
     * @param Throwable $throwable
115
     * @param string    $message
116
     *
117
     * @return void
118
     */
119
    private function logError(Throwable $throwable, string $message): void
120
    {
121
        if ($this->logger !== null && $this->shouldBeLogged($throwable) === true) {
122
            // on error (e.g. no permission to write on disk or etc) ignore
123
            try {
124
                $this->logger->error($message, ['error' => $throwable]);
125
            } catch (Exception $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
126
            }
127
        }
128
    }
129
130
    /**
131
     * @return int
132
     */
133
    private function getHttpCodeForUnexpectedThrowable(): int
134
    {
135
        return $this->httpCodeForUnexpected;
136
    }
137
138
    /**
139
     * @param Throwable $throwable
140
     *
141
     * @return bool
142
     */
143
    private function shouldBeLogged(Throwable $throwable): bool
144
    {
145
        $result = array_key_exists(get_class($throwable), $this->doNotLogClassesAsKeys) === false;
146
147
        return $result;
148
    }
149
150
    /**
151
     * @param Throwable $throwable
152
     * @param string    $content
153
     * @param int       $status
154
     *
155
     * @return ThrowableResponseInterface
156
     */
157
    private function createThrowableJsonApiResponse(Throwable $throwable, string $content, int $status): ThrowableResponseInterface
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 131 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
158
    {
159
        return new class ($throwable, $content, $status) extends JsonApiResponse implements ThrowableResponseInterface
160
        {
161
            /**
162
             * @var Throwable
163
             */
164
            private $throwable;
165
166
            /**
167
             * @param Throwable $throwable
168
             * @param string    $content
169
             * @param int       $status
170
             */
171
            public function __construct(Throwable $throwable, string $content, int $status)
172
            {
173
                parent::__construct($content, $status);
174
                $this->throwable = $throwable;
175
            }
176
177
            /**
178
             * @return Throwable
179
             */
180
            public function getThrowable(): Throwable
181
            {
182
                return $this->throwable;
183
            }
184
        };
185
    }
186
}
187