NamespaceResolver should remove unresolved names
ruudk opened this issue · comments
I'm having issues with the namespace resolver. It contains unresolved names like void
, true
and null
. Shouldn't these be removed when they are not resolved
test.php
<?php
declare(strict_types=1);
namespace App\Domain\Handler\Cart;
use SimpleBus\Message\Recorder\RecordsMessages;
use App\Domain\Command\ChangeCurrencyCommand;
use App\Domain\Repository\CartRepository;
use App\Domain\Event\CurrencyChangedEvent as CurrencyChangedEventWithAlias;
class ChangeCurrencyHandler
{
/**
* @var CartRepository
*/
private $cartRepository;
/**
* @var RecordsMessages
*/
private $eventRecorder;
public function __construct(
CartRepository $cartRepository,
RecordsMessages $eventRecorder
) {
$this->cartRepository = $cartRepository;
$this->eventRecorder = $eventRecorder;
}
public function __invoke(ChangeCurrencyCommand $command) : void
{
if (true === $command->getBool()) {
// Do something
}
if (null !== $command->getNull()) {
// Do something
}
$this->eventRecorder->record(new CurrencyChangedEventWithAlias());
}
}
main.go
package main
import (
"fmt"
"github.com/z7zmey/php-parser/php7"
"github.com/z7zmey/php-parser/visitor"
"os"
"reflect"
)
func main() {
for _, file := range os.Args[1:] {
fmt.Printf("Checking %s\n", file)
checkFile(file)
}
}
func checkFile(file string) {
src, err := os.Open(file)
if err != nil {
panic(err)
}
parser := php7.NewParser(src, file)
parser.Parse()
for _, e := range parser.GetErrors() {
fmt.Println(e)
}
nsResolver := visitor.NewNamespaceResolver()
parser.GetRootNode().Walk(nsResolver)
for n, fqcn := range nsResolver.ResolvedNames {
fmt.Printf("Found %s: %s\n", reflect.TypeOf(n), fqcn)
}
}
output
Checking ./test.php
Found *name.Name: SimpleBus\Message\Recorder\RecordsMessages
Found *name.Name: App\Domain\Command\ChangeCurrencyCommand
Found *name.Name: void
Found *name.Name: true
Found *name.Name: null
Found *name.Name: App\Domain\Event\CurrencyChangedEvent
Found *stmt.Class: App\Domain\Handler\Cart\ChangeCurrencyHandler
Found *name.Name: App\Domain\Repository\CartRepository
I modified the namespace resolver with the following patch:
diff --git a/visitor/namespace_resolver.go b/visitor/namespace_resolver.go
index dc7f78f..31e8400 100644
--- a/visitor/namespace_resolver.go
+++ b/visitor/namespace_resolver.go
@@ -289,7 +289,7 @@ func (ns *Namespace) ResolveName(nameNode node.Node, aliasType string) (string,
if aliasType == "const" && len(n.Parts) == 1 {
part := strings.ToLower(n.Parts[0].(*name.NamePart).Value)
if part == "true" || part == "false" || part == "null" {
- return part, nil
+ return "", errors.New("ignore")
}
}
@@ -316,7 +316,7 @@ func (ns *Namespace) ResolveName(nameNode node.Node, aliasType string) (string,
case "iterable":
fallthrough
case "object":
- return part, nil
+ return "", errors.New("ignore")
}
}
And now it outputs correctly
Checking ./test.php
Found *name.Name: App\Domain\Repository\CartRepository
Found *name.Name: SimpleBus\Message\Recorder\RecordsMessages
Found *name.Name: App\Domain\Command\ChangeCurrencyCommand
Found *name.Name: App\Domain\Event\CurrencyChangedEvent
Found *stmt.Class: App\Domain\Handler\Cart\ChangeCurrencyHandler
The main idea is that all *name.Name
must have an appropriate resolved name. It allows working with them in a similar way. So when we traverse AST there is no need to check is it a function name or is it a name of a reserved constant.
<?
namespace foo;
function true() {
return true;
}
But since they all are *name.Name
there is no way to tell if it's a FQCN or something else?
There are few ways to resolve only fully qualified class names
First(simple) - write the same visitor that resolves only FQCN.
It must be implemented in the client code. In this way, you can apply your logic how to process nodes.
Second - add in the all nodes link to their parent node so you will able filter FQN by their parent nodes.
It most flexible approach but It would create an additional load at the parsing level.