Completed
Pull Request — master (#30)
by
unknown
06:05
created

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