├── LICENSE ├── NoDynamicProperties.php ├── Php82.php ├── README.md ├── Random └── Engine │ └── Secure.php ├── Resources └── stubs │ ├── AllowDynamicProperties.php │ ├── Random │ ├── BrokenRandomEngineError.php │ ├── CryptoSafeEngine.php │ ├── Engine.php │ ├── Engine │ │ └── Secure.php │ ├── RandomError.php │ └── RandomException.php │ ├── SensitiveParameter.php │ └── SensitiveParameterValue.php ├── SensitiveParameterValue.php ├── bootstrap.php └── composer.json /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-present Fabien Potencier 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. 20 | -------------------------------------------------------------------------------- /NoDynamicProperties.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Polyfill\Php82; 13 | 14 | /** 15 | * @internal 16 | */ 17 | trait NoDynamicProperties 18 | { 19 | public function __set(string $name, $value): void 20 | { 21 | throw new \Error('Cannot create dynamic property '.self::class.'::$'.$name); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Php82.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Polyfill\Php82; 13 | 14 | /** 15 | * @author Alexander M. Turek 16 | * @author Greg Roach 17 | * 18 | * @internal 19 | */ 20 | class Php82 21 | { 22 | /** 23 | * Determines if a string matches the ODBC quoting rules. 24 | * 25 | * A valid quoted string begins with a '{', ends with a '}', and has no '}' 26 | * inside of the string that aren't repeated (as to be escaped). 27 | * 28 | * These rules are what .NET also follows. 29 | * 30 | * @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L31-L57 31 | */ 32 | public static function odbc_connection_string_is_quoted(string $str): bool 33 | { 34 | if ('' === $str || '{' !== $str[0]) { 35 | return false; 36 | } 37 | 38 | /* Check for } that aren't doubled up or at the end of the string */ 39 | $length = \strlen($str) - 1; 40 | for ($i = 0; $i < $length; ++$i) { 41 | if ('}' !== $str[$i]) { 42 | continue; 43 | } 44 | 45 | if ('}' !== $str[++$i]) { 46 | return $i === $length; 47 | } 48 | } 49 | 50 | return true; 51 | } 52 | 53 | /** 54 | * Determines if a value for a connection string should be quoted. 55 | * 56 | * The ODBC specification mentions: 57 | * "Because of connection string and initialization file grammar, keywords and 58 | * attribute values that contain the characters []{}(),;?*=!@ not enclosed 59 | * with braces should be avoided." 60 | * 61 | * Note that it assumes that the string is *not* already quoted. You should 62 | * check beforehand. 63 | * 64 | * @see https://github.com/php/php-src/blob/838f6bffff6363a204a2597cbfbaad1d7ee3f2b6/main/php_odbc_utils.c#L59-L73 65 | */ 66 | public static function odbc_connection_string_should_quote(string $str): bool 67 | { 68 | return false !== strpbrk($str, '[]{}(),;?*=!@'); 69 | } 70 | 71 | public static function odbc_connection_string_quote(string $str): string 72 | { 73 | return '{'.str_replace('}', '}}', $str).'}'; 74 | } 75 | 76 | /** 77 | * Implementation closely based on the original C code - including the GOTOs 78 | * and pointer-style string access. 79 | * 80 | * @see https://github.com/php/php-src/blob/master/Zend/zend_ini.c 81 | */ 82 | public static function ini_parse_quantity(string $value): int 83 | { 84 | // Avoid dependency on ctype_space() 85 | $ctype_space = " \t\v\r\n\f"; 86 | 87 | $str = 0; 88 | $str_end = \strlen($value); 89 | $digits = $str; 90 | $overflow = false; 91 | 92 | /* Ignore leading whitespace, but keep it for error messages. */ 93 | while ($digits < $str_end && false !== strpos($ctype_space, $value[$digits])) { 94 | ++$digits; 95 | } 96 | 97 | /* Ignore trailing whitespace, but keep it for error messages. */ 98 | while ($digits < $str_end && false !== strpos($ctype_space, $value[$str_end - 1])) { 99 | --$str_end; 100 | } 101 | 102 | if ($digits === $str_end) { 103 | return 0; 104 | } 105 | 106 | $is_negative = false; 107 | 108 | if ('+' === $value[$digits]) { 109 | ++$digits; 110 | } elseif ('-' === $value[$digits]) { 111 | $is_negative = true; 112 | ++$digits; 113 | } 114 | 115 | if ($value[$digits] < '0' || $value[$digits] > 9) { 116 | $message = sprintf( 117 | 'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility', 118 | self::escapeString($value) 119 | ); 120 | 121 | trigger_error($message, \E_USER_WARNING); 122 | 123 | return 0; 124 | } 125 | 126 | $base = 10; 127 | $allowed_digits = '0123456789'; 128 | 129 | if ('0' === $value[$digits] && ($digits + 1 === $str_end || false === strpos($allowed_digits, $value[$digits + 1]))) { 130 | if ($digits + 1 === $str_end) { 131 | return 0; 132 | } 133 | 134 | switch ($value[$digits + 1]) { 135 | case 'g': 136 | case 'G': 137 | case 'm': 138 | case 'M': 139 | case 'k': 140 | case 'K': 141 | goto evaluation; 142 | case 'x': 143 | case 'X': 144 | $base = 16; 145 | $allowed_digits = '0123456789abcdefABCDEF'; 146 | break; 147 | case 'o': 148 | case 'O': 149 | $base = 8; 150 | $allowed_digits = '01234567'; 151 | break; 152 | case 'b': 153 | case 'B': 154 | $base = 2; 155 | $allowed_digits = '01'; 156 | break; 157 | default: 158 | $message = sprintf( 159 | 'Invalid prefix "0%s", interpreting as "0" for backwards compatibility', 160 | $value[$digits + 1] 161 | ); 162 | trigger_error($message, \E_USER_WARNING); 163 | 164 | return 0; 165 | } 166 | 167 | $digits += 2; 168 | if ($digits === $str_end) { 169 | $message = sprintf( 170 | 'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility', 171 | self::escapeString($value) 172 | ); 173 | trigger_error($message, \E_USER_WARNING); 174 | 175 | return 0; 176 | } 177 | 178 | $digits_consumed = $digits; 179 | /* Ignore leading whitespace. */ 180 | while ($digits_consumed < $str_end && false !== strpos($ctype_space, $value[$digits_consumed])) { 181 | ++$digits_consumed; 182 | } 183 | if ($digits_consumed !== $str_end && ('+' === $value[$digits_consumed] || '-' === $value[$digits_consumed])) { 184 | ++$digits_consumed; 185 | } 186 | 187 | if ('0' === $value[$digits_consumed]) { 188 | /* Value is just 0 */ 189 | if ($digits_consumed + 1 === $str_end) { 190 | goto evaluation; 191 | } 192 | switch ($value[$digits_consumed + 1]) { 193 | case 'x': 194 | case 'X': 195 | case 'o': 196 | case 'O': 197 | case 'b': 198 | case 'B': 199 | $digits_consumed += 2; 200 | break; 201 | } 202 | } 203 | 204 | if ($digits !== $digits_consumed) { 205 | $message = sprintf( 206 | 'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility', 207 | self::escapeString($value) 208 | ); 209 | trigger_error($message, \E_USER_WARNING); 210 | 211 | return 0; 212 | } 213 | } 214 | 215 | evaluation: 216 | 217 | if (10 === $base && '0' === $value[$digits]) { 218 | $base = 8; 219 | $allowed_digits = '01234567'; 220 | } 221 | 222 | while ($digits < $str_end && ' ' === $value[$digits]) { 223 | ++$digits; 224 | } 225 | 226 | if ($digits < $str_end && '+' === $value[$digits]) { 227 | ++$digits; 228 | } elseif ($digits < $str_end && '-' === $value[$digits]) { 229 | $is_negative = true; 230 | $overflow = true; 231 | ++$digits; 232 | } 233 | 234 | $digits_end = $digits; 235 | 236 | while ($digits_end < $str_end && false !== strpos($allowed_digits, $value[$digits_end])) { 237 | ++$digits_end; 238 | } 239 | 240 | $retval = base_convert(substr($value, $digits, $digits_end - $digits), $base, 10); 241 | 242 | if ($is_negative && '0' === $retval) { 243 | $is_negative = false; 244 | $overflow = false; 245 | } 246 | 247 | // Check for overflow - remember that -PHP_INT_MIN = 1 + PHP_INT_MAX 248 | if ($is_negative) { 249 | $signed_max = strtr((string) \PHP_INT_MIN, ['-' => '']); 250 | } else { 251 | $signed_max = (string) \PHP_INT_MAX; 252 | } 253 | 254 | $max_length = max(\strlen($retval), \strlen($signed_max)); 255 | 256 | $tmp1 = str_pad($retval, $max_length, '0', \STR_PAD_LEFT); 257 | $tmp2 = str_pad($signed_max, $max_length, '0', \STR_PAD_LEFT); 258 | 259 | if ($tmp1 > $tmp2) { 260 | $retval = -1; 261 | $overflow = true; 262 | } elseif ($is_negative) { 263 | $retval = '-'.$retval; 264 | } 265 | 266 | $retval = (int) $retval; 267 | 268 | if ($digits_end === $digits) { 269 | $message = sprintf( 270 | 'Invalid quantity "%s": no valid leading digits, interpreting as "0" for backwards compatibility', 271 | self::escapeString($value) 272 | ); 273 | trigger_error($message, \E_USER_WARNING); 274 | 275 | return 0; 276 | } 277 | 278 | /* Allow for whitespace between integer portion and any suffix character */ 279 | while ($digits_end < $str_end && false !== strpos($ctype_space, $value[$digits_end])) { 280 | ++$digits_end; 281 | } 282 | 283 | /* No exponent suffix. */ 284 | if ($digits_end === $str_end) { 285 | goto end; 286 | } 287 | 288 | switch ($value[$str_end - 1]) { 289 | case 'g': 290 | case 'G': 291 | $shift = 30; 292 | break; 293 | case 'm': 294 | case 'M': 295 | $shift = 20; 296 | break; 297 | case 'k': 298 | case 'K': 299 | $shift = 10; 300 | break; 301 | default: 302 | /* Unknown suffix */ 303 | $invalid = self::escapeString($value); 304 | $interpreted = self::escapeString(substr($value, $str, $digits_end - $str)); 305 | $chr = self::escapeString($value[$str_end - 1]); 306 | 307 | $message = sprintf( 308 | 'Invalid quantity "%s": unknown multiplier "%s", interpreting as "%s" for backwards compatibility', 309 | $invalid, 310 | $chr, 311 | $interpreted 312 | ); 313 | 314 | trigger_error($message, \E_USER_WARNING); 315 | 316 | return $retval; 317 | } 318 | 319 | $factor = 1 << $shift; 320 | 321 | if (!$overflow) { 322 | if ($retval > 0) { 323 | $overflow = $retval > \PHP_INT_MAX / $factor; 324 | } else { 325 | $overflow = $retval < \PHP_INT_MIN / $factor; 326 | } 327 | } 328 | 329 | if (\is_float($retval * $factor)) { 330 | $overflow = true; 331 | $retval <<= $shift; 332 | } else { 333 | $retval *= $factor; 334 | } 335 | 336 | if ($digits_end !== $str_end - 1) { 337 | /* More than one character in suffix */ 338 | $message = sprintf( 339 | 'Invalid quantity "%s", interpreting as "%s%s" for backwards compatibility', 340 | self::escapeString($value), 341 | self::escapeString(substr($value, $str, $digits_end - $str)), 342 | self::escapeString($value[$str_end - 1]) 343 | ); 344 | trigger_error($message, \E_USER_WARNING); 345 | 346 | return $retval; 347 | } 348 | 349 | end: 350 | 351 | if ($overflow) { 352 | /* Not specifying the resulting value here because the caller may make 353 | * additional conversions. Not specifying the allowed range 354 | * because the caller may do narrower range checks. */ 355 | $message = sprintf( 356 | 'Invalid quantity "%s": value is out of range, using overflow result for backwards compatibility', 357 | self::escapeString($value) 358 | ); 359 | trigger_error($message, \E_USER_WARNING); 360 | } 361 | 362 | return $retval; 363 | } 364 | 365 | /** 366 | * Escape the string to avoid null bytes and to make non-printable chars visible. 367 | */ 368 | private static function escapeString(string $string): string 369 | { 370 | $escaped = ''; 371 | 372 | for ($n = 0, $len = \strlen($string); $n < $len; ++$n) { 373 | $c = \ord($string[$n]); 374 | 375 | if ($c < 32 || '\\' === $string[$n] || $c > 126) { 376 | switch ($string[$n]) { 377 | case "\n": $escaped .= '\\n'; break; 378 | case "\r": $escaped .= '\\r'; break; 379 | case "\t": $escaped .= '\\t'; break; 380 | case "\f": $escaped .= '\\f'; break; 381 | case "\v": $escaped .= '\\v'; break; 382 | case '\\': $escaped .= '\\\\'; break; 383 | case "\x1B": $escaped .= '\\e'; break; 384 | default: 385 | $escaped .= '\\x'.strtoupper(sprintf('%02x', $c)); 386 | } 387 | } else { 388 | $escaped .= $string[$n]; 389 | } 390 | } 391 | 392 | return $escaped; 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Symfony Polyfill / Php82 2 | ======================== 3 | 4 | This component provides features added to PHP 8.2 core: 5 | 6 | - [`AllowDynamicProperties`](https://wiki.php.net/rfc/deprecate_dynamic_properties) 7 | - [`SensitiveParameter`](https://wiki.php.net/rfc/redact_parameters_in_back_traces) 8 | - [`SensitiveParameterValue`](https://wiki.php.net/rfc/redact_parameters_in_back_traces) 9 | - [`Random\Engine`](https://wiki.php.net/rfc/rng_extension) 10 | - [`Random\Engine\CryptoSafeEngine`](https://wiki.php.net/rfc/rng_extension) 11 | - [`Random\Engine\Secure`](https://wiki.php.net/rfc/rng_extension) (check [arokettu/random-polyfill](https://packagist.org/packages/arokettu/random-polyfill) for more engines) 12 | - [`odbc_connection_string_is_quoted()`](https://php.net/odbc_connection_string_is_quoted) 13 | - [`odbc_connection_string_should_quote()`](https://php.net/odbc_connection_string_should_quote) 14 | - [`odbc_connection_string_quote()`](https://php.net/odbc_connection_string_quote) 15 | - [`ini_parse_quantity()`](https://php.net/ini_parse_quantity) 16 | 17 | More information can be found in the 18 | [main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). 19 | 20 | License 21 | ======= 22 | 23 | This library is released under the [MIT license](LICENSE). 24 | -------------------------------------------------------------------------------- /Random/Engine/Secure.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Polyfill\Php82\Random\Engine; 13 | 14 | use Random\RandomException; 15 | use Symfony\Polyfill\Php82\NoDynamicProperties; 16 | 17 | /** 18 | * @author Tim Düsterhus 19 | * @author Anton Smirnov 20 | * 21 | * @internal 22 | */ 23 | class Secure 24 | { 25 | use NoDynamicProperties; 26 | 27 | public function generate(): string 28 | { 29 | try { 30 | return random_bytes(\PHP_INT_SIZE); 31 | } catch (\Exception $e) { 32 | throw new RandomException($e->getMessage(), $e->getCode(), $e->getPrevious()); 33 | } 34 | } 35 | 36 | public function __sleep(): array 37 | { 38 | throw new \Exception("Serialization of 'Random\Engine\Secure' is not allowed"); 39 | } 40 | 41 | public function __wakeup(): void 42 | { 43 | throw new \Exception("Unserialization of 'Random\Engine\Secure' is not allowed"); 44 | } 45 | 46 | public function __clone() 47 | { 48 | throw new \Error('Trying to clone an uncloneable object of class Random\Engine\Secure'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Resources/stubs/AllowDynamicProperties.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | if (\PHP_VERSION_ID < 80200) { 13 | #[Attribute(Attribute::TARGET_CLASS)] 14 | final class AllowDynamicProperties 15 | { 16 | public function __construct() 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Resources/stubs/Random/BrokenRandomEngineError.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random; 13 | 14 | if (\PHP_VERSION_ID < 80200) { 15 | class BrokenRandomEngineError extends RandomError 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Resources/stubs/Random/CryptoSafeEngine.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random; 13 | 14 | if (\PHP_VERSION_ID < 80200) { 15 | interface CryptoSafeEngine extends Engine 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Resources/stubs/Random/Engine.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random; 13 | 14 | if (\PHP_VERSION_ID < 80200) { 15 | interface Engine 16 | { 17 | public function generate(): string; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Resources/stubs/Random/Engine/Secure.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random\Engine; 13 | 14 | use Symfony\Polyfill\Php82 as p; 15 | 16 | if (\PHP_VERSION_ID < 80200) { 17 | final class Secure extends p\Random\Engine\Secure implements \Random\CryptoSafeEngine 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Resources/stubs/Random/RandomError.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random; 13 | 14 | use Symfony\Polyfill\Php82\NoDynamicProperties; 15 | 16 | if (\PHP_VERSION_ID < 80200) { 17 | class RandomError extends \Error 18 | { 19 | use NoDynamicProperties; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Resources/stubs/Random/RandomException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Random; 13 | 14 | use Symfony\Polyfill\Php82\NoDynamicProperties; 15 | 16 | if (\PHP_VERSION_ID < 80200) { 17 | class RandomException extends \Exception 18 | { 19 | use NoDynamicProperties; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Resources/stubs/SensitiveParameter.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | if (\PHP_VERSION_ID < 80200) { 13 | #[Attribute(Attribute::TARGET_PARAMETER)] 14 | final class SensitiveParameter 15 | { 16 | public function __construct() 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Resources/stubs/SensitiveParameterValue.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | if (\PHP_VERSION_ID < 80200) { 13 | final class SensitiveParameterValue extends Symfony\Polyfill\Php82\SensitiveParameterValue 14 | { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SensitiveParameterValue.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Polyfill\Php82; 13 | 14 | /** 15 | * @author Tim Düsterhus 16 | * 17 | * @internal 18 | */ 19 | class SensitiveParameterValue 20 | { 21 | private $value; 22 | 23 | public function __construct($value) 24 | { 25 | $this->value = $value; 26 | } 27 | 28 | public function getValue() 29 | { 30 | return $this->value; 31 | } 32 | 33 | public function __debugInfo(): array 34 | { 35 | return []; 36 | } 37 | 38 | public function __sleep(): array 39 | { 40 | throw new \Exception("Serialization of 'SensitiveParameterValue' is not allowed"); 41 | } 42 | 43 | public function __wakeup(): void 44 | { 45 | throw new \Exception("Unserialization of 'SensitiveParameterValue' is not allowed"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | use Symfony\Polyfill\Php82 as p; 13 | 14 | if (\PHP_VERSION_ID >= 80200) { 15 | return; 16 | } 17 | 18 | if (extension_loaded('odbc')) { 19 | if (!function_exists('odbc_connection_string_is_quoted')) { 20 | function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); } 21 | } 22 | 23 | if (!function_exists('odbc_connection_string_should_quote')) { 24 | function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); } 25 | } 26 | 27 | if (!function_exists('odbc_connection_string_quote')) { 28 | function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); } 29 | } 30 | } 31 | 32 | if (!function_exists('ini_parse_quantity')) { 33 | function ini_parse_quantity(string $shorthand): int { return p\Php82::ini_parse_quantity($shorthand); } 34 | } 35 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/polyfill-php82", 3 | "type": "library", 4 | "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", 5 | "keywords": ["polyfill", "shim", "compatibility", "portable"], 6 | "homepage": "https://symfony.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Nicolas Grekas", 11 | "email": "p@tchwork.com" 12 | }, 13 | { 14 | "name": "Symfony Community", 15 | "homepage": "https://symfony.com/contributors" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=7.2" 20 | }, 21 | "autoload": { 22 | "psr-4": { "Symfony\\Polyfill\\Php82\\": "" }, 23 | "files": [ "bootstrap.php" ], 24 | "classmap": [ "Resources/stubs" ] 25 | }, 26 | "minimum-stability": "dev", 27 | "extra": { 28 | "thanks": { 29 | "name": "symfony/polyfill", 30 | "url": "https://github.com/symfony/polyfill" 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------