2018-02-23 18:26:13 +00:00
|
|
|
// Package visitor contains walker.visitor implementations
|
|
|
|
package visitor
|
|
|
|
|
|
|
|
import (
|
2018-02-26 18:21:11 +00:00
|
|
|
"errors"
|
2018-02-23 21:59:26 +00:00
|
|
|
"strings"
|
|
|
|
|
2018-02-26 21:29:36 +00:00
|
|
|
"github.com/z7zmey/php-parser/node/expr"
|
|
|
|
|
2018-02-23 18:26:13 +00:00
|
|
|
"github.com/z7zmey/php-parser/node"
|
|
|
|
"github.com/z7zmey/php-parser/node/name"
|
|
|
|
"github.com/z7zmey/php-parser/node/stmt"
|
|
|
|
"github.com/z7zmey/php-parser/walker"
|
|
|
|
)
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
func concatNameParts(parts ...[]node.Node) string {
|
|
|
|
str := ""
|
|
|
|
|
|
|
|
for _, p := range parts {
|
|
|
|
for _, n := range p {
|
|
|
|
if str == "" {
|
|
|
|
str = n.(*name.NamePart).Value
|
|
|
|
} else {
|
|
|
|
str = str + "\\" + n.(*name.NamePart).Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
2018-02-23 21:59:26 +00:00
|
|
|
type Namespace struct {
|
|
|
|
Namespace string
|
|
|
|
Aliases map[string]map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewNamespace(NSName string) *Namespace {
|
|
|
|
return &Namespace{
|
2018-02-26 18:21:11 +00:00
|
|
|
Namespace: NSName,
|
2018-02-23 21:59:26 +00:00
|
|
|
Aliases: map[string]map[string]string{
|
|
|
|
"": {},
|
|
|
|
"const": {},
|
|
|
|
"function": {},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ns *Namespace) AddAlias(aliasType string, aliasName string, alias string) {
|
|
|
|
aliasType = strings.ToLower(aliasType)
|
|
|
|
|
|
|
|
if aliasType == "const" {
|
|
|
|
ns.Aliases[aliasType][alias] = aliasName
|
|
|
|
} else {
|
|
|
|
ns.Aliases[aliasType][strings.ToLower(alias)] = aliasName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
func (ns *Namespace) resolveName(nameNode node.Node, aliasType string) string {
|
|
|
|
switch n := nameNode.(type) {
|
|
|
|
case *name.FullyQualified:
|
|
|
|
return concatNameParts(n.Parts)
|
|
|
|
|
|
|
|
case *name.Relative:
|
|
|
|
return ns.Namespace + "\\" + concatNameParts(n.Parts)
|
|
|
|
|
|
|
|
case *name.Name:
|
|
|
|
aliasName, err := ns.resolveAlias(nameNode, aliasType)
|
|
|
|
if err != nil {
|
|
|
|
return ns.Namespace + "\\" + concatNameParts(n.Parts)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(n.Parts) > 1 {
|
|
|
|
return aliasName + "\\" + concatNameParts(n.Parts[1:])
|
|
|
|
}
|
|
|
|
|
|
|
|
return aliasName
|
|
|
|
}
|
|
|
|
|
|
|
|
panic("invalid nameNode variable type")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ns *Namespace) resolveAlias(nameNode node.Node, aliasType string) (string, error) {
|
|
|
|
aliasType = strings.ToLower(aliasType)
|
|
|
|
nameParts := nameNode.(*name.Name).Parts
|
|
|
|
|
|
|
|
firstPartStr := nameParts[0].(*name.NamePart).Value
|
|
|
|
|
|
|
|
if len(nameParts) > 1 { // resolve aliases for qualified names, always against class alias table
|
|
|
|
firstPartStr = strings.ToLower(firstPartStr)
|
|
|
|
aliasType = ""
|
|
|
|
} else {
|
|
|
|
if aliasType != "const" { // constans are case-sensitive
|
|
|
|
firstPartStr = strings.ToLower(firstPartStr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aliasName, ok := ns.Aliases[aliasType][firstPartStr]
|
|
|
|
if !ok {
|
|
|
|
return "", errors.New("Not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return aliasName, nil
|
|
|
|
}
|
|
|
|
|
2018-02-23 18:26:13 +00:00
|
|
|
type NsResolver struct {
|
2018-02-23 21:59:26 +00:00
|
|
|
Namespace *Namespace
|
2018-02-23 18:26:13 +00:00
|
|
|
ResolvedNames map[node.Node]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewNsResolver NsResolver type constructor
|
|
|
|
func NewNsResolver() *NsResolver {
|
|
|
|
return &NsResolver{
|
2018-02-23 21:59:26 +00:00
|
|
|
Namespace: NewNamespace(""),
|
2018-02-23 18:26:13 +00:00
|
|
|
ResolvedNames: map[node.Node]string{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnterNode is invoked at every node in heirerchy
|
|
|
|
func (nsr *NsResolver) EnterNode(w walker.Walkable) bool {
|
|
|
|
switch n := w.(type) {
|
|
|
|
case *stmt.Namespace:
|
2018-02-23 21:59:26 +00:00
|
|
|
if n.NamespaceName == nil {
|
|
|
|
nsr.Namespace = NewNamespace("")
|
|
|
|
} else {
|
|
|
|
NSParts := n.NamespaceName.(*name.Name).Parts
|
|
|
|
nsr.Namespace = NewNamespace(concatNameParts(NSParts))
|
|
|
|
}
|
2018-02-23 22:17:51 +00:00
|
|
|
|
2018-02-23 21:59:26 +00:00
|
|
|
case *stmt.UseList:
|
|
|
|
useType := ""
|
|
|
|
if n.UseType != nil {
|
|
|
|
useType = n.UseType.(*node.Identifier).Value
|
|
|
|
}
|
2018-02-23 22:17:51 +00:00
|
|
|
|
2018-02-23 21:59:26 +00:00
|
|
|
for _, nn := range n.Uses {
|
2018-02-23 22:11:53 +00:00
|
|
|
nsr.AddAlias(useType, nn, nil)
|
2018-02-23 21:59:26 +00:00
|
|
|
}
|
2018-02-23 22:17:51 +00:00
|
|
|
|
|
|
|
// no reason to iterate into depth
|
|
|
|
return false
|
|
|
|
|
2018-02-23 21:59:26 +00:00
|
|
|
case *stmt.GroupUse:
|
|
|
|
useType := ""
|
|
|
|
if n.UseType != nil {
|
|
|
|
useType = n.UseType.(*node.Identifier).Value
|
|
|
|
}
|
2018-02-23 22:17:51 +00:00
|
|
|
|
2018-02-23 21:59:26 +00:00
|
|
|
for _, nn := range n.UseList {
|
2018-02-23 22:11:53 +00:00
|
|
|
nsr.AddAlias(useType, nn, n.Prefix.(*name.Name).Parts)
|
2018-02-23 21:59:26 +00:00
|
|
|
}
|
2018-02-23 22:17:51 +00:00
|
|
|
|
|
|
|
// no reason to iterate into depth
|
|
|
|
return false
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
case *stmt.Class:
|
|
|
|
if n.Extends != nil {
|
|
|
|
nsr.resolveName(n.Extends, "")
|
|
|
|
}
|
|
|
|
|
2018-02-26 21:29:36 +00:00
|
|
|
for _, interfaceName := range n.Implements {
|
|
|
|
nsr.resolveName(interfaceName, "")
|
2018-02-26 18:21:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsr.addNamespacedName(n, n.ClassName.(*node.Identifier).Value)
|
2018-02-26 21:29:36 +00:00
|
|
|
|
|
|
|
case *stmt.Interface:
|
|
|
|
for _, interfaceName := range n.Extends {
|
|
|
|
nsr.resolveName(interfaceName, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
nsr.addNamespacedName(n, n.InterfaceName.(*node.Identifier).Value)
|
|
|
|
|
|
|
|
case *stmt.Trait:
|
|
|
|
nsr.addNamespacedName(n, n.TraitName.(*node.Identifier).Value)
|
|
|
|
|
|
|
|
case *stmt.Function:
|
|
|
|
nsr.addNamespacedName(n, n.FunctionName.(*node.Identifier).Value)
|
|
|
|
|
|
|
|
for _, parameter := range n.Params {
|
|
|
|
nsr.resolveType(parameter.(*node.Parameter).VariableType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.ReturnType != nil {
|
|
|
|
nsr.resolveType(n.ReturnType)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *stmt.ClassMethod:
|
|
|
|
for _, parameter := range n.Params {
|
|
|
|
nsr.resolveType(parameter.(*node.Parameter).VariableType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.ReturnType != nil {
|
|
|
|
nsr.resolveType(n.ReturnType)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.Closure:
|
|
|
|
for _, parameter := range n.Params {
|
|
|
|
nsr.resolveType(parameter.(*node.Parameter).VariableType)
|
|
|
|
}
|
|
|
|
|
|
|
|
if n.ReturnType != nil {
|
|
|
|
nsr.resolveType(n.ReturnType)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *stmt.ConstList:
|
|
|
|
for _, constant := range n.Consts {
|
|
|
|
nsr.addNamespacedName(constant, constant.(*stmt.Constant).ConstantName.(*node.Identifier).Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.StaticCall:
|
|
|
|
switch nn := n.Class.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.StaticPropertyFetch:
|
|
|
|
switch nn := n.Class.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.ClassConstFetch:
|
|
|
|
switch nn := n.Class.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.New:
|
|
|
|
switch nn := n.Class.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.InstanceOf:
|
|
|
|
switch nn := n.Class.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *stmt.Catch:
|
|
|
|
for _, t := range n.Types {
|
|
|
|
nsr.resolveName(t, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.FunctionCall:
|
|
|
|
switch nn := n.Function.(type) {
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(nn, "function")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(nn, "function")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(nn, "function")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *expr.ConstFetch:
|
|
|
|
nsr.resolveName(n.Constant, "const")
|
|
|
|
|
|
|
|
case *stmt.TraitUse:
|
|
|
|
for _, t := range n.Traits {
|
|
|
|
nsr.resolveName(t, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, a := range n.Adaptations {
|
|
|
|
switch aa := a.(type) {
|
|
|
|
case *stmt.TraitUsePrecedence:
|
|
|
|
refTrait := aa.Ref.(*stmt.TraitMethodRef).Trait
|
|
|
|
if refTrait != nil {
|
|
|
|
nsr.resolveName(refTrait, "")
|
|
|
|
}
|
|
|
|
for _, insteadOf := range aa.Insteadof {
|
|
|
|
nsr.resolveName(insteadOf, "")
|
|
|
|
}
|
|
|
|
|
|
|
|
case *stmt.TraitUseAlias:
|
|
|
|
refTrait := aa.Ref.(*stmt.TraitMethodRef).Trait
|
|
|
|
if refTrait != nil {
|
|
|
|
nsr.resolveName(refTrait, "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 18:26:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
// GetChildrenVisitor is invoked at every node parameter that contains children nodes
|
|
|
|
func (nsr *NsResolver) GetChildrenVisitor(key string) walker.Visitor {
|
|
|
|
return nsr
|
|
|
|
}
|
|
|
|
|
|
|
|
// LeaveNode is invoked after node process
|
|
|
|
func (nsr *NsResolver) LeaveNode(w walker.Walkable) {
|
|
|
|
switch n := w.(type) {
|
|
|
|
case *stmt.Namespace:
|
|
|
|
if n.Stmts != nil {
|
|
|
|
nsr.Namespace = NewNamespace("")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-23 22:11:53 +00:00
|
|
|
func (nsr *NsResolver) AddAlias(useType string, nn node.Node, prefix []node.Node) {
|
|
|
|
switch use := nn.(type) {
|
|
|
|
case *stmt.Use:
|
|
|
|
if use.UseType != nil {
|
|
|
|
useType = use.UseType.(*node.Identifier).Value
|
|
|
|
}
|
|
|
|
|
|
|
|
useNameParts := use.Use.(*name.Name).Parts
|
|
|
|
var alias string
|
|
|
|
if use.Alias == nil {
|
|
|
|
alias = useNameParts[len(useNameParts)-1].(*name.NamePart).Value
|
|
|
|
} else {
|
|
|
|
alias = use.Alias.(*node.Identifier).Value
|
|
|
|
}
|
|
|
|
|
|
|
|
nsr.Namespace.AddAlias(useType, concatNameParts(prefix, useNameParts), alias)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
func (nsr *NsResolver) addNamespacedName(nn node.Node, nodeName string) {
|
|
|
|
if nsr.Namespace.Namespace == "" {
|
|
|
|
nsr.ResolvedNames[nn] = nodeName
|
|
|
|
} else {
|
|
|
|
nsr.ResolvedNames[nn] = nsr.Namespace.Namespace + "\\" + nodeName
|
2018-02-23 18:26:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 18:21:11 +00:00
|
|
|
func (nsr *NsResolver) resolveName(nameNode node.Node, aliasType string) {
|
|
|
|
nsr.ResolvedNames[nameNode] = nsr.Namespace.resolveName(nameNode, aliasType)
|
2018-02-23 18:26:13 +00:00
|
|
|
}
|
2018-02-26 21:29:36 +00:00
|
|
|
|
|
|
|
func (nsr *NsResolver) resolveType(n node.Node) {
|
|
|
|
switch nn := n.(type) {
|
|
|
|
case *node.Nullable:
|
|
|
|
nsr.resolveType(nn.Expr)
|
|
|
|
case *name.Name:
|
|
|
|
nsr.resolveName(n, "")
|
|
|
|
case *name.Relative:
|
|
|
|
nsr.resolveName(n, "")
|
|
|
|
case *name.FullyQualified:
|
|
|
|
nsr.resolveName(n, "")
|
|
|
|
}
|
|
|
|
}
|