├── LICENSE ├── README.md ├── bin └── generate_readme ├── composer.json ├── docs ├── filters │ ├── SI.md │ ├── _package.md │ ├── age.md │ ├── bytes.md │ ├── date.md │ ├── filePermissions.md │ ├── lowerRoman.md │ ├── upperFirst.md │ └── upperRoman.md └── tests │ ├── _package.md │ └── numeric.md └── src └── Twig ├── Filters ├── AgeFilter.php ├── BytesFilter.php ├── DateFilter.php ├── FilePermissionsFilter.php ├── LowerRomanFilter.php ├── SIFilter.php ├── UpperFirstFilter.php └── UpperRomanFilter.php ├── Tests └── NumericTest.php └── Text └── RomanNumeralsTrait.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) https://github.com/GeckoPackages 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Abandoned! 2 | 3 | We will try to move anything of value from here to twigphp/Twig-extensions if possible. 4 | -------------------------------------------------------------------------------- /bin/generate_readme: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | generateReadMe())) { 19 | echo sprintf("\nFailed to write content to \"%s\".", $readMeFile); 20 | exit(-1); 21 | } 22 | 23 | exit(0); 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gecko-packages/gecko-twig", 3 | "type": "library", 4 | "homepage": "https://github.com/GeckoPackages", 5 | "description": "Additional Twig filters and tests.", 6 | "license": "MIT", 7 | "keywords": ["Twig", "Filter", "Bytes", "SI", "Roman"], 8 | "require": { 9 | "php": ">=7.0 <7.3", 10 | "twig/twig": "^2.0" 11 | }, 12 | "require-dev": { 13 | "phpunit/phpunit": "^4.5|^5", 14 | "symfony/phpunit-bridge": "^2.8|^3.0" 15 | }, 16 | "suggest": { 17 | "ext-mbstring": "For multi byte character support." 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "GeckoPackages\\Twig\\": "src\\Twig" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/filters/SI.md: -------------------------------------------------------------------------------- 1 | ### SI 2 | 3 | Formats a number with a SI symbol, either automatically or by given symbol. 4 | 5 | Formats a number with a [SI symbol](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes), either automatically or by given symbol. 6 | 7 | Symbols supported: 8 | 9 | | Symbol | Description | | | 10 | | ------ | ----------------------- | ------------- | ----------------------------------- | 11 | | y | yocto | septillionth | 0.000 000 000 000 000 000 000 001 | 12 | | z | zepto | sextillionth | 0.000 000 000 000 000 000 001 | 13 | | a | atto | quintillionth | 0.000 000 000 000 000 001 | 14 | | f | femto | quadrillionth | 0.000 000 000 000 001 | 15 | | p | pico | trillionth | 0.000 000 000 001 | 16 | | n | nano | billionth | 0.000 000 001 | 17 | | μ(/u) | micro | millionth | 0.000 001 | 18 | | m | milli | thousandth | 0.001 | 19 | | c | centi | hundredth | 0.01 | 20 | | d | deci | tenth | 0.1 | 21 | | | one | one | 1 | 22 | | da | deca | ten | 10 | 23 | | h | hecto | hundred | 100 | 24 | | k(/K) | kilo | thousand | 1000 | 25 | | M | mega | million | 1000 000 | 26 | | G | giga | billion | 1000 000 000 | 27 | | T | tera | trillion | 1000 000 000 000 | 28 | | P | peta | quadrillion | 1000 000 000 000 000 | 29 | | E | exa | quintillion | 1000 000 000 000 000 000 | 30 | | Z | zetta | sextillion | 1000 000 000 000 000 000 000 | 31 | | Y | yotta | septillion | 1000 000 000 000 000 000 000 000 | 32 | | 'auto' | Auto match (best match) | - | * | 33 | 34 | The default used by the filter is `auto`. 35 | 36 | #### Examples 37 | 38 | ```Twig 39 | {{ 1|SI('z') }} 40 | {# 1,000,000,000,000,000,000,000z #} 41 | 42 | {{ '1337e0'|SI }} 43 | {# 1K #} 44 | 45 | {{ 4.2|SI('μ') }} 46 | {# 4,200,000μ #} 47 | 48 | {{ '-8123456'|SI('', '%number% %symbol%', 2, ',', '.') }} 49 | {# -8.123.456,00 #} 50 | 51 | {{ 1500000000000000000000000|SI('Y', '%number% %symbol%', 2, ',', '')|raw }} 52 | {# 1,50 Y #} 53 | 54 | ``` 55 | 56 | The filter uses the number formatting set on the `Core` extension of Twig. The output can be customized even more by passing a `format`. 57 | -------------------------------------------------------------------------------- /docs/filters/_package.md: -------------------------------------------------------------------------------- 1 | You can add any of the filters to the Twig environment. 2 | Example: 3 | 4 | ```php 5 | /** @var Twig_Environment $env */ 6 | $env->addFilter(new \GeckoPackages\Twig\Filters\AgeFilter()); 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/filters/age.md: -------------------------------------------------------------------------------- 1 | ### Age 2 | 3 | Calculates the time difference (age) between a date and the current date. 4 | 5 | Calculates and returns the time difference (age) between a date and the current date. 6 | 7 | You use an accuracy: 8 | 9 | | symbol | accuracy | 10 | | ------ | -------- | 11 | | y | Year | 12 | | d | Day | 13 | | h | Hour | 14 | | i | Minute | 15 | | s | Seconds | 16 | 17 | The default is `y`. Symbols are case insensitive. 18 | 19 | #### Examples 20 | 21 | ```Twig 22 | {# today is new \DateTime() #} 23 | 24 | {{ today|date_modify("-36 hours")|age('d') }} day. 25 | {# 1.5 day. #} 26 | 27 | {{ today|date_modify("180 minutes")|age('h') }} hours. 28 | {# -3 hours. note the "minus" "#} 29 | 30 | {{ today|date_modify("-180 minutes")|age('i') }} minutes. 31 | {# 180 minutes. #} 32 | 33 | {{ today|date_modify("-180 minutes")|age('s') }} seconds. 34 | {# 10800 seconds. #} 35 | 36 | ``` 37 | 38 | Pass a timezone as second argument to set for the date passed. \*
39 | Pass a timezone as third argument for creating the current date. \* 40 | 41 | * Pass `null` to use the default, `false` to leave unchanged. 42 | -------------------------------------------------------------------------------- /docs/filters/bytes.md: -------------------------------------------------------------------------------- 1 | ### Bytes 2 | 3 | Formats a number of bytes with binary or SI prefix multiple, either automatically or by given symbol. 4 | 5 | Formats a number of bytes to a specific SI or binary unit or in a auto (best match) way. 6 | Specific units of IEC 60027-2 A.2 (1024 based [binary prefix](https://en.wikipedia.org/wiki/Binary_prefix)) and SI (1000 based [SI prefix](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes)) are supported. 7 | 8 | Symbols supported: 9 | 10 | | binary | SI | 11 | | ----------- | ------------- | 12 | | b (bit) | b (bit) | 13 | | B (byte) | B (byte) | 14 | | Ki (kibi) | k(/K) (kilo) | 15 | | Mi (mebi) | M (mega) | 16 | | Gi (gibi) | G (giga) | 17 | | Ti (tebi) | T (tera) | 18 | | Pi (pebi) | P (peta) | 19 | | Ei (exbi) | E (exa) | 20 | | Zi (zebi) | Z (zetta) | 21 | | Yi (yobi) | Y (yotta) | 22 | | 'auto,bin' | 'auto,SI' | 23 | 24 | The default is `auto,bin`. 25 | 26 | #### Examples 27 | 28 | ```Twig 29 | 30 | {{ 1099511627776|bytes }} 31 | {# 1TiB #} 32 | 33 | {{ 1000|bytes('auto,SI') }} 34 | {# 1KB #} 35 | 36 | {{ (1024*2)|bytes('Kib') }} 37 | {# 16Kib #} 38 | 39 | {{ (250.5 * 1048576)|bytes('auto,bin', '%number% %symbol%', 4, ',', '') }} 40 | {# 250,5000 MiB #} 41 | 42 | ``` 43 | 44 | The filter uses the number formatting set on the `Core` extension of Twig. The output can be customized even more by passing a `format`. 45 | -------------------------------------------------------------------------------- /docs/filters/date.md: -------------------------------------------------------------------------------- 1 | ### Date 2 | 3 | Replacement for the date filter of Twig, returns an empty string if the date is `empty()`. 4 | 5 | Replacement of the date filter provided by [Twig](http://twig.sensiolabs.org/doc/filters/date.html). Returns an empty string `""` if the give date to format is `empty()` (and not an `array`). 6 | If it is not the method returns the value as provided by the default date filter of Twig. 7 | 8 | #### Examples 9 | 10 | ```Twig 11 | zero int [{{ 0|date }}] 12 | {# zero int [] #} 13 | zero float [{{ 0.0|date }}] 14 | {# zero float [] #} 15 | zero string [{{ '0'|date }}] 16 | {# zero string [] #} 17 | empty string [{{ ''|date }}] 18 | {# empty string [] #} 19 | null value [{{ null|date }}] 20 | {# null value [] #} 21 | false value [{{ false|date }}] 22 | {# false value [] #} 23 | timestamp [{{ timestamp|date('m/d/Y') }}] 24 | {# timestamp [09/19/2016] #} 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/filters/filePermissions.md: -------------------------------------------------------------------------------- 1 | ### File Permissions 2 | 3 | Formats file permissions in symbolic (UNIX) notation. 4 | 5 | Formats file permissions in symbolic (UNIX) notation. 6 | 7 | #### Examples 8 | 9 | ```Twig 10 | 11 | {{ 755|filePermissions}} 12 | {# urwxr-xr-x #} 13 | 14 | {{ './'|filePermissions}} 15 | {# drwxrwxr-x #} 16 | 17 | {{ '0444'|filePermissions}} 18 | {# ur--r--r-- #} 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/filters/lowerRoman.md: -------------------------------------------------------------------------------- 1 | ### Lower Roman 2 | 3 | Lowercase Roman numerals in a string. 4 | 5 | Lowercase Roman numerals in a string. 6 | Supports the Roman numerals in modern notation (`strict`) or `loose` notation. 7 | 8 | | Roman | Value | 9 | | ----- |------ | 10 | | I | 1 | 11 | | IV | 4 | 12 | | V | 5 | 13 | | IX | 9 | 14 | | X | 10 | 15 | | XL | 40 | 16 | | L | 50 | 17 | | XC | 90 | 18 | | C | 100 | 19 | | CD | 400 | 20 | | D | 500 | 21 | | CM | 900 | 22 | | M | 1000 | 23 | 24 | Note: large numbers, for example in 'apostrophus' and 'vinculum' are not supported. 25 | 26 | In `strict` mode: 27 | - Symbols are combined from left to right, high to low values. 28 | - Symbols are not repeated more than 3 times. 29 | - C may b placed after D or M. 30 | - X may be placed before L or C. 31 | - I may be placed before V or X. 32 | - This makes the maximum number supported 'MMMCMXCIX'. 33 | 34 | In `loose` mode, follows `strict` mode with the following exceptions: 35 | - Symbols may be repeated more than 3 times. 36 | - There is no more maximum number. 37 | 38 | In `loose-order`, follows `loose` mode with the following exception: 39 | - Symbols may appear in any order. 40 | 41 | The default mode is `strict`. 42 | 43 | More details and background information on [Wikipedia](https://en.wikipedia.org/wiki/Roman_numerals). 44 | 45 | #### Examples 46 | 47 | ```Twig 48 | 49 | {{ 'MCMLIV. Chapter sub XI NOT XICA'|lowerRoman }} 50 | {# mcmliv. Chapter sub xi NOT XICA #} 51 | 52 | {{ 'IIXX, XIiX, III, MDcdIII, TESTXI, MMMM'|lowerRoman('loose-order') }} 53 | {# iixx, xiix, iii, mdcdiii, TESTXI, mmmm #} 54 | 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/filters/upperFirst.md: -------------------------------------------------------------------------------- 1 | ### Upper First 2 | 3 | Uppercase the first character of a string. 4 | 5 | Uppercase the first character of a string. For multi byte character support the filter will use [`mbstring`](https://secure.php.net/manual/en/book.mbstring.php). 6 | 7 | #### Examples 8 | 9 | ```Twig 10 | 11 | {{ 'hello world!'|upperFirst }} 12 | {# Hello world! #} 13 | 14 | {{ 'čůrá Test'|upperFirst }} 15 | {# Čůrá Test #} 16 | 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/filters/upperRoman.md: -------------------------------------------------------------------------------- 1 | ### Upper Roman 2 | 3 | Uppercase Roman numerals in a string. 4 | 5 | Uppercase Roman numerals in a string. 6 | For details about the supported `modes` see the `Lower Roman` filter. 7 | 8 | The default mode is `strict`. 9 | 10 | #### Examples 11 | 12 | ```Twig 13 | 14 | {{ 'a vi b'|upperRoman }} 15 | {# a VI b #} 16 | 17 | {{ 'mcmliv. chapter sub xi not XiCa'|upperRoman }} 18 | {# MCMLIV. chapter sub XI not XiCa #} 19 | 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/tests/_package.md: -------------------------------------------------------------------------------- 1 | You can add any of the tests to the Twig environment. 2 | Example: 3 | 4 | ```php 5 | /** @var Twig_Environment $env */ 6 | $env->addTest(new \GeckoPackages\Twig\Tests\NumericTest()); 7 | ``` 8 | -------------------------------------------------------------------------------- /docs/tests/numeric.md: -------------------------------------------------------------------------------- 1 | ### Numeric 2 | 3 | Test given value is numeric (behaviour like PHP 7). 4 | 5 | Test if a given value is `numeric`. 6 | The test will return false for hexadecimal strings as this is the behaviour of [`is_numeric`](https://secure.php.net/manual/en/function.is-numeric.php) on PHP 7. 7 | 8 | #### Examples 9 | 10 | ```Twig 11 | 12 | {{ 12 is numeric ? 'Yes' : 'No' }} 13 | {# Yes #} 14 | 15 | {{ '-1.3' is numeric ? 'Yes' : 'No' }} 16 | {# Yes #} 17 | 18 | {{ '0x539' is not numeric ? 'Hex. is not numeric' : 'N'}} 19 | {# Hex. is not numeric #} 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /src/Twig/Filters/AgeFilter.php: -------------------------------------------------------------------------------- 1 | diff($date, false); 49 | 50 | switch (strtolower($acc)) { 51 | case 'y': 52 | $v = 53 | $diff->y 54 | + ($diff->m / 12) 55 | + (($diff->d + (($diff->h + (($diff->i + ($diff->s / 60)) / 60)) / 24)) / 365) 56 | ; 57 | 58 | break; 59 | // case 'm' is not supported by design 60 | case 'd': 61 | $v = 62 | (int) $diff->format('%a') 63 | + (($diff->h + (($diff->i + ($diff->s / 60)) / 60)) / 24) 64 | ; 65 | 66 | break; 67 | case 'h': 68 | $v = 69 | 24 * (int) $diff->format('%a') 70 | + $diff->h 71 | + (($diff->i + ($diff->s / 60)) / 60) 72 | ; 73 | 74 | break; 75 | case 'i': 76 | $v = 77 | 60 * 24 * (int) $diff->format('%a') 78 | + 60 * $diff->h 79 | + $diff->i 80 | + ($diff->s / 60) 81 | ; 82 | 83 | break; 84 | case 's': 85 | return (int) $now->format('U') - (int) $date->format('U'); 86 | default: 87 | throw new \Twig_Error_Runtime(sprintf('Accuracy must be any of "y, d, h, i, s", got "%s".', $acc)); 88 | } 89 | 90 | // (int) cast for HHVM =< 3.9.10 91 | // https://github.com/facebook/hhvm/pull/6134 / https://github.com/facebook/hhvm/issues/5537 92 | return 1 === (int) $diff->invert ? $v : -1 * $v; 93 | }, 94 | ['needs_environment' => true] 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Twig/Filters/BytesFilter.php: -------------------------------------------------------------------------------- 1 | 1, // kilo 73 | 'K' => 1, // " 74 | 'M' => 2, // mega 75 | 'G' => 3, // giga 76 | 'T' => 4, // tera 77 | 'P' => 5, // peta 78 | 'E' => 6, // exa 79 | 'Z' => 7, // zetta 80 | 'Y' => 8, // yotta 81 | ]; 82 | 83 | if ($symbolLength === 1) { 84 | if ('b' === $symbol) { 85 | $number *= 8; 86 | } elseif ('B' !== $symbol) { 87 | throw new \Twig_Error_Runtime(sprintf('Unsupported symbol \'%s\'.', $symbol)); 88 | } 89 | } elseif ($symbolLength <= 3) { 90 | // SI vs. bin. 91 | switch ($symbol[1]) { 92 | case 'i': { // Binary 93 | if ($symbolLength < 3) { 94 | throw new \Twig_Error_Runtime(sprintf('Binary symbol must be end with either \'b\' or \'B\', got "%s".', $symbol)); 95 | } elseif ('b' === $symbol[2]) { // binary| bit 96 | $number *= 8; 97 | } elseif ('B' !== $symbol[2]) { // binary| byte 98 | throw new \Twig_Error_Runtime(sprintf('Binary symbol must be end with either \'b\' or \'B\', got "%s".', $symbol)); 99 | } 100 | 101 | $magnitude = 1024; 102 | break; 103 | } 104 | case 'b': { // SI | bit 105 | $magnitude = 1000; 106 | $number *= 8; 107 | break; 108 | } 109 | case 'B': { // SI | byte 110 | $magnitude = 1000; 111 | break; 112 | } 113 | default: { 114 | throw new \Twig_Error_Runtime(sprintf('Symbol must be binary (b|B[x]) or SI and must end with either \'b\' or \'B\', got "%s".', $symbol)); 115 | } 116 | } 117 | 118 | if (!array_key_exists($symbol[0], $symbolMag)) { 119 | throw new \Twig_Error_Runtime(sprintf('Symbol must start with \'k\', \'K\', \'M\', \'G\', \'T\', \'P\', \'E\', \'Z\', or \'Y\', got "%s".', $symbol)); 120 | } 121 | 122 | $number /= pow($magnitude, $symbolMag[$symbol[0]]); // ** on PHP 5.6 123 | } elseif ('auto,bin' === $symbol) { 124 | if ($number < 1024 && $number > -1024) { 125 | $symbol = 'B'; 126 | } else { 127 | $negative = $number < 0; 128 | $number = $negative ? abs($number) : $number; 129 | 130 | // since it is not guaranteed that array() will set the pointer to the first element 131 | reset($symbolMag); 132 | $mag = 0; 133 | 134 | while ($number >= 1023.9999999999 && $mag <= 8) { // large numbers rounding issues 135 | ++$mag; 136 | $number /= 1024; // $number >>= 10; doesn't work for large numbers (> PHP_INT_MAX) and looses the decimals 137 | next($symbolMag); 138 | } 139 | 140 | if ($negative) { 141 | $number *= -1; 142 | } 143 | $symbol = key($symbolMag).'iB'; 144 | } 145 | } elseif ('auto,SI' === $symbol) { 146 | if ($number < 1000 && $number > -1000) { 147 | $symbol = 'B'; 148 | } else { 149 | $negative = $number < 0; 150 | $number = $negative ? abs($number) : $number; 151 | 152 | // since it is not guaranteed that array() will set the pointer to the first element 153 | reset($symbolMag); 154 | $mag = 0; 155 | while ($number >= 1000 && $mag <= 8) { 156 | ++$mag; 157 | $number /= 1000; 158 | next($symbolMag); 159 | } 160 | 161 | if ($negative) { 162 | $number *= -1; 163 | } 164 | 165 | $symbol = key($symbolMag).'B'; 166 | } 167 | } else { 168 | throw new \Twig_Error_Runtime(sprintf('Unsupported symbol "%s".', $symbol)); 169 | } 170 | 171 | $defaults = $env->getExtension('Twig_Extension_Core')->getNumberFormat(); 172 | if (null === $decimal) { 173 | $decimal = $defaults[0]; 174 | } 175 | 176 | if (null === $decimalPoint) { 177 | $decimalPoint = $defaults[1]; 178 | } 179 | 180 | if (null === $thousandSep) { 181 | $thousandSep = $defaults[2]; 182 | } 183 | 184 | $number = number_format((float) $number, $decimal, $decimalPoint, $thousandSep); 185 | $format = str_replace('%symbol%', $symbol, $format); 186 | 187 | return str_replace('%number%', $number, $format); 188 | }, 189 | ['needs_environment' => true] // 'is_safe' => false: since the given $format which might need escaping is returned. 190 | ); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Twig/Filters/DateFilter.php: -------------------------------------------------------------------------------- 1 | true] 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Twig/Filters/FilePermissionsFilter.php: -------------------------------------------------------------------------------- 1 | false: since the given $string which might need escaping is returned. 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Twig/Filters/LowerRomanFilter.php: -------------------------------------------------------------------------------- 1 | numeralRomanMatchCallBack( 41 | $string, 42 | $matchMode, 43 | function (array $matches) { 44 | if (empty($matches[1])) { 45 | return $matches[1]; 46 | } 47 | 48 | return strtolower($matches[1]); 49 | } 50 | ); 51 | } 52 | // array $options, 'is_safe' => false: since the given $string which might need escaping is returned. 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Twig/Filters/SIFilter.php: -------------------------------------------------------------------------------- 1 | 1 note: double 'k'/'K 'and two char. 'da' 74 | ]; 75 | 76 | if (1 === strlen($symbol) || 'μ' === $symbol) { // note: string length of 'μ' is 2 77 | $index = array_search($symbol, $symbolMag, true); 78 | if (false === $index) { 79 | throw new \Twig_Error_Runtime(sprintf('Unsupported symbol "%s".', $symbol)); 80 | } 81 | 82 | if ($index > 10) { 83 | // division 84 | if ($index > 13) { 85 | $index -= 3; // double 'k' correction 86 | $pow = 1000; 87 | } else { 88 | $pow = 10; 89 | } 90 | 91 | $number /= pow($pow, $index - 10); // -10 includes the double 'u' correction 92 | } else { 93 | // multiply 94 | if ($index > 8) { 95 | $pow = 10; 96 | $index -= 3; // includes the double 'u' correction 97 | } else { 98 | if ($index > 6) { 99 | --$index; // double 'u' correction 100 | } 101 | 102 | $pow = 1000; 103 | } 104 | 105 | $number *= pow($pow, 8 - $index); 106 | } 107 | } elseif ($symbol === 'da') { 108 | $number /= 10; 109 | } elseif ($symbol === 'auto') { 110 | $negative = $number < 0; 111 | $number = $negative ? abs($number) : $number; 112 | 113 | if (($number >= 1 && $number <= 10) || 0 === $number) { 114 | $symbol = ''; 115 | } elseif ($number < 1) { 116 | if ($number < 0.001) { 117 | $mag = 8; 118 | while ($number < 1 && $mag >= 0) { 119 | --$mag; // first decrement is double 'u' correction 120 | $number *= 1000; 121 | } 122 | } else { 123 | $mag = 11; 124 | while ($number < 1) { 125 | --$mag; 126 | $number *= 10; 127 | } 128 | } 129 | 130 | $symbol = $symbolMag[$mag]; 131 | } else { 132 | if ($number < 1000) { 133 | $mag = 0; 134 | while ($number >= 10) { 135 | ++$mag; 136 | $number /= 10; 137 | } 138 | } else { 139 | $mag = 2; 140 | while ($number >= 1000 && $mag <= 10) { 141 | ++$mag; 142 | $number /= 1000; 143 | } 144 | } 145 | 146 | $mag += $mag > 2 ? 11 : 10; 147 | $symbol = $symbolMag[$mag]; 148 | } 149 | 150 | if ($negative) { 151 | $number *= -1; 152 | } 153 | } elseif ($symbol !== '') { 154 | throw new \Twig_Error_Runtime(sprintf('Unsupported symbol "%s".', $symbol)); 155 | } 156 | 157 | $defaults = $env->getExtension('Twig_Extension_Core')->getNumberFormat(); 158 | if (null === $decimal) { 159 | $decimal = $defaults[0]; 160 | } 161 | 162 | if (null === $decimalPoint) { 163 | $decimalPoint = $defaults[1]; 164 | } 165 | 166 | if (null === $thousandSep) { 167 | $thousandSep = $defaults[2]; 168 | } 169 | 170 | $number = number_format((float) $number, $decimal, $decimalPoint, $thousandSep); 171 | $format = str_replace('%symbol%', $symbol, $format); 172 | if ('' === $symbol) { 173 | $format = trim($format); 174 | } 175 | 176 | return str_replace('%number%', $number, $format); 177 | }, 178 | ['needs_environment' => true] // 'is_safe' => false: since the given $format which might need escaping is returned. 179 | ); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Twig/Filters/UpperFirstFilter.php: -------------------------------------------------------------------------------- 1 | getCharset()) { 50 | return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_substr($string, 1, mb_strlen($string, $charset), $charset); 51 | } 52 | 53 | return ucfirst($string); 54 | }, 55 | ['needs_environment' => true] // 'is_safe' => false: since the given $format which might need escaping is returned. 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Twig/Filters/UpperRomanFilter.php: -------------------------------------------------------------------------------- 1 | numeralRomanMatchCallBack( 41 | $string, 42 | $matchMode, 43 | function (array $matches) { 44 | if (empty($matches[1])) { 45 | return $matches[1]; 46 | } 47 | 48 | return strtoupper($matches[1]); 49 | } 50 | ); 51 | } 52 | // array $options, 'is_safe' => false: since the given $string which might need escaping is returned. 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Twig/Tests/NumericTest.php: -------------------------------------------------------------------------------- 1 | 'GeckoPackages\Twig\Tests\NumericTestNode'] 29 | ); 30 | } 31 | } 32 | 33 | final class NumericTestNode extends \Twig_Node_Expression_Test 34 | { 35 | public function compile(\Twig_Compiler $compiler) 36 | { 37 | $compiler->raw('is_numeric(')->subcompile($this->getNode('node'))->raw(')'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Twig/Text/RomanNumeralsTrait.php: -------------------------------------------------------------------------------- 1 |