Completed
Push — develop ( 5f7df1...9cb564 )
by Matteo
10s
created

DiffObject::__construct()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

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

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

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