Completed
Push — master ( b350e3...8d2c4d )
by Arne
01:49
created

StandardOperationCollectionBuilder::getVault()   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 Archivr\OperationCollectionBuilder;
4
5
use Archivr\Exception\Exception;
6
use Archivr\Index;
7
use Archivr\IndexObject;
8
use Archivr\Operation\ChmodOperation;
9
use Archivr\Operation\DownloadOperation;
10
use Archivr\Operation\MkdirOperation;
11
use Archivr\Operation\SymlinkOperation;
12
use Archivr\Operation\TouchOperation;
13
use Archivr\Operation\UnlinkOperation;
14
use Archivr\Operation\UploadOperation;
15
use Archivr\OperationCollection;
16
use Archivr\Vault;
17
18
class StandardOperationCollectionBuilder implements OperationCollectionBuilderInterface
19
{
20
    /**
21
     * @var Vault
22
     */
23
    protected $vault;
24
25
    public function __construct(Vault $vault)
26
    {
27
        $this->vault = $vault;
28
    }
29
30
    public function getVault(): Vault
31
    {
32
        return $this->vault;
33
    }
34
35
    public function buildOperationCollection(Index $mergedIndex, Index $localIndex, Index $remoteIndex = null): OperationCollection
36
    {
37
        $localPath = $this->vault->getLocalPath();
38
        $vaultConnection = $this->vault->getVaultConnection();
39
40
41
        $uploadStreamFilters = [
42
            'zlib.deflate' => []
43
        ];
44
        $downloadStreamFilters = [
45
            'zlib.inflate' => []
46
        ];
47
48
49
        $operationCollection = new OperationCollection();
50
51
        // mtimes to be set for directories are collected and applied afterwards as they get modified by synchronization operations as well
52
        $directoryMtimes = [];
53
54
        // set of modified paths that can be populated and is later used to add parent directory touch()es
55
        $modifiedPaths = [];
56
57
        // relies on the directory tree structure being traversed in pre-order (or at least a directory appears before its content)
58
        foreach ($mergedIndex as $mergedIndexObject)
59
        {
60
            /** @var IndexObject $mergedIndexObject */
61
62
            $relativePath = $localPath . $mergedIndexObject->getRelativePath();
63
64
            $localObject = $localIndex->getObjectByPath($mergedIndexObject->getRelativePath());
65
            $remoteObject = $remoteIndex ? $remoteIndex->getObjectByPath($mergedIndexObject->getRelativePath()) : null;
66
67
            // unlink to-be-overridden local path with different type
68 View Code Duplication
            if ($localObject !== null && $localObject->getType() !== $mergedIndexObject->getType())
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
69
            {
70
                $operationCollection->addOperation(new UnlinkOperation($relativePath));
71
72
                $modifiedPaths[] = $mergedIndexObject->getRelativePath();
73
            }
74
75
76
            if ($mergedIndexObject->isDirectory())
77
            {
78
                if ($localObject === null || !$localObject->isDirectory())
79
                {
80
                    $operationCollection->addOperation(new MkdirOperation($relativePath, $mergedIndexObject->getMode()));
81
82
                    $directoryMtimes[$mergedIndexObject->getRelativePath()] = $mergedIndexObject->getMtime();
83
                }
84
85
                if ($localObject !== null && $localObject->isDirectory())
86
                {
87
                    if ($localObject->getMtime() !== $mergedIndexObject->getMtime())
88
                    {
89
                        // fix wrong mtime
90
                        $directoryMtimes[$mergedIndexObject->getRelativePath()] = $mergedIndexObject->getMtime();
91
                    }
92
                }
93
            }
94
95
            elseif ($mergedIndexObject->isFile())
96
            {
97
                // local file did not exist, hasn't been a file before or is outdated
98
                $doDownloadFile = $localObject === null || !$localObject->isFile() || $localObject->getMtime() < $mergedIndexObject->getMtime();
99
100
                // file has to be restored as it does not equal the local version
101
                $doDownloadFile |= $localObject !== null && $mergedIndexObject->getBlobId() !== $localObject->getBlobId();
102
103
                if ($doDownloadFile)
104
                {
105
                    $operationCollection->addOperation(new DownloadOperation($relativePath, $mergedIndexObject->getBlobId(), $vaultConnection, $downloadStreamFilters));
106
                    $operationCollection->addOperation(new TouchOperation($relativePath, $mergedIndexObject->getMtime()));
107
                    $operationCollection->addOperation(new ChmodOperation($relativePath, $mergedIndexObject->getMode()));
108
109
                    $modifiedPaths[] = $mergedIndexObject->getRelativePath();
110
                }
111
112
                // local file got created or updated
113
                elseif ($remoteObject === null || $mergedIndexObject->getBlobId() === null)
114
                {
115
                    // generate blob id
116
                    do
117
                    {
118
                        $newBlobId = $mergedIndex->generateNewBlobId();
119
                    }
120
                    while ($vaultConnection->exists($newBlobId));
121
122
                    $mergedIndexObject->setBlobId($newBlobId);
123
124
                    $operationCollection->addOperation(new UploadOperation($relativePath, $mergedIndexObject->getBlobId(), $vaultConnection, $uploadStreamFilters));
125
                }
126
            }
127
128
            elseif ($mergedIndexObject->isLink())
129
            {
130
                $absoluteLinkTarget = dirname($relativePath) . DIRECTORY_SEPARATOR . $mergedIndexObject->getLinkTarget();
131
132
                if ($localObject !== null && $localObject->getLinkTarget() !== $mergedIndexObject->getLinkTarget())
133
                {
134
                    $operationCollection->addOperation(new UnlinkOperation($relativePath));
135
                    $operationCollection->addOperation(new SymlinkOperation($relativePath, $absoluteLinkTarget, $mergedIndexObject->getMode()));
136
137
                    $modifiedPaths[] = $mergedIndexObject->getRelativePath();
138
                }
139
            }
140
141
            else
142
            {
143
                // unknown/invalid object type
144
                throw new Exception();
145
            }
146
147
148
            if ($localObject !== null && $localObject->getMode() !== $mergedIndexObject->getMode())
149
            {
150
                $operationCollection->addOperation(new ChmodOperation($relativePath, $mergedIndexObject->getMode()));
151
            }
152
        }
153
154
        // remove superfluous local files
155
        foreach ($localIndex as $localObject)
156
        {
157
            /** @var IndexObject $localObject */
158
159 View Code Duplication
            if ($mergedIndex->getObjectByPath($localObject->getRelativePath()) === null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
            {
161
                $operationCollection->addOperation(new UnlinkOperation($localPath . $localObject->getRelativePath()));
162
163
                $modifiedPaths[] = $localObject->getRelativePath();
164
            }
165
        }
166
167
        // add modified paths to directory mtimes to be set
168
        foreach ($modifiedPaths as $modifiedPath)
169
        {
170
            if (($dir = dirname($modifiedPath)) !== '.')
171
            {
172
                $dirObject = $mergedIndex->getObjectByPath($dir);
173
174
                $directoryMtimes[$dirObject->getRelativePath()] = $dirObject->getMtime();
175
            }
176
        }
177
178
        // set directory mtimes after all other modifications have been performed
179
        krsort($directoryMtimes);
180
        foreach ($directoryMtimes as $relativePath => $mtime)
181
        {
182
            $operationCollection->addOperation(new TouchOperation($localPath . $relativePath, $mtime));
183
        }
184
185
        return $operationCollection;
186
    }
187
}
188