Completed
Pull Request — master (#68)
by Hiraku
04:37 queued 02:21
created

HttpGetRequest::importURL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 2
Bugs 2 Features 0
Metric Value
c 2
b 2
f 0
dl 0
loc 20
ccs 11
cts 11
cp 1
rs 9.4285
cc 3
eloc 12
nc 3
nop 1
crap 3
1
<?php
2
/*
3
 * hirak/prestissimo
4
 * @author Hiraku NAKANO
5
 * @license MIT https://github.com/hirak/prestissimo
6
 */
7
namespace Hirak\Prestissimo\Aspects;
8
9
use Composer\IO;
10
use Composer\Composer;
11
use Composer\Config as CConfig;
12
use Composer\Downloader;
13
14
/**
15
 * Simple Container for http-get request
16
 */
17
class HttpGetRequest
18
{
19
    public $origin;
20
    public $scheme = 'http';
21
    public $host = 'example.com';
22
    public $port = 80;
23
    public $path = '/';
24
25
    public $query = array();
26
    public $headers = array();
27
28
    public $curlOpts = array();
29
30
    public $username = null;
31
    public $password = null;
32
33
    public $maybePublic = false;
34
    public $verbose = false;
35
36
    /** @var CConfig */
37
    protected $config;
38
39
    /** @internal */
40
    const TOKEN_LABEL = 'access_token';
41
42
    /**
43
     * normalize url and authentication info
44
     * @param string $origin domain text
45
     * @param string $url
46
     * @param IO\IOInterface $io
47
     */
48 33
    public function __construct($origin, $url, IO\IOInterface $io)
49
    {
50 33
        $this->origin = $origin;
51 33
        $this->importURL($url);
52
53 33
        if ($this->username && $this->password) {
54 3
            $io->setAuthentication($origin, $this->username, $this->password);
55 31
        } elseif ($io->hasAuthentication($origin)) {
56 2
            $auth = $io->getAuthentication($origin);
57 2
            $this->username = $auth['username'];
58 2
            $this->password = $auth['password'];
59
        }
60 33
    }
61
62
    /**
63
     * @param string $url
64
     */
65 33
    public function importURL($url)
66
    {
67 33
        $struct = parse_url($url);
68
        // @codeCoverageIgnoreStart
69
        if (! $struct) {
70
            throw new \InvalidArgumentException("$url is not valid URL");
71
        }
72
        // @codeCoverageIgnoreEnd
73
74 33
        $this->scheme = self::setOr($struct, 'scheme', $this->scheme);
75 33
        $this->host = self::setOr($struct, 'host', $this->host);
76 33
        $this->port = self::setOr($struct, 'port', null);
77 33
        $this->path = self::setOr($struct, 'path', '');
78 33
        $this->username = self::setOr($struct, 'user', null);
79 33
        $this->password = self::setOr($struct, 'pass', null);
80
81 33
        if (! empty($struct['query'])) {
82 9
            parse_str($struct['query'], $this->query);
83
        }
84 33
    }
85
86
87
    /**
88
     * @param array $struct
89
     * @param string $key
90
     * @param string $default
91
     * @return mixed
92
     */
93 33
    private static function setOr(array $struct, $key, $default = null)
94
    {
95 33
        if (!empty($struct[$key])) {
96 33
            return $struct[$key];
97
        }
98
99 33
        return $default;
100
    }
101
102
    /**
103
     * process option for RemortFileSystem
104
     * @param array $options
105
     * @return void
106
     */
107 7
    public function processRFSOption(array $options)
108
    {
109 7
        if (isset($options[static::TOKEN_LABEL])) {
110 1
            $this->query['access_token'] = $options[static::TOKEN_LABEL];
111
        }
112 7
    }
113
114
    /**
115
     * @return array
116
     */
117 10
    public function getCurlOpts()
118
    {
119 10
        $headers = $this->headers;
120 10
        if ($this->username && $this->password) {
121 1
            foreach ($headers as $i => $header) {
122
                if (0 === strpos($header, 'Authentication:')) {
123
                    unset($headers[$i]);
124
                }
125
            }
126 1
            $headers[] = 'Authentication: Basic ' . base64_encode("$this->username:$this->password");
127
        }
128
129 10
        $curlOpts = $this->curlOpts + array(
130 10
            CURLOPT_HTTPGET => true,
131 10
            CURLOPT_FOLLOWLOCATION => true,
132 10
            CURLOPT_MAXREDIRS => 20,
133 10
            CURLOPT_ENCODING => 'gzip',
134 10
            CURLOPT_HTTPHEADER => $headers,
135 10
            CURLOPT_USERAGENT => $this->genUA(),
136 10
            CURLOPT_VERBOSE => (bool)$this->verbose,
137 10
            CURLOPT_URL => $this->getUrl(),
138
        );
139 10
        unset($curlOpts[CURLOPT_USERPWD]);
140
141 10
        return $curlOpts;
142
    }
143
144 17
    public function getURL()
145
    {
146 17
        if ($this->scheme) {
147 17
            $url = "$this->scheme://";
148
        } else {
149 1
            $url = '';
150
        }
151 17
        $url .= $this->host;
152
153 17
        if ($this->port) {
154 9
            $url .= ":$this->port";
155
        }
156
157 17
        $url .= $this->path;
158
159 17
        if ($this->query) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->query of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
160 9
            $url .= '?' . http_build_query($this->query);
161
        }
162
163 17
        return $url;
164
    }
165
166 5
    public function setConfig(CConfig $config)
167
    {
168 5
        $this->config = $config;
169 5
    }
170
171 6
    public function promptAuth(HttpGetResponse $res, IO\IOInterface $io)
172
    {
173 6
        $httpCode = $res->info['http_code'];
174
        // 404s are only handled for github
175 6
        if (404 === $httpCode) {
176 1
            return false;
177
        }
178
179
        // fail if the console is not interactive
180 5
        if (!$io->isInteractive() && ($httpCode === 401 || $httpCode === 403)) {
181 3
            $message = "The '{$this->getURL()}' URL required authentication.\nYou must be using the interactive console to authenticate";
182 3
            throw new Downloader\TransportException($message, $httpCode);
183
        }
184
185
        // fail if we already have auth
186 2
        if ($io->hasAuthentication($this->origin)) {
187 1
            throw new Downloader\TransportException("Invalid credentials for '{$this->getURL()}', aborting.", $httpCode);
188
        }
189
190 1
        $io->overwrite("    Authentication required (<info>$this->host</info>):");
191 1
        $username = $io->ask('      Username: ');
192 1
        $password = $io->askAndHideAnswer('      Password: ');
193 1
        $io->setAuthentication($this->origin, $username, $password);
194 1
        return true;
195
    }
196
197
    /**
198
     * @internal
199
     */
200 2
    public function promptAuthWithUtil($privateCode, $util, HttpGetResponse $res, IO\IOInterface $io)
201
    {
202 2
        $httpCode = $res->info['http_code'];
203 2
        $message = "\nCould not fetch {$this->getURL()}, enter your $this->origin credentials ";
204 2
        if ($privateCode === $httpCode) {
205 1
            $message .= 'to access private repos';
206
        } else {
207 1
            $message .= 'to go over the API rate limit';
208
        }
209 2
        if ($util->authorizeOAuth($this->origin)) {
210 1
            return true;
211
        }
212 2
        if ($io->isInteractive() &&
213 2
            $util->authorizeOAuthInteractively($this->origin, $message)) {
214 1
            return true;
215
        }
216
217 1
        throw new Downloader\TransportException("Could not authenticate against $this->origin", $httpCode);
218
    }
219
220 10
    public static function genUA()
221
    {
222 10
        static $ua;
223 10
        if ($ua) {
224 9
            return $ua;
225
        }
226 2
        $phpVersion = defined('HHVM_VERSION') ? 'HHVM ' . HHVM_VERSION : 'PHP ' . PHP_VERSION;
227
228 2
        return $ua = sprintf(
229 2
            'Composer/%s (%s; %s; %s)',
230 2
            str_replace('@package_version@', 'source', Composer::VERSION),
231 2
            php_uname('s'),
232 2
            php_uname('r'),
233
            $phpVersion
234
        );
235
    }
236
}
237