Completed
Push — master ( 23332c...755174 )
by Joschi
03:30
created

FileAdapterStrategy::findObjectResourceLocators()   F

Complexity

Conditions 20
Paths > 20000

Size

Total Lines 89
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 63
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 20
eloc 54
c 1
b 0
f 1
nc 184320
nop 2
dl 0
loc 89
ccs 63
cts 63
cp 1
crap 20
rs 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Infrastructure
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Infrastructure\Repository;
38
39
use Apparat\Kernel\Ports\Kernel;
40
use Apparat\Object\Application\Repository\AbstractAdapterStrategy;
41
use Apparat\Object\Domain\Model\Object\Id;
42
use Apparat\Object\Domain\Model\Object\ObjectInterface;
43
use Apparat\Object\Domain\Model\Object\ResourceInterface;
44
use Apparat\Object\Domain\Model\Object\Revision;
45
use Apparat\Object\Domain\Model\Uri\LocatorInterface;
46
use Apparat\Object\Domain\Model\Uri\RepositoryLocator;
47
use Apparat\Object\Domain\Model\Uri\RepositoryLocatorInterface;
48
use Apparat\Object\Domain\Repository\AdapterStrategyInterface;
49
use Apparat\Object\Domain\Repository\RepositoryInterface;
50
use Apparat\Object\Domain\Repository\RuntimeException as DomainRepositoryRuntimeException;
51
use Apparat\Object\Domain\Repository\Selector;
52
use Apparat\Object\Domain\Repository\SelectorInterface;
53
use Apparat\Object\Infrastructure\Factory\ResourceFactory;
54
use Apparat\Object\Infrastructure\Utilities\File;
55
use Apparat\Object\Ports\Types\Object;
56
use Apparat\Resource\Domain\Model\Resource\AbstractResource;
57
use Apparat\Resource\Infrastructure\Io\File\AbstractFileReaderWriter;
58
use Apparat\Resource\Infrastructure\Io\File\Writer;
59
60
/**
61
 * File adapter strategy
62
 *
63
 * @package Apparat\Object
64
 * @subpackage Apparat\Object\Infrastructure
65
 */
