| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """PostToolUse hook (GLOBAL): 写 PHP 文件后做语法自检。
- 后端 php -l:本环境 php -l 对语法错误返回退出码 0(异常),但 stdout 明确含
- "Parse error" / "Errors parsing"。故以【输出字符串】判定(非退出码),对真实项目路径可靠闭环。
- hook 跑的是工作区内真实文件(非 tempfile),故输出判定稳定。
- - 仅对 .php 文件生效。
- - 非阻塞:发现语法错误时 exit 1(warning,回显 AI),不阻断已完成的写入。
- - 其他故障(如 php 找不到)静默 exit 0,避免误报卡住工作。
- """
- import sys
- import os
- import json
- import shutil
- import subprocess
- # PHP 可执行文件探测协议(对齐 references/cms-development.md「PHP 可执行文件探测协议」):
- # 严禁硬编码唯一路径,按优先级链解析,换机/换用户/跨平台可用。
- _WIN_CANDIDATES = [
- r"F:\BtSoft\php\{ver}\php.exe",
- r"D:\phpstudy\php\{ver}\php.exe",
- r"C:\xampp\php\php.exe",
- r"C:\Program Files\php\php.exe",
- ]
- _MAC_CANDIDATES = [
- "/opt/homebrew/bin/php@{ver}",
- "/usr/local/bin/php@{ver}",
- "/Applications/MAMP/bin/php/php{ver}/bin/php",
- ]
- _LIN_CANDIDATES = [
- "/usr/bin/php{ver}",
- "/usr/local/bin/php{ver}",
- "/opt/php/{ver}/bin/php",
- ]
- # 版本号高到低探测(含本机 F:\BtSoft\php\85 等)
- _VERSIONS = ["85", "84", "83", "82", "81", "80", "74"]
- def resolve_php():
- """按探测协议解析 PHP 可执行文件路径;全部未命中返回 None(调用方静默跳过)。"""
- env = os.environ.get("PHP_BIN")
- if env and os.path.isfile(env):
- return env
- # 多版本环境变量(PHP_85 / PHP_82 ...)
- for ver in _VERSIONS:
- envv = os.environ.get("PHP_" + ver)
- if envv and os.path.isfile(envv):
- return envv
- # 系统 PATH
- p = shutil.which("php")
- if p:
- return p
- # 常见安装位置自动扫描
- for tpl in _WIN_CANDIDATES + _MAC_CANDIDATES + _LIN_CANDIDATES:
- for ver in _VERSIONS:
- cand = tpl.format(ver=ver)
- if os.path.isfile(cand):
- return cand
- return None
- def main():
- try:
- raw = sys.stdin.read()
- if not raw.strip():
- return 0
- data = json.loads(raw)
- except Exception:
- return 0
- tool_input = data.get("tool_input", {}) or {}
- path = (
- tool_input.get("filePath")
- or tool_input.get("file_path")
- or tool_input.get("path")
- or ""
- )
- if not path:
- return 0
- path = os.path.abspath(path)
- if os.path.splitext(path)[1].lower() != ".php":
- return 0
- if not os.path.isfile(path):
- return 0
- php_bin = resolve_php()
- if not php_bin:
- # PHP 不可解析(未配置 PHP_BIN 且不在 PATH/常见位置):静默跳过,避免误报卡住工作
- return 0
- try:
- r = subprocess.run(
- [php_bin, "-l", path],
- capture_output=True, text=True, timeout=30,
- )
- except Exception:
- return 0
- out = (r.stdout or "") + (r.stderr or "")
- # 本环境 php -l 退出码对语法错误仍为 0(异常),但 stdout 含 Parse error 可靠。
- # 成功信息 "No syntax errors detected" 不含 "Parse error",不会误判。
- if "Parse error" in out or "Errors parsing" in out:
- print("[LINT-FAIL] php -l 发现语法错误:")
- print(out.strip())
- return 1 # warning,回显给 AI
- return 0
- if __name__ == "__main__":
- sys.exit(main())
|