diff --git a/README.md b/README.md index c8602c8e..ff864dd8 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Polyfills are provided for: - the `get_mangled_object_vars`, `mb_str_split` and `password_algos` functions introduced in PHP 7.4; - the `fdiv` function introduced in PHP 8.0; +- the `get_debug_type` function introduced in PHP 8.0; - the `preg_last_error_msg` function introduced in PHP 8.0; - the `str_contains` function introduced in PHP 8.0; - the `ValueError` class introduced in PHP 8.0; diff --git a/src/Php80/Php80.php b/src/Php80/Php80.php index 557ace2b..4606980c 100644 --- a/src/Php80/Php80.php +++ b/src/Php80/Php80.php @@ -14,6 +14,7 @@ /** * @author Ion Bazan * @author Nico Oelgart + * @author Nicolas Grekas * * @internal */ @@ -24,6 +25,38 @@ public static function fdiv(float $dividend, float $divisor): float return @($dividend / $divisor); } + public static function get_debug_type($value): string + { + switch (true) { + case null === $value: return 'null'; + case \is_bool($value): return 'bool'; + case \is_string($value): return 'string'; + case \is_array($value): return 'array'; + case \is_int($value): return 'int'; + case \is_float($value): return 'float'; + case \is_object($value): break; + case $value instanceof \__PHP_Incomplete_Class: return '__PHP_Incomplete_Class'; + default: + if (null === $type = @get_resource_type($value)) { + return 'unknown'; + } + + if ('Unknown' === $type) { + $type = 'closed'; + } + + return "resource ($type)"; + } + + $class = \get_class($value); + + if (false === strpos($class, '@')) { + return $class; + } + + return (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous'; + } + public static function preg_last_error_msg(): string { switch (preg_last_error()) { diff --git a/src/Php80/README.md b/src/Php80/README.md index e569c113..f87b33db 100644 --- a/src/Php80/README.md +++ b/src/Php80/README.md @@ -7,6 +7,7 @@ This component provides features added to PHP 8.0 core: - [`fdiv`](https://php.net/fdiv) - `ValueError` class - `FILTER_VALIDATE_BOOL` constant +- [`get_debug_type`](https://php.net/get_debug_type) - [`preg_last_error_msg`](https://php.net/preg_last_error_msg) - [`str_contains`](https://php.net/str_contains) diff --git a/src/Php80/bootstrap.php b/src/Php80/bootstrap.php index 6b1d27d9..489be5e5 100644 --- a/src/Php80/bootstrap.php +++ b/src/Php80/bootstrap.php @@ -27,4 +27,8 @@ function str_contains(string $haystack, string $needle): bool { return p\Php80:: if (!defined('FILTER_VALIDATE_BOOL') && defined('FILTER_VALIDATE_BOOLEAN')) { define('FILTER_VALIDATE_BOOL', FILTER_VALIDATE_BOOLEAN); } + + if (!function_exists('get_debug_type')) { + function get_debug_type($value): string { return p\Php80::get_debug_type($value); } + } } diff --git a/tests/Php80/Php80Test.php b/tests/Php80/Php80Test.php index ba5233d3..e4f0c87e 100644 --- a/tests/Php80/Php80Test.php +++ b/tests/Php80/Php80Test.php @@ -155,6 +155,34 @@ public function invalidFloatProvider() ); } + /** + * @covers \Symfony\Polyfill\Php80\Php80::get_debug_type + */ + public function testGetDebugType() + { + $this->assertSame(__CLASS__, get_debug_type($this)); + $this->assertSame('stdClass', get_debug_type(new \stdClass())); + $this->assertSame('class@anonymous', get_debug_type(eval('return new class() {};'))); + $this->assertSame('stdClass@anonymous', get_debug_type(eval('return new class() extends stdClass {};'))); + $this->assertSame('Reflector@anonymous', get_debug_type(eval('return new class() implements Reflector { function __toString() {} public static function export() {} };'))); + + $this->assertSame('string', get_debug_type('foo')); + $this->assertSame('bool', get_debug_type(false)); + $this->assertSame('bool', get_debug_type(true)); + $this->assertSame('null', get_debug_type(null)); + $this->assertSame('array', get_debug_type(array())); + $this->assertSame('int', get_debug_type(1)); + $this->assertSame('float', get_debug_type(1.2)); + $this->assertSame('resource (stream)', get_debug_type($h = fopen(__FILE__, 'r'))); + $this->assertSame('resource (closed)', get_debug_type(fclose($h) ? $h : $h)); + + $unserializeCallbackHandler = ini_set('unserialize_callback_func', null); + $var = unserialize('O:8:"Foo\Buzz":0:{}'); + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + + $this->assertSame('__PHP_Incomplete_Class', get_debug_type($var)); + } + public function setExpectedException($exception, $message = '', $code = null) { if (!class_exists('PHPUnit\Framework\Error\Notice')) {