Completed
Pull Request — master (#44)
by Hiraku
02:30
created

CurlRemoteFilesystem::getLastHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * hirak/prestissimo
4
 * @author Hiraku NAKANO
5
 * @license MIT https://github.com/hirak/prestissimo
6
 */
7
namespace Hirak\Prestissimo;
8
9
use Composer\Config as CConfig;
10
use Composer\IO;
11
use Composer\Downloader;
12
use Composer\Util;
13
14
/**
15
 * yet another implementation about Composer\Util\RemoteFilesystem
16
 * non thread safe
17
 */
18
class CurlRemoteFilesystem extends Util\RemoteFilesystem
19
{
20
    protected $io;
21
    protected $config;
22
    protected $options;
23
24
    protected $retryAuthFailure = true;
25
26
    protected $pluginConfig;
27
28
    private $_lastHeaders = array();
29
30
    // global flags
31
    private $_retry = false;
32
33
    /** @var Aspects\JoinPoint */
34
    public $onPreDownload;
35
36
    /** @var Aspects\JoinPoint */
37
    public $onPostDownload;
38
39
    /**
40
     * @param IO\IOInterface $io
41
     * @param CConfig $config
42
     * @param array $options
43
     */
44 8
    public function __construct(IO\IOInterface $io, CConfig $config, array $options = array())
45
    {
46 8
        $this->io = $io;
47 8
        $this->config = $config;
48 8
        $this->options = $options;
49 8
    }
50
51 8
    public function setPluginConfig(array $pluginConfig)
52
    {
53 8
        $this->pluginConfig = $pluginConfig;
54 8
    }
55
56
    /**
57
     * Copy the remote file in local.
58
     *
59
     * @param string $origin    host/domain text
60
     * @param string $fileUrl   targeturl
61
     * @param string $fileName  the local filename
62
     * @param bool   $progress  Display the progression
63
     * @param array  $options   Additional context options
64
     *
65
     * @return bool true
66
     */
67 4
    public function copy($origin, $fileUrl, $fileName, $progress = true, $options = array())
68
    {
69 4
        $that = $this; // for PHP5.3
70
71
        return $this->fetch($origin, $fileUrl, $progress, $options, function($ch, $request) use ($that, $fileName) {
72 4
            $outputFile = new OutputFile($fileName);
73 4
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
74 4
            curl_setopt($ch, CURLOPT_FILE, $outputFile->getPointer());
75
76 4
            list(, $response) = $result = $that->exec($ch, $request);
77
78 2
            curl_setopt($ch, CURLOPT_FILE, STDOUT);
79
80 2
            if (200 === $response->info['http_code']) {
81 1
                $outputFile->setSuccess();
82 1
            }
83
84 2
            return $result;
85 4
        });
86
    }
87
88
    /**
89
     * Get the content.
90
     *
91
     * @param string $origin The origin URL
92
     * @param string $fileUrl   The file URL
93
     * @param bool   $progress  Display the progression
94
     * @param array  $options   Additional context options
95
     *
96
     * @return bool|string The content
97
     */
98 1
    public function getContents($origin, $fileUrl, $progress = true, $options = array())
99
    {
100 1
        $that = $this; // for PHP5.3
101
102 1
        return $this->fetch($origin, $fileUrl, $progress, $options, function($ch, $request) use ($that) {
103
            // This order is important.
104 1
            curl_setopt($ch, CURLOPT_FILE, STDOUT);
105 1
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
106
107 1
            return $that->exec($ch, $request);
108 1
        });
109
    }
110
111
    /**
112
     * @param string $origin
113
     * @param string $fileUrl
114
     * @param boolean $progress
115
     * @param \Closure $exec
116
     */
117 5
    protected function fetch($origin, $fileUrl, $progress, $options, $exec)
118
    {
119
        do {
120 5
            $this->_retry = false;
121
122 5
            $request = Factory::getHttpGetRequest($origin, $fileUrl, $this->io, $this->config, $this->pluginConfig);
123 5
            $this->onPreDownload = Factory::getPreEvent($request);
124 5
            $this->onPostDownload = Factory::getPostEvent($request);
125
126 5
            $options += $this->options;
127 5
            $request->processRFSOption($options);
128
129 5
            if ($this->io->isDebug()) {
130
                $this->io->write('Downloading ' . $fileUrl);
131
            }
132
133 5
            if ($progress) {
134 4
                $this->io->write("    Downloading: <comment>Connecting...</comment>", false);
135 4
                $request->curlOpts[CURLOPT_NOPROGRESS] = false;
136 4
                $request->curlOpts[CURLOPT_PROGRESSFUNCTION] = array($this, 'progress');
137 4
            } else {
138 1
                $request->curlOpts[CURLOPT_NOPROGRESS] = true;
139 1
                $request->curlOpts[CURLOPT_PROGRESSFUNCTION] = null;
140
            }
141
142 5
            $this->onPreDownload->notify();
143
144 5
            $opts = $request->getCurlOpts();
145 5
            $ch = Factory::getConnection($origin, isset($opts[CURLOPT_USERPWD]));
146
147 5
            curl_setopt_array($ch, $opts);
148
149 5
            list($execStatus,) = $exec($ch, $request);
150 3
        } while ($this->_retry);
151
152 3
        if ($progress) {
153 2
            $this->io->overwrite("    Downloading: <comment>100%</comment>");
154 2
        }
155
156 3
        return $execStatus;
157
    }
158
159
    /**
160
     * Retrieve the options set in the constructor
161
     *
162
     * @return array Options
163
     */
164 1
    public function getOptions()
165
    {
166 1
        return $this->options;
167
    }
168
169
    /**
170
     * Returns the headers of the last request
171
     *
172
     * @return array
173
     */
174 1
    public function getLastHeaders()
175
    {
176 1
        return $this->_lastHeaders;
177
    }
178
179
    /**
180
     * @internal
181
     * @param resource $ch
182
     * @param Aspects\HttpGetRequest $request
183
     * @return array {int, Aspects\HttpGetResponse}
184
     */
185 5
    public function exec($ch, Aspects\HttpGetRequest $request)
186
    {
187 5
        $this->_lastHeaders = array();
188 5
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'processHeader'));
189 5
        $execStatus = curl_exec($ch);
190
191 5
        $response = new Aspects\HttpGetResponse(
192 5
            curl_errno($ch),
193 5
            curl_error($ch),
194 5
            curl_getinfo($ch)
195 5
        );
196 5
        $this->onPostDownload->setResponse($response);
197 5
        $this->onPostDownload->notify();
198
199 5
        if ($response->needAuth()) {
200 3
            $this->promptAuth($request, $response);
201 1
        }
202
203 3
        return array($execStatus, $response);
204
    }
