Passed
Push — master ( 13736a...b75b16 )
by Sebastian
02:19
created

Mailcode_Parser::prepareString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
2
/**
3
 * File containing the {@see Mailcode_Parser} class.
4
 * 
5
 * @package Mailcode
6
 * @subpackage Parser
7
 * @see Mailcode_Parser
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use AppUtils\ConvertHelper;
15
16
/**
17
 * Mailcode parser, capable of detecting commands in strings.
18
 * 
19
 * @package Mailcode
20
 * @subpackage Parser
21
 * @author Sebastian Mordziol <[email protected]>
22
 */
23
class Mailcode_Parser
24
{
25
    const COMMAND_REGEX_PARTS = array( 
26
        '{\s*([a-z]+)\s*}',
27
        '{\s*([a-z]+)\s*:([^}]*)}',
28
        '{\s*([a-z]+)\s+([a-z]+)\s*:([^}]*)}'
29
    );
30
    
31
   /**
32
    * @var Mailcode
33
    */
34
    protected $mailcode;
35
    
36
   /**
37
    * @var Mailcode_Commands
38
    */
39
    protected $commands;
40
    
41
    public function __construct(Mailcode $mailcode)
42
    {
43
        $this->mailcode = $mailcode;
44
        $this->commands = $this->mailcode->getCommands();
45
    }
46
    
47
   /**
48
    * Gets the regex format string used to detect commands.
49
    * 
50
    * @return string
51
    */
52
    protected static function getRegex() : string
53
    {
54
        return '/'.implode('|', self::COMMAND_REGEX_PARTS).'/sixU';
55
    }
56
    
57
   /**
58
    * Parses a string to detect all commands within. Returns a
59
    * collection instance that contains information on all the 
60
    * commands.
61
    * 
62
    * @param string $string
63
    * @return Mailcode_Collection A collection with all unique commands found.
64
    */
65
    public function parseString(string $string) : Mailcode_Collection
66
    {
67
        $collection = new Mailcode_Collection();
68
        
69
        $string = $this->prepareString($string);
70
        
71
        $matches = array();
72
        preg_match_all(self::getRegex(), $string, $matches, PREG_PATTERN_ORDER);
73
        
74
        $total = count($matches[0]);
75
        
76
        for($i=0; $i < $total; $i++)
77
        {
78
            $match = $this->parseMatch($matches, $i);
79
            
80
            $this->processMatch($match, $collection);
81
        }
82
        
83
        return $collection;
84
    }
85
    
86
    protected function prepareString(string $subject) : string
87
    {
88
        if(!ConvertHelper::isStringHTML($subject))
89
        {
90
            return $subject;
91
        }
92
93
        // remove all <style> tags to avoid conflicts with CSS code
94
        return preg_replace('%<style\b[^>]*>(.*?)</style>%six', '', $subject);
95
    }
96
    
97
   /**
98
    * Processes a single match found in the string: creates the command,
99
    * and adds it to the collection if it's a valid command, or to the list
100
    * of invalid commands otherwise.
101
    * 
102
    * @param Mailcode_Parser_Match $match
103
    * @param Mailcode_Collection $collection
104
    */
105
    protected function processMatch(Mailcode_Parser_Match $match, Mailcode_Collection $collection) : void
106
    {
107
        $name = $match->getName();
108
        
109
        if(!$this->commands->nameExists($name))
110
        {
111
            $collection->addErrorMessage(
112
                $match->getMatchedString(),
113
                t('No command found with the name %1$s.', $name),
114
                Mailcode_Commands_Command::VALIDATION_UNKNOWN_COMMAND_NAME
115
            );
116
            return;
117
        }
118
        
119
        $cmd = $this->commands->createCommand(
120
            $this->commands->getIDByName($name),
121
            $match->getType(),
122
            $match->getParams(),
123
            $match->getMatchedString()
124
        );
125
        
126
        if($cmd->isValid())
127
        {
128
            $collection->addCommand($cmd);
129
            return;
130
        }
131
        
132
        $collection->addInvalidCommand($cmd);
133
    }
134
    
135
   /**
136
    * Parses a single regex match: determines which named group
137
    * matches, and retrieves the according information.
138
    * 
139
    * @param array[] $matches The regex results array.
140
    * @param int $index The matched index.
141
    * @return Mailcode_Parser_Match
142
    */
143
    protected function parseMatch(array $matches, int $index) : Mailcode_Parser_Match
144
    {
145
        $name = ''; // the command name, e.g. "showvar"
146
        $type = '';
147
        $params = '';
148
        
149
        // 1 = single command
150
        // 2 = parameter command, name
151
        // 3 = parameter command, params
152
        // 4 = parameter type command, name
153
        // 5 = parameter type command, type
154
        // 6 = parameter type command, params
155
        
156
        if(!empty($matches[1][$index]))
157
        {
158
            $name = $matches[1][$index];
159
        }
160
        else if(!empty($matches[2][$index]))
161
        {
162
            $name = $matches[2][$index];
163
            $params = $matches[3][$index];
164
        }
165
        else if(!empty($matches[4][$index]))
166
        {
167
            $name = $matches[4][$index];
168
            $type = $matches[5][$index];
169
            $params = $matches[6][$index];
170
        }
171
        
172
        return new Mailcode_Parser_Match(
173
            $name, 
174
            $type, 
175
            $params, 
176
            $matches[0][$index]
177
        );
178
    }
179
    
180
   /**
181
    * Creates an instance of the safeguard tool, which
182
    * is used to safeguard commands in a string with placeholders.
183
    * 
184
    * @param string $subject The string to use to safeguard commands in.
185
    * @return Mailcode_Parser_Safeguard
186
    * @see Mailcode_Parser_Safeguard
187
    */
188
    public function createSafeguard(string $subject) : Mailcode_Parser_Safeguard
189
    {
190
        return new Mailcode_Parser_Safeguard($this, $subject);
191
    }
192
    
193
   /**
194
    * Creates a statement parser, which is used to validate arbitrary
195
    * command statements.
196
    * 
197
    * @param string $statement
198
    * @return Mailcode_Parser_Statement
199
    */
200
    public function createStatement(string $statement) : Mailcode_Parser_Statement
201
    {
202
        return new Mailcode_Parser_Statement($statement);
203
    }
204
}
205