Diff: STRATO-apps/wordpress_03/app/wp-includes/pomo/plural-forms.php

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + <?php
2 +
3 + /**
4 + * A gettext Plural-Forms parser.
5 + *
6 + * @since 4.9.0
7 + */
8 + if ( ! class_exists( 'Plural_Forms', false ) ) :
9 + #[AllowDynamicProperties]
10 + class Plural_Forms {
11 + /**
12 + * Operator characters.
13 + *
14 + * @since 4.9.0
15 + * @var string OP_CHARS Operator characters.
16 + */
17 + const OP_CHARS = '|&><!=%?:';
18 +
19 + /**
20 + * Valid number characters.
21 + *
22 + * @since 4.9.0
23 + * @var string NUM_CHARS Valid number characters.
24 + */
25 + const NUM_CHARS = '0123456789';
26 +
27 + /**
28 + * Operator precedence.
29 + *
30 + * Operator precedence from highest to lowest. Higher numbers indicate
31 + * higher precedence, and are executed first.
32 + *
33 + * @see https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence
34 + *
35 + * @since 4.9.0
36 + * @var array $op_precedence Operator precedence from highest to lowest.
37 + */
38 + protected static $op_precedence = array(
39 + '%' => 6,
40 +
41 + '<' => 5,
42 + '<=' => 5,
43 + '>' => 5,
44 + '>=' => 5,
45 +
46 + '==' => 4,
47 + '!=' => 4,
48 +
49 + '&&' => 3,
50 +
51 + '||' => 2,
52 +
53 + '?:' => 1,
54 + '?' => 1,
55 +
56 + '(' => 0,
57 + ')' => 0,
58 + );
59 +
60 + /**
61 + * Tokens generated from the string.
62 + *
63 + * @since 4.9.0
64 + * @var array $tokens List of tokens.
65 + */
66 + protected $tokens = array();
67 +
68 + /**
69 + * Cache for repeated calls to the function.
70 + *
71 + * @since 4.9.0
72 + * @var array $cache Map of $n => $result
73 + */
74 + protected $cache = array();
75 +
76 + /**
77 + * Constructor.
78 + *
79 + * @since 4.9.0
80 + *
81 + * @param string $str Plural function (just the bit after `plural=` from Plural-Forms)
82 + */
83 + public function __construct( $str ) {
84 + $this->parse( $str );
85 + }
86 +
87 + /**
88 + * Parse a Plural-Forms string into tokens.
89 + *
90 + * Uses the shunting-yard algorithm to convert the string to Reverse Polish
91 + * Notation tokens.
92 + *
93 + * @since 4.9.0
94 + *
95 + * @throws Exception If there is a syntax or parsing error with the string.
96 + *
97 + * @param string $str String to parse.
98 + */
99 + protected function parse( $str ) {
100 + $pos = 0;
101 + $len = strlen( $str );
102 +
103 + // Convert infix operators to postfix using the shunting-yard algorithm.
104 + $output = array();
105 + $stack = array();
106 + while ( $pos < $len ) {
107 + $next = substr( $str, $pos, 1 );
108 +
109 + switch ( $next ) {
110 + // Ignore whitespace.
111 + case ' ':
112 + case "\t":
113 + ++$pos;
114 + break;
115 +
116 + // Variable (n).
117 + case 'n':
118 + $output[] = array( 'var' );
119 + ++$pos;
120 + break;
121 +
122 + // Parentheses.
123 + case '(':
124 + $stack[] = $next;
125 + ++$pos;
126 + break;
127 +
128 + case ')':
129 + $found = false;
130 + while ( ! empty( $stack ) ) {
131 + $o2 = $stack[ count( $stack ) - 1 ];
132 + if ( '(' !== $o2 ) {
133 + $output[] = array( 'op', array_pop( $stack ) );
134 + continue;
135 + }
136 +
137 + // Discard open paren.
138 + array_pop( $stack );
139 + $found = true;
140 + break;
141 + }
142 +
143 + if ( ! $found ) {
144 + throw new Exception( 'Mismatched parentheses' );
145 + }
146 +
147 + ++$pos;
148 + break;
149 +
150 + // Operators.
151 + case '|':
152 + case '&':
153 + case '>':
154 + case '<':
155 + case '!':
156 + case '=':
157 + case '%':
158 + case '?':
159 + $end_operator = strspn( $str, self::OP_CHARS, $pos );
160 + $operator = substr( $str, $pos, $end_operator );
161 + if ( ! array_key_exists( $operator, self::$op_precedence ) ) {
162 + throw new Exception( sprintf( 'Unknown operator "%s"', $operator ) );
163 + }
164 +
165 + while ( ! empty( $stack ) ) {
166 + $o2 = $stack[ count( $stack ) - 1 ];
167 +
168 + // Ternary is right-associative in C.
169 + if ( '?:' === $operator || '?' === $operator ) {
170 + if ( self::$op_precedence[ $operator ] >= self::$op_precedence[ $o2 ] ) {
171 + break;
172 + }
173 + } elseif ( self::$op_precedence[ $operator ] > self::$op_precedence[ $o2 ] ) {
174 + break;
175 + }
176 +
177 + $output[] = array( 'op', array_pop( $stack ) );
178 + }
179 + $stack[] = $operator;
180 +
181 + $pos += $end_operator;
182 + break;
183 +
184 + // Ternary "else".
185 + case ':':
186 + $found = false;
187 + $s_pos = count( $stack ) - 1;
188 + while ( $s_pos >= 0 ) {
189 + $o2 = $stack[ $s_pos ];
190 + if ( '?' !== $o2 ) {
191 + $output[] = array( 'op', array_pop( $stack ) );
192 + --$s_pos;
193 + continue;
194 + }
195 +
196 + // Replace.
197 + $stack[ $s_pos ] = '?:';
198 + $found = true;
199 + break;
200 + }
201 +
202 + if ( ! $found ) {
203 + throw new Exception( 'Missing starting "?" ternary operator' );
204 + }
205 + ++$pos;
206 + break;
207 +
208 + // Default - number or invalid.
209 + default:
210 + if ( $next >= '0' && $next <= '9' ) {
211 + $span = strspn( $str, self::NUM_CHARS, $pos );
212 + $output[] = array( 'value', intval( substr( $str, $pos, $span ) ) );
213 + $pos += $span;
214 + break;
215 + }
216 +
217 + throw new Exception( sprintf( 'Unknown symbol "%s"', $next ) );
218 + }
219 + }
220 +
221 + while ( ! empty( $stack ) ) {
222 + $o2 = array_pop( $stack );
223 + if ( '(' === $o2 || ')' === $o2 ) {
224 + throw new Exception( 'Mismatched parentheses' );
225 + }
226 +
227 + $output[] = array( 'op', $o2 );
228 + }
229 +
230 + $this->tokens = $output;
231 + }
232 +
233 + /**
234 + * Get the plural form for a number.
235 + *
236 + * Caches the value for repeated calls.
237 + *
238 + * @since 4.9.0
239 + *
240 + * @param int $num Number to get plural form for.
241 + * @return int Plural form value.
242 + */
243 + public function get( $num ) {
244 + if ( isset( $this->cache[ $num ] ) ) {
245 + return $this->cache[ $num ];
246 + }
247 + $this->cache[ $num ] = $this->execute( $num );
248 + return $this->cache[ $num ];
249 + }
250 +
251 + /**
252 + * Execute the plural form function.
253 + *
254 + * @since 4.9.0
255 + *
256 + * @throws Exception If the plural form value cannot be calculated.
257 + *
258 + * @param int $n Variable "n" to substitute.
259 + * @return int Plural form value.
260 + */
261 + public function execute( $n ) {
262 + $stack = array();
263 + $i = 0;
264 + $total = count( $this->tokens );
265 + while ( $i < $total ) {
266 + $next = $this->tokens[ $i ];
267 + ++$i;
268 + if ( 'var' === $next[0] ) {
269 + $stack[] = $n;
270 + continue;
271 + } elseif ( 'value' === $next[0] ) {
272 + $stack[] = $next[1];
273 + continue;
274 + }
275 +
276 + // Only operators left.
277 + switch ( $next[1] ) {
278 + case '%':
279 + $v2 = array_pop( $stack );
280 + $v1 = array_pop( $stack );
281 + $stack[] = $v1 % $v2;
282 + break;
283 +
284 + case '||':
285 + $v2 = array_pop( $stack );
286 + $v1 = array_pop( $stack );
287 + $stack[] = $v1 || $v2;
288 + break;
289 +
290 + case '&&':
291 + $v2 = array_pop( $stack );
292 + $v1 = array_pop( $stack );
293 + $stack[] = $v1 && $v2;
294 + break;
295 +
296 + case '<':
297 + $v2 = array_pop( $stack );
298 + $v1 = array_pop( $stack );
299 + $stack[] = $v1 < $v2;
300 + break;
301 +
302 + case '<=':
303 + $v2 = array_pop( $stack );
304 + $v1 = array_pop( $stack );
305 + $stack[] = $v1 <= $v2;
306 + break;
307 +
308 + case '>':
309 + $v2 = array_pop( $stack );
310 + $v1 = array_pop( $stack );
311 + $stack[] = $v1 > $v2;
312 + break;
313 +
314 + case '>=':
315 + $v2 = array_pop( $stack );
316 + $v1 = array_pop( $stack );
317 + $stack[] = $v1 >= $v2;
318 + break;
319 +
320 + case '!=':
321 + $v2 = array_pop( $stack );
322 + $v1 = array_pop( $stack );
323 + $stack[] = $v1 !== $v2;
324 + break;
325 +
326 + case '==':
327 + $v2 = array_pop( $stack );
328 + $v1 = array_pop( $stack );
329 + $stack[] = $v1 === $v2;
330 + break;
331 +
332 + case '?:':
333 + $v3 = array_pop( $stack );
334 + $v2 = array_pop( $stack );
335 + $v1 = array_pop( $stack );
336 + $stack[] = $v1 ? $v2 : $v3;
337 + break;
338 +
339 + default:
340 + throw new Exception( sprintf( 'Unknown operator "%s"', $next[1] ) );
341 + }
342 + }
343 +
344 + if ( count( $stack ) !== 1 ) {
345 + throw new Exception( 'Too many values remaining on the stack' );
346 + }
347 +
348 + return (int) $stack[0];
349 + }
350 + }
351 + endif;
352 +