1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Polyfill\Php72; 13 14/** 15 * @author Nicolas Grekas <p@tchwork.com> 16 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com> 17 * 18 * @internal 19 */ 20final class Php72 21{ 22 private static $hashMask; 23 24 public static function utf8_encode($s) 25 { 26 $s .= $s; 27 $len = \strlen($s); 28 29 for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) { 30 switch (true) { 31 case $s[$i] < "\x80": $s[$j] = $s[$i]; break; 32 case $s[$i] < "\xC0": $s[$j] = "\xC2"; $s[++$j] = $s[$i]; break; 33 default: $s[$j] = "\xC3"; $s[++$j] = \chr(\ord($s[$i]) - 64); break; 34 } 35 } 36 37 return substr($s, 0, $j); 38 } 39 40 public static function utf8_decode($s) 41 { 42 $s = (string) $s; 43 $len = \strlen($s); 44 45 for ($i = 0, $j = 0; $i < $len; ++$i, ++$j) { 46 switch ($s[$i] & "\xF0") { 47 case "\xC0": 48 case "\xD0": 49 $c = (\ord($s[$i] & "\x1F") << 6) | \ord($s[++$i] & "\x3F"); 50 $s[$j] = $c < 256 ? \chr($c) : '?'; 51 break; 52 53 case "\xF0": 54 ++$i; 55 // no break 56 57 case "\xE0": 58 $s[$j] = '?'; 59 $i += 2; 60 break; 61 62 default: 63 $s[$j] = $s[$i]; 64 } 65 } 66 67 return substr($s, 0, $j); 68 } 69 70 public static function php_os_family() 71 { 72 if ('\\' === \DIRECTORY_SEPARATOR) { 73 return 'Windows'; 74 } 75 76 $map = [ 77 'Darwin' => 'Darwin', 78 'DragonFly' => 'BSD', 79 'FreeBSD' => 'BSD', 80 'NetBSD' => 'BSD', 81 'OpenBSD' => 'BSD', 82 'Linux' => 'Linux', 83 'SunOS' => 'Solaris', 84 ]; 85 86 return isset($map[\PHP_OS]) ? $map[\PHP_OS] : 'Unknown'; 87 } 88 89 public static function spl_object_id($object) 90 { 91 if (null === self::$hashMask) { 92 self::initHashMask(); 93 } 94 if (null === $hash = spl_object_hash($object)) { 95 return; 96 } 97 98 // On 32-bit systems, PHP_INT_SIZE is 4, 99 return self::$hashMask ^ hexdec(substr($hash, 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1))); 100 } 101 102 public static function sapi_windows_vt100_support($stream, $enable = null) 103 { 104 if (!\is_resource($stream)) { 105 trigger_error('sapi_windows_vt100_support() expects parameter 1 to be resource, '.\gettype($stream).' given', \E_USER_WARNING); 106 107 return false; 108 } 109 110 $meta = stream_get_meta_data($stream); 111 112 if ('STDIO' !== $meta['stream_type']) { 113 trigger_error('sapi_windows_vt100_support() was not able to analyze the specified stream', \E_USER_WARNING); 114 115 return false; 116 } 117 118 // We cannot actually disable vt100 support if it is set 119 if (false === $enable || !self::stream_isatty($stream)) { 120 return false; 121 } 122 123 // The native function does not apply to stdin 124 $meta = array_map('strtolower', $meta); 125 $stdin = 'php://stdin' === $meta['uri'] || 'php://fd/0' === $meta['uri']; 126 127 return !$stdin 128 && (false !== getenv('ANSICON') 129 || 'ON' === getenv('ConEmuANSI') 130 || 'xterm' === getenv('TERM') 131 || 'Hyper' === getenv('TERM_PROGRAM')); 132 } 133 134 public static function stream_isatty($stream) 135 { 136 if (!\is_resource($stream)) { 137 trigger_error('stream_isatty() expects parameter 1 to be resource, '.\gettype($stream).' given', \E_USER_WARNING); 138 139 return false; 140 } 141 142 if ('\\' === \DIRECTORY_SEPARATOR) { 143 $stat = @fstat($stream); 144 // Check if formatted mode is S_IFCHR 145 return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; 146 } 147 148 return \function_exists('posix_isatty') && @posix_isatty($stream); 149 } 150 151 private static function initHashMask() 152 { 153 $obj = (object) []; 154 self::$hashMask = -1; 155 156 // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below 157 $obFuncs = ['ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush']; 158 foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? \DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) { 159 if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) { 160 $frame['line'] = 0; 161 break; 162 } 163 } 164 if (!empty($frame['line'])) { 165 ob_start(); 166 debug_zval_dump($obj); 167 self::$hashMask = (int) substr(ob_get_clean(), 17); 168 } 169 170 self::$hashMask ^= hexdec(substr(spl_object_hash($obj), 16 - (\PHP_INT_SIZE * 2 - 1), (\PHP_INT_SIZE * 2 - 1))); 171 } 172 173 public static function mb_chr($code, $encoding = null) 174 { 175 if (0x80 > $code %= 0x200000) { 176 $s = \chr($code); 177 } elseif (0x800 > $code) { 178 $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); 179 } elseif (0x10000 > $code) { 180 $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); 181 } else { 182 $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); 183 } 184 185 if ('UTF-8' !== $encoding = $encoding ?? mb_internal_encoding()) { 186 $s = mb_convert_encoding($s, $encoding, 'UTF-8'); 187 } 188 189 return $s; 190 } 191 192 public static function mb_ord($s, $encoding = null) 193 { 194 if (null === $encoding) { 195 $s = mb_convert_encoding($s, 'UTF-8'); 196 } elseif ('UTF-8' !== $encoding) { 197 $s = mb_convert_encoding($s, 'UTF-8', $encoding); 198 } 199 200 if (1 === \strlen($s)) { 201 return \ord($s); 202 } 203 204 $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; 205 if (0xF0 <= $code) { 206 return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; 207 } 208 if (0xE0 <= $code) { 209 return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; 210 } 211 if (0xC0 <= $code) { 212 return (($code - 0xC0) << 6) + $s[2] - 0x80; 213 } 214 215 return $code; 216 } 217} 218