Completed
Pull Request — master (#54)
by Hiraku
02:14
created

CurlRemoteFilesystem::processHeader()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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