205
206
    /**
207
     * @internal
208
     */
209 4
    public function progress()
210
    {
211
        // @codeCoverageIgnoreStart
212
        if (PHP_VERSION_ID >= 50500) {
213
            list(, $downBytesMax, $downBytes,,) = func_get_args();
214
        } else {
215
            list($downBytesMax, $downBytes,,) = func_get_args();
216
        }
217
        // @codeCoverageIgnoreEnd
218
219 4
        if ($downBytesMax <= 0 || $downBytesMax < $downBytes) {
220 1
            return 0;
221
        }
222
223 4
        $progression = intval($downBytes / $downBytesMax * 100);
224 4
        $this->io->overwrite("    Downloading: <comment>$progression%</comment>", false);
225 4
        return 0;
226
    }
227
228
    /**
229
     * @internal
230
     * @param resource $ch
231
     * @param string $header
232
     * @return int
233
     */
234 5
    public function processHeader($ch, $header)
235
    {
236 5
        $this->_lastHeaders[] = trim($header);
237 5
        return strlen($header);
238
    }
239
240 3
    protected function promptAuth(Aspects\HttpGetRequest $req, Aspects\HttpGetResponse $res)
241
    {
242 3
        $this->_retry = $req->promptAuth($res, $this->config, $this->io);
243 1
    }
244
}
245