Passed
Push — master ( bfd298...52b2ea )
by Sebastian
03:13
created

Localization_Parser_Language::trimText()   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
declare(strict_types=1);
4
5
namespace AppLocalize;
6
7
abstract class Localization_Parser_Language
8
{
9
    const ERROR_SOURCE_FILE_NOT_FOUND = 40501;
10
    
11
    const ERROR_FAILED_READING_SOURCE_FILE = 40502;
12
    
13
    protected $debug = false;
14
    
15
    /**
16
     * @var Localization_Parser
17
     */
18
    protected $parser;
19
20
   /**
21
    * The function names that are included in the search.
22
    * @var array
23
    */
24
    protected $functionNames = array();
25
    
26
   /**
27
    * The tokens definitions.
28
    * @var array
29
    */
30
    protected $tokens = array();
31
    
32
   /**
33
    * The total amount of tokens found in the content.
34
    * @var integer
35
    */
36
    protected $totalTokens = 0;
37
    
38
   /**
39
    * All texts that have been collected.
40
    * @var array
41
    */
42
    protected $texts = array();
43
    
44
   /**
45
    * @var string
46
    */
47
    protected $content = '';
48
49
   /**
50
    * @var string
51
    */
52
    protected $id;
53
    
54
   /**
55
    * @var array
56
    */
57
    protected $warnings = array();
58
    
59
   /**
60
    * The source file that was parsed (if any)
61
    * @var string
62
    */
63
    protected $sourceFile = '';
64
    
65
    public function __construct(Localization_Parser $parser)
66
    {
67
        $this->parser = $parser;
68
        $this->functionNames = $this->getFunctionNames();
69
    }
70
    
71
    abstract protected function getTokens() : array;
72
    
73
   /**
74
    * Retrieves the ID of the language.
75
    * @return string E.g. "PHP", "Javascript"
76
    */
77
    public function getID() : string
78
    {
79
        if(!isset($this->id)) {
80
            $this->id = str_replace('AppLocalize\Localization_Parser_Language_', '', get_class($this));
81
        }
82
        
83
        return $this->id;
84
    }
85
    
86
    public function hasSourceFile() : bool
87
    {
88
        return !empty($this->sourceFile);
89
    }
90
    
91
    public function getSourceFile() : string
92
    {
93
        return $this->sourceFile;
94
    }
95
    
96
   /**
97
    * Parses the code from a file.
98
    * 
99
    * @param string $path
100
    * @throws Localization_Exception
101
    */
102
    public function parseFile(string $path) : void
103
    {
104
        if(!file_exists($path)) 
105
        {
106
            throw new Localization_Exception(
107
                sprintf('Source code file [%s] not found', basename($path)),
108
                sprintf(
109
                    'Tried looking for the file in path [%s].',
110
                    $path
111
                ),
112
                self::ERROR_SOURCE_FILE_NOT_FOUND
113
            );
114
        }
115
        
116
        $this->sourceFile = $path;
117
        $this->content = file_get_contents($path);
118
        
119
        if($this->content !== false) {
120
            $this->parse();
121
            return;
122
        }
123
        
124
        throw new Localization_Exception(
125
            sprintf('Source code file [%s] could not be read', basename($path)),
126
            sprintf(
127
                'Tried opening the file located at [%s].',
128
                $path
129
            ),
130
            self::ERROR_FAILED_READING_SOURCE_FILE
131
        );
132
    }
133
    
134
   /**
135
    * Parses a source code string.
136
    * @param string $content
137
    */
138
    public function parseString($content) : void
139
    {
140
        $this->content = $content;
141
        $this->sourceFile = '';
142
        
143
        $this->parse();
144
    }
145
    
146
    protected function parse()
147
    {
148
        $this->texts = array();
149
        $this->warnings = array();
150
        $this->tokens = $this->getTokens();
151
        $this->totalTokens = count($this->tokens);
152
        
153
        for($i = 0; $i < $this->totalTokens; $i++)
154
        {
155
            $token = $this->createToken($this->tokens[$i]);
156
            
157
            if($token->isTranslationFunction()) {
158
                $this->parseToken($i+1, $token);
159
            }
160
        }
161
    }
162
    
163
    public function getTexts()
164
    {
165
        return $this->texts;
166
    }
167
    
168
    protected function addResult($text, $line=null)
169
    {
170
        $this->log(sprintf('Line [%1$s] | Found string [%2$s]', $line, $text));
171
        
172
        $this->texts[] = array(
173
            'text' => $text, 
174
            'line' => $line
175
        );
176
    }
177
178
   /**
179
    * Retrieves a list of all the function names that are
180
    * used as translation functions in the language.
181
    * @return array
182
    */
183
    public function getFunctionNames() : array
184
    {
185
        return $this->createToken('dummy')->getFunctionNames();
186
    }
187
188
    protected function log($message)
189
    {
190
        Localization::log(sprintf('%1$s parser | %2$s', $this->getID(), $message));
191
    }
192
193
   /**
194
    * Adds a warning message when a text cannot be parsed correctly for some reason.
195
    * 
196
    * @param Localization_Parser_Token $token
197
    * @param string $message
198
    * @return Localization_Parser_Warning
199
    */
200
    protected function addWarning(Localization_Parser_Token $token, string $message) : Localization_Parser_Warning
201
    {
202
        $warning = new Localization_Parser_Warning($this, $token, $message);
203
        
204
        $this->warnings[] = $warning;
205
        
206
        return $warning;
207
    }
208
    
209
   /**
210
    * Whether any warnings were generated during parsing.
211
    * @return bool
212
    */
213
    public function hasWarnings() : bool
214
    {
215
        return !empty($this->warnings);
216
    }
217
    
218
   /**
219
    * Retrieves all warnings that were generated during parsing,
220
    * if any.
221
    * 
222
    * @return Localization_Parser_Warning[]
223
    */
224
    public function getWarnings()
225
    {
226
        return $this->warnings;
227
    }
228
    
229
   /**
230
    * Creates a token instance: this retrieves information on
231
    * the language token being parsed.
232
    * 
233
    * @param array|string $definition The token definition.
234
    * @param Localization_Parser_Token $parentToken
235
    * @return Localization_Parser_Token
236
    */
237
    protected function createToken($definition, Localization_Parser_Token $parentToken=null) : Localization_Parser_Token
238
    {
239
        $class = '\AppLocalize\Localization_Parser_Token_'.$this->getID();
240
        
241
        return new $class($definition, $parentToken);
242
    }
243
244
   /**
245
    * Parses a translation function token.
246
    * 
247
    * @param int $number
248
    * @param Localization_Parser_Token $token
249
    */
250
    protected function parseToken(int $number, Localization_Parser_Token $token)
251
    {
252
        $textParts = array();
253
        $max = $number + 200;
254
        $open = false;
255
        
256
        for($i = $number; $i < $max; $i++)
257
        {
258
            if(!isset($this->tokens[$i])) {
259
                break;
260
            }
261
            
262
            $subToken = $this->createToken($this->tokens[$i], $token);
263
            
264
            if(!$open && $subToken->isOpeningFuncParams())
265
            {
266
                $open = true;
267
                continue;
268
            }
269
            
270
            if($open && $subToken->isClosingFuncParams()) {
271
                break;
272
            }
273
            
274
            // additional parameters in the translation function, we don't want to capture these now.
275
            if($open && $subToken->isArgumentSeparator()) {
276
                break;
277
            }
278
            
279
            if($open && $subToken->isEncapsedString())
280
            {
281
                $textParts[] = $this->trimText($subToken->getValue());
282
                continue;
283
            }
284
            
285
            if($open && $subToken->isVariableOrFunction()) {
286
                $textParts = null;
287
                $this->addWarning($subToken, t('Variables or functions are not supported in translation functions.'));
288
                break;
289
            }
290
        }
291
        
292
        if(empty($textParts)) {
293
            return;
294
        }
295
        
296
        $text = implode('', $textParts);
297
        
298
        $this->addResult($text, $token->getLine());
299
    }
300
    
301
    protected function debug($text)
302
    {
303
        if($this->debug) {
304
            echo $text;
305
        }
306
    }
307
308
    /**
309
     * Used to trim the text from the code. Also strips slashes
310
     * from the text, as it comes raw from the code.
311
     *
312
     * @param string $text
313
     * @return string
314
     */
315
    public function trimText($text)
316
    {
317
        return stripslashes(trim($text, "'\""));
318
    }
319
}