Completed
Push — development ( f93eb8...ffa1a0 )
by Thomas
20s
created

htdocs/src/OcLegacy/SmartyPlugins/prefilter.t.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/***************************************************************************
3
 * You can find the license in the docs directory
4
 *
5
 *
6
 * Replacement for smarty-gettext
7
 *
8
 * Advantages:
9
 *  - caching of gettext-translations within nocache-sections
10
 *  - gettext is called at compile-time and not at page redering
11
 *    this improves performance when caching is disabled, too
12
 *  - translations stored in mysql-database
13
 *
14
 * Disadvantages:
15
 *  - you have to clear the cache after changing translations
16
 *  - use the locale in compile_id (maybe your compile_dir will grow)
17
 *  - only one plural version can be supplied
18
 *
19
 * Working:
20
 *
21
 * Block-definition:
22
 * {t[ count=<number> plural=<plural-text>][ 1=<value1>[ 2=<value2>[...]]]}<text-to-translate>{/t}
23
 *
24
 * <number>             ... number=1 means "use singluar", number!=1 means "use plural"
25
 * <plural>             ... plural version of <text-to-translate>
26
 * <value[1..n]>        ... values of parameters (can be string, number or smarty-variable with modifiers)
27
 * <text-to-translate>  ... text that has to be translated. If no translation exists, this text will be used
28
 *                          parameters <value[1..n]> can be used with %1 ... %n
29
 *
30
 * Expamle:
31
 * {t 1="text"}my %1{/t}
32
 *
33
 * If no plural is given, the block will be replaced by the appropriate translation
34
 * If plural is given, <text-to-translate> and <plural-text> will be translated, but block will persist and
35
 * processed by block.t at rendering time.
36
 *
37
 * Original idea by Sagi Bashari <[email protected]>
38
 * see http://sourceforge.net/projects/smarty-gettext/
39
 *
40
 * Concept was heavily modified by Opencaching.de
41
 *
42
 * Copyright 2007 Opencaching.de
43
 */
44
45
/*
46
 * Smarty plugin for gettext compilation
47
 *
48
 * Find all {t}...{/t} and translate its input with gettext
49
 *
50
 */
51
/**
52
 * @param $source
53
 * @param $smarty
54
 *
55
 * @return string
56
 */
57
function smarty_prefilter_t($source, &$smarty)
58
{
59
    $output = '';
60
    $output_start = 0;
61
62
    $end = 0;
63
    while (($start = smarty_prefilter_t_strpos_multi(
64
            $source,
65
            [$smarty->left_delimiter . 't ', $smarty->left_delimiter . 't' . $smarty->right_delimiter],
66
            $end
67
        )) !== false) {
68
        $end = mb_strpos($source, $smarty->left_delimiter . '/t' . $smarty->right_delimiter, $start);
69
        $block_t = mb_substr($source, $start, $end - $start);
70
71
        $messgage_start = mb_strrpos($block_t, '}') + 1;
72
        $block_t = smarty_prefilter_t_process_block(
73
            mb_substr($block_t, 0, $messgage_start),
74
            mb_substr($block_t, $messgage_start),
75
            $smarty,
76
            0
77
        );
78
79
        $output .= mb_substr($source, $output_start, $start - $output_start);
80
        $output_start = $end + mb_strlen($smarty->left_delimiter . $smarty->right_delimiter) + 2;
81
82
        $output .= $block_t;
83
    }
84
    $output .= mb_substr($source, $output_start);
85
86
    return $output;
87
}
88
89
/* $block ... {t[ a=$a|nbsp b="a" ...]}
90
 *
91
 */
92
/**
93
 * @param string $block
94
 * @param string $message
95
 * @param $smarty
96
 * @param int $line
97
 *
98
 * @return string
99
 */
100
function smarty_prefilter_t_process_block($block, $message, &$smarty, $line)
101
{
102
    if ($message != '') {
103
        $start_attr = mb_strpos($block, ' ');
104
        if ($start_attr !== false) {
105
            if ((mb_substr($block, 0, 1) != $smarty->left_delimiter) || $start_attr == 1 || mb_substr(
106
                    $block,
107
                    - 1,
108
                    1
109
                ) != $smarty->right_delimiter
110
            ) {
111
                $smarty->_syntax_error("internal processing error: '$block'", E_USER_ERROR, __FILE__, __LINE__);
112
            }
113
            $block = mb_substr($block, $start_attr + 1, mb_strlen($block) - $start_attr - 2);
114
115
            // parse the attributes
116
            $attrs = smarty_prefilter_t_parse_attrs($block, $smarty);
117
118
            if (isset($attrs['plural']) && isset($attrs['count'])) {
119
                $message = smarty_prefilter_t_gettext($message, [], $smarty, $line);
120
121
                if ((mb_substr($attrs['plural'], 0, 1) == '"') && mb_substr($attrs['plural'], - 1, 1) == '"') {
122
                    $attrs['plural'] = mb_substr($attrs['plural'], 1, mb_strlen($attrs['plural']) - 2);
123
                }
124
                $attrs['plural'] = smarty_prefilter_t_gettext($attrs['plural'], [], $smarty, $line);
125
126
                // rebuild block with replaced plural
127
                $block = '';
128
                foreach ($attrs as $k => $v) {
129
                    if ($block != '') {
130
                        $block .= ' ';
131
                    }
132
                    $block .= $k . '=' . $v;
133
                }
134
135
                // pass it to block.t
136
                return $smarty->left_delimiter . 't ' . $block . $smarty->right_delimiter . $message . $smarty->left_delimiter . '/t' . $smarty->right_delimiter;
137
            }
138
            unset($attrs['plural']);
139
            unset($attrs['count']);
140
141
            $message = smarty_prefilter_t_gettext($message, $attrs, $smarty, $line);
142
        } else {
143
            $message = smarty_prefilter_t_gettext($message, [], $smarty, $line);
144
        }
145
    }
146
147
    return $message;
148
}
149
150
/**
151
 * Parse attribute string
152
 *   copied from Smarty_comiler.class.php
153
 *   we need the same source, expect _parse_vars_props at the end
154
 *
155
 * @param string $tag_args
156
 * @param & $smarty
0 ignored issues
show
The doc-type & could not be parsed: Unknown type name "&" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
157
 *
158
 * @return array
159
 */
160
function smarty_prefilter_t_parse_attrs($tag_args, &$smarty)
161
{
162
163
    /* Tokenize tag attributes. */
164
    preg_match_all(
165
        '~(?:' . $smarty->_obj_call_regexp . '|' . $smarty->_qstr_regexp . ' | (?>[^"\'=\s]+)
166
                      )+ |
167
                      [=]
168
                    ~x',
169
        $tag_args,
170
        $match
171
    );
172
    $tokens = $match[0];
173
174
    $attrs = [];
175
    /* Parse state:
176
        0 - expecting attribute name
177
        1 - expecting '='
178
        2 - expecting attribute value (not '=') */
179
    $state = 0;
180
181
    foreach ($tokens as $token) {
182
        switch ($state) {
183
            case 0:
184
                /* If the token is a valid identifier, we set attribute name
185
                    and go to state 1. */
186
                if (preg_match('~^\w+$~', $token)) {
187
                    $attr_name = $token;
188
                    $state = 1;
189
                } else {
190
                    $smarty->_syntax_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);
191
                }
192
                break;
193
194
            case 1:
195
                /* If the token is '=', then we go to state 2. */
196 View Code Duplication
                if ($token == '=') {
197
                    $state = 2;
198
                } else {
199
                    $smarty->_syntax_error(
200
                        "expecting '=' after attribute name '$last_token'",
201
                        E_USER_ERROR,
202
                        __FILE__,
203
                        __LINE__
204
                    );
205
                }
206
                break;
207
208
            case 2:
209
                /* If token is not '=', we set the attribute value and go to
210
                    state 0. */
211
                if ($token != '=') {
212
                    /* We booleanize the token if it's a non-quoted possible
213
                        boolean value. */
214
                    if (preg_match('~^(on|yes|true)$~', $token)) {
215
                        $token = 'true';
216
                    } else {
217
                        if (preg_match('~^(off|no|false)$~', $token)) {
218
                            $token = 'false';
219
                        } else {
220
                            if ($token == 'null') {
221
                                $token = 'null';
222
                            } else {
223
                                if (preg_match('~^' . $smarty->_num_const_regexp . '|0[xX][0-9a-fA-F]+$~', $token)) {
224
                                    /* treat integer literally */
225
                                } else {
226
                                    if (!preg_match(
227
                                        '~^' . $smarty->_obj_call_regexp . '|' . $smarty->_var_regexp . '(?:' . $smarty->_mod_regexp . ')*$~',
228
                                        $token
229
                                    )
230
                                    ) {
231
                                        /* treat as a string, double-quote it escaping quotes */
232
                                        $token = '"' . addslashes($token) . '"';
233
                                    }
234
                                }
235
                            }
236
                        }
237
                    }
238
239
                    $attrs[$attr_name] = $token;
240
                    $state = 0;
241
                } else {
242
                    $smarty->_syntax_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);
243
                }
244
                break;
245
        }
246
        $last_token = $token;
247
    }
248
249
    if ($state != 0) {
250 View Code Duplication
        if ($state == 1) {
251
            $smarty->_syntax_error(
252
                "expecting '=' after attribute name '$last_token'",
253
                E_USER_ERROR,
254
                __FILE__,
255
                __LINE__
256
            );
257
        } else {
258
            $smarty->_syntax_error('missing attribute value', E_USER_ERROR, __FILE__, __LINE__);
259
        }
260
    }
261
262
    // this call would translate the attrs to php code
263
    // we don't need it, because its a prefilter ...
264
    //$smarty->_parse_vars_props($attrs);
265
266
    return $attrs;
267
}
268
269
/**
270
 * @param $haystack
271
 * @param string[] $needles
272
 *
273
 * @return bool|int
274
 */
275
function smarty_prefilter_t_strpos_multi($haystack, $needles)
276
{
277
    $arg = func_get_args();
278
    $start = false;
279
280
    foreach ($needles as $needle) {
281
        $thisstart = mb_strpos($haystack, $needle, $arg[2]);
282
        if ($start == false) {
283
            $start = $thisstart;
284
        } else {
285
            if ($thisstart == false) {
286
            } else {
287
                if ($start > $thisstart) {
288
                    $start = $thisstart;
289
                }
290
            }
291
        }
292
    }
293
294
    return $start;
295
}
296
297
/**
298
 * @param $message
299
 * @param $attrs
300
 * @param $smarty
301
 * @param int $line
302
 *
303
 * @return string
304
 */
305
function smarty_prefilter_t_gettext($message, $attrs, &$smarty, $line)
306
{
307
    global $opt, $translate;
308
309
    if (!isset($translate)) {
310
        return $message;
311
    }
312
313
    $trans = $translate->t($message, $opt['template']['style'], '', 0);
314
315
    // TODO concept escapement
316
    if (isset($attrs['escape'])) {
317
        unset($attrs['escape']);
318
    }
319
    if (isset($attrs['plural'])) {
320
        unset($attrs['plural']);
321
    }
322
    if (isset($attrs['count'])) {
323
        unset($attrs['count']);
324
    }
325
326
    // replace params
327
    $number = 1;
328
    foreach ($attrs as $attr) {
329
        if (is_numeric($attr)) {
330
            $trans = mb_ereg_replace('%' . $number, $attr, $trans);
331
        } else {
332
            $trans = mb_ereg_replace('%' . $number, $smarty->left_delimiter . $attr . $smarty->right_delimiter, $trans);
333
        }
334
335
        $number++;
336
    }
337
338
    return $trans;
339
}
340