Completed
Push — 1.0 ( 0e2cc5...14cdbe )
by Bernhard
02:42
created

AbstractPathMappingRepository   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 302
Duplicated Lines 3.64 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 93.52%

Importance

Changes 12
Bugs 4 Features 5
Metric Value
wmc 35
c 12
b 4
f 5
lcom 1
cbo 13
dl 11
loc 302
ccs 101
cts 108
cp 0.9352
rs 9

16 Methods

Rating   Name   Duplication   Size   Complexity  
A add() 11 20 3
addFilesystemResource() 0 1 ?
addLinkResource() 0 1 ?
A clear() 0 10 1
A ensureDirectoryExists() 0 13 3
A createRoot() 0 8 2
A countStore() 0 8 2
A sortStore() 0 8 2
A createLinkResource() 0 7 1
A createFilesystemResource() 0 21 4
A createVirtualResource() 0 7 1
A resolveRelativePath() 0 9 2
A resolveRelativePaths() 0 8 2
A createResource() 0 18 4
A __construct() 0 9 1
C addResource() 0 28 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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