diff --git a/bin/clean-phpt-artifacts.php b/bin/clean-phpt-artifacts.php new file mode 100755 index 00000000..f04ffb4c --- /dev/null +++ b/bin/clean-phpt-artifacts.php @@ -0,0 +1,140 @@ +#!/usr/bin/env php +getPathname(); + if (str_ends_with($filePath, '.phpt')) { + $phptFiles[$filePath] = true; + } + } +} + +$artifacts = []; +foreach (array_keys($phptFiles) as $phptFile) { + $base = substr($phptFile, 0, -5); + foreach ($artifactSuffixes as $suffix) { + $artifact = $base . $suffix; + if (is_file($artifact)) { + $artifacts[$artifact] = true; + } + } + + $binary = $root . DIRECTORY_SEPARATOR . str_replace('-', '_', basename($base)) . $binarySuffix; + if (is_file($binary) && is_executable($binary)) { + $artifacts[$binary] = true; + } +} + +$artifacts = array_keys($artifacts); +sort($artifacts); + +$deleted = 0; +foreach ($artifacts as $artifact) { + $displayPath = relativePath($root, $artifact); + if ($dryRun) { + echo $displayPath, PHP_EOL; + continue; + } + if (!unlink($artifact)) { + fwrite(STDERR, "Failed to remove: {$displayPath}\n"); + exit(1); + } + ++$deleted; +} + +if ($dryRun) { + printf("Found %d PHPT artifact file(s).\n", count($artifacts)); +} else { + printf("Removed %d PHPT artifact file(s).\n", $deleted); +} + +function printUsage(string $script): void +{ + echo << +--EXPECT-- +array(4) { + [0]=> + string(3) "FOO" + [1]=> + string(7) "BAR+BAZ" + [2]=> + string(3) "BAR" + [3]=> + string(3) "BAZ" +} diff --git a/tests/aot/symfony/conditional-first-class-callable-property.phpt b/tests/aot/symfony/conditional-first-class-callable-property.phpt new file mode 100644 index 00000000..ff731ad5 --- /dev/null +++ b/tests/aot/symfony/conditional-first-class-callable-property.phpt @@ -0,0 +1,37 @@ +--TEST-- +Symfony pattern: conditional first-class callable stored in property +--FILE-- +callback = null !== $callback ? $callback(...) : null; + } + + public function run(string $value): ?string + { + if (null === $callback = $this->callback) { + return null; + } + + return $callback($value); + } +} + +function main(): void +{ + $callback = new DeferredCallback(); + $callback->set(static fn (string $value): string => strtoupper($value)); + var_dump($callback->run('symfony')); + + $callback->set(null); + var_dump($callback->run('symfony')); +} +?> +--EXPECT-- +string(7) "SYMFONY" +NULL diff --git a/tests/aot/symfony/dynamic-static-property-multidim-isset.phpt b/tests/aot/symfony/dynamic-static-property-multidim-isset.phpt new file mode 100644 index 00000000..16cbb91b --- /dev/null +++ b/tests/aot/symfony/dynamic-static-property-multidim-isset.phpt @@ -0,0 +1,40 @@ +--TEST-- +Symfony ErrorHandler pattern: dynamic static property name with multidimensional isset +--FILE-- + [ + 'STATUS' => 'ParentClass::STATUS', + ], + ]; + + private static array $finalProperties = [ + 'ParentClass' => [ + 'name' => 'ParentClass::$name', + ], + ]; + + public static function lookup(string $type, string $class, string $name): ?string + { + if (isset(self::${$type}[$class][$name])) { + return self::${$type}[$class][$name]; + } + + return null; + } +} + +function main(): void +{ + var_dump(FinalMemberRegistry::lookup('finalConstants', 'ParentClass', 'STATUS')); + var_dump(FinalMemberRegistry::lookup('finalProperties', 'ParentClass', 'name')); + var_dump(FinalMemberRegistry::lookup('finalProperties', 'ParentClass', 'missing')); +} +?> +--EXPECT-- +string(19) "ParentClass::STATUS" +string(18) "ParentClass::$name" +NULL diff --git a/tests/aot/symfony/env-array-diff-ukey-union.phpt b/tests/aot/symfony/env-array-diff-ukey-union.phpt new file mode 100644 index 00000000..695456ae --- /dev/null +++ b/tests/aot/symfony/env-array-diff-ukey-union.phpt @@ -0,0 +1,39 @@ +--TEST-- +Symfony Process pattern: environment union with array_diff_ukey callback +--FILE-- + '/bin']; + $result += $windows ? array_diff_ukey($env, $result, 'strcasecmp') : $env; + $result += $windows ? array_diff_ukey($defaults, $result, 'strcasecmp') : $defaults; + + return $result; +} + +function main(): void +{ + var_dump(mergeEnv(['Path' => '/usr/bin', 'APP_ENV' => 'dev'], ['HOME' => '/tmp'], true)); + var_dump(mergeEnv(['Path' => '/usr/bin', 'APP_ENV' => 'dev'], ['HOME' => '/tmp'], false)); +} +?> +--EXPECT-- +array(3) { + ["PATH"]=> + string(4) "/bin" + ["APP_ENV"]=> + string(3) "dev" + ["HOME"]=> + string(4) "/tmp" +} +array(4) { + ["PATH"]=> + string(4) "/bin" + ["Path"]=> + string(8) "/usr/bin" + ["APP_ENV"]=> + string(3) "dev" + ["HOME"]=> + string(4) "/tmp" +} diff --git a/tests/aot/symfony/late-static-new-static-return.phpt b/tests/aot/symfony/late-static-new-static-return.phpt new file mode 100644 index 00000000..ddf36955 --- /dev/null +++ b/tests/aot/symfony/late-static-new-static-return.phpt @@ -0,0 +1,41 @@ +--TEST-- +Symfony pattern: late static binding with new static and static return type +--FILE-- +name = $name; + + return $instance; + } +} + +class ChildFactory extends BaseFactory +{ + public string $extra = 'child'; +} + +function main(): void +{ + $base = BaseFactory::create('root'); + $child = ChildFactory::create('leaf'); + + var_dump($base::class); + var_dump($base->name); + var_dump($child::class); + var_dump($child->name); + var_dump($child->extra); +} +?> +--EXPECT-- +string(11) "BaseFactory" +string(4) "root" +string(12) "ChildFactory" +string(4) "leaf" +string(5) "child" diff --git a/tests/aot/symfony/new-expression-constructor-default.phpt b/tests/aot/symfony/new-expression-constructor-default.phpt new file mode 100644 index 00000000..bde2e912 --- /dev/null +++ b/tests/aot/symfony/new-expression-constructor-default.phpt @@ -0,0 +1,33 @@ +--TEST-- +Symfony AssetMapper pattern: new expression as constructor default +--FILE-- +compressor->compress($value); + } +} + +function main(): void +{ + var_dump((new GzipAsset())->encode('asset')); +} +?> +--EXPECT-- +string(8) "gz:asset" diff --git a/tests/aot/symfony/nullsafe-attribute-newinstance-chain.phpt b/tests/aot/symfony/nullsafe-attribute-newinstance-chain.phpt new file mode 100644 index 00000000..bfb0dd34 --- /dev/null +++ b/tests/aot/symfony/nullsafe-attribute-newinstance-chain.phpt @@ -0,0 +1,36 @@ +--TEST-- +Symfony pattern: nullsafe attribute newInstance chain +--FILE-- +getAttributes(TaggedItem::class)[0] ?? null)?->newInstance()->priority; +} + +function main(): void +{ + var_dump(priorityOf(TaggedService::class)); + var_dump(priorityOf(UntaggedService::class)); +} +?> +--EXPECT-- +int(20) +NULL diff --git a/tests/aot/symfony/process-string-offset-commandline.phpt b/tests/aot/symfony/process-string-offset-commandline.phpt new file mode 100644 index 00000000..582df15e --- /dev/null +++ b/tests/aot/symfony/process-string-offset-commandline.phpt @@ -0,0 +1,40 @@ +--TEST-- +Symfony Process pattern: nested isset on commandline string offset +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + string(12) "resolved:php" + [1]=> + string(2) "-v" +} +array(2) { + [0]=> + string(12) "/usr/bin/php" + [1]=> + string(2) "-v" +} +array(2) { + [0]=> + string(0) "" + [1]=> + string(2) "-v" +} diff --git a/tests/aot/symfony/static-property-array-spread-merge.phpt b/tests/aot/symfony/static-property-array-spread-merge.phpt new file mode 100644 index 00000000..016a9cea --- /dev/null +++ b/tests/aot/symfony/static-property-array-spread-merge.phpt @@ -0,0 +1,39 @@ +--TEST-- +Symfony pattern: static property array spread merge assignment +--FILE-- + 'base', + ]; + + public static function add(array $casters): void + { + self::$casters = [...self::$casters, ...$casters]; + } + + public static function all(): array + { + return self::$casters; + } +} + +function main(): void +{ + CasterRegistry::add(['a' => 'A']); + CasterRegistry::add(['default' => 'override', 'b' => 'B']); + + var_dump(CasterRegistry::all()); +} +?> +--EXPECT-- +array(3) { + ["default"]=> + string(8) "override" + ["a"]=> + string(1) "A" + ["b"]=> + string(1) "B" +}