Failed Conditions
Pull Request — 1.0 (#64)
by Titouan
02:55
created

AbstractPathMappingRepository::isLinkTarget()   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 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the puli/repository package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Repository;
13
14
use Puli\Repository\Api\Resource\FilesystemResource;
15
use Puli\Repository\Api\Resource\PuliResource;
16
use Puli\Repository\Api\ResourceCollection;
17
use Puli\Repository\Api\UnsupportedResourceException;
18
use Puli\Repository\Resource\DirectoryResource;
19
use Puli\Repository\Resource\FileResource;
20
use Puli\Repository\Resource\GenericResource;
21
use Puli\Repository\Resource\LinkResource;
22
use RuntimeException;
23
use Webmozart\KeyValueStore\Api\CountableStore;
24
use Webmozart\KeyValueStore\Api\KeyValueStore;
25
use Webmozart\KeyValueStore\Api\SortableStore;
26
use Webmozart\KeyValueStore\Decorator\CountableDecorator;
27
use Webmozart\KeyValueStore\Decorator\SortableDecorator;
28
use Webmozart\PathUtil\Path;
29
30
/**
31
 * Abstract base for Path mapping repositories.
32
 *
33
 * @since  1.0
34
 *
35
 * @author Bernhard Schussek <[email protected]>
36
 * @author Titouan Galopin <[email protected]>
37
 */
38
abstract class AbstractPathMappingRepository extends AbstractRepository
39
{
40
    /**
41
     * @var KeyValueStore
42
     */
43
    protected $store;
44
45
    /**
46
     * @var string
47
     */
48
    protected $baseDirectory;
49
50
    /**
51
     * Creates a new repository.
52
     *
53
     * @param KeyValueStore $store         The store of all the paths.
54
     * @param string        $baseDirectory The store of all the paths.
55
     */
56 168
    public function __construct(KeyValueStore $store, $baseDirectory)
57
    {
58 168
        $this->store = $store;
59 168
        $this->baseDirectory = $baseDirectory;
60
61 168
        $this->createRoot();
62 168
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 146
    public function add($path, $resource)
68
    {
69 146
        $path = $this->sanitizePath($path);
70
71 140 View Code Duplication
        if ($resource instanceof ResourceCollection) {
72 2
            $this->ensureDirectoryExists($path);
73
74 2
            foreach ($resource as $child) {
75 2
                $this->addResource($path.'/'.$child->getName(), $child);
76 2
            }
77
78 2
            $this->sortStore();
79
80 2
            return;
81
        }
82
83 138
        $this->ensureDirectoryExists(Path::getDirectory($path));
84 138
        $this->addResource($path, $resource);
85 134
        $this->sortStore();
86 134
    }
87
88
    /**
89
     * Add the resource (internal method after checks of add()).
90
     *
91
     * @param string       $path
92
     * @param PuliResource $resource
93
     */
94 140
    private function addResource($path, $resource)
95
    {
96 140
        if (!($resource instanceof FilesystemResource || $resource instanceof LinkResource)) {
97 2
            throw new UnsupportedResourceException(sprintf(
98 2
                'PathMapping repositories only supports FilesystemResource and LinkedResource. Got: %s',
99 2
                is_object($resource) ? get_class($resource) : gettype($resource)
100 2
            ));
101
        }
102
103
        // Don't modify resources attached to other repositories
104 138
        if ($resource->isAttached()) {
105 2
            $resource = clone $resource;
106 2
        }
107
108 138
        if ($resource instanceof LinkResource) {
109 6
            $this->addLinkResource($path, $resource);
110 138
        } elseif (Path::isBasePath($this->baseDirectory, $resource->getFilesystemPath())) {
111 136
            $this->addFilesystemResource($path, $resource);
112 136
        } else {
113 2
            throw new UnsupportedResourceException(sprintf(
114 2
                'Can only add resources from %s. Tried to add %s.',
115 2
                $this->baseDirectory,
116 2
                $resource->getFilesystemPath()
117 2
            ));
118
        }
119 136
    }
120
121
    /**
122
     * Add the filesystem resource.
123
     *
124
     * @param string             $path
125
     * @param FilesystemResource $resource
126
     */
127
    abstract protected function addFilesystemResource($path, FilesystemResource $resource);
128
129
    /**
130
     * Add the link resource.
131
     *
132
     * @param string       $path
133
     * @param LinkResource $resource
134
     */
135
    abstract protected function addLinkResource($path, LinkResource $resource);
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 2
    public function clear()
141
    {
142
        // Subtract root
143 2
        $removed = $this->countStore() - 1;
144
145 2
        $this->store->clear();
146 2
        $this->createRoot();
147
148 2
        return $removed;
149
    }
150
151
    /**
152
     * Recursively creates a directory for a path.
153
     *
154
     * @param string $path A directory path.
155
     */
156 140
    protected function ensureDirectoryExists($path)
157
    {
158 140
        if ($this->store->exists($path)) {
159 140
            return;
160
        }
161
162
        // Recursively initialize parent directories
163 42
        if ('/' !== $path) {
164 42
            $this->ensureDirectoryExists(Path::getDirectory($path));
165 42
        }
166
167 42
        $this->store->set($path, null);
168 42
    }
169
170
    /**
171
     * Create the repository root.
172
     */
173 168
    protected function createRoot()
174
    {
175 168
        if ($this->store->exists('/')) {
176 6
            return;
177
        }
178
179 168
        $this->store->set('/', null);
180 168
    }
181
182
    /**
183
     * Count the number of elements in the store.
184
     *
185
     * @return int
186
     */
187 9
    protected function countStore()
188
    {
189 9
        if (!$this->store instanceof CountableStore) {
190
            $this->store = new CountableDecorator($this->store);
191
        }
192
193 9
        return $this->store->count();
194
    }
195
196
    /**
197
     * Sort the store by keys.
198
     */
199 136
    protected function sortStore()
200
    {
201 136
        if (!$this->store instanceof SortableStore) {
202
            $this->store = new SortableDecorator($this->store);
203
        }
204
205 136
        $this->store->sort();
206 136
    }
207
208
    /**
209
     * Create a filesystem or generic resource.
210
     *
211
     * @param string|null $filesystemPath
212
     *
213
     * @return DirectoryResource|FileResource|GenericResource
214
     */
215 89
    protected function createResource($filesystemPath, $path = null)
216
    {
217
        // Link resource
218 89
        if (0 === strpos($filesystemPath, 'l:')) {
219 6
            return $this->createLinkResource(substr($filesystemPath, 2), $path);
220
        }
221
222
        // Filesystem resource
223 89
        if (is_string($filesystemPath)) {
224 87
            $filesystemPath = $this->resolveRelativePath($filesystemPath);
225
226 87
            if (file_exists($filesystemPath)) {
227 87
                return $this->createFilesystemResource($filesystemPath, $path);
228
            }
229
        }
230
231 11
        return $this->createVirtualResource($path);
232
    }
233
234
    /**
235
     * Create a link resource to another resource of the repository.
236
     *
237
     * @param string      $targetPath The target path.
238
     * @param string|null $path       The repository path.
239
     *
240
     * @return LinkResource The link resource.
241
     *
242
     * @throws RuntimeException If the targeted resource does not exist.
243
     */
244 6
    protected function createLinkResource($targetPath, $path = null)
245
    {
246 6
        $resource = new LinkResource($targetPath);
247 6
        $resource->attachTo($this, $path);
248
249 6
        return $resource;
250
    }
251
252
    /**
253
     * Create a resource using its filesystem path.
254
     *
255
     * If the filesystem path is a directory, a DirectoryResource will be created.
256
     * If the filesystem path is a file, a FileResource will be created.
257
     * If the filesystem does not exists, a GenericResource will be created.
258
     *
259
     * @param string      $filesystemPath The filesystem path.
260
     * @param string|null $path           The repository path.
261
     *
262
     * @return DirectoryResource|FileResource The created resource.
263
     *
264
     * @throws RuntimeException If the file / directory does not exist.
265
     */
266 87
    protected function createFilesystemResource($filesystemPath, $path = null)
267
    {
268 87
        $resource = null;
269
270 87
        if (is_dir($filesystemPath)) {
271 53
            $resource = new DirectoryResource($filesystemPath);
272 87
        } elseif (is_file($filesystemPath)) {
273 62
            $resource = new FileResource($filesystemPath);
274 62
        }
275
276 87
        if ($resource) {
277 87
            $resource->attachTo($this, $path);
278
279 87
            return $resource;
280
        }
281
282
        throw new RuntimeException(sprintf(
283
            'Trying to create a FilesystemResource on a non-existing file or directory "%s"',
284
            $filesystemPath
285
        ));
286
    }
287
288
    /**
289
     * @param string|null $path
290
     *
291
     * @return GenericResource
292
     */
293 11
    protected function createVirtualResource($path = null)
294
    {
295 11
        $resource = new GenericResource();
296 11
        $resource->attachTo($this, $path);
297
298 11
        return $resource;
299
    }
300
301
    /**
302
     * Transform a relative path into an absolute path.
303
     *
304
     * @param string $relativePath
305
     *
306
     * @return string
307
     */
308 92
    protected function resolveRelativePath($relativePath)
309
    {
310 92
        if (0 === strpos($relativePath, 'l:')) {
311
            // Link
312 3
            return $relativePath;
313
        }
314
315 92
        return Path::makeAbsolute($relativePath, $this->baseDirectory);
316
    }
317
318
    /**
319
     * Transform a collection of relative paths into a collection of absolute paths.
320
     *
321
     * @param string[] $relativePaths
322
     *
323
     * @return string[]
324
     */
325 56
    protected function resolveRelativePaths($relativePaths)
326
    {
327 56
        foreach ($relativePaths as $key => $relativePath) {
328 56
            $relativePaths[$key] = $this->resolveRelativePath($relativePath);
329 56
        }
330
331 56
        return $relativePaths;
332
    }
333
334
    /**
335
     * Check if the given target path represents a link.
336
     *
337
     * @param string $targetPath
338
     *
339
     * @return bool
340
     */
341 10
    public static function isLinkTarget($targetPath)
342
    {
343 10
        return 0 === strpos($targetPath, 'l:');
344
    }
345
346
    /**
347
     * Add a link tag at the beginning of the given target path to represent a link.
348
     *
349
     * @param string $targetPath
350
     *
351
     * @return string
352
     */
353 6
    public static function createLinkTarget($targetPath)
354
    {
355 6
        if (! self::isLinkTarget($targetPath)) {
356 6
            $targetPath = 'l:'.$targetPath;
357 6
        }
358
359 6
        return $targetPath;
360
    }
361
362
    /**
363
     * Get the target path of a link by removing the link tag.
364
     *
365
     * @param string $targetPath
366
     *
367
     * @return string
368
     */
369 2
    public static function getLinkTarget($targetPath)
370
    {
371 2
        if (self::isLinkTarget($targetPath)) {
372 2
            $targetPath = substr($targetPath, 2);
373 2
        }
374
375 2
        return $targetPath;
376
    }
377
}
378