WholeTextFinder   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 138
rs 10
c 0
b 0
f 0
wmc 18

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getSearchPattern() 0 12 6
A findAndReplace() 0 12 2
A find() 0 13 2
A mbFindTheCorrectPosition() 0 11 2
A mbCorrectMatchPositions() 0 12 3
A getPatternAndHaystack() 0 9 3
1
<?php
2
3
namespace Matecat\Finder;
4
5
use Matecat\Finder\Helper\RegexEscaper;
6
use Matecat\Finder\Helper\Strings;
7
use Matecat\Finder\Helper\Replacer;
8
9
class WholeTextFinder
10
{
11
    /**
12
     * @param string $haystack
13
     * @param string $needle
14
     * @param bool   $skipHtmlEntities
15
     * @param bool   $exactMatch
16
     * @param bool   $caseSensitive
17
     * @param bool   $preserveNbsps
18
     *
19
     * @return array
20
     */
21
    public static function find($haystack, $needle, $skipHtmlEntities = true, $exactMatch = false, $caseSensitive = false, $preserveNbsps = false)
22
    {
23
        $patternAndHaystack = self::getPatternAndHaystack($haystack, $needle, $skipHtmlEntities, $exactMatch, $caseSensitive, $preserveNbsps);
24
25
        preg_match_all($patternAndHaystack['pattern'], $patternAndHaystack['haystack'], $matches, PREG_OFFSET_CAPTURE);
26
27
        if($skipHtmlEntities === true){
28
            $patternAndHaystack['haystack'] = Strings::unprotectHTMLTags($patternAndHaystack['haystack']);
29
        }
30
31
        self::mbCorrectMatchPositions($patternAndHaystack['haystack'], $matches);
32
33
        return $matches[0];
34
    }
35
36
    /**
37
     * Correct position for multi byte strings
38
     *
39
     * @param string $haystack
40
     * @param array $matches
41
     *
42
     * @return mixed
43
     */
44
    private static function mbCorrectMatchPositions( $haystack, &$matches)
45
    {
46
        if(!Strings::isMultibyte($haystack) ){
47
            return $matches[0];
48
        }
49
50
        foreach ($matches[0] as $index => $match){
51
            $word = $match[0];
52
            $position = $match[1];
53
54
            $correctPosition = self::mbFindTheCorrectPosition($haystack, $word, $position);
55
            $matches[0][$index][1] = $correctPosition;
56
        }
57
    }
58
59
    /**
60
     * @param string $haystack
61
     * @param string $word
62
     * @param int $position
63
     *
64
     * @return int
65
     */
66
    private static function mbFindTheCorrectPosition( $haystack, $word, &$position)
67
    {
68
        $wordCheck = mb_substr($haystack, $position, mb_strlen($word));
69
70
        if($wordCheck !== $word){
71
            $position = $position - 1;
72
73
            self::mbFindTheCorrectPosition($haystack, $word, $position);
74
        }
75
76
        return $position;
77
    }
78
79
    /**
80
     * @param string $haystack
81
     * @param string $needle
82
     * @param bool $skipHtmlEntities
83
     * @param bool $exactMatch
84
     * @param bool $caseSensitive
85
     * @param bool $preserveNbsps
86
     *
87
     * @return array
88
     */
89
    private static function getPatternAndHaystack($haystack, $needle, $skipHtmlEntities = true, $exactMatch = false, $caseSensitive = false, $preserveNbsps = false)
90
    {
91
        $pattern = self::getSearchPattern($needle, $skipHtmlEntities, $exactMatch, $caseSensitive, $preserveNbsps);
92
        $haystack = ($skipHtmlEntities) ? Strings::htmlEntityDecode(Strings::protectHTMLTags($haystack)) : $haystack;
93
        $haystack = (false === $preserveNbsps) ? Strings::cutNbsps($haystack) : $haystack;
94
95
        return [
96
            'pattern' => $pattern,
97
            'haystack' => $haystack,
98
        ];
99
    }
100
101
    /**
102
     * @param string $needle
103
     * @param bool   $skipHtmlEntities
104
     * @param bool   $exactMatch
105
     * @param bool   $caseSensitive
106
     * @param bool   $preserveNbsps
107
     *
108
     * @return string
109
     */
110
    private static function getSearchPattern($needle, $skipHtmlEntities = true, $exactMatch = false, $caseSensitive = false, $preserveNbsps = false)
111
    {
112
        $needle = (false === $preserveNbsps) ? Strings::cutNbsps($needle) : $needle;
113
        $needle = ($skipHtmlEntities) ? Strings::htmlEntityDecode($needle) : $needle;
114
115
        $pattern = '/';
116
        $pattern .= ($exactMatch) ? RegexEscaper::escapeWholeTextPattern($needle) : RegexEscaper::escapeRegularPattern($needle);
117
        $pattern .= '/';
118
        $pattern .= (false === $caseSensitive) ? 'i' : '';
119
        $pattern .= ($skipHtmlEntities) ? 'u' : '';
120
121
        return $pattern;
122
    }
123
124
    /**
125
     * @param string $haystack
126
     * @param string $needle
127
     * @param string $replacement
128
     * @param bool $skipHtmlEntities
129
     * @param bool $exactMatch
130
     * @param bool $caseSensitive
131
     * @param bool $preserveNbsps
132
     *
133
     * @return array
134
     */
135
    public static function findAndReplace($haystack, $needle, $replacement, $skipHtmlEntities = true, $exactMatch = false, $caseSensitive = false, $preserveNbsps = false)
136
    {
137
        $patternAndHaystack = self::getPatternAndHaystack($haystack, $needle, $skipHtmlEntities, $exactMatch, $caseSensitive, $preserveNbsps);
138
        $replacement = Replacer::replace($patternAndHaystack['pattern'], $replacement, $patternAndHaystack['haystack']);
139
140
        if($skipHtmlEntities === true){
141
            $replacement = Strings::unprotectHTMLTags($replacement);
142
        }
143
144
        return [
145
            'replacement' => $replacement,
146
            'occurrencies' => self::find($haystack, $needle, $skipHtmlEntities, $exactMatch, $caseSensitive, $preserveNbsps),
147
        ];
148
    }
149
}
150