Skip to content

Instantly share code, notes, and snippets.

@cs278
Created June 11, 2024 23:34
Show Gist options
  • Save cs278/54ba13cbeff371d7a6587c67203389ce to your computer and use it in GitHub Desktop.
Save cs278/54ba13cbeff371d7a6587c67203389ce to your computer and use it in GitHub Desktop.
Generate test data for implementing PHP comparisons in userland.
<?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