Skip to content

Commit

Permalink
Make imports() return classes, functions and constants
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Jul 30, 2023
1 parent 8193eff commit 0b935fd
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 23 deletions.
35 changes: 25 additions & 10 deletions src/main/php/lang/meta/FromAttributes.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,24 @@ public function imports($reflect) {
static $types= [T_WHITESPACE => true, 44 => true, 59 => true, 123 => true];

$tokens= PhpToken::tokenize(file_get_contents($reflect->getFileName()));
$imports= [];
$imports= ['class' => [], 'function' => [], 'const' => []];
for ($i= 0, $s= sizeof($tokens); $i < $s; $i++) {
if (isset($break[$tokens[$i]->id])) break;
if (T_USE !== $tokens[$i]->id) continue;

do {
$type= '';
for ($i+= 2; $i < $s, !isset($types[$tokens[$i]->id]); $i++) {
$i+= 2;
if (T_FUNCTION === $tokens[$i]->id) {
$kind= 'function';
$i+= 2;
} else if (T_CONST === $tokens[$i]->id) {
$kind= 'const';
$i+= 2;
} else {
$kind= 'class';
}

for ($type= ''; $i < $s, !isset($types[$tokens[$i]->id]); $i++) {
$type.= $tokens[$i]->text;
}

Expand All @@ -86,11 +96,11 @@ public function imports($reflect) {
$group= '';
for ($i+= 1; $i < $s; $i++) {
if (44 === $tokens[$i]->id) {
$imports[$alias ?? $group]= $type.$group;
$imports[$kind][$alias ?? $group]= $type.$group;
$alias= null;
$group= '';
} else if (125 === $tokens[$i]->id) {
$imports[$alias ?? $group]= $type.$group;
$imports[$kind][$alias ?? $group]= $type.$group;
break;
} else if (T_AS === $tokens[$i]->id) {
$i+= 2;
Expand All @@ -101,11 +111,11 @@ public function imports($reflect) {
}
} else if (T_AS === $tokens[$i]->id) {
$i+= 2;
$imports[$tokens[$i]->text]= $type;
$imports[$kind][$tokens[$i]->text]= $type;
} else if (false === ($p= strrpos($type, '\\'))) {
$imports[$type]= null;
$imports[$kind][$type]= null;
} else {
$imports[substr($type, strrpos($type, '\\') + 1)]= $type;
$imports[$kind][substr($type, strrpos($type, '\\') + 1)]= $type;
}

// Skip over whitespace
Expand All @@ -120,8 +130,13 @@ public function evaluate($reflect, $code) {
if ($namespace= $reflect->getNamespaceName()) {
$header.= 'namespace '.$namespace.';';
}
foreach ($this->imports($reflect) as $import => $type) {
$header.= $type ? "use {$type} as {$import};" : "use {$import};";

// Recreate all imports
foreach ($this->imports($reflect) as $kind => $list) {
$use= 'class' === $kind ? 'use' : 'use '.$kind;
foreach ($list as $import => $type) {
$header.= $type ? "{$use} {$type} as {$import};" : "{$use} {$import};";
}
}

$f= eval($header.' return static function() { return '.$code.'; };');
Expand Down
11 changes: 7 additions & 4 deletions src/main/php/lang/meta/FromSyntaxTree.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,13 @@ private function annotations($tree, $annotated) {
}

public function imports($reflect) {
$resolver= $this->tree($reflect)->resolver();
$imports= [];
foreach ($resolver->imports as $alias => $type) {
$imports[$alias]= ltrim($type, '\\');
$imports= ['class' => [], 'function' => [], 'const' => []];
foreach ($this->tree($reflect)->root()->children() as $child) {
if ($child->is('import')) {
foreach ($child->names as $import => $alias) {
$imports[$child->type ?? 'class'][$alias ?? false === ($p= strrpos($import, '\\')) ? $import : substr($import, $p + 1)]= $import;
}
}
}
return $imports;
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/php/lang/meta/SyntaxTree.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public function __construct($tree, $type) {
/** @return lang.ast.TypeDeclaration */
public function type() { return $this->type; }

/** @return lang.ast.ParseTree */
public function root() { return $this->tree; }

public function resolver() { return $this->tree->scope(); }

private function resolve($type) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/php/lang/reflection/Member.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static function resolve($reflect) {
'*' => function($type) use($reflect) {
$declared= $reflect->getDeclaringClass();
$imports= Reflection::meta()->scopeImports($declared);
return XPClass::forName($imports[$type] ?? $declared->getNamespaceName().'\\'.$type);
return XPClass::forName($imports['class'][$type] ?? $declared->getNamespaceName().'\\'.$type);
},
];
}
Expand Down
23 changes: 15 additions & 8 deletions src/test/php/lang/reflection/unittest/FromAttributesTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
use test\{Assert, Test, Ignore as Skip};
use util\Comparison as WithComparison;

use const MODIFIER_PUBLIC;
use function strncmp;

#[Runtime(php: '>=8.0')]
class FromAttributesTest {

Expand All @@ -18,14 +21,18 @@ public function can_create() {
public function imports() {
Assert::equals(
[
'ReflectionClass' => null,
'FromAttributes' => FromAttributes::class,
'Dynamic' => Dynamic::class,
'Runtime' => Runtime::class,
'Assert' => Assert::class,
'Test' => Test::class,
'WithComparison' => WithComparison::class,
'Skip' => Skip::class,
'const' => ['MODIFIER_PUBLIC' => null],
'function' => ['strncmp' => null],
'class' => [
'ReflectionClass' => null,
'FromAttributes' => FromAttributes::class,
'Dynamic' => Dynamic::class,
'Runtime' => Runtime::class,
'Assert' => Assert::class,
'Test' => Test::class,
'WithComparison' => WithComparison::class,
'Skip' => Skip::class,
]
],
(new FromAttributes())->imports(new ReflectionClass(self::class))
);
Expand Down

0 comments on commit 0b935fd

Please sign in to comment.