Completed
Pull Request — master (#44)
by Hiraku
08:54 queued 06:28
created

CurlRemoteFilesystem::processHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 5
ccs 0
cts 0
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 2
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
    public function __construct(IO\IOInterface $io, CConfig $config, array $options = array())
45 8
    {
46
        $this->io = $io;
47 8
        $this->config = $config;
48 8
        $this->options = $options;
49 8
    }
50 8
51
    public function setPluginConfig(array $pluginConfig)
52 8
    {
53
        $this->pluginConfig = $pluginConfig;
54 8
    }
55 8
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
    public function copy($origin, $fileUrl, $fileName, $progress = true, $options = array())
68 4
    {
69
        $that = $this; // for PHP5.3
70 4
71
        return $this->fetch($origin, $fileUrl, $progress, $options, function($ch, $request) use ($that, $fileName) {
72
            $outputFile = new OutputFile($fileName);
73 4
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
74 4
            curl_setopt($ch, CURLOPT_FILE, $outputFile->getPointer());
75 4
76
            list(, $response) = $result = $that->exec($ch, $request);
77 4
78
            curl_setopt($ch, CURLOPT_FILE, STDOUT);
79 2
80
            if (200 === $response->info['http_code']) {
81 2
                $outputFile->setSuccess();
82 1
            }
83 1
84
            return $result;
85 2
        });
86 4
    }
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
    public function getContents($origin, $fileUrl, $progress = true, $options = array())
99 1
    {
100
        $that = $this; // for PHP5.3
101 1
102
        return $this->fetch($origin, $fileUrl, $progress, $options, function($ch, $request) use ($that) {
103 1
            // This order is important.
104
            curl_setopt($ch, CURLOPT_FILE, STDOUT);
105 1
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
106 1
107
            return $that->exec($ch, $request);
108 1
        });
109 1
    }
110
111
    /**
112
     * @param string $origin
113
     * @param string $fileUrl
114
     * @param boolean $progress
115
     * @param \Closure $exec
116
     */
117
    protected function fetch($origin, $fileUrl, $progress, $options, $exec)
118 5
    {
119
        do {
120
            $this->_retry = false;
121 5
122
            $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 5
126 5
            $options += $this->options;
127 5
            $request->processRFSOption($options);
128 5
129 5
            if ($this->io->isDebug()) {
130 5
                $this->io->write('Downloading ' . $fileUrl);
131
            }
132
133
            if ($progress) {
134 5
                $this->io->write("    Downloading: <comment>Connecting...</comment>", false);
135
                $request->curlOpts[CURLOPT_NOPROGRESS] = false;
136 5
                $request->curlOpts[CURLOPT_PROGRESSFUNCTION] = array($this, 'progress');
137
            } else {
138
                $request->curlOpts[CURLOPT_NOPROGRESS] = true;
139 5
                $request->curlOpts[CURLOPT_PROGRESSFUNCTION] = null;
140
            }
141
142
            $this->onPreDownload->notify();
143 5
144
            $opts = $request->getCurlOpts();
145
            $ch = Factory::getConnection($origin, isset($opts[CURLOPT_USERPWD]));
146
147 5
            curl_setopt_array($ch, $opts);
148 4
149 4
            list($execStatus,) = $exec($ch, $request);
150 4
        } while ($this->_retry);
151 4
152 1
        if ($progress) {
153 1
            $this->io->overwrite("    Downloading: <comment>100%</comment>");
154
        }
155
156 5
        return $execStatus;
157
    }
158 5
159 5
    /**
160
     * Retrieve the options set in the constructor
161 5
     *
162
     * @return array Options
163
     */
164 5
    public function getOptions()
165
    {
166
        return $this->options;
167 5
    }
168
169
    /**
170
     * Returns the headers of the last request
171 5
     *
172
     * @return array
173 5
     */
174 3
    public function getLastHeaders()
175
    {
176 3
        return $this->_lastHeaders;
177 2
    }
178 2
179
    /**
180 3
     * @internal
181
     * @param resource $ch
182
     * @param Aspects\HttpGetRequest $request
183
     * @return array {int, Aspects\HttpGetResponse}
184
     */
185
    public function exec($ch, Aspects\HttpGetRequest $request)
186
    {
187
        $this->_lastHeaders = array();
188 1
        curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'processHeader'));
189
        $execStatus = curl_exec($ch);
190 1
191
        $response = new Aspects\HttpGetResponse(
192
            curl_errno($ch),
193
            curl_error($ch),
194
            curl_getinfo($ch)
195
        );
196
        $this->onPostDownload->setResponse($response);
197
        $this->onPostDownload->notify();
198 1
199
        if ($response->needAuth()) {
200 1
            $this->promptAuth($request, $response);
201
        }
202
203
        return array($execStatus, $response);
204
    }
205
206
    /**
207
     * @internal
208
     */
209 5
    public function progress()
210
    {
211 5
        // @codeCoverageIgnoreStart
212 5
        if (PHP_VERSION_ID >= 50500) {
213 5
            list(, $downBytesMax, $downBytes,,) = func_get_args();
214
        } else {
215 5
            list($downBytesMax, $downBytes,,) = func_get_args();
216 5
        }
217 5
        // @codeCoverageIgnoreEnd
218 5
219 5
        if ($downBytesMax <= 0 || $downBytesMax < $downBytes) {
220 5
            return 0;
221 5
        }
222
223 5
        $progression = intval($downBytes / $downBytesMax * 100);
224 3
        $this->io->overwrite("    Downloading: <comment>$progression%</comment>", false);
225 1
        return 0;
226
    }
227 3
228
    /**
229
     * @internal
230
     * @param resource $ch
231
     * @param string $header
232
     * @return int
233 4
     */
234
    public function processHeader($ch, $header)
235
    {
236
        $this->_lastHeaders[] = trim($header);
237
        return strlen($header);
238
    }
239
240
    protected function promptAuth(Aspects\HttpGetRequest $req, Aspects\HttpGetResponse $res)
241
    {
242
        $this->_retry = $req->promptAuth($res, $this->config, $this->io);
243 4
    }
244
}
245