Completed
Push — master ( b6e8a8...113071 )
by Márk
02:33
created

RequestException::obfuscateUri()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace GuzzleHttp\Exception;
3
4
use Psr\Http\Message\RequestInterface;
5
use Psr\Http\Message\ResponseInterface;
6
use GuzzleHttp\Promise\PromiseInterface;
7
use Psr\Http\Message\UriInterface;
8
9
/**
10
 * HTTP Request exception
11
 */
12
class RequestException extends TransferException
13
{
14
    /** @var RequestInterface */
15
    private $request;
16
17
    /** @var ResponseInterface */
18
    private $response;
19
20
    /** @var array */
21
    private $handlerContext;
22
23
    public function __construct(
24
        $message,
25
        RequestInterface $request,
26
        ResponseInterface $response = null,
27
        \Exception $previous = null,
28
        array $handlerContext = []
29
    ) {
30
        // Set the code of the exception if the response is set and not future.
31
        $code = $response && !($response instanceof PromiseInterface)
32
            ? $response->getStatusCode()
33
            : 0;
34
        parent::__construct($message, $code, $previous);
35
        $this->request = $request;
36
        $this->response = $response;
37
        $this->handlerContext = $handlerContext;
38
    }
39
40
    /**
41
     * Wrap non-RequestExceptions with a RequestException
42
     *
43
     * @param RequestInterface $request
44
     * @param \Exception       $e
45
     *
46
     * @return RequestException
47
     */
48
    public static function wrapException(RequestInterface $request, \Exception $e)
49
    {
50
        return $e instanceof RequestException
51
            ? $e
52
            : new RequestException($e->getMessage(), $request, null, $e);
53
    }
54
55
    /**
56
     * Factory method to create a new exception with a normalized error message
57
     *
58
     * @param RequestInterface  $request  Request
59
     * @param ResponseInterface $response Response received
60
     * @param \Exception        $previous Previous exception
61
     * @param array             $ctx      Optional handler context.
62
     *
63
     * @return self
64
     */
65
    public static function create(
66
        RequestInterface $request,
67
        ResponseInterface $response = null,
68
        \Exception $previous = null,
69
        array $ctx = []
70
    ) {
71
        if (!$response) {
72
            return new self(
73
                'Error completing request',
74
                $request,
75
                null,
76
                $previous,
77
                $ctx
78
            );
79
        }
80
81
        $level = (int) floor($response->getStatusCode() / 100);
82
        if ($level === 4) {
83
            $label = 'Client error';
84
            $className = __NAMESPACE__ . '\\ClientException';
85
        } elseif ($level === 5) {
86
            $label = 'Server error';
87
            $className = __NAMESPACE__ . '\\ServerException';
88
        } else {
89
            $label = 'Unsuccessful request';
90
            $className = __CLASS__;
91
        }
92
93
        $uri = $request->getUri();
94
        $uri = static::obfuscateUri($uri);
0 ignored issues
show
Bug introduced by
Since obfuscateUri() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of obfuscateUri() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
95
96
        // Server Error: `GET /` resulted in a `404 Not Found` response:
97
        // <html> ... (truncated)
98
        $message = sprintf(
99
            '%s: `%s` resulted in a `%s` response',
100
            $label,
101
            $request->getMethod() . ' ' . $uri,
102
            $response->getStatusCode() . ' ' . $response->getReasonPhrase()
103
        );
104
105
        $summary = static::getResponseBodySummary($response);
106
107
        if ($summary !== null) {
108
            $message .= ":\n{$summary}\n";
109
        }
110
111
        return new $className($message, $request, $response, $previous, $ctx);
112
    }
113
114
    /**
115
     * Get a short summary of the response
116
     *
117
     * Will return `null` if the response is not printable.
118
     *
119
     * @param ResponseInterface $response
120
     *
121
     * @return string|null
122
     */
123
    public static function getResponseBodySummary(ResponseInterface $response)
124
    {
125
        $body = $response->getBody();
126
127
        if (!$body->isSeekable()) {
128
            return null;
129
        }
130
131
        $size = $body->getSize();
132
        $summary = $body->read(120);
133
        $body->rewind();
134
135
        if ($size > 120) {
136
            $summary .= ' (truncated...)';
137
        }
138
139
        // Matches any printable character, including unicode characters:
140
        // letters, marks, numbers, punctuation, spacing, and separators.
141
        if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
142
            return null;
143
        }
144
145
        return $summary;
146
    }
147
148
    /**
149
     * Obfuscates URI if there is an username and a password present
150
     *
151
     * @param UriInterface $uri
152
     *
153
     * @return UriInterface
154
     */
155
    private static function obfuscateUri($uri)
156
    {
157
        $userInfo = $uri->getUserInfo();
158
159
        if (false !== ($pos = strpos($userInfo, ':'))) {
160
            return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
161
        }
162
163
        return $uri;
164
    }
165
166
    /**
167
     * Get the request that caused the exception
168
     *
169
     * @return RequestInterface
170
     */
171
    public function getRequest()
172
    {
173
        return $this->request;
174
    }
175
176
    /**
177
     * Get the associated response
178
     *
179
     * @return ResponseInterface|null
180
     */
181
    public function getResponse()
182
    {
183
        return $this->response;
184
    }
185
186
    /**
187
     * Check if a response was received
188
     *
189
     * @return bool
190
     */
191
    public function hasResponse()
192
    {
193
        return $this->response !== null;
194
    }
195
196
    /**
197
     * Get contextual information about the error from the underlying handler.
198
     *
199
     * The contents of this array will vary depending on which handler you are
200
     * using. It may also be just an empty array. Relying on this data will
201
     * couple you to a specific handler, but can give more debug information
202
     * when needed.
203
     *
204
     * @return array
205
     */
206
    public function getHandlerContext()
207
    {
208
        return $this->handlerContext;
209
    }
210
}
211