Created
June 11, 2024 23:34
-
-
Save cs278/54ba13cbeff371d7a6587c67203389ce to your computer and use it in GitHub Desktop.
Generate test data for implementing PHP comparisons in userland.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
require 'vendor/autoload.php'; | |
interface Ref | |
{ | |
public function value(); | |
public function export(): string; | |
} | |
final class VarRef implements Ref | |
{ | |
/** @readonly */ | |
public string $name; | |
public function __construct(string $name) | |
{ | |
$this->name = $name; | |
} | |
public function value() | |
{ | |
if (!isset($GLOBALS[$this->name])) { | |
throw new \RuntimeException('Undefined variable '. $this->name); | |
} | |
return $GLOBALS[$this->name]; | |
} | |
public function export(): string | |
{ | |
return '$'.$this->name; | |
} | |
} | |
final class ConstRef implements Ref | |
{ | |
/** @readonly */ | |
public string $name; | |
public function __construct(string $name) | |
{ | |
$this->name = $name; | |
} | |
public function value() | |
{ | |
return constant($this->name); | |
} | |
public function export(): string | |
{ | |
return $this->name; | |
} | |
} | |
function compare($a, $b): int | |
{ | |
$a = $a instanceof Ref ? $a->value() : $a; | |
$b = $b instanceof Ref ? $b->value() : $b; | |
return @($a <=> $b); | |
} | |
function export($value): string | |
{ | |
if ($value === null) { | |
return 'null'; | |
} | |
if ($value instanceof Ref) { | |
return $value->export(); | |
} | |
if ($value instanceof \stdClass) { | |
return sprintf('(object) %s', export(get_object_vars($value))); | |
} | |
if ($value === []) { | |
return '[]'; | |
} | |
if (is_string($value)) { | |
$isSimple = $value === '' || preg_match('{^[[:graph:] ]+$}', $value) > 0; | |
if ($isSimple) { | |
return var_export($value, true); | |
} | |
return sprintf('hex2bin(%s)', var_export(bin2hex($value), true)); | |
} | |
if (\is_array($value) && array_is_list($value)) { | |
$result = '['; | |
foreach ($value as $innerValue) { | |
$result .= export($innerValue); | |
$result .= ','; | |
} | |
$result[strlen($result) - 1] = ']'; | |
return $result; | |
} | |
if (\is_array($value)) { | |
$result = '['; | |
foreach ($value as $innerKey => $innerValue) { | |
$result .= export($innerKey); | |
$result .= ' => '; | |
$result .= export($innerValue); | |
$result .= ','; | |
} | |
$result[strlen($result) - 1] = ']'; | |
return $result; | |
} | |
if (\is_object($value)) { | |
return sprintf('unserialize(%s)', var_export(serialize($value), true)); | |
} | |
return var_export($value, true); | |
} | |
$generator = function () { | |
$arrays = [ | |
'[]' => [], | |
'[[]]' => [[]], | |
'[1 => []]' => [1 => []], | |
'[0]' => [0], | |
'[0.0]' => [0.0], | |
'[null]' => [null], | |
'[false]' => [false], | |
'[true]' => [true], | |
'[a, b, c]' => ['a', 'b', 'c'], | |
'[$fh1]' => [new VarRef('fh1')], | |
]; | |
yield 'false' => false; | |
yield 'true' => true; | |
yield 'null' => null; | |
yield '0' => 0; | |
yield '1' => 1; | |
yield '-1' => -1; | |
yield '0.0' => 0.0; | |
yield '-1.0' => -1.0; | |
yield '1.0' => 1.0; | |
yield "''" => ''; | |
yield "'0'" => '0'; | |
yield "'-0'" => '-0'; | |
yield "'+0'" => '+0'; | |
yield "'0.0'" => '0.0'; | |
yield "'-0.0'" => '-0.0'; | |
yield "'+0.0'" => '+0.0'; | |
yield "'1.0'" => '1.0'; | |
yield "'-1.0'" => '-1.0'; | |
yield "'+1.0'" => '+1.0'; | |
yield "'-1'" => '-1'; | |
yield "'+1'" => '+1'; | |
yield "'1'" => '1'; | |
// Spaces and 0 | |
yield "' 0'" => ' 0'; | |
yield "' -0'" => ' -0'; | |
yield "' +0'" => ' +0'; | |
yield "' 0' " => ' 0 '; | |
yield "' -0'" => ' -0 '; | |
yield "' +0'" => ' +0 '; | |
yield "'0' " => '0 '; | |
yield "'-0'" => '-0 '; | |
yield "'+0'" => '+0 '; | |
// Spaces and 0.0 | |
yield "' 0.0'" => ' 0.0'; | |
yield "' -0.0'" => ' -0.0'; | |
yield "' +0.0'" => ' +0.0'; | |
yield "' 0.0' " => ' 0.0 '; | |
yield "' -0.0'" => ' -0.0 '; | |
yield "' +0.0'" => ' +0.0 '; | |
yield "'0.0' " => '0.0 '; | |
yield "'-0.0'" => '-0.0 '; | |
yield "'+0.0'" => '+0.0 '; | |
yield "'03'" => '03'; // Octal? | |
yield "'2abc'" => '2abc'; | |
yield "'2.5abc'" => '2.5abc'; | |
yield "'abc2abc'" => 'abc2abc'; | |
yield "'abc2.5abc'" => 'abc2.5abc'; | |
yield '\'\0\'' => "\0"; | |
yield '\'\t\n\r\v\f5\t\n\r\v\f\'' => "\t\n\r\v\f5\t\n\r\v\f"; | |
yield '\'\0\t\n\r\v\f5\0\t\n\r\v\f\'' => "\0\t\n\r\v\f5\0\t\n\r\v\f"; | |
yield '12.0000000000001' => 12.0000000000001; | |
yield '12.000000000000002' => 12.000000000000002; | |
yield 'pi' => new ConstRef('M_PI'); | |
yield 'int(max)' => new ConstRef('PHP_INT_MAX'); | |
yield 'int(min)' => new ConstRef('PHP_INT_MIN'); | |
yield 'float(epsilon)' => new ConstRef('PHP_FLOAT_EPSILON'); | |
yield 'float(min)' => new ConstRef('PHP_FLOAT_MIN'); | |
yield 'float(max)' => new ConstRef('PHP_FLOAT_MAX'); | |
// var_export() has special handling for these constants. | |
// https://github.com/php/php-src/blob/d545b1d64350cac9cbf27859ad44d3ba32f6b736/Zend/zend_strtod.c#L4518-L4522 | |
yield 'float(inf)' => INF; | |
yield 'float(-inf)' => -INF; | |
yield 'float(nan)' => NAN; | |
yield '$closure1' => new VarRef('closure1'); | |
yield '$closure2' => new VarRef('closure2'); | |
yield '$fh1' => new VarRef('fh1'); | |
yield '$fh2' => new VarRef('fh2'); | |
yield '$curl' => new VarRef('curl'); | |
yield 'DateTime(2024-01-01T12:00:00Z)' => new \DateTime('2024-01-01 12:00:00', new \DateTimeZone('UTC')); | |
yield 'DateTime(2024-01-01T12:00:01Z)' => new \DateTime('2024-01-01 12:00:01', new \DateTimeZone('UTC')); | |
yield 'DateTimeImmutable(2024-01-01T12:00:00Z)' => new \DateTimeImmutable('2024-01-01 12:00:00', new \DateTimeZone('UTC')); | |
yield 'DateTimeImmutable(2024-01-01T12:00:01Z)' => new \DateTimeImmutable('2024-01-01 12:00:01', new \DateTimeZone('UTC')); | |
yield from $arrays; | |
foreach ($arrays as $label => $value) { | |
yield sprintf('object%s', $label) => (object) $value; | |
} | |
}; | |
$vars = [ | |
'closure1' => "function () {}", | |
'closure2' => "function () {}", | |
'fh1' => "fopen('php://memory', 'rb')", | |
'fh2' => "fopen('php://memory', 'wb')", | |
'curl' => "curl_init()", | |
]; | |
printf('<'."?php\n"); | |
printf("return (static function () {\n"); | |
foreach ($vars as $name => $code) { | |
$GLOBALS[$name] = eval(sprintf('return %s;', $code)); | |
printf('$%s = %s;', $name, $code); | |
echo "\n"; | |
} | |
printf("/"."** @var array<string,array{int<-1,1>, array{type: int, message: string}|null, mixed, mixed}> */\n"); | |
printf("\$tests = [];\n"); | |
foreach ($generator() as $l1 => $v1) { | |
foreach ($generator() as $l2 => $v2) { | |
error_clear_last(); | |
$result = compare($v1, $v2); | |
$error = error_get_last(); | |
printf( | |
"\$tests[%s] = [%d, %s, %s, %s];\n", | |
export(sprintf('%s <=> %s', $l1, $l2)), | |
$result, | |
export($error !== null ? ['type' => $error['type'], 'message' => $error['message']] : null), | |
export($v1), | |
export($v2), | |
); | |
} | |
} | |
printf("return \$tests;\n"); | |
printf("})();\n"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment