Completed
Pull Request — master (#24)
by
unknown
08:13
created

WebDAVAdapter::setUseStreamedCopy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace League\Flysystem\WebDAV;
4
5
use League\Flysystem\Adapter\AbstractAdapter;
6
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
7
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
8
use League\Flysystem\Adapter\Polyfill\StreamedTrait;
9
use League\Flysystem\Config;
10
use League\Flysystem\Util;
11
use LogicException;
12
use Sabre\DAV\Client;
13
use Sabre\DAV\Exception;
14
use Sabre\DAV\Exception\NotFound;
15
use Sabre\HTTP\ClientHttpException;
16
use Sabre\HTTP\HttpException;
17
18
class WebDAVAdapter extends AbstractAdapter
19
{
20
    use StreamedTrait;
21
    use StreamedCopyTrait {
22
        StreamedCopyTrait::copy as streamedCopy;
23
    }
24
    use NotSupportingVisibilityTrait;
25
26
    /**
27
     * @var array
28
     */
29
    protected static $resultMap = [
30
        '{DAV:}getcontentlength' => 'size',
31
        '{DAV:}getcontenttype' => 'mimetype',
32
        'content-length' => 'size',
33
        'content-type' => 'mimetype',
34
    ];
35
36
    /**
37
     * @var Client
38
     */
39
    protected $client;
40
41
    /**
42
     * @var bool
43
     */
44
    protected $useStreamedCopy = true;
45
46
    /**
47
     * Constructor.
48
     *
49
     * @param Client $client
50
     * @param string $prefix
51
     * @param bool $useStreamedCopy
52
     */
53
    public function __construct(Client $client, $prefix = null, $useStreamedCopy = true)
54
    {
55
        $this->client = $client;
56
        $this->setPathPrefix($prefix);
57
        $this->setUseStreamedCopy($useStreamedCopy);
58
    }
59
60
    /**
61
     * url encode a path
62
     *
63
     * @param string $path
64
     *
65
     * @return string
66
     */
67
    protected function encodePath($path)
68
	{
69
		$a = explode('/', $path);
70
		for ($i=0; $i<count($a); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
71
			$a[$i] = rawurlencode($a[$i]);
72
		}
73
		return implode('/', $a);
74
	}
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function getMetadata($path)
80
    {
81
        $location = $this->applyPathPrefix($this->encodePath($path));
82
83
        try {
84
            $result = $this->client->propFind($location, [
85
                '{DAV:}displayname',
86
                '{DAV:}getcontentlength',
87
                '{DAV:}getcontenttype',
88
                '{DAV:}getlastmodified',
89
            ]);
90
91
            return $this->normalizeObject($result, $path);
92
        } catch (Exception $e) {
93
            return false;
94
        } catch (HttpException $e) {
95
            return false;
96
        }
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function has($path)
103
    {
104
        return $this->getMetadata($path);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function read($path)
111
    {
112
        $location = $this->applyPathPrefix($this->encodePath($path));
113
114
        try {
115
            $response = $this->client->request('GET', $location);
116
117
            if ($response['statusCode'] !== 200) {
118
                return false;
119
            }
120
121
            return array_merge([
122
                'contents' => $response['body'],
123
                'timestamp' => strtotime(is_array($response['headers']['last-modified'])
124
                    ? current($response['headers']['last-modified'])
125
                    : $response['headers']['last-modified']),
126
                'path' => $path,
127
            ], Util::map($response['headers'], static::$resultMap));
128
        } catch (Exception $e) {
129
            return false;
130
        }
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function write($path, $contents, Config $config)
137
    {
138
        if (!$this->createDir(dirname($path), $config)) {
139
            return false;
140
        }
141
142
        $location = $this->applyPathPrefix($this->encodePath($path));
143
        $response = $this->client->request('PUT', $location, $contents);
144
145
        if ($response['statusCode'] >= 400) {
146
            return false;
147
        }
148
149
        $result = compact('path', 'contents');
150
151
        if ($config->get('visibility')) {
152
            throw new LogicException(__CLASS__.' does not support visibility settings.');
153
        }
154
155
        return $result;
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function update($path, $contents, Config $config)
162
    {
163
        return $this->write($path, $contents, $config);
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function rename($path, $newpath)
170
    {
171
        $location = $this->applyPathPrefix($this->encodePath($path));
172
        $newLocation = $this->applyPathPrefix($this->encodePath($newpath));
173
174
        try {
175
            $response = $this->client->request('MOVE', '/'.ltrim($location, '/'), null, [
176
                'Destination' => '/'.ltrim($newLocation, '/'),
177
            ]);
178
179
            if ($response['statusCode'] >= 200 && $response['statusCode'] < 300) {
180
                return true;
181
            }
182
        } catch (NotFound $e) {
183
            // Would have returned false here, but would be redundant
184
        }
185
186
        return false;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function copy($path, $newpath)
193
    {
194
        if ($this->useStreamedCopy === true) {
195
            return $this->streamedCopy($path, $newpath);
196
        } else {
197
            return $this->nativeCopy($path, $newpath);
198
        }
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function delete($path)
205
    {
206
        $location = $this->applyPathPrefix($this->encodePath($path));
207
208
        try {
209
            $this->client->request('DELETE', $location);
210
211
            return true;
212
        } catch (NotFound $e) {
213
            return false;
214
        }
215
    }
216
217
    /**
218
     * {@inheritdoc}
219
     */
220
    public function createDir($path, Config $config)
221
    {
222
        $encodedPath = $this->encodePath($path);
223
        $path = trim($path, '/');
224
225
        $result = compact('path') + ['type' => 'dir'];
226
227
        if ($path === '' || $path === '.' || $this->has($path)) {
228
            return $result;
229
        }
230
231
        $directories = explode('/', $path);
232
        if (count($directories) > 1) {
233
            $parentDirectories = array_splice($directories, 0, count($directories) - 1);
234
            if (!$this->createDir(implode('/', $parentDirectories), $config)) {
235
                return false;
236
            }
237
        }
238
239
        $location = $this->applyPathPrefix($encodedPath);
240
        $response = $this->client->request('MKCOL', $location);
241
242
        if ($response['statusCode'] !== 201) {
243
            return false;
244
        }
245
246
        return $result;
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function deleteDir($dirname)
253
    {
254
        return $this->delete($dirname);
255
    }
256
257
    /**
258
     * {@inheritdoc}
259
     */
260
    public function listContents($directory = '', $recursive = false)
261
    {
262
        $location = $this->applyPathPrefix($this->encodePath($directory));
263
        $response = $this->client->propFind($location . '/', [
264
            '{DAV:}displayname',
265
            '{DAV:}getcontentlength',
266
            '{DAV:}getcontenttype',
267
            '{DAV:}getlastmodified',
268
        ], 1);
269
270
        array_shift($response);
271
        $result = [];
272
273
        foreach ($response as $path => $object) {
274
            $path = urldecode($this->removePathPrefix($path));
275
            $object = $this->normalizeObject($object, $path);
276
            $result[] = $object;
277
278
            if ($recursive && $object['type'] === 'dir') {
279
                $result = array_merge($result, $this->listContents($object['path'], true));
280
            }
281
        }
282
283
        return $result;
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function getSize($path)
290
    {
291
        return $this->getMetadata($path);
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    public function getTimestamp($path)
298
    {
299
        return $this->getMetadata($path);
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function getMimetype($path)
306
    {
307
        return $this->getMetadata($path);
308
    }
309
310
    /**
311
     * @return boolean
312
     */
313
    public function getUseStreamedCopy()
314
    {
315
        return $this->useStreamedCopy;
316
    }
317
318
    /**
319
     * @param boolean $useStreamedCopy
320
     */
321
    public function setUseStreamedCopy($useStreamedCopy)
322
    {
323
        $this->useStreamedCopy = (bool)$useStreamedCopy;
324
    }
325
326
    /**
327
     * Copy a file through WebDav COPY method.
328
     *
329
     * @param string $path
330
     * @param string $newPath
331
     *
332
     * @return bool
333
     */
334
    protected function nativeCopy($path, $newPath)
335
    {
336
        if (!$this->createDir(dirname($newPath), new Config())) {
337
            return false;
338
        }
339
340
        $location = $this->applyPathPrefix($this->encodePath($path));
341
        $newLocation = $this->applyPathPrefix($this->encodePath($newPath));
342
343
        try {
344
            $destination = $this->client->getAbsoluteUrl($newLocation);
345
            $response = $this->client->request('COPY', '/'.ltrim($location, '/'), null, [
346
                'Destination' => $destination,
347
            ]);
348
349
            if ($response['statusCode'] >= 200 && $response['statusCode'] < 300) {
350
                return true;
351
            }
352
        } catch (NotFound $e) {
353
            // Would have returned false here, but would be redundant
354
        }
355
356
        return false;
357
    }
358
359
    /**
360
     * Normalise a WebDAV repsonse object.
361
     *
362
     * @param array  $object
363
     * @param string $path
364
     *
365
     * @return array
366
     */
367
    protected function normalizeObject(array $object, $path)
368
    {
369
        if (! isset($object['{DAV:}getcontentlength']) or $object['{DAV:}getcontentlength'] == "") {
370
            return ['type' => 'dir', 'path' => trim($path, '/')];
371
        }
372
373
        $result = Util::map($object, static::$resultMap);
374
375
        if (isset($object['{DAV:}getlastmodified'])) {
376
            $result['timestamp'] = strtotime($object['{DAV:}getlastmodified']);
377
        }
378
379
        $result['type'] = 'file';
380
        $result['path'] = trim($path, '/');
381
382
        return $result;
383
    }
384
}
385