66
class FileAdapterStrategy extends AbstractAdapterStrategy
67
{
68
    /**
69
     * Adapter strategy type
70
     *
71
     * @var string
72
     */
73
    const TYPE = 'file';
74
    /**
75
     * Glob visibilities
76
     *
77
     * @var array
78
     */
79
    protected static $globVisibilities = [
80
        SelectorInterface::VISIBLE => '[!.]',
81
        SelectorInterface::HIDDEN => '.',
82
        SelectorInterface::ALL => '{.,}',
83
    ];
84
    /**
85
     * Regex visibilities
86
     *
87
     * @var array
88
     */
89
    protected static $regexVisibilities = [
90
        SelectorInterface::VISIBLE => '',
91
        SelectorInterface::HIDDEN => '\\.',
92
        SelectorInterface::ALL => '\\.?',
93
    ];
94
    /**
95
     * Glob draft states
96
     *
97
     * @var array
98
     */
99
    protected static $globDrafts = [
100
        SelectorInterface::PUBLISHED => '[!.]',
101
        SelectorInterface::DRAFT => '.',
102
        SelectorInterface::ALL => '{.,}',
103
    ];
104
    /**
105
     * Regex draft states
106
     *
107
     * @var array
108
     */
109
    protected static $regexDrafts = [
110
        SelectorInterface::PUBLISHED => '',
111
        SelectorInterface::DRAFT => '\\.',
112
        SelectorInterface::ALL => '\\.?',
113
    ];
114
    /**
115
     * Configuration
116
     *
117
     * @var array
118
     */
119
    protected $config = null;
120
    /**
121
     * Root directory (without trailing directory separator)
122
     *
123
     * @var string
124
     */
125
    protected $root = null;
126
    /**
127
     * Configuration directory (including trailing directory separator)
128
     *
129
     * @var string
130
     */
131
    protected $configDir = null;
132
133
    /**
134
     * Adapter strategy constructor
135
     *
136
     * @param array $config Adapter strategy configuration
137
     * @throws InvalidArgumentException If the root directory configuration is empty
138
     * @throws InvalidArgumentException If the root directory configuration is invalid
139
     */
140 23
    public function __construct(array $config)
141
    {
142 23
        parent::__construct($config, ['root']);
143
144
        // If the root directory configuration is empty
145 22
        if (empty($this->config['root'])) {
146 1
            throw new InvalidArgumentException(
147 1
                'Empty file adapter strategy root',
148
                InvalidArgumentException::EMTPY_FILE_STRATEGY_ROOT
149 1
            );
150
        }
151
152
        // Get the real locator of the root directory
153 21
        $this->root = realpath($this->config['root']);
154
155
        // If the repository should be initialized
156 21
        if (!empty($this->config['init'])
157 21
            && (boolean)$this->config['init']
158 21
            && $this->initializeRepository()
159 19
        ) {
160 8
            $this->root = realpath($this->config['root']);
161 8
        }
162
163
        // If the root directory configuration is still invalid
164 19
        if (empty($this->root) || !@is_dir($this->root)) {
165 1
            throw new InvalidArgumentException(
166 1
                sprintf(
167 1
                    'Invalid file adapter strategy root "%s"',
168 1
                    $this->config['root']
169 1
                ),
170
                InvalidArgumentException::INVALID_FILE_STRATEGY_ROOT
171 1
            );
172
        }
173
174 18
        $this->configDir = $this->root.DIRECTORY_SEPARATOR.'.repo'.DIRECTORY_SEPARATOR;
175 18
    }
176
177
    /**
178
     * Initialize the repository
179
     *
180
     * @return boolean Success
181
     * @throws DomainRepositoryRuntimeException If the repository cannot be initialized
182
     * @throws DomainRepositoryRuntimeException If the repository size descriptor can not be created
183
     */
184 10
    public function initializeRepository()
185
    {
186
        // Successively create the repository directories
187 10
        $repoDirectories = [$this->config['root'], $this->config['root'].DIRECTORY_SEPARATOR.'.repo'];
188 10
        foreach ($repoDirectories as $repoDirectory) {
189
            // If the repository cannot be initialized
190 10
            if (file_exists($repoDirectory) ? !is_dir($repoDirectory) : !mkdir($repoDirectory, 0777, true)) {
191 1
                throw new DomainRepositoryRuntimeException(
192 1
                    'Could not initialize repository',
193
                    DomainRepositoryRuntimeException::REPO_NOT_INITIALIZED
194 1
                );
195
            }
196 9
        }
197
198
        // If the repository size descriptor can not be created
199 9
        $configDir = $this->config['root'].DIRECTORY_SEPARATOR.'.repo'.DIRECTORY_SEPARATOR;
200 9
        if ((file_exists($configDir.'size.txt') && !is_file($configDir.'size.txt'))
201 8
            || !file_put_contents($configDir.'size.txt', '0')
202 9
        ) {
203 1
            throw new DomainRepositoryRuntimeException(
204 1
                'Could not create repository size descriptor',
205
                DomainRepositoryRuntimeException::REPO_SIZE_DESCRIPTOR_NOT_CREATED
206 1
            );
207
        }
208
209 8
        return true;
210
    }
211
212
    /**
213
     * Find objects by selector
214
     *
215
     * @param Selector|SelectorInterface $selector Object selector
216
     * @param RepositoryInterface $repository Object repository
217
     * @return LocatorInterface[] Object locators
218
     */
219 8
    public function findObjectResourceLocators(SelectorInterface $selector, RepositoryInterface $repository)
220
    {
221 8
        chdir($this->root);
222
223
        // Build a glob string from the selector
224 8
        $glob = '';
225 8
        $globFlags = GLOB_NOSORT | GLOB_BRACE;
226 8
        $filter = 0;
227
228 8
        $year = $selector->getYear();
229 8
        if ($year !== null) {
230 8
            $glob .= '/'.$year;
231 8
        }
232
233 8
        $month = $selector->getMonth();
234 8
        if ($month !== null) {
235 8
            $glob .= '/'.$month;
236 8
        }
237
238 8
        $day = $selector->getDay();
239 8
        if ($day !== null) {
240 8
            $glob .= '/'.$day;
241 8
        }
242
243 8
        $hour = $selector->getHour();
244 8
        if ($hour !== null) {
245 2
            $glob .= '/'.$hour;
246 2
        }
247
248 8
        $minute = $selector->getMinute();
249 8
        if ($minute !== null) {
250 2
            $glob .= '/'.$minute;
251 2
        }
252
253 8
        $second = $selector->getSecond();
254 8
        if ($second !== null) {
255 2
            $glob .= '/'.$second;
256 2
        }
257
258
        // Visibility, ID & type
259 8
        $visibility = $selector->getVisibility();
260 8
        $uid = $selector->getId();
261 8
        $type = $selector->getObjectType();
262 8
        $supportedTypes = Object::getSupportedTypes();
263 8
        $types = (count($supportedTypes) == 1) ? current($supportedTypes) : '{'.implode(',', $supportedTypes).'}';
264 8
        $glob .= '/';
265 8
        $glob .= (($visibility == SelectorInterface::VISIBLE) && (is_int($uid) || ++$filter)) ?
266 8
            '' : self::$globVisibilities[$visibility];
267 8
        $glob .= $uid.'-';
268 8
        $glob .= ($type == SelectorInterface::WILDCARD) ? $types : $type;
269
270
        // Draft, revision & extension
271 8
        $draft = $selector->getDraft();
272 8
        $revision = $selector->getRevision();
273 8
        $glob .= '/'.((is_int($uid) && ($draft != SelectorInterface::PUBLISHED)) ? self::$globDrafts[$draft] : '').$uid;
274 8
        $glob .= (!is_int($revision) && (($uid != SelectorInterface::WILDCARD) || ++$filter)) ? '' : '-'.$revision;
275 8
        $glob .= '.'.getenv('OBJECT_RESOURCE_EXTENSION');
276
277
278
//        echo 'glob: '.$glob.'<br/>';
279
//        echo 'filter: '.$filter.'<br/>';
280
//        print_r(glob(ltrim($glob, '/'), $globFlags));
281
282
        // Find the object resources
283 8
        $objectResources = glob(ltrim($glob, '/'), $globFlags);
284
285
        // If the resources need to get post-filtered (because of visibility, draft state and / or revision)
286 8
        if ($filter) {
287 8
            $filterRegex = '/'.self::$regexVisibilities[$visibility];
288 8
            $filterRegex .= '(?P<id>\d+)-';
289 8
            $filterRegex .= (count($supportedTypes) == 1) ?
290 8
                current($supportedTypes) : '(?:'.implode('|', $supportedTypes).')';
291 8
            $filterRegex .= '/'.self::$regexDrafts[$draft];
292 8
            $filterRegex .= '(?:\\k<id>)';
293 8
            if ($revision !== Revision::CURRENT) {
294 1
                $filterRegex .= '-'.preg_quote($revision);
295 1
            }
296 8
            $filterRegex .= '\\.'.getenv('OBJECT_RESOURCE_EXTENSION');
297 8
            $objectResources = preg_grep("%$filterRegex$%", $objectResources);
298 8
        }
299
300
        // Return as repository locators
301 8
        return array_map(
302 8
            function ($objectResourcePath) use ($repository) {
303 8
                return Kernel::create(RepositoryLocator::class, [$repository, '/'.$objectResourcePath]);
304 8
            },
305
            $objectResources
306 8
        );
307
    }
308
309
    /**
310
     * Return an individual hash for a resource
311
     *
312
     * @param string $resourcePath Repository relative resource path
313
     * @return string|null Resource hash
314
     */
315 1
    public function getResourceHash($resourcePath)
316
    {
317 1
        return $this->hasResource($this->root.$resourcePath) ? File::hash($this->root.$resourcePath) : null;
318
    }
319
320
    /**
321
     * Test if an object resource exists
322
     *
323
     * @param string $resourcePath Repository relative resource path
324
     * @return boolean Object resource exists
325
     */
326 44
    public function hasResource($resourcePath)
327
    {
328 44
        return is_file($this->root.$resourcePath);
329
    }
330
331
    /**
332
     * Import a resource into this repository
333
     *
334
     * @param string $source Source resource
335
     * @param string $target Repository relative target resource locator
336
     * @return boolean Success
337
     */
338 2
    public function importResource($source, $target)
339
    {
340 2
        return copy($source, $this->root.$target);
341
    }
342
343
    /**
344
     * Find and return an object resource
345
     *
346
     * @param string $resourcePath Repository relative resource locator
347
     * @return ResourceInterface|AbstractResource Object resource
348
     */
349 40
    public function getObjectResource($resourcePath)
350
    {
351 40
        return ResourceFactory::createFromSource(AbstractFileReaderWriter::WRAPPER.$this->root.$resourcePath);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \Apparat\Object\I...>root . $resourcePath); (Apparat\Resource\Domain\...source\AbstractResource) is incompatible with the return type declared by the interface Apparat\Object\Domain\Re...face::getObjectResource of type Apparat\Object\Domain\Mo...bject\ResourceInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
352
    }
353
354
    /**
355
     * Allocate an object ID and create an object resource
356
     *
357
     * @param \Closure $creator Object creation closure
358
     * @return ObjectInterface Object
359
     * @throws DomainRepositoryRuntimeException If no object could be created
360
     * @throws \Exception If another error occurs
361
     */
362 7
    public function createObjectResource(\Closure $creator)
363
    {
364 7
        $sizeDescriptor = null;
365
366
        try {
367
            // Open the size descriptor
368 7
            $sizeDescriptor = fopen($this->configDir.'size.txt', 'r+');
369
370
            // If a lock of the size descriptor can be acquired
371 7
            if (flock($sizeDescriptor, LOCK_EX)) {
372
                // Determine the current repository size
373 6
                $repositorySize = '';
374 6
                while (!feof($sizeDescriptor)) {
375 6
                    $repositorySize .= fread($sizeDescriptor, 8);
376 6
                }
377 6
                $repositorySize = intval(trim($repositorySize));
378
379
                // Instantiate the next consecutive object ID
380 6
                $nextObjectId = Kernel::create(Id::class, [++$repositorySize]);
381
382
                // Create & persist the object (bypassing the repository)
383 6
                $object = $creator($nextObjectId);
384 5
                $this->persistObject($object);
385
386
                // Dump the new repository size, unlock the size descriptor
387 5
                ftruncate($sizeDescriptor, 0);
388 5
                fwrite($sizeDescriptor, $repositorySize);
389 5
                fflush($sizeDescriptor);
390 5
                flock($sizeDescriptor, LOCK_UN);
391
392
                // Return the newly created object
393 5
                return $object;
394
            }
395
396
            // If no object could be created
397 1
            throw new DomainRepositoryRuntimeException(
398 1
                'The repository size descriptor is unlockable',
399
                DomainRepositoryRuntimeException::REPO_SIZE_DESCRIPTOR_UNLOCKABLE
400 1
            );
401
402
            // If any exception is thrown
403 2
        } catch (\Exception $e) {
404
            // Release the size descriptor lock
405 2
            if (is_resource($sizeDescriptor)) {
406 2
                flock($sizeDescriptor, LOCK_UN);
407 2
            }
408
409
            // Forward the thrown exception
410 2
            throw $e;
411
        }
412
    }
413
414
    /**
415
     * Persist an object in the repository
416
     *
417
     * @param ObjectInterface $object Object
418
     * @return AdapterStrategyInterface Self reference
419
     */
420 5
    public function persistObject(ObjectInterface $object)
421
    {
422
        // If the object has just been deleted
423 5
        if ($object->hasBeenDeleted()) {
424 3
            return $this->deleteObject($object);
425
426
            // Elseif the object has just been undeleted
427 5
        } elseif ($object->hasBeenUndeleted()) {
428 2
            return $this->undeleteObject($object);
429
430
            // If the object has just been published
431 5
        } elseif ($object->hasBeenPublished()) {
432 2
            $this->publishObject($object);
433 2
        }
434
435
        // Persist the object resource
436 5
        return $this->persistObjectResource($object);
437
    }
438
439
    /**
440
     * Delete all revisions of an object
441
     *
442
     * @param ObjectInterface $object Object
443
     * @return ObjectInterface Object
444
     */
445 3
    protected function deleteObject(ObjectInterface $object)
446
    {
447
        // Hide object directory
448 3
        $objContainerDir = dirname(dirname($this->getAbsoluteResourcePath($object->getRepositoryLocator())));
449 3
        $objContainerName = $object->getId()->getId().'-'.$object->getObjectType()->getType();
450 3
        $objPublicContainer = $objContainerDir.DIRECTORY_SEPARATOR.$objContainerName;
451 3
        $objHiddenContainer = $objContainerDir.DIRECTORY_SEPARATOR.'.'.$objContainerName;
452 3
        if (file_exists($objPublicContainer)
453 3
            && is_dir($objPublicContainer)
454 3
            && !rename($objPublicContainer, $objHiddenContainer)
455 3
        ) {
456 1
            throw new RuntimeException(
457 1
                sprintf('Cannot hide object container "%s"', $objContainerName),
458
                RuntimeException::CANNOT_HIDE_OBJECT_CONTAINER
459 1
            );
460
        }
461
462
        // Delete all object revisions
463
        /** @var ObjectInterface $objectRevision */
464 2
        foreach ($object as $objectRevision) {
465 2
            $this->persistObjectResource($objectRevision->delete());
466 2
        }
467
468 2
        return $this;
469
    }
470
471
    /**
472
     * Build an absolute repository resource locator
473
     *
474
     * @param RepositoryLocatorInterface $repositoryLocator Repository locator
475
     * @return string Absolute repository resource locator
476
     */
477 5
    public function getAbsoluteResourcePath(RepositoryLocatorInterface $repositoryLocator)
478
    {
479 5
        return $this->root.str_replace(
480 5
            '/',
481 5
            DIRECTORY_SEPARATOR,
482 5
            $repositoryLocator->withExtension(getenv('OBJECT_RESOURCE_EXTENSION'))
483 5
        );
484
    }
485
486
    /**
487
     * Persist an object resource in the repository
488
     *
489
     * @param ObjectInterface $object Object
490
     * @return AdapterStrategyInterface Self reference
491
     */
492 5
    protected function persistObjectResource(ObjectInterface $object)
493
    {
494
        /** @var \Apparat\Object\Infrastructure\Resource $objectResource */
495 5
        $objectResource = ResourceFactory::createFromObject($object);
496
497
        // Create the absolute object resource path
498 5
        $objectResourcePath = $this->getAbsoluteResourcePath($object->getRepositoryLocator());
499
500
        /** @var Writer $fileWriter */
501 5
        $fileWriter = Kernel::create(
502 5
            Writer::class,
503 5
            [$objectResourcePath, Writer::FILE_CREATE | Writer::FILE_CREATE_DIRS | Writer::FILE_OVERWRITE]
504 5
        );
505 5
        $objectResource->dump($fileWriter);
506
507 5
        return $this;
508
    }
509
510
    /**
511
     * Undelete all revisions of an object
512
     *
513
     * @param ObjectInterface $object Object
514
     * @return ObjectInterface Object
515
     */
516 2
    protected function undeleteObject(ObjectInterface $object)
517
    {
518
        // Hide object directory
519 2
        $objContainerDir = dirname(dirname($this->getAbsoluteResourcePath($object->getRepositoryLocator())));
520 2
        $objContainerName = $object->getId()->getId().'-'.$object->getObjectType()->getType();
521 2
        $objPublicContainer = $objContainerDir.DIRECTORY_SEPARATOR.$objContainerName;
522 2
        $objHiddenContainer = $objContainerDir.DIRECTORY_SEPARATOR.'.'.$objContainerName;
523 2
        if (file_exists($objHiddenContainer)
524 2
            && is_dir($objHiddenContainer)
525 2
            && !rename($objHiddenContainer, $objPublicContainer)
526 2
        ) {
527 1
            throw new RuntimeException(
528 1
                sprintf('Cannot unhide object container "%s"', $objContainerName),
529
                RuntimeException::CANNOT_UNHIDE_OBJECT_CONTAINER
530 1
            );
531
        }
532
533
        // Undelete all object revisions
534
        /** @var ObjectInterface $objectRevision */
535 1
        foreach ($object as $objectRevision) {
536 1
            $this->persistObjectResource($objectRevision->undelete());
537 1
        }
538
539 1
        return $this;
540
    }
541
542
    /**
543
     * Publish an object in the repository
544
     *
545
     * @param ObjectInterface $object
546
     */
547 2
    protected function publishObject(ObjectInterface $object)
548
    {
549 2
        $objectRepoLocator = $object->getRepositoryLocator();
550
551
        // If the object had been persisted as a draft: Remove the draft resource
552 2
        $objectDraftLocator = $objectRepoLocator->setRevision($object->getRevision()->setDraft(true));
553 2
        $absObjectDraftPath = $this->getAbsoluteResourcePath($objectDraftLocator);
554 2
        if (@file_exists($absObjectDraftPath)) {
555 2
            unlink($absObjectDraftPath);
556 2
        }
557
558
        // If it's not the first object revision: Rotate the previous revision resource
559 2
        $objectRevisionNumber = $object->getRevision()->getRevision();
560 2
        if ($objectRevisionNumber > 1) {
561
            // Build the "current" object repository locator
562 2
            $currentRevision = Revision::current();
563
            $curObjectResPath =
564 2
                $this->getAbsoluteResourcePath($objectRepoLocator->setRevision($currentRevision));
565
566
            // Build the previous object repository locator
567
            /** @var Revision $previousRevision */
568 2
            $previousRevision = Kernel::create(Revision::class, [$objectRevisionNumber - 1]);
569
            $prevObjectResPath
570 2
                = $this->getAbsoluteResourcePath($objectRepoLocator->setRevision($previousRevision));
571
572
            // Rotate the previous revision's resource path
573 2
            if (file_exists($curObjectResPath)) {
574 2
                rename($curObjectResPath, $prevObjectResPath);
575 2
            }
576 2
        }
577 2
    }
578
579
    /**
580
     * Return the repository size (number of objects in the repository)
581
     *
582
     * @return int Repository size
583
     */
584 7
    public function getRepositorySize()
585
    {
586 7
        $sizeDescriptorFile = $this->configDir.'size.txt';
587 7
        $repositorySize = 0;
588 7
        if (is_file($sizeDescriptorFile) && is_readable($sizeDescriptorFile)) {
589 7
            $repositorySize = intval(file_get_contents($this->configDir.'size.txt'));
590 7
        }
591 7
        return $repositorySize;
592
    }
593
}
594