Test Failed
Push — master ( 81711e...bf8774 )
by Sebastian
03:41
created

Mailcode_StringContainer::updateString()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 30
rs 9.7998
1
<?php
2
/**
3
 * File containing the {@see Mailcode_StringContainer} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Parser
7
 * @see Mailcode_StringContainer
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use function AppUtils\parseVariable;
15
16
/**
17
 * Utility class made to hold a string, and notify listeners
18
 * whenever the string is modified.
19
 *
20
 * @package Mailcode
21
 * @subpackage Parser
22
 * @author Sebastian Mordziol <[email protected]>
23
 */
24
class Mailcode_StringContainer
25
{
26
    const ERROR_INVALID_CALLABLE = 65701;
27
    const ERROR_UPDATE_CALLED_DURING_UPDATE_OPERATION = 65702;
28
    
29
   /**
30
    * @var string
31
    */
32
    private $subject;
33
    
34
   /**
35
    * @var integer
36
    */
37
    private static $listenerCounter = 0;
38
    
39
   /**
40
    * @var callable[]
41
    */
42
    private $listeners = array();
43
    
44
   /**
45
    * @var boolean
46
    */
47
    private $updating = false;
48
    
49
   /**
50
    * @var integer
51
    */
52
    private static $idCounter = 0;
53
    
54
   /**
55
    * @var integer
56
    */
57
    private $id;
58
    
59
   /**
60
    * @var integer
61
    */
62
    private $length;
63
    
64
    public function __construct(string $subject)
65
    {
66
        self::$idCounter++;
67
        
68
        $this->id = self::$idCounter;
69
        
70
        $this->updateString($subject);
71
    }
72
    
73
    public function getID() : int
74
    {
75
        return $this->id;
76
    }
77
    
78
   /**
79
    * Updates the string with the specified string.
80
    * Notifies all listeners of the change.
81
    * 
82
    * @param string $subject
83
    * @throws Mailcode_Exception
84
    * @return bool Whether the string had modifications.
85
    * 
86
    * @see Mailcode_StringContainer::ERROR_UPDATE_CALLED_DURING_UPDATE_OPERATION
87
    */
88
    public function updateString(string $subject) : bool
89
    {
90
        // avoid triggering an update if there are no changes in the string
91
        if($subject === $this->subject)
92
        {
93
            return false;
94
        }
95
        
96
        if($this->updating)
97
        {
98
            throw new Mailcode_Exception(
99
                'Cannot modify subject string during update',
100
                'Tried calling update() on a subject string during a running update, which is not allowed.',
101
                self::ERROR_UPDATE_CALLED_DURING_UPDATE_OPERATION
102
            );
103
        }
104
        
105
        $this->updating = true;
106
        
107
        $this->subject = $subject;
108
        $this->length = mb_strlen($this->subject);
109
        
110
        foreach($this->listeners as $listener)
111
        {
112
            $listener($this);
113
        }
114
        
115
        $this->updating = false;
116
        
117
        return true;
118
    }
119
    
120
   /**
121
    * Retrieves the stored string.
122
    * 
123
    * @return string
124
    */
125
    public function getString() : string
126
    {
127
        return $this->subject;
128
    }
129
    
130
   /**
131
    * Adds a listener that will be informed every time the string is modified.
132
    * The callback gets the string container instance as parameter.
133
    * 
134
    * @param callable $callback
135
    * @throws Mailcode_Exception If it is not a valid callable.
136
    * @return int The listener number, to be able to remove it using `removeListener()`.
137
    * 
138
    * @see Mailcode_StringContainer::removeListener()
139
    * @see Mailcode_StringContainer::ERROR_INVALID_CALLABLE
140
    */
141
    public function addListener($callback) : int
142
    {
143
        self::$listenerCounter++;
144
        
145
        if(!is_callable($callback))
146
        {
147
            throw new Mailcode_Exception(
148
                'Not a valid callable',
149
                sprintf(
150
                    'The specified callback parameter is not callable: [%s].',
151
                    parseVariable($callback)->enableType()->toString()
152
                ),
153
                self::ERROR_INVALID_CALLABLE
154
            );
155
        }
156
        
157
        $this->listeners[self::$listenerCounter] = $callback;
158
            
159
        return self::$listenerCounter;
160
    }
161
    
162
    public function getLength() : int
163
    {
164
        return $this->length;
165
    }
166
    
167
   /**
168
    * Removes an existing listener by its ID.
169
    * Has no effect if it does not exist, or has already been removed.
170
    * 
171
    * @param int $listenerID
172
    */
173
    public function removeListener(int $listenerID) : void
174
    {
175
        if(isset($this->listeners[$listenerID]))
176
        {
177
            unset($this->listeners[$listenerID]);
178
        }
179
    }
180
    
181
   /**
182
    * Replaces all substrings matching needle with the replacement text.
183
    *  
184
    * @param string $needle
185
    * @param string $replacement
186
    * @return bool
187
    */
188
    public function replaceSubstrings(string $needle, string $replacement) : bool
189
    {
190
        $string = str_replace($needle, $replacement, $this->subject);
191
        
192
        return $this->updateString($string);
193
    }
194
195
   /**
196
    * Get the position of a substring in the string.
197
    * 
198
    * @param string $needle
199
    * @return int|bool The zero-based position, or false if not found.
200
    */
201
    public function getSubstrPosition(string $needle)
202
    {
203
        return mb_strpos($this->subject, $needle);
204
    }
205
    
206
    public function getSubstr(int $start, ?int $length=null) : string
207
    {
208
        return mb_substr($this->subject, $start, $length);
209
    }
210
}
211