Plural_Forms WordPress Class
The Plural Forms WordPress class is a class that helps you manage plural forms in WordPress. It provides an easy way to add and manage plural forms for any language in WordPress. The class is easy to use and provides a simple interface for managing plural forms.
Plural_Forms #
Source
File: wp-includes/pomo/plural-forms.php
class Plural_Forms { /** * Operator characters. * * @since 4.9.0 * @var string OP_CHARS Operator characters. */ const OP_CHARS = '|&><!=%?:'; /** * Valid number characters. * * @since 4.9.0 * @var string NUM_CHARS Valid number characters. */ const NUM_CHARS = '0123456789'; /** * Operator precedence. * * Operator precedence from highest to lowest. Higher numbers indicate * higher precedence, and are executed first. * * @see https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence * * @since 4.9.0 * @var array $op_precedence Operator precedence from highest to lowest. */ protected static $op_precedence = array( '%' => 6, '<' => 5, '<=' => 5, '>' => 5, '>=' => 5, '==' => 4, '!=' => 4, '&&' => 3, '||' => 2, '?:' => 1, '?' => 1, '(' => 0, ')' => 0, ); /** * Tokens generated from the string. * * @since 4.9.0 * @var array $tokens List of tokens. */ protected $tokens = array(); /** * Cache for repeated calls to the function. * * @since 4.9.0 * @var array $cache Map of $n => $result */ protected $cache = array(); /** * Constructor. * * @since 4.9.0 * * @param string $str Plural function (just the bit after `plural=` from Plural-Forms) */ public function __construct( $str ) { $this->parse( $str ); } /** * Parse a Plural-Forms string into tokens. * * Uses the shunting-yard algorithm to convert the string to Reverse Polish * Notation tokens. * * @since 4.9.0 * * @throws Exception If there is a syntax or parsing error with the string. * * @param string $str String to parse. */ protected function parse( $str ) { $pos = 0; $len = strlen( $str ); // Convert infix operators to postfix using the shunting-yard algorithm. $output = array(); $stack = array(); while ( $pos < $len ) { $next = substr( $str, $pos, 1 ); switch ( $next ) { // Ignore whitespace. case ' ': case "\t": $pos++; break; // Variable (n). case 'n': $output[] = array( 'var' ); $pos++; break; // Parentheses. case '(': $stack[] = $next; $pos++; break; case ')': $found = false; while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; if ( '(' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); continue; } // Discard open paren. array_pop( $stack ); $found = true; break; } if ( ! $found ) { throw new Exception( 'Mismatched parentheses' ); } $pos++; break; // Operators. case '|': case '&': case '>': case '<': case '!': case '=': case '%': case '?': $end_operator = strspn( $str, self::OP_CHARS, $pos ); $operator = substr( $str, $pos, $end_operator ); if ( ! array_key_exists( $operator, self::$op_precedence ) ) { throw new Exception( sprintf( 'Unknown operator "%s"', $operator ) ); } while ( ! empty( $stack ) ) { $o2 = $stack[ count( $stack ) - 1 ]; // Ternary is right-associative in C. if ( '?:' === $operator || '?' === $operator ) { if ( self::$op_precedence[ $operator ] >= self::$op_precedence[ $o2 ] ) { break; } } elseif ( self::$op_precedence[ $operator ] > self::$op_precedence[ $o2 ] ) { break; } $output[] = array( 'op', array_pop( $stack ) ); } $stack[] = $operator; $pos += $end_operator; break; // Ternary "else". case ':': $found = false; $s_pos = count( $stack ) - 1; while ( $s_pos >= 0 ) { $o2 = $stack[ $s_pos ]; if ( '?' !== $o2 ) { $output[] = array( 'op', array_pop( $stack ) ); $s_pos--; continue; } // Replace. $stack[ $s_pos ] = '?:'; $found = true; break; } if ( ! $found ) { throw new Exception( 'Missing starting "?" ternary operator' ); } $pos++; break; // Default - number or invalid. default: if ( $next >= '0' && $next <= '9' ) { $span = strspn( $str, self::NUM_CHARS, $pos ); $output[] = array( 'value', intval( substr( $str, $pos, $span ) ) ); $pos += $span; break; } throw new Exception( sprintf( 'Unknown symbol "%s"', $next ) ); } } while ( ! empty( $stack ) ) { $o2 = array_pop( $stack ); if ( '(' === $o2 || ')' === $o2 ) { throw new Exception( 'Mismatched parentheses' ); } $output[] = array( 'op', $o2 ); } $this->tokens = $output; } /** * Get the plural form for a number. * * Caches the value for repeated calls. * * @since 4.9.0 * * @param int $num Number to get plural form for. * @return int Plural form value. */ public function get( $num ) { if ( isset( $this->cache[ $num ] ) ) { return $this->cache[ $num ]; } $this->cache[ $num ] = $this->execute( $num ); return $this->cache[ $num ]; } /** * Execute the plural form function. * * @since 4.9.0 * * @throws Exception If the plural form value cannot be calculated. * * @param int $n Variable "n" to substitute. * @return int Plural form value. */ public function execute( $n ) { $stack = array(); $i = 0; $total = count( $this->tokens ); while ( $i < $total ) { $next = $this->tokens[ $i ]; $i++; if ( 'var' === $next[0] ) { $stack[] = $n; continue; } elseif ( 'value' === $next[0] ) { $stack[] = $next[1]; continue; } // Only operators left. switch ( $next[1] ) { case '%': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 % $v2; break; case '||': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 || $v2; break; case '&&': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 && $v2; break; case '<': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 < $v2; break; case '<=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 <= $v2; break; case '>': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 > $v2; break; case '>=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 >= $v2; break; case '!=': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 != $v2; break; case '==': $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 == $v2; break; case '?:': $v3 = array_pop( $stack ); $v2 = array_pop( $stack ); $v1 = array_pop( $stack ); $stack[] = $v1 ? $v2 : $v3; break; default: throw new Exception( sprintf( 'Unknown operator "%s"', $next[1] ) ); } } if ( count( $stack ) !== 1 ) { throw new Exception( 'Too many values remaining on the stack' ); } return (int) $stack[0]; } }
Expand full source codeCollapse full source codeView on TracView on GitHub
Methods
- __construct— Constructor.
- execute— Execute the plural form function.
- get— Get the plural form for a number.
- parse— Parse a Plural-Forms string into tokens.