PHP AST → 图节点映射表

June 19, 2026 · View on GitHub

本文档描述 phply AST 节点类型到统一图结构的映射规则。 基于 core/graph/normalizers/php/normalizer.py必须与代码同步维护


1. 映射分类总览

图标签phply AST 节点类型集合Normalizer 方法
file(顶层入口,非 phply 节点)normalize()
classClass, Interface, Trait, Enum_walk_class()
functionFunction, Method, Closure, ArrowFunction_walk_function()
parameterFormalParameter(函数定义参数)_walk_function() 内部
returnReturn_walk_return()
branchIf, ElseIf, Else, TernaryOp, For, While, DoWhile, Foreach, Switch, Case, Default, Try, Catch, Finally, Match_walk_branch()
operatorAssignment, AssignOp, BinaryOp, UnaryOp, PostIncDecOp, PreIncDecOp, New, Cast, Throw, Yield, Break, Continue, Echo, Print, Eval, Silence, IsSet, Empty, Unset, Clone, Exit, ListAssignment_walk_operator()
identifierVariable, NamedParameter, StaticVariable, Global_walk_node() / _emit_identifier()
constConstant, ClassConstant, MagicConstant, Python 原始类型(str/int/float/bool)_emit_const()
importInclude, Require, UseDeclaration_walk_import()
dependency由 import/use 生成的依赖节点_walk_import() 内部

特殊类型(不直接映射为图节点,而是生成 member 边):

phply AST 节点类型图表示Normalizer 方法
ObjectPropertyidentifier(property) + member[access_type=property]_walk_node()
NullsafePropertyidentifier(property) + member[access_type=property]_walk_node()
ArrayOffsetidentifier(key) + member[access_type=array_offset]_walk_node()
StaticPropertyidentifier(prop) + member[access_type=static_property]_walk_node()
StringOffsetidentifier(key) + member[access_type=array_offset]_walk_node()

透明节点(不创建图节点,直接 walk 子节点):

phply AST 节点类型处理方式
Block直接 walk .nodes(不是 .children()
Namespace直接 walk .nodes
Parameter(函数调用参数包装器)解包 .node 后 walk
ForeachVariableEmit identifier(variable 或 static)

2. Class 节点映射

_walk_class()

phply 类型graph labelgraph typenameattrsast 子节点
Classclassclass类名fullname=namespace\Class, parent_class, interfacesown→ method/property/constant
Interfaceclassinterface接口名fullnameown→ method
Traitclasstraittrait 名fullnameown→ method/property
Enumclassenumenum 名fullnameown→ method/case

命名约定:

  • fullname 包含命名空间前缀:App\Models\User
  • Method 的 fullname 为 Class::method

3. Function 节点映射

_walk_function()

phply 类型graph labelgraph typenameattrs子节点
Functionfunctionfunction函数名fullname, params, visibility, static, namespace, modifiersown→ parameter/operator/branch/return
Methodfunctionmethod方法名fullname=Class::method同上
Method__constructfunctionconstructor__constructfullname=Class::__construct同上
Method__destructfunctiondestructor__destructfullname=Class::__destruct同上
Closurefunctionlambda{closure}paramsown→ parameter/operator/branch/return
ArrowFunctionfunctionlambda{closure}paramsown→ parameter/operator/branch/return

参数处理:

  • FormalParameterparameter 节点,attrs.param_index = 顺序索引
  • attrs.params = 参数名列表(字符串列表)

4. Branch 节点映射

_walk_branch()

映射表

phply 类型graph labelgraph type条件处理own 子节点⚠️ 注意事项
Ifbranchifast[condition]→ expr(walk 子树)body 中所有 operator/branch/return
ElseIfbranchelifast[condition]→ exprbody
Elsebranchelse无条件body不继承父 if 的条件约束
TernaryOpbranchternaryast[condition]→ exprast[iftrue]→ value, ast[iffalse]→ valueiftrue 继承条件约束,iffalse 不继承
Forbranchforbody
Whilebranchwhileast[condition]→ exprbody
DoWhilebranchwhilebody
Foreachbranchforeachbody + own→ keyvar/valvar identifierskey/value 变量 walk 为 identifier
Switchbranchswitchast[condition]→ exprown→ case/default branches
Casebranchcaseast[condition]→ expr(⚠️ 见下)bodyexpr 可能是 Python str/int 原始类型 → 自动创建 const 节点
Defaultbranchdefault无条件body不继承 switch 的条件约束
Trybranchtryown→ body/catch/finally
Catchbranchcatchbody
Finallybranchfinallybody
Matchbranchmatchown→ arms

条件表达式处理(_COND_EXPR_NODES

branch 的条件表达式(node.expr)如果是以下类型,会被 walk 成 AST 子树并连接到 branch:

  • BinaryOpUnaryOpFunctionCallMethodCallStaticMethodCall
  • Variable(identifier)
  • IsSetEmpty(call 类型 operator)
  • BooleanNumber(phply 节点)
  • ArrayOffsetObjectPropertyStaticProperty(member 模式)
  • Python 原始类型 str/int/float/bool(自动创建 const 节点)

连接方式branch --ast[role=condition]--> condition_root_node

⚠️ phply 已知陷阱

陷阱说明处理方式
Case.expr 是 Python str$x == 'a' 的 Case.expr 直接是字符串 'a',不是 AST 节点Normalizer 自动创建 const 节点
BinaryOp.left/right 是 Python str$x == 'a' 的 right 可能是 Python str 'a'Normalizer 自动创建 const 节点
Echo.node 是列表Echo 节点的 .node 属性返回一个列表遍历列表每个元素 walk
Print.node 是单个节点Print 节点的 .node 属性返回单个 AST 节点(不是列表)直接 walk 单个节点

5. Operator 节点映射

_walk_operator() / _walk_call()

phply 类型graph typenameast 子节点⚠️ 注意事项
FunctionCallcall函数名ast[callee]→ callee_name, ast[arg, arg_index=N]→ argsparams 是 Parameter 包装对象,需 .node 解包;.node 可能是 Python str/int 原始类型
MethodCallmethod_callmethod 名对象通过 member 边连接, ast[callee]→ method_name, ast[arg]→ args
StaticMethodCallstatic_callClass::method类通过 member 边连接, ast[callee]→ method_name, ast[arg]→ args
NullsafeMethodCallmethod_callmethod 名同 MethodCall
AssignmentassignLHS 变量名文本ast[lhs]→ target, ast[rhs]→ expression
AssignOpaug_assignLHS 变量名文本 + 操作符ast[lhs]→ target, ast[rhs]→ expression
BinaryOpbinary_op操作符符号 (==, +, .等)ast[left]→ left, ast[right]→ rightleft/right 可能是 Python str/int 原始类型 → 自动创建 const
UnaryOpunary_op操作符符号 (!, -等)ast[operand]→ expr
PostIncDecOpunary_op操作符 (++, --)ast[operand]→ expr
PreIncDecOpunary_op操作符 (++, --)ast[operand]→ expr
Newnew类名ast[arg]→ params⚠️ 使用 node.params 而非通用参数接口
Casttype_cast目标类型 ((int)等)ast[value]→ expression
Throwthrowast[value]→ expression
Yieldyieldast[value]→ expression
Breakbreak叶子节点,无子节点
Continuecontinue叶子节点,无子节点
Echocallechoast[arg]→ expressions (多个).node 是列表,需遍历
Printcallprintast[arg]→ expression (单个).node 是单个节点,不是列表
Evalcallevalast[arg]→ expression
Silencecall@ast[value]→ expression
IsSetcallissetast[arg]→ args
Emptycallemptyast[arg]→ args
Unsetcallunsetast[arg]→ args
Clonecallcloneast[arg]→ expression
Exitcallexitast[arg]→ expression
ListAssignmentassignLHS 变量名ast[lhs]→ target, ast[rhs]→ expression

_walk_call() 详细流程

  1. 创建 operator(type=call/static_call/method_call) 节点
  2. 解析 callee 名称:
    • FunctionCall: 直接取 node.name 的文本(可能是 Variable 或 MemberAccess)
    • MethodCall: object 通过 member 边处理,method name 为 node.name
    • StaticMethodCall: class 通过 member 边处理,method name 为 node.name + node.class_
  3. 添加 use 边:operator → function(如果找到同名 function 节点)
  4. 遍历 node.params(Parameter 包装列表):
    • param.node(unwrap)
    • walk 成图节点
    • 添加 ast[arg, arg_index=idx]

6. Identifier 节点映射

phply 类型graph labelgraph typename说明
Variableidentifiervariable$ 前缀(如 $idphply 的 Variable.name 自带 $
Variable$this/$selfidentifierthis$this$selfPHP 当前对象引用
NamedParameteridentifiervariable参数名PHP 8 命名参数
StaticVariableidentifierstatic变量名静态变量
Globalidentifierglobal变量名global 声明
ForeachVariableidentifiervariablestatic变量名foreach key/value 变量

7. Const 节点映射

phply 类型graph labelgraph typename说明
Constanttrue/falseconstbooleantruefalsePHP 布尔常量
ConstantnullconstnullnullPHP null 常量
Constant(其他)constconstant常量名 (PHP_INT_MAX等)
ClassConstantconstconstant常量名MyClass::CONST
MagicConstantconstconstant魔术常量名 (__LINE__/__FILE__等)
Python strconststringrepr(value)⚠️ 自动创建,来自 Parameter.node、BinaryOp.right、Case.expr
Python int/floatconstnumberrepr(value)同上
Python boolconstbooleanrepr(value)同上

⚠️ repr 的影响:

  • Python str "hello"repr"\"hello\"" → 图上 name = "'hello'"
  • PHP string '/^\d+$/' → phply 传为 Python str → repr"'/^\\\\d+$/'"
  • 在条件检查中需要 strip("'\"") 还原

8. Import 节点映射

_walk_import()

phply 类型graph labelgraph typenameattrs
Includeimportinclude文件路径
Requireimportrequire文件路径
UseDeclarationimportuse类/命名空间名alias(如有别名)

Include/Require 的后缀(_once)通过 expr_type 区分 → 对应 include_once/require_once

DEPENDENCY 节点(_walk_import() 内部生成)

每个 import 节点会额外生成一个 dependency 子节点,通过 frg 边连接:

图标签graph typenameattrs说明
dependencydependency导入的类/命名空间/文件名source=导入名import --frg[type=import/use/include]--> dependency表示文件的外部依赖关系

注意dependency 节点本身无语句级语义,主要用于文件级依赖追踪。


9. Member 边映射

_walk_node() 中的 member 处理

Member 访问不创建独立的图节点标签,而是用 member 边表达。

phply 类型处理流程member 边
ObjectProperty1. walk node.node(object) → 返回 obj_pos
2. emit identifier(name=prop_name, type=property)
3. add member edge: obj_pos → prop_pos
member[access_type=property]
NullsafeProperty同 ObjectPropertymember[access_type=property]
ArrayOffset1. walk node.node(array) → 返回 arr_pos
2. walk node.expr(index) → 返回 idx_pos(或 auto-create const for str/int)
3. emit identifier(name=index_text, type=property)
4. add member edge: arr_pos → idx_pos
member[access_type=array_offset]
StaticProperty1. walk node.node(class) → 返回 cls_pos
2. emit identifier(name=prop_name, type=property)
3. add member edge: cls_pos → prop_pos
member[access_type=static_property]
StringOffset同 ArrayOffsetmember[access_type=array_offset]

示例$_GET['id']

identifier(name=$_GET, type=variable) --member[array_offset]--> identifier(name=id, type=property)

10. 透传节点

这些 phply AST 节点不创建图节点,只负责递归 walk 子节点。

phply 类型处理方式说明
Blockwalk .nodes 列表phply 的 Block 用 .nodes(不是 .children()
Namespacewalk .nodes 列表命名空间是透明容器
Parameter(调用参数)walk .node 属性Parameter(expr, default) 包装函数调用参数
ForeachVariableemit identifier作为 foreach 的 own 子节点
InlineHTML忽略PHP 模板外的 HTML 片段