diff --git a/composer.json b/composer.json index d8e863cc8..40e58869b 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,6 @@ "phpbench/phpbench": "^1.0", "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^8.5|^9.6", - "symfony/phpunit-bridge": "^5.2|6.4.25|7.3.3", "vimeo/psalm": "^4.17" }, "suggest": { diff --git a/phpstan.neon b/phpstan.neon index 91af9c48e..61e18b96c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -10,6 +10,7 @@ parameters: excludePaths: - tests/resources - tests/Fixtures + - src/Util/ClockMock.php dynamicConstantNames: - Monolog\Logger::API bootstrapFiles: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d064f8775..8d5384d66 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -34,10 +34,6 @@ - - - - diff --git a/psalm.xml.dist b/psalm.xml.dist index ea60216a6..502f940b3 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -10,6 +10,7 @@ + diff --git a/src/Util/ClockMock.php b/src/Util/ClockMock.php new file mode 100644 index 000000000..ea3f18f18 --- /dev/null +++ b/src/Util/ClockMock.php @@ -0,0 +1,202 @@ + + * @author Dominic Tubach + */ +class ClockMock +{ + private static $now; + + public static function withClockMock($enable = null): ?bool + { + if ($enable === null) { + return self::$now !== null; + } + + self::$now = is_numeric($enable) ? (float) $enable : ($enable ? microtime(true) : null); + + return null; + } + + public static function time(): int + { + if (self::$now === null) { + return time(); + } + + return (int) self::$now; + } + + public static function sleep($s): int + { + if (self::$now === null) { + return sleep($s); + } + + self::$now += (int) $s; + + return 0; + } + + public static function usleep($us): void + { + if (self::$now === null) { + usleep($us); + } else { + self::$now += $us / 1000000; + } + } + + /** + * @return string|float + */ + public static function microtime($asFloat = false) + { + if (self::$now === null) { + return microtime($asFloat); + } + + if ($asFloat) { + return self::$now; + } + + return \sprintf('%0.6f00 %d', self::$now - (int) self::$now, (int) self::$now); + } + + public static function date($format, $timestamp = null): string + { + if ($timestamp === null) { + $timestamp = self::time(); + } + + return date($format, $timestamp); + } + + public static function gmdate($format, $timestamp = null): string + { + if ($timestamp === null) { + $timestamp = self::time(); + } + + return gmdate($format, $timestamp); + } + + /** + * @return array|int|float + */ + public static function hrtime($asNumber = false) + { + $ns = (self::$now - (int) self::$now) * 1000000000; + + if ($asNumber) { + $number = \sprintf('%d%d', (int) self::$now, $ns); + + return \PHP_INT_SIZE === 8 ? (int) $number : (float) $number; + } + + return [(int) self::$now, (int) $ns]; + } + + /** + * @return false|int + */ + public static function strtotime(string $datetime, ?int $timestamp = null) + { + if ($timestamp === null) { + $timestamp = self::time(); + } + + return strtotime($datetime, $timestamp); + } + + public static function register($class): void + { + $self = static::class; + + $mockedNs = [substr($class, 0, strrpos($class, '\\'))]; + if (strpos($class, '\\Tests\\') > 0) { + $ns = str_replace('\\Tests\\', '\\', $class); + $mockedNs[] = substr($ns, 0, strrpos($ns, '\\')); + } elseif (str_starts_with($class, 'Tests\\')) { + $mockedNs[] = substr($class, 6, strrpos($class, '\\') - 6); + } + foreach ($mockedNs as $ns) { + if (\function_exists($ns . '\time')) { + continue; + } + eval(<<