DiffObject::__construct()   B
last analyzed

Complexity

Conditions 7
Paths 9

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 8.2464

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 12
cts 17
cp 0.7059
rs 8.5386
c 0
b 0
f 0
cc 7
nc 9
nop 1
crap 8.2464
1
<?php
2
3
/**
4
 * GitElephant - An abstraction layer for git written in PHP
5
 * Copyright (C) 2013  Matteo Giachino
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see [http://www.gnu.org/licenses/].
19
 */
20
21
namespace GitElephant\Objects\Diff;
22
23
use GitElephant\Utilities;
24
25
/**
26
 * Represent a diff for a single object in the repository
27
 *
28
 * @author Matteo Giachino <[email protected]>
29
 */
30
class DiffObject implements \ArrayAccess, \Countable, \Iterator
31
{
32
    public const MODE_INDEX = 'index';
33
    public const MODE_MODE = 'mode';
34
    public const MODE_NEW_FILE = 'new_file';
35
    public const MODE_DELETED_FILE = 'deleted_file';
36
    public const MODE_RENAMED = 'renamed_file';
37
38
    /**
39
     * the cursor position
40
     *
41
     * @var int|null
42
     */
43
    private $position;
44
45
    /**
46
     * the original file path for the diff object
47
     *
48
     * @var string|null
49
     */
50
    private $originalPath;
51
52
    /**
53
     * the destination path for the diff object
54
     *
55
     * @var string|null
56
     */
57
    private $destinationPath;
58
59
    /**
60
     * rename similarity index
61
     *
62
     * @var int|null
63
     */
64
    private $similarityIndex;
65
66
    /**
67
     * the diff mode
68
     *
69
     * @var string|null
70
     */
71
    private $mode;
72
73
    /**
74
     * the diff chunks
75
     *
76
     * @var array
77
     */
78
    private $chunks = [];
79
80
    /**
81
     * Class constructor
82
     *
83
     * @param array $lines output lines for the diff
84
     *
85
     * @throws \InvalidArgumentException
86
     */
87 2
    public function __construct(array $lines)
88
    {
89 2
        $this->position = 0;
90 2
        $this->chunks = [];
91
92 2
        $this->findPath($lines[0]);
93
94 2
        $sliceIndex = 4;
95
96 2
        if ($this->hasPathChanged()) {
97
            $this->findSimilarityIndex($lines[1]);
98
            if (isset($lines[4]) && !empty($lines[4])) {
99
                $this->findMode($lines[4]);
100
                $sliceIndex = 7;
101
            } else {
102
                $this->mode = self::MODE_RENAMED;
103
            }
104
        } else {
105 2
            $this->findMode($lines[1]);
106
        }
107
108 2
        if ($this->mode === self::MODE_INDEX || $this->mode === self::MODE_NEW_FILE) {
109 2
            $lines = array_slice($lines, $sliceIndex);
110 2
            if (!empty($lines)) {
111 2
                $this->findChunks($lines);
112
            }
113
        }
114 2
    }
115
116
    /**
117
     * toString magic method
118
     *
119
     * @return string|null
120
     */
121
    public function __toString(): ?string
122
    {
123
        return $this->originalPath;
124
    }
125
126
    /**
127
     * Find the diff chunks
128
     *
129
     * @param array $lines output lines for the diff
130
     *
131
     * @throws \InvalidArgumentException
132
     */
133 2
    private function findChunks(array $lines): void
134
    {
135 2
        $arrayChunks = Utilities::pregSplitArray(
136 2
            $lines,
137 2
            '/^@@ -(\d+,\d+)|(\d+) \+(\d+,\d+)|(\d+) @@(.*)$/'
138
        );
139 2
        foreach ($arrayChunks as $chunkLines) {
140 2
            $this->chunks[] = new DiffChunk($chunkLines);
141
        }
142 2
    }
143
144
    /**
145
     * look for the path in the line
146
     *
147
     * @param string $line line content
148
     */
149 2
    private function findPath(string $line): void
150
    {
151 2
        $matches = [];
152 2
        if (preg_match('/^diff --git SRC\/(.*) DST\/(.*)$/', $line, $matches)) {
153 2
            $this->originalPath = $matches[1];
154 2
            $this->destinationPath = $matches[2];
155
        }
156 2
    }
157
158
    /**
159
     * find the line mode
160
     *
161
     * @param string $line line content
162
     */
163 2
    private function findMode(string $line): void
164
    {
165 2
        if (preg_match('/^index (.*)\.\.(.*) (.*)$/', $line)) {
166 1
            $this->mode = self::MODE_INDEX;
167
        }
168
169 2
        if (preg_match('/^mode (.*)\.\.(.*) (.*)$/', $line)) {
170
            $this->mode = self::MODE_MODE;
171
        }
172
173 2
        if (preg_match('/^new file mode (.*)/', $line)) {
174 1
            $this->mode = self::MODE_NEW_FILE;
175
        }
176
177 2
        if (preg_match('/^deleted file mode (.*)/', $line)) {
178
            $this->mode = self::MODE_DELETED_FILE;
179
        }
180 2
    }
181
182
    /**
183
     * look for similarity index in the line
184
     *
185
     * @param string $line line content
186
     */
187
    private function findSimilarityIndex(string $line): void
188
    {
189
        $matches = [];
190
        if (preg_match('/^similarity index (.*)\%$/', $line, $matches)) {
191
            $this->similarityIndex = $matches[1];
0 ignored issues
show
Documentation Bug introduced by
It seems like $matches[1] of type string is incompatible with the declared type integer|null of property $similarityIndex.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
192
        }
193
    }
194
195
    /**
196
     * chunks getter
197
     *
198
     * @return array
199
     */
200
    public function getChunks(): array
201
    {
202
        return $this->chunks;
203
    }
204
205
    /**
206
     * destinationPath getter
207
     *
208
     * @return string
209
     */
210
    public function getDestinationPath(): string
211
    {
212
        return $this->destinationPath;
213
    }
214
215
    /**
216
     * mode getter
217
     *
218
     * @return string
219
     */
220
    public function getMode(): string
221
    {
222
        return $this->mode;
223
    }
224
225
    /**
226
     * originalPath getter
227
     *
228
     * @return string
229
     */
230
    public function getOriginalPath(): string
231
    {
232
        return $this->originalPath;
233
    }
234
235
    /**
236
     * Check if path has changed (file was renamed)
237
     *
238
     * @return bool
239
     */
240 2
    public function hasPathChanged(): bool
241
    {
242 2
        return $this->originalPath !== $this->destinationPath;
243
    }
244
245
    /**
246
     * Get similarity index
247
     *
248
     * @return int
249
     * @throws \RuntimeException if not a rename
250
     */
251
    public function getSimilarityIndex(): int
252
    {
253
        if ($this->hasPathChanged()) {
254
            return $this->similarityIndex;
255
        }
256
257
        throw new \RuntimeException('Cannot get similarity index on non-renames');
258
    }
259
260
    /**
261
     * ArrayAccess interface
262
     *
263
     * @param int $offset offset
264
     *
265
     * @return bool
266
     */
267
    public function offsetExists($offset): bool
268
    {
269
        return isset($this->chunks[$offset]);
270
    }
271
272
    /**
273
     * ArrayAccess interface
274
     *
275
     * @param int $offset offset
276
     *
277
     * @return DiffChunk|null
278
     */
279 1
    public function offsetGet($offset)
280
    {
281 1
        return isset($this->chunks[$offset]) ? $this->chunks[$offset] : null;
282
    }
283
284
    /**
285
     * ArrayAccess interface
286
     *
287
     * @param int|null   $offset offset
288
     * @param mixed $value  value
289
     */
290
    public function offsetSet($offset, $value): void
291
    {
292
        if (is_null($offset)) {
293
            $this->chunks[] = $value;
294
        } else {
295
            $this->chunks[$offset] = $value;
296
        }
297
    }
298
299
    /**
300
     * ArrayAccess interface
301
     *
302
     * @param int $offset offset
303
     */
304
    public function offsetUnset($offset): void
305
    {
306
        unset($this->chunks[$offset]);
307
    }
308
309
    /**
310
     * Countable interface
311
     *
312
     * @return int
313
     */
314 1
    public function count(): int
315
    {
316 1
        return count($this->chunks);
317
    }
318
319
    /**
320
     * Iterator interface
321
     *
322
     * @return mixed
323
     */
324
    public function current()
325
    {
326
        return $this->chunks[$this->position];
327
    }
328
329
    /**
330
     * Iterator interface
331
     */
332
    public function next(): void
333
    {
334
        ++$this->position;
335
    }
336
337
    /**
338
     * Iterator interface
339
     *
340
     * @return int
341
     */
342
    public function key(): int
343
    {
344
        return $this->position;
345
    }
346
347
    /**
348
     * Iterator interface
349
     *
350
     * @return bool
351
     */
352
    public function valid(): bool
353
    {
354
        return isset($this->chunks[$this->position]);
355
    }
356
357
    /**
358
     * Iterator interface
359
     */
360
    public function rewind(): void
361
    {
362
        $this->position = 0;
363
    }
364
}
365