Passed
Push — master ( 5d6ae0...deb6f9 )
by Sebastian
03:48
created

addWildcards()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Mailcode;
6
7
class Mailcode_Translator_Syntax_ApacheVelocity_Contains_StatementBuilder
8
{
9
    const ERROR_INVALID_LIST_VARIABLE_NAME = 76701;
10
11
    /**
12
     * @var Mailcode_Variables_Variable
13
     */
14
    private $variable;
15
16
    /**
17
     * @var bool
18
     */
19
    private $caseSensitive;
20
21
    /**
22
     * @var Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral[]
23
     */
24
    private $searchTerms;
25
26
    /**
27
     * @var string
28
     */
29
    private $containsType;
30
31
    /**
32
     * @var Mailcode_Translator_Syntax_ApacheVelocity
33
     */
34
    private $translator;
35
36
    /**
37
     * @var bool
38
     */
39
    private $regexEnabled;
40
41
    /**
42
     * Mailcode_Translator_Syntax_ApacheVelocity_Contains_StatementBuilder constructor.
43
     * @param Mailcode_Translator_Syntax_ApacheVelocity $translator
44
     * @param Mailcode_Variables_Variable $variable
45
     * @param bool $caseSensitive
46
     * @param bool $regexEnabled
47
     * @param Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral[] $searchTerms
48
     * @param string $containsType
49
     */
50
    public function __construct(Mailcode_Translator_Syntax_ApacheVelocity $translator, Mailcode_Variables_Variable $variable, bool $caseSensitive, bool $regexEnabled, array $searchTerms, string $containsType)
51
    {
52
        $this->translator = $translator;
53
        $this->variable = $variable;
54
        $this->caseSensitive = $caseSensitive;
55
        $this->searchTerms = $searchTerms;
56
        $this->containsType = $containsType;
57
        $this->regexEnabled = $regexEnabled;
58
    }
59
60
    /**
61
     * Is this a not contains command? (list or regular)
62
     * @return bool
63
     */
64
    public function isNotContains() : bool
65
    {
66
        return strstr($this->containsType, 'not-contains') !== false;
67
    }
68
69
    /**
70
     * Is this a contains command to be used on a list variable?
71
     * @return bool
72
     */
73
    public function isList() : bool
74
    {
75
        return strstr($this->containsType, 'list-') !== false;
76
    }
77
78
    /**
79
     * Gets the sign to prepend the command with, i.e.
80
     * whether to add the negation "!" or not, depending
81
     * on the type of command.
82
     *
83
     * @return string
84
     */
85
    public function getSign() : string
86
    {
87
        if($this->isNotContains())
88
        {
89
            return '!';
90
        }
91
92
        return '';
93
    }
94
95
    /**
96
     * Gets the logical connector sign to combine several search
97
     * terms with, i.e. "&&" or "||" depending on whether it is
98
     * a regular contains or not contains.
99
     *
100
     * @return string
101
     */
102
    public function getConnector()
103
    {
104
        if($this->isNotContains())
105
        {
106
            return '&&';
107
        }
108
109
        return '||';
110
    }
111
112
    /**
113
     * @return string
114
     * @throws Mailcode_Exception
115
     */
116
    public function render() : string
117
    {
118
        $parts = array();
119
120
        foreach($this->searchTerms as $token)
121
        {
122
            $parts[] = $this->renderCommand($token);
123
        }
124
125
        return implode(' '.$this->getConnector().' ', $parts);
126
    }
127
128
    /**
129
     * @param Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm
130
     * @return string
131
     * @throws Mailcode_Exception
132
     */
133
    private function renderCommand(Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm) : string
134
    {
135
        if($this->isList())
136
        {
137
            $command = $this->renderListCommand($searchTerm);
138
        }
139
        else
140
        {
141
            $command = $this->renderRegularCommand($searchTerm);
142
        }
143
144
        return $this->getSign().$command;
145
    }
146
147
    private function renderRegularCommand(Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm) : string
148
    {
149
        return sprintf(
150
            '%s.matches(%s)',
151
            $this->variable->getFullName(),
152
            $this->renderRegex($searchTerm)
153
        );
154
    }
155
156
    /**
157
     * @param Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm
158
     * @return string
159
     * @throws Mailcode_Exception
160
     */
161
    private function renderListCommand(Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm) : string
162
    {
163
        $name = $this->parseVarName();
164
165
        return sprintf(
166
            '$map.hasElement(%s.list(), "%s", %s)',
167
            '$'.$name['path'],
168
            $name['name'],
169
            $this->renderRegex($searchTerm)
170
        );
171
    }
172
173
    private function renderRegex(Mailcode_Parser_Statement_Tokenizer_Token_StringLiteral $searchTerm) : string
174
    {
175
        $opts = 's';
176
        if($this->caseSensitive)
177
        {
178
            $opts = 'is';
179
        }
180
181
        $filtered = trim($searchTerm->getNormalized(), '"');
182
183
        if(!$this->regexEnabled)
184
        {
185
            $filtered = $this->translator->filterRegexString($filtered);
186
            $filtered = $this->addWildcards($filtered);
187
        }
188
189
        return sprintf(
190
            '"(?%s)%s"',
191
            $opts,
192
            $filtered
193
        );
194
    }
195
196
    /**
197
     * Adds the search wildcard before or after the search string
198
     * for the `list-begins-with` and `list-ends-with` command
199
     * flavors.
200
     *
201
     * @param string $searchTerm
202
     * @return string
203
     */
204
    private function addWildcards(string $searchTerm) : string
205
    {
206
        if($this->containsType === 'list-begins-with')
207
        {
208
            return '.*'.$searchTerm;
209
        }
210
211
        if($this->containsType === 'list-ends-with')
212
        {
213
            return $searchTerm.'.*';
214
        }
215
216
        return $searchTerm;
217
    }
218
219
    /**
220
     * @return array<string,string>
221
     * @throws Mailcode_Exception
222
     */
223
     private function parseVarName() : array
224
     {
225
         $tokens = explode('.', ltrim($this->variable->getFullName(), '$'));
226
227
         if(count($tokens) === 2)
228
         {
229
             return array(
230
                 'path' => $tokens[0],
231
                 'name' => $tokens[1]
232
             );
233
         }
234
235
         throw new Mailcode_Exception(
236
             'Invalid variable name for a list property.',
237
             sprintf(
238
                 'Exactly 2 parts are required, variable [%s] has [%s].',
239
                 $this->variable->getFullName(),
240
                 count($tokens)
241
             ),
242
             self::ERROR_INVALID_LIST_VARIABLE_NAME
243
         );
244
     }
245
}
246
247