Passed
Push — master ( 256f03...6c6d52 )
by Josh
59s
created

AbstractDiff::clearContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
namespace Caxy\HtmlDiff;
4
5
/**
6
 * Class AbstractDiff.
7
 */
8
abstract class AbstractDiff
9
{
10
    /**
11
     * @var array
12
     *
13
     * @deprecated since 0.1.0
14
     */
15
    public static $defaultSpecialCaseTags = array('strong', 'b', 'i', 'big', 'small', 'u', 'sub', 'sup', 'strike', 's', 'p');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 125 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
16
    /**
17
     * @var array
18
     *
19
     * @deprecated since 0.1.0
20
     */
21
    public static $defaultSpecialCaseChars = array('.', ',', '(', ')', '\'');
22
    /**
23
     * @var bool
24
     *
25
     * @deprecated since 0.1.0
26
     */
27
    public static $defaultGroupDiffs = true;
28
29
    /**
30
     * @var HtmlDiffConfig
31
     */
32
    protected $config;
33
34
    /**
35
     * @var string
36
     */
37
    protected $content;
38
    /**
39
     * @var string
40
     */
41
    protected $oldText;
42
    /**
43
     * @var string
44
     */
45
    protected $newText;
46
    /**
47
     * @var array
48
     */
49
    protected $oldWords = array();
50
    /**
51
     * @var array
52
     */
53
    protected $newWords = array();
54
55
    /**
56
     * @var DiffCache[]
57
     */
58
    private $diffCaches = array();
59
60
    /**
61
     * AbstractDiff constructor.
62
     *
63
     * @param string     $oldText
64
     * @param string     $newText
65
     * @param string     $encoding
66
     * @param null|array $specialCaseTags
67
     * @param null|bool  $groupDiffs
68
     */
69 11
    public function __construct($oldText, $newText, $encoding = 'UTF-8', $specialCaseTags = null, $groupDiffs = null)
70
    {
71 11
        mb_substitute_character(0x20);
72
73 11
        $this->config = HtmlDiffConfig::create()->setEncoding($encoding);
74
75 11
        if ($specialCaseTags !== null) {
76 11
            $this->config->setSpecialCaseTags($specialCaseTags);
77 11
        }
78
79 11
        if ($groupDiffs !== null) {
80
            $this->config->setGroupDiffs($groupDiffs);
81
        }
82
83 11
        $this->oldText = $this->purifyHtml(trim($oldText));
84 11
        $this->newText = $this->purifyHtml(trim($newText));
85 11
        $this->content = '';
86 11
    }
87
88
    /**
89
     * @return bool|string
90
     */
91
    abstract public function build();
92
93
    /**
94
     * @return DiffCache|null
95
     */
96
    protected function getDiffCache()
97
    {
98
        if (!$this->hasDiffCache()) {
99
            return;
100
        }
101
102
        $hash = spl_object_hash($this->getConfig()->getCacheProvider());
103
104
        if (!array_key_exists($hash, $this->diffCaches)) {
105
            $this->diffCaches[$hash] = new DiffCache($this->getConfig()->getCacheProvider());
0 ignored issues
show
Bug introduced by
It seems like $this->getConfig()->getCacheProvider() can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
106
        }
107
108
        return $this->diffCaches[$hash];
109
    }
110
111
    /**
112
     * @return bool
113
     */
114 11
    protected function hasDiffCache()
115
    {
116 11
        return null !== $this->getConfig()->getCacheProvider();
117
    }
118
119
    /**
120
     * @return HtmlDiffConfig
121
     */
122 11
    public function getConfig()
123
    {
124 11
        return $this->config;
125
    }
126
127
    /**
128
     * @param HtmlDiffConfig $config
129
     *
130
     * @return AbstractDiff
131
     */
132 8
    public function setConfig(HtmlDiffConfig $config)
133
    {
134 8
        $this->config = $config;
135
136 8
        return $this;
137
    }
138
139
    /**
140
     * @return int
141
     *
142
     * @deprecated since 0.1.0
143
     */
144
    public function getMatchThreshold()
145
    {
146
        return $this->config->getMatchThreshold();
147
    }
148
149
    /**
150
     * @param int $matchThreshold
151
     *
152
     * @return AbstractDiff
153
     *
154
     * @deprecated since 0.1.0
155
     */
156
    public function setMatchThreshold($matchThreshold)
157
    {
158
        $this->config->setMatchThreshold($matchThreshold);
159
160
        return $this;
161
    }
162
163
    /**
164
     * @param array $chars
165
     *
166
     * @deprecated since 0.1.0
167
     */
168
    public function setSpecialCaseChars(array $chars)
169
    {
170
        $this->config->setSpecialCaseChars($chars);
171
    }
172
173
    /**
174
     * @return array|null
175
     *
176
     * @deprecated since 0.1.0
177
     */
178
    public function getSpecialCaseChars()
179
    {
180
        return $this->config->getSpecialCaseChars();
181
    }
182
183
    /**
184
     * @param string $char
185
     *
186
     * @deprecated since 0.1.0
187
     */
188
    public function addSpecialCaseChar($char)
189
    {
190
        $this->config->addSpecialCaseChar($char);
191
    }
192
193
    /**
194
     * @param string $char
195
     *
196
     * @deprecated since 0.1.0
197
     */
198
    public function removeSpecialCaseChar($char)
199
    {
200
        $this->config->removeSpecialCaseChar($char);
201
    }
202
203
    /**
204
     * @param array $tags
205
     *
206
     * @deprecated since 0.1.0
207
     */
208
    public function setSpecialCaseTags(array $tags = array())
209
    {
210
        $this->config->setSpecialCaseChars($tags);
211
    }
212
213
    /**
214
     * @param string $tag
215
     *
216
     * @deprecated since 0.1.0
217
     */
218
    public function addSpecialCaseTag($tag)
219
    {
220
        $this->config->addSpecialCaseTag($tag);
221
    }
222
223
    /**
224
     * @param string $tag
225
     *
226
     * @deprecated since 0.1.0
227
     */
228
    public function removeSpecialCaseTag($tag)
229
    {
230
        $this->config->removeSpecialCaseTag($tag);
231
    }
232
233
    /**
234
     * @return array|null
235
     *
236
     * @deprecated since 0.1.0
237
     */
238
    public function getSpecialCaseTags()
239
    {
240
        return $this->config->getSpecialCaseTags();
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getOldHtml()
247
    {
248
        return $this->oldText;
249
    }
250
251
    /**
252
     * @return string
253
     */
254
    public function getNewHtml()
255
    {
256
        return $this->newText;
257
    }
258
259
    /**
260
     * @return string
261
     */
262
    public function getDifference()
263
    {
264
        return $this->content;
265
    }
266
267
    /**
268
     * Clears the diff content.
269
     *
270
     * @return void
271
     */
272
    public function clearContent()
273
    {
274
        $this->content = null;
275
    }
276
277
    /**
278
     * @param bool $boolean
279
     *
280
     * @return $this
281
     *
282
     * @deprecated since 0.1.0
283
     */
284
    public function setGroupDiffs($boolean)
285
    {
286
        $this->config->setGroupDiffs($boolean);
287
288
        return $this;
289
    }
290
291
    /**
292
     * @return bool
293
     *
294
     * @deprecated since 0.1.0
295
     */
296
    public function isGroupDiffs()
297
    {
298
        return $this->config->isGroupDiffs();
299
    }
300
301
    /**
302
     * @param string $tag
303
     *
304
     * @return string
305
     */
306
    protected function getOpeningTag($tag)
307
    {
308
        return '/<'.$tag.'[^>]*/i';
309
    }
310
311
    /**
312
     * @param string $tag
313
     *
314
     * @return string
315
     */
316
    protected function getClosingTag($tag)
317
    {
318
        return '</'.$tag.'>';
319
    }
320
321
    /**
322
     * @param string $str
323
     * @param string $start
324
     * @param string $end
325
     *
326
     * @return string
327
     */
328
    protected function getStringBetween($str, $start, $end)
329
    {
330
        $expStr = explode($start, $str, 2);
331
        if (count($expStr) > 1) {
332
            $expStr = explode($end, $expStr[ 1 ]);
333
            if (count($expStr) > 1) {
334
                array_pop($expStr);
335
336
                return implode($end, $expStr);
337
            }
338
        }
339
340
        return '';
341
    }
342
343
    /**
344
     * @param string $html
345
     *
346
     * @return string
347
     */
348 11
    protected function purifyHtml($html)
349
    {
350 11
        if (class_exists('Tidy') && false) {
351
            $config = array('output-xhtml' => true, 'indent' => false);
352
            $tidy = new tidy();
353
            $tidy->parseString($html, $config, 'utf8');
354
            $html = (string) $tidy;
355
356
            return $this->getStringBetween($html, '<body>');
0 ignored issues
show
Bug introduced by
The call to getStringBetween() misses a required argument $end.

This check looks for function calls that miss required arguments.

Loading history...
357
        }
358
359 11
        return $html;
360
    }
361
362 11
    protected function splitInputsToWords()
363
    {
364 11
        $this->oldWords = $this->convertHtmlToListOfWords($this->explode($this->oldText));
365 11
        $this->newWords = $this->convertHtmlToListOfWords($this->explode($this->newText));
366 11
    }
367
368
    /**
369
     * @param string $text
370
     *
371
     * @return bool
372
     */
373 11
    protected function isPartOfWord($text)
374
    {
375 11
        return ctype_alnum(str_replace($this->config->getSpecialCaseChars(), '', $text));
376
    }
377
378
    /**
379
     * @param array $characterString
380
     *
381
     * @return array
382
     */
383 11
    protected function convertHtmlToListOfWords($characterString)
384
    {
385 11
        $mode = 'character';
386 11
        $current_word = '';
387 11
        $words = array();
388 11
        foreach ($characterString as $i => $character) {
389
            switch ($mode) {
390 11
                case 'character':
391 11
                if ($this->isStartOfTag($character)) {
392 11
                    if ($current_word != '') {
393 10
                        $words[] = $current_word;
394 10
                    }
395 11
                    $current_word = '<';
396 11
                    $mode = 'tag';
397 11
                } elseif (preg_match("/\s/", $character)) {
398 11
                    if ($current_word !== '') {
399 11
                        $words[] = $current_word;
400 11
                    }
401 11
                    $current_word = preg_replace('/\s+/S', ' ', $character);
402 11
                    $mode = 'whitespace';
403 11
                } else {
404
                    if (
405 11
                        (ctype_alnum($character) && (strlen($current_word) == 0 || $this->isPartOfWord($current_word))) ||
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
406 11
                        (in_array($character, $this->config->getSpecialCaseChars()) && isset($characterString[$i + 1]) && $this->isPartOfWord($characterString[$i + 1]))
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 168 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
407 11
                    ) {
408 11
                        $current_word .= $character;
409 11
                    } else {
410 11
                        $words[] = $current_word;
411 11
                        $current_word = $character;
412
                    }
413
                }
414 11
                break;
415 11
                case 'tag' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
416 11
                if ($this->isEndOfTag($character)) {
417 11
                    $current_word .= '>';
418 11
                    $words[] = $current_word;
419 11
                    $current_word = '';
420
421 11
                    if (!preg_match('[^\s]', $character)) {
422 11
                        $mode = 'whitespace';
423 11
                    } else {
424
                        $mode = 'character';
425
                    }
426 11
                } else {
427 11
                    $current_word .= $character;
428
                }
429 11
                break;
430 11
                case 'whitespace':
431 11
                if ($this->isStartOfTag($character)) {
432 11
                    if ($current_word !== '') {
433 11
                        $words[] = $current_word;
434 11
                    }
435 11
                    $current_word = '<';
436 11
                    $mode = 'tag';
437 11
                } elseif (preg_match("/\s/", $character)) {
438 10
                    $current_word .= $character;
439 10
                    $current_word = preg_replace('/\s+/S', ' ', $current_word);
440 10
                } else {
441 11
                    if ($current_word != '') {
442 11
                        $words[] = $current_word;
443 11
                    }
444 11
                    $current_word = $character;
445 11
                    $mode = 'character';
446
                }
447 11
                break;
448
                default:
449
                break;
450
            }
451 11
        }
452 11
        if ($current_word != '') {
453
            $words[] = $current_word;
454
        }
455
456 11
        return $words;
457
    }
458
459
    /**
460
     * @param string $val
461
     *
462
     * @return bool
463
     */
464 11
    protected function isStartOfTag($val)
465
    {
466 11
        return $val == '<';
467
    }
468
469
    /**
470
     * @param string $val
471
     *
472
     * @return bool
473
     */
474 11
    protected function isEndOfTag($val)
475
    {
476 11
        return $val == '>';
477
    }
478
479
    /**
480
     * @param string $value
481
     *
482
     * @return bool
483
     */
484
    protected function isWhiteSpace($value)
485
    {
486
        return !preg_match('[^\s]', $value);
487
    }
488
489
    /**
490
     * @param string $value
491
     *
492
     * @return array
493
     */
494 11
    protected function explode($value)
495
    {
496
        // as suggested by @onassar
497 11
        return preg_split('//u', $value);
498
    }
499
}
500