Passed
Push — master ( caf38d...9bab94 )
by Sebastian
02:11
created

Mailcode_Parser_Safeguard::makeHighlighted()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * File containing the {@see Mailcode_Parser_Safeguard} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Parser
7
 * @see Mailcode_Parser_Safeguard
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
/**
15
 * Command safeguarder: used to replace the mailcode commands
16
 * in a string with placeholders, to allow safe text transformation
17
 * and filtering operations on strings, without risking to break 
18
 * any of the contained commands (if any).
19
 * 
20
 * Usage:
21
 * 
22
 * <pre>
23
 * $safeguard = Mailcode::create()->createSafeguard($sourceString);
24
 * 
25
 * // replace all commands with placeholders
26
 * $workString = $safeguard->makeSafe();
27
 * 
28
 * // dome something with the work string - filtering, parsing...
29
 * 
30
 * // restore all command placeholders
31
 * $resultString = $safeguard->makeWhole($workString);
32
 * </pre>
33
 * 
34
 * Note that by default, the placeholders are delimited with
35
 * two underscores, e.g. <code>__PCH0001__</code>. If the text
36
 * transformations include replacing or modifying underscores,
37
 * you should use a different delimiter:
38
 * 
39
 * <pre>
40
 * $safeguard = Mailcode::create()->createSafeguard($sourceString);
41
 * 
42
 * // change the delimiter to %%. Can be any arbitrary string.
43
 * $safeguard->setDelimiter('%%');
44
 * </pre>
45
 *
46
 * @package Mailcode
47
 * @subpackage Parser
48
 * @author Sebastian Mordziol <[email protected]>
49
 */
50
class Mailcode_Parser_Safeguard
51
{
52
    const ERROR_INVALID_COMMANDS = 47801;
53
    
54
    const ERROR_COMMAND_PLACEHOLDER_MISSING = 47802;
55
    
56
    const ERROR_EMPTY_DELIMITER = 47803;
57
    
58
   /**
59
    * @var Mailcode_Parser
60
    */
61
    protected $parser;
62
    
63
   /**
64
    * @var Mailcode_Collection
65
    */
66
    protected $commands;
67
    
68
   /**
69
    * @var string
70
    */
71
    protected $originalString;
72
    
73
   /**
74
    * @var Mailcode_Collection
75
    */
76
    protected $collection;
77
    
78
   /**
79
    * Counter for the placeholders, global for all placeholders.
80
    * @var integer
81
    */
82
    private static $counter = 0;
83
    
84
   /**
85
    * @var Mailcode_Parser_Safeguard_Placeholder[]
86
    */
87
    protected $placeholders;
88
    
89
   /**
90
    * @var string
91
    */
92
    protected $delimiter = '__';
93
    
94
    public function __construct(Mailcode_Parser $parser, string $subject)
95
    {
96
        $this->parser = $parser;
97
        $this->originalString = $subject;
98
    }
99
    
100
   /**
101
    * Sets the delimiter character sequence used to prepend
102
    * and append to the placeholders.
103
    * 
104
    * The delimiter's default is "__" (two underscores).
105
    * 
106
    * @param string $delimiter
107
    * @return Mailcode_Parser_Safeguard
108
    */
109
    public function setDelimiter(string $delimiter) : Mailcode_Parser_Safeguard
110
    {
111
        if(empty($delimiter))
112
        {
113
            throw new Mailcode_Exception(
114
                'Empty delimiter',
115
                'Delimiters may not be empty.',
116
                self::ERROR_EMPTY_DELIMITER
117
            );
118
        }
119
        
120
        $this->delimiter = $delimiter;
121
        
122
        return $this;
123
    }
124
    
125
    public function getDelimiter() : string
126
    {
127
        return $this->delimiter;
128
    }
129
    
130
   /**
131
    * Retrieves the safe string in which all commands have been replaced
132
    * by placeholder strings.
133
    * 
134
    * @return string
135
    * @throws Mailcode_Exception 
136
    *
137
    * @see Mailcode_Parser_Safeguard::ERROR_INVALID_COMMANDS
138
    */
139
    public function makeSafe() : string
140
    {
141
        $this->requireValidCollection();
142
        
143
        $replaces = $this->getReplaces();
144
                
145
        return str_replace(array_values($replaces), array_keys($replaces), $this->originalString);
146
    }
147
    
148
    protected function getReplaces(bool $highlighted=false) : array
149
    {
150
        $placeholders = $this->getPlaceholders();
151
        
152
        $replaces = array();
153
        
154
        foreach($placeholders as $placeholder)
155
        {
156
            $replace = '';
157
            
158
            if($highlighted)
159
            {
160
                $replace = $placeholder->getHighlightedText();
161
            }
162
            else 
163
            {
164
                $replace = $placeholder->getOriginalText();
165
            }
166
            
167
            $replaces[$placeholder->getReplacementText()] = $replace;
168
        }
169
        
170
        return $replaces;
171
    }
172
    
173
    
174
   /**
175
    * Retrieves all placeholders that have to be added to
176
    * the subject text.
177
    * 
178
    * @return \Mailcode\Mailcode_Parser_Safeguard_Placeholder[]
179
    */
180
    public function getPlaceholders()
181
    {
182
        if(isset($this->placeholders))
183
        {
184
            return $this->placeholders;
185
        }
186
        
187
        $this->placeholders = array();
188
        
189
        $cmds = $this->getCollection()->getCommands();
190
        
191
        foreach($cmds as $command)
192
        {
193
            self::$counter++;
194
            
195
            $this->placeholders[] = new Mailcode_Parser_Safeguard_Placeholder(
196
                self::$counter,
197
                $command,
198
                $this
199
            );
200
        }
201
202
        return $this->placeholders;
203
    }
204
    
205
    protected function restore(string $string, bool $highlighted=false) : string
206
    {
207
        $this->requireValidCollection();
208
        
209
        $replaces = $this->getReplaces($highlighted);
210
        
211
        $placeholderStrings = array_keys($replaces);
212
        
213
        foreach($placeholderStrings as $search)
214
        {
215
            if(!strstr($string, $search))
216
            {
217
                throw new Mailcode_Exception(
218
                    'Command placeholder not found',
219
                    sprintf(
220
                        'A placeholder for a command could not be found in the string to restore: [%s].',
221
                        $search
222
                    ),
223
                    self::ERROR_COMMAND_PLACEHOLDER_MISSING
224
                );
225
            }
226
        }
227
        
228
        return str_replace($placeholderStrings, array_values($replaces), $string);
229
    }
230
    
231
   /**
232
    * Makes the string whole again after transforming or filtering it,
233
    * by replacing the command placeholders with the original commands.
234
    *
235
    * @param string $string
236
    * @return string
237
    * @throws Mailcode_Exception
238
    *
239
    * @see Mailcode_Parser_Safeguard::ERROR_INVALID_COMMANDS
240
    * @see Mailcode_Parser_Safeguard::ERROR_COMMAND_PLACEHOLDER_MISSING
241
    */
242
    public function makeWhole(string $string) : string
243
    {
244
        return $this->restore($string, false);
245
    }
246
247
   /**
248
    * Like makeWhole(), but replaces the commands with a syntax
249
    * highlighted version, meant for human readable texts only.
250
    * 
251
    * Note: the commands lose their functionality (They cannot be 
252
    * parsed from that string again).
253
    *
254
    * @param string $string
255
    * @return string
256
    * @throws Mailcode_Exception
257
    *
258
    * @see Mailcode_Parser_Safeguard::ERROR_INVALID_COMMANDS
259
    * @see Mailcode_Parser_Safeguard::ERROR_COMMAND_PLACEHOLDER_MISSING
260
    */
261
    public function makeHighlighted(string $string) : string
262
    {
263
        return $this->restore($string, true);
264
    }
265
    
266
   /**
267
    * Retrieves the commands collection contained in the string.
268
    * 
269
    * @return Mailcode_Collection
270
    */
271
    public function getCollection() : Mailcode_Collection
272
    {
273
        if(isset($this->collection))
274
        {
275
            return $this->collection;
276
        }
277
        
278
        $this->collection = $this->parser->parseString($this->originalString);
279
        
280
        return $this->collection;
281
    }
282
    
283
    public function isValid() : bool
284
    {
285
        return $this->getCollection()->isValid();
286
    }
287
    
288
   /**
289
    * @throws Mailcode_Exception
290
    * 
291
    * @see Mailcode_Parser_Safeguard::ERROR_INVALID_COMMANDS
292
    */
293
    protected function requireValidCollection() : void
294
    {
295
        if($this->getCollection()->isValid())
296
        {
297
            return;
298
        }
299
        
300
        throw new Mailcode_Exception(
301
            'Cannot safeguard invalid commands',
302
            sprintf(
303
                'The collection contains invalid commands. Safeguarding is only allowed with valid commands. Source string:<br>'.
304
                $this->originalString
305
            ),
306
            self::ERROR_INVALID_COMMANDS
307
        );
308
    }
309
}
310