GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Builder::httpError()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 25
ccs 14
cts 14
cp 1
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 16
nc 1
nop 2
crap 1
1
<?php declare(strict_types=1);
2
3
namespace OpenStack\Common\Error;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\ClientInterface;
7
use GuzzleHttp\Exception\ClientException;
8
use Psr\Http\Message\MessageInterface;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
12
/**
13
 * Class responsible for building meaningful exceptions. For HTTP problems, it produces a {@see HttpError}
14
 * exception, and supplies a error message with reasonable defaults. For user input problems, it produces a
15
 * {@see UserInputError} exception. For both, the problem is described, a potential solution is offered and
16
 * a link to further information is included.
17
 *
18
 * @package OpenStack\Common\Error
19
 */
20
class Builder
21
{
22
    /**
23
     * The default domain to use for further link documentation.
24
     *
25
     * @var string
26
     */
27
    private $docDomain = 'http://docs.php-opencloud.com/en/latest/';
28
29
    /**
30
     * The HTTP client required to validate the further links.
31
     *
32
     * @var ClientInterface
33
     */
34
    private $client;
35
36
    /**
37
     * @param ClientInterface $client
38
     */
39 2
    public function __construct(ClientInterface $client = null)
40
    {
41 2
        $this->client = $client ?: new Client();
42 2
    }
43
44
    /**
45
     * Internal method used when outputting headers in the error description.
46
     *
47
     * @param $name
48
     *
49
     * @return string
50
     */
51 4
    private function header(string $name): string
52
    {
53 4
        return sprintf("%s\n%s\n", $name, str_repeat('~', strlen($name)));
54
    }
55
56
    /**
57
     * Before outputting custom links, it is validated to ensure that the user is not
58
     * directed off to a broken link. If a 404 is detected, it is hidden.
59
     *
60
     * @param $link The proposed link
61
     *
62
     * @return bool
63
     */
64 2
    private function linkIsValid(string $link): bool
65
    {
66 2
        $link = $this->docDomain . $link;
67
68
        try {
69 2
            return $this->client->request('HEAD', $link)->getStatusCode() < 400;
70 2
        } catch (ClientException $e) {
71
            return false;
72
        }
73 2
    }
74
75
    /**
76 2
     * @param MessageInterface $message
77
     *
78 2
     * @codeCoverageIgnore
79 2
     * @return string
80 2
     */
81 2
    public function str(MessageInterface $message): string
82 2
    {
83 1
        if ($message instanceof RequestInterface) {
84 1
            $msg = trim($message->getMethod() . ' '
85 2
                    . $message->getRequestTarget())
86 2
                . ' HTTP/' . $message->getProtocolVersion();
87 2
            if (!$message->hasHeader('host')) {
88 2
                $msg .= "\r\nHost: " . $message->getUri()->getHost();
89 2
            }
90
        } elseif ($message instanceof ResponseInterface) {
91 2
            $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
92 1
                . $message->getStatusCode() . ' '
93 2
                . $message->getReasonPhrase();
94
        }
95 2
96 2
        foreach ($message->getHeaders() as $name => $values) {
97 2
            $msg .= "\r\n{$name}: " . implode(', ', $values);
0 ignored issues
show
Bug introduced by
The variable $msg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
98
        }
99 2
100
        if (ini_get('memory_limit') < 0 || $message->getBody()->getSize() < ini_get('memory_limit')) {
101
            $msg .= "\r\n\r\n" . $message->getBody();
102
        }
103
104
        return $msg;
105
    }
106
107
    /**
108
     * Helper method responsible for constructing and returning {@see BadResponseError} exceptions.
109
     *
110 2
     * @param RequestInterface  $request  The faulty request
111
     * @param ResponseInterface $response The error-filled response
112 2
     *
113
     * @return BadResponseError
114 2
     */
115 2
    public function httpError(RequestInterface $request, ResponseInterface $response): BadResponseError
116
    {
117 2
        $message = $this->header('HTTP Error');
118 2
119
        $message .= sprintf("The remote server returned a \"%d %s\" error for the following transaction:\n\n",
120 2
            $response->getStatusCode(), $response->getReasonPhrase());
121 2
122
        $message .= $this->header('Request');
123 2
        $message .= trim($this->str($request)) . PHP_EOL . PHP_EOL;
124 2
125
        $message .= $this->header('Response');
126
        $message .= trim($this->str($response)) . PHP_EOL . PHP_EOL;
127 2
128
        $message .= $this->header('Further information');
129 2
        $message .= $this->getStatusCodeMessage($response->getStatusCode());
130 2
131 2
        $message .= "Visit http://docs.php-opencloud.com/en/latest/http-codes for more information about debugging "
132
            . "HTTP status codes, or file a support issue on https://github.com/php-opencloud/openstack/issues.";
133 2
134
        $e = new BadResponseError($message);
135
        $e->setRequest($request);
136 2
        $e->setResponse($response);
137
138
        return $e;
139 2
    }
140 2
141 2
    private function getStatusCodeMessage(int $statusCode): string
142 2
    {
143 2
        $errors = [
144
            400 => 'Please ensure that your input values are valid and well-formed. ',
145 2
            401 => 'Please ensure that your authentication credentials are valid. ',
146
            404 => "Please ensure that the resource you're trying to access actually exists. ",
147
            500 => 'Please try this operation again once you know the remote server is operational. ',
148
        ];
149
150
        return isset($errors[$statusCode]) ? $errors[$statusCode] : '';
151
    }
152
153
    /**
154
     * Helper method responsible for constructing and returning {@see UserInputError} exceptions.
155
     *
156
     * @param string      $expectedType The type that was expected from the user
157 2
     * @param mixed       $userValue    The incorrect value the user actually provided
158
     * @param string|null $furtherLink  A link to further information if necessary (optional).
159 2
     *
160
     * @return UserInputError
161 2
     */
162 2
    public function userInputError(string $expectedType, $userValue, string $furtherLink = null): UserInputError
163
    {
164 2
        $message = $this->header('User Input Error');
165
166 2
        $message .= sprintf("%s was expected, but the following value was passed in:\n\n%s\n",
167 1
            $expectedType, print_r($userValue, true));
168 1
169
        $message .= "Please ensure that the value adheres to the expectation above. ";
170 2
171
        if ($furtherLink && $this->linkIsValid($furtherLink)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $furtherLink of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
172 2
            $message .= sprintf("Visit %s for more information about input arguments. ", $this->docDomain . $furtherLink);
173
        }
174
175
        $message .= 'If you run into trouble, please open a support issue on https://github.com/php-opencloud/openstack/issues.';
176
177
        return new UserInputError($message);
178
    }
179
}
180