Completed
Push — 1.0 ( ca57a4...6b0f55 )
by Bernhard
02:48
created

createVirtualResource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4286
cc 1
eloc 4
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 169
    public function __construct(KeyValueStore $store, $baseDirectory)
57
    {
58 169
        $this->store = $store;
59 169
        $this->baseDirectory = $baseDirectory;
60
61 169
        $this->createRoot();
62 169
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 144
    public function add($path, $resource)
68
    {
69 144
        $path = $this->sanitizePath($path);
70
71 138 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 136
        $this->ensureDirectoryExists(Path::getDirectory($path));
84 136
        $this->addResource($path, $resource);
85 132
        $this->sortStore();
86 132
    }
87
88
    /**
89
     * Add the resource (internal method after checks of add()).
90
     *
91
     * @param string       $path
92
     * @param PuliResource $resource
93
     */
94 138
    private function addResource($path, $resource)
95
    {
96 138
        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 136
        if ($resource->isAttached()) {
105 2
            $resource = clone $resource;
106 2
        }
107
108 136
        if ($resource instanceof LinkResource) {
109 4
            $this->addLinkResource($path, $resource);
110 136
        } elseif (Path::isBasePath($this->baseDirectory, $resource->getFilesystemPath())) {
111 134
            $this->addFilesystemResource($path, $resource);
112 134
        } 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 134
    }
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 138
    protected function ensureDirectoryExists($path)
157
    {
158 138
        if ($this->store->exists($path)) {
159 138
            return;
160
        }
161
162
        // Recursively initialize parent directories
163 40
        if ('/' !== $path) {
164 40
            $this->ensureDirectoryExists(Path::getDirectory($path));
165 40
        }
166
167 40
        $this->store->set($path, null);
168 40
    }
169
170
    /**
171
     * Create the repository root.
172
     */
173 169
    protected function createRoot()
174
    {
175 169
        if ($this->store->exists('/')) {
176 70
            return;
177
        }
178
179 169
        $this->store->set('/', null);
180 169
    }
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 134
    protected function sortStore()
200
    {
201 134
        if (!$this->store instanceof SortableStore) {
202
            $this->store = new SortableDecorator($this->store);
203
        }
204
205 134
        $this->store->sort();
206 134
    }
207
208
    /**
209
     * Create a filesystem or generic resource.
210
     *
211
     * @param string|null $filesystemPath
212
     *
213
     * @return DirectoryResource|FileResource|GenericResource
214
     */
215 90
    protected function createResource($filesystemPath, $path = null)
216
    {
217
        // Link resource
218 90
        if (0 === strpos($filesystemPath, 'l:')) {
219 4
            return $this->createLinkResource(substr($filesystemPath, 2), $path);
220
        }
221
222
        // Filesystem resource
223 90
        if (is_string($filesystemPath)) {
224 88
            $filesystemPath = $this->resolveRelativePath($filesystemPath);
225
226 88
            if (file_exists($filesystemPath)) {
227 88
                return $this->createFilesystemResource($filesystemPath, $path);
228
            }
229 1
        }
230
231 12
        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 4
    protected function createLinkResource($targetPath, $path = null)
245
    {
246 4
        $resource = new LinkResource($targetPath);
247 4
        $resource->attachTo($this, $path);
248
249 4
        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 88
    protected function createFilesystemResource($filesystemPath, $path = null)
267
    {
268 88
        $resource = null;
269
270 88
        if (is_dir($filesystemPath)) {
271 51
            $resource = new DirectoryResource($filesystemPath);
272 88
        } elseif (is_file($filesystemPath)) {
273 63
            $resource = new FileResource($filesystemPath);
274 63
        }
275
276 88
        if ($resource) {
277 88
            $resource->attachTo($this, $path);
278
279 88
            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 12
    protected function createVirtualResource($path = null)
294
    {
295 12
        $resource = new GenericResource();
296 12
        $resource->attachTo($this, $path);
297
298 12
        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 93
    protected function resolveRelativePath($relativePath)
309
    {
310 93
        if (0 === strpos($relativePath, 'l:')) {
311
            // Link
312 2
            return $relativePath;
313
        }
314
315 93
        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 58
    protected function resolveRelativePaths($relativePaths)
326
    {
327 58
        foreach ($relativePaths as $key => $relativePath) {
328 58
            $relativePaths[$key] = $this->resolveRelativePath($relativePath);
329 58
        }
330
331 58
        return $relativePaths;
332
    }
333
}
334