feat: preserve trait metadata for class_uses

master
韩天峰 2 days ago
parent d39bd7fdbd
commit 59ef088571
  1. 65
      src/Translator.php
  2. 129
      tests/compiler/trait/class-uses-static-and-dynamic.phpt

@ -860,6 +860,57 @@ class Translator extends Preprocessor
}
}
/** @return array{declarations: string, registration: string} */
private function genTraitMetadataCode(): array
{
if ($this->isWasiTarget()) {
return ['declarations' => '', 'registration' => ''];
}
$entries = [];
foreach ($this->symbols->classes() as $classDef) {
$isTrait = $classDef->trait !== null;
if (!$isTrait && ($classDef->nativeObject || $classDef->usedTraits === [])) {
continue;
}
$entries[] = [
'name' => $classDef->getNamespacedName(false),
'traits' => array_values(array_unique($classDef->usedTraits)),
'isTrait' => $isTrait,
];
}
if ($entries === []) {
return ['declarations' => '', 'registration' => ''];
}
$code = '// TypePHP compile-time trait-use metadata' . PHP_EOL;
foreach ($entries as $index => $entry) {
if ($entry['traits'] === []) {
continue;
}
$traits = array_map(
fn (string $trait): string => 'std::string_view{"' . $this->escapeString($trait) . '"}',
$entry['traits'],
);
$code .= 'static constexpr std::string_view php_trait_metadata_traits_' . $index . '[] = {'
. implode(', ', $traits) . '};' . PHP_EOL;
}
$code .= 'static constexpr typephp_trait_metadata_entry php_trait_metadata[] = {' . PHP_EOL;
foreach ($entries as $index => $entry) {
$traitPointer = $entry['traits'] === [] ? 'nullptr' : 'php_trait_metadata_traits_' . $index;
$code .= $this->getIndent() . '{std::string_view{"' . $this->escapeString($entry['name']) . '"}, '
. $traitPointer . ', ' . count($entry['traits']) . ', '
. ($entry['isTrait'] ? 'true' : 'false') . '},' . PHP_EOL;
}
$code .= '};' . PHP_EOL . PHP_EOL;
return [
'declarations' => $code,
'registration' => 'typephp_register_trait_metadata(module_number, php_trait_metadata, '
. count($entries) . ')',
];
}
private function doGenExtension(): string
{
if ($this->isBuildModeBin()) {
@ -1066,6 +1117,9 @@ CODE;
}
$code .= $this->genRequestArrayDefaultInitializers();
$traitMetadata = $this->genTraitMetadataCode();
$code .= $traitMetadata['declarations'];
$code .= "// clang-format off\n";
$code .= "static const zend_function_entry ext_functions[] = {\n";
if ($this->isBuildModeBin() && !$this->isWasiTarget()) {
@ -1116,7 +1170,15 @@ CODE;
$code .= '}' . PHP_EOL;
}
$code .= '// class/interface class entries' . PHP_EOL;
if ($traitMetadata['registration'] !== '') {
$code .= 'if (' . $traitMetadata['registration'] . ' != SUCCESS) {' . PHP_EOL;
$code .= $this->getIndent() . 'return FAILURE;' . PHP_EOL;
$code .= '}' . PHP_EOL;
}
$code .= 'if (typephp_install_reflection_attribute_handlers() != SUCCESS) {' . PHP_EOL;
if ($traitMetadata['registration'] !== '') {
$code .= $this->getIndent() . 'typephp_unregister_trait_metadata(module_number);' . PHP_EOL;
}
$code .= $this->getIndent() . 'return FAILURE;' . PHP_EOL;
$code .= '}' . PHP_EOL;
if (!$this->isWasiTarget()) {
@ -1169,6 +1231,9 @@ CODE;
if (!$this->isWasiTarget()) {
$code .= 'typephp_unregister_fiber_generator_class();' . PHP_EOL;
}
if ($traitMetadata['registration'] !== '') {
$code .= 'typephp_unregister_trait_metadata(module_number);' . PHP_EOL;
}
$code .= 'typephp_uninstall_reflection_attribute_handlers();' . PHP_EOL;
$code .= 'return SUCCESS;' . PHP_EOL;
$code .= '}' . PHP_EOL . PHP_EOL;

@ -0,0 +1,129 @@
--TEST--
class_uses reads TypePHP trait metadata and falls back to dynamic PHP classes
--FILE--
<?php
namespace StaticMetadata {
trait LeafTrait
{
}
trait OuterTrait
{
use LeafTrait;
}
class CompiledClass
{
use OuterTrait;
}
/** @return array<string, string> */
function classUsesRecursive(string $class): array
{
$result = [];
foreach (class_uses($class) as $trait) {
$result[$trait] = $trait;
foreach (classUsesRecursive($trait) as $nested) {
$result[$nested] = $nested;
}
}
return $result;
}
function namedTarget(): string
{
echo "target\n";
return CompiledClass::class;
}
function namedAutoload(): bool
{
echo "autoload\n";
return false;
}
}
namespace {
function main(): void
{
var_dump(array_keys(class_uses(StaticMetadata\CompiledClass::class)));
var_dump(array_keys(class_uses('\\StaticMetadata\\CompiledClass')));
var_dump(array_keys(class_uses(new StaticMetadata\CompiledClass())));
var_dump(array_keys(class_uses(StaticMetadata\OuterTrait::class)));
var_dump(array_keys(class_uses('\\staticmetadata\\outertrait')));
var_dump(array_keys(class_uses(StaticMetadata\LeafTrait::class)));
var_dump(array_keys(StaticMetadata\classUsesRecursive(StaticMetadata\CompiledClass::class)));
class_alias(StaticMetadata\CompiledClass::class, 'StaticMetadata\\CompiledAlias');
var_dump(array_keys(class_uses('StaticMetadata\\CompiledAlias')));
var_dump(array_keys(class_uses(
autoload: StaticMetadata\namedAutoload(),
object_or_class: StaticMetadata\namedTarget(),
)));
eval(<<<'PHP'
namespace DynamicMetadata;
trait RuntimeTrait {}
class RuntimeClass { use RuntimeTrait; }
function inspect(string $class): array { return class_uses($class); }
PHP);
var_dump(array_keys(class_uses('DynamicMetadata\\RuntimeClass')));
var_dump(array_keys(DynamicMetadata\inspect(StaticMetadata\CompiledClass::class)));
var_dump(array_keys(DynamicMetadata\inspect(StaticMetadata\OuterTrait::class)));
}
}
?>
--EXPECT--
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
array(1) {
[0]=>
string(24) "StaticMetadata\LeafTrait"
}
array(1) {
[0]=>
string(24) "StaticMetadata\LeafTrait"
}
array(0) {
}
array(2) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
[1]=>
string(24) "StaticMetadata\LeafTrait"
}
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
autoload
target
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
array(1) {
[0]=>
string(28) "DynamicMetadata\RuntimeTrait"
}
array(1) {
[0]=>
string(25) "StaticMetadata\OuterTrait"
}
array(1) {
[0]=>
string(24) "StaticMetadata\LeafTrait"
}
Loading…
Cancel
Save