IntelliJ PHP自定义检查:如何触发visitPhpFunctionCall调用?
解决IntelliJ PHP插件自定义检查中
visitPhpFunctionCall未触发的问题 看起来你遇到的核心问题是使用了非递归的PhpElementVisitor,导致无法遍历到PHP文件中的嵌套函数调用元素,所以visitPhpFunctionCall始终没被触发。咱们一步步来解决这个问题:
问题根源
PhpElementVisitor是JetBrains PHP PSI提供的基础访问者,默认不会递归遍历子元素。你的PHP文件中header($headerName)作为根元素的子节点,不会被非递归访问者触及,只会触发根元素的visitElement方法,而不会深入到函数调用节点。
解决方案:改用递归访问者
将你的访问者从PhpElementVisitor替换为PhpRecursiveElementVisitor,它会自动递归遍历所有PSI元素,包括嵌套的函数调用。同时移除不必要的visitElement重写,避免干扰默认的类型调度逻辑。
修改后的检查类代码如下:
package com.ge.sdc.intellij.mtaplugin.security.php; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElementVisitor; import com.jetbrains.php.lang.inspections.PhpInspection; import com.jetbrains.php.lang.psi.elements.FunctionReference; import com.jetbrains.php.lang.psi.visitors.PhpRecursiveElementVisitor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class UnsafeCallToHeaderInspection extends PhpInspection { private Logger log = Logger.getInstance(UnsafeCallToHeaderInspection.class); @Nullable @Override public String getStaticDescription() { return "Calls to 'header()' function must only use constant strings or safe-known patterns." + " Otherwise, this could allow arbitrary data to be passed in HTTP headers, and would then alter the behavior of the browser (or client)." + " Ie: Inserting a custom Content-Security-Policy or a custom Content-Type can break several securities and be a breach."; } @NotNull @Override public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder problemsHolder, final boolean isOnTheFly) { return new PhpRecursiveElementVisitor() { @Override public void visitPhpFunctionCall(FunctionReference reference) { log.debug("visitPhpFunctionCall called for: " + reference.getName()); // 只处理header函数调用 if ("header".equals(reference.getName())) { // 这里添加你的安全检查逻辑,比如判断参数是否为常量/安全值 problemsHolder.registerProblem(reference, "Unsafe call to 'header()' function detected"); } // 如果需要继续递归遍历函数调用的子元素(比如参数),保留此行;否则可以移除 super.visitPhpFunctionCall(reference); } }; } }
关键修改说明
- 切换到递归访问者:
PhpRecursiveElementVisitor会自动遍历所有嵌套的PSI元素,确保header()调用能被检测到。 - 移除冗余的
visitElement:默认的递归访问者会正确调度对应类型的visit方法,手动重写visitElement反而可能干扰类型匹配。 - 增加函数名校验:确保只处理
header()函数,避免触发其他函数的检查逻辑。
额外调试技巧
如果修改后仍未触发visitPhpFunctionCall,可以临时添加以下代码确认PSI元素类型:
// 在递归访问者中临时添加 @Override public void visitElement(com.intellij.psi.PsiElement element) { log.debug("Visited element type: " + element.getClass().getSimpleName()); super.visitElement(element); }
运行后查看日志,确认header()对应的元素是否为FunctionReference——如果不是,可能需要检查PHP SDK的版本兼容性,或者PSI元素的识别逻辑。
内容的提问来源于stack exchange,提问作者Xenos
相关产品推荐
相关产品推荐

