gariev/xref

xref-lint

dev-master 2014-01-24 07:13 UTC

This package is not auto-updated.

Last update: 2024-09-24 05:10:47 UTC


README

XRef 是一组用于处理 PHP 源文件的工具。目前包括

  • xref-lint

    一个 linter,即静态代码分析工具,用于查找错误。PHP 不是一种完美的语言来捕获程序员的错误,所以 xref 会做到。请参阅下文“由 Linter 报告的错误信息”部分。

  • xref-ci

    持续集成工具,用于监控您的项目存储库,并在发现错误时发送电子邮件通知。

  • GIT linter 插件

    在您的 Git 仓库根目录中输入 'git xref-lint' 并在提交之前,您将获得修改文件的 linter 报告

  • xref-doc

    一个工具,用于创建关于您的项目的交叉引用文档(哪些类/方法/属性被定义以及它们在哪里被使用)

  • 在线试试!

    http://xref-lint.net/

安装

PEAR 安装

pear channel-discover pear.xref-lint.net
pear install xref/XRef

Composer 安装

php composer.phar --stability=dev create-project gariev/xref
export PATH=$PATH:`pwd`/vendor/bin

从 git/source 代码安装

git clone git@github.com:gariev/xref.git
export PATH=$PATH:xref/bin

以上任何一种方法都将为您提供工作命令行 linter 工具 xref-lint(或 Windows 平台上的 xref-lint.bat)。

设置

要充分利用 xref 包,请在安装后将其配置为您的项目。

cd <your-project-dir>
xref-lint --init

这将为快速检查创建 xref 数据目录 (.xref),默认配置文件 (.xref/xref.ini) 并索引文件。

  1. 编辑配置文件 (.xref/xref.ini)。请参阅下文“配置文件”部分;还可以查看 examples/xref.ini.sample 中的示例配置。

  2. [如果通过 Composer 安装,Smarty 将作为依赖项安装,此步骤不需要] 下载并安装 Smarty 模板引擎。 https://smarty.php.ac.cn/;任何 2.x 或 3.x 版本都应正常工作,2.x 版本占用内存较少。在配置文件中设置 Smarty.class.php 文件的路径为 xref.smarty-class 参数。

  3. [此步骤是可选的] 配置 web 服务器以从 web-scripts 目录 XRef/web-scripts/ 运行脚本,请参阅 examples/httpd.conf 中的示例 Apache 配置文件。要查看示例和 web-scripts 的安装位置,请运行 'xref-lint --help'。

  4. 设置 crontab 以运行 xref-ci 以监控您的项目,请参阅 examples/ci.crontab 中的示例 cronab 脚本。

报告的错误

  • 无法解析文件 (%s)(严重性:致命,代码:xr001)

    源 PHP 文件中有语法错误。

  • 使用未知变量 (%s)(严重性:错误,代码:xr010)

    此错误是由于使用了在此作用域中从未引入的变量而引起的。这通常是因为变量名拼写错误或重构错误。

    示例代码

    // COUNTEREXAMPLE
    function example1() {
        $message = "hello, world";
        echo $Message;  // <-- error: there is no variable $Message
    }
  • 可能使用未知变量 (%s)(严重性:警告,代码:xr011)

    类似于 xr010,但在 xref 无法可靠地检测变量是否可以合法存在于该作用域时引发(宽松模式)。

    <?php
    //
    // COUNTEREXAMPLE
    //
    // global scope is always in relaxed mode -
    // variables defined in other files can be used here
    //
    echo $this_variable_may_be_declared_in_other_file;

    function foo($params, $var_name) {
        // functions starts in strict mode, but
        include "other_file.php";       // all of these
        extract($a);                    // makes xref switch to
        $$var_name = 1;                 // relaxed error-reporting
        eval("\$res = $expression_str");// mode

        echo $foo;                      // <-- can $foo be here? maybe.
    }
How to get rid of this warning:

- Test for existence of the variable before using it
- Use doc comment annotations
- rewrite the code - use array indexing instead of *extract()*,
- if your project heavily depends on these features, disable
  the error code
  (see xref.ignore-error config parameter/command-line option below)
    //
    // making variables known in relaxed mode
    //

    // checking for a var
    if (isset($foo)) {
        // ok, we know $foo now
    }

    // doc comment annotations
    /** @var MyClass $bar */
    $bar->doSomething();

    // rewrite the code:
    // use array indexing instead of extract, returning for of eval etc
    $baz = $params["baz"];
    $res = eval("return $expression_str");
  • 可能将未知变量用作函数参数 (%s)(严重性:警告,代码:xr012)

    类似于 xr010,在将未知变量用作具有未知签名的函数的参数时引发。

    // COUNTEREXAMPLE

    unknown_function($foo);     // <-- is $foo legitimate here?
                                // maybe, if unknown_function takes param
                                // by reference and assigns value to it

    function known_function(&$param) {
        $param = 1;
    }

    known_function($bar);       // no warning here - since
                                // known_function() is defined in the same
                                // file, xref knows its signature
How to get rid out of this warning:

    - Initialize the variable with some appropriate value (e.g. null)
      before using it as parameter
    - add function signature with *lint.add-function-signature*
      config file/command-line parameter (see below)
  • 数组自动初始化 (%s)(严重性:警告,代码:xr013)

    类似于上述内容 - 当将值分配给从未在数组上下文中定义的变量时,将创建一个新的数组。这可能是有意为之的行为或错误;这就是为什么它是警告。始终使用空数组初始化数组变量是一个好主意。

    示例代码

    // COUNTEREXAMPLE
    $letters = explode('', $str);
    $letter[] = '!';    // <-- a new array $letter is instantiated here.
                        // is it intended or array $letters should be here?
                        // to remove the warning, initialize var before usage:
                        // $letter = array();
  • 标量自动初始化 (%s) (严重程度:警告,代码:xr014)

    类似于上面,由对从未初始化的变量进行的 ++、.= 或 += 操作引起。这可能是有意为之的行为,也可能掩盖了一个真正的错误。在使用前请初始化相关的变量。

    // COUNTEREXAMPLE
    $sum = 0;
    foreach ($prices as $price) {
        $total += $price;       // <-- warning, new variable $total is instantiated here
    }
    return $sum;
  • 尝试通过引用传递非变量 (%s) (严重程度:错误,代码:xr015)

    如果函数通过引用接受参数,但给出的是除了变量以外的其他东西,则发出此消息。

    // COUNTEREXAMPLE
    $last_word = array_pop(explode(" ", $text));        // <--
        // Error - array_pop() takes an array by reference and modifies it
        // this code may work but will break in E_ALL | E_STRICT mode.
        // Use temp variable to fix it:

    $tmp = explode(" ", $text);
    $last_word = array_pop($tmp);   // ok
  • 混合/小写未引号字符串字面值 (%s) (严重程度:警告,代码:xr021)

    未引号(裸)字符串要么是常量名称,如果没有定义具有该名称的常量,则解释为字符串。对于常量,最佳实践是使用大写名称。

    示例代码

    // COUNTEREXAMPLE
    echo time;              // <-- should it be "time" or time(), $time, or even some constant TIME here?

    // xref knows about constants defined in current file:
    define("foo", 1);
    const bar = 2;
    echo foo + bar;         // ok, no warning here

    // all upper-case literals are assumed to be constants
    echo UPPER_CASE;        // ok, no warning here
If you get warnings about a lower-case constant defined somewhere else, you can disable the warning
listing the constant in lint.add-constant setting (see below).
    // COUNTEREXAMPLE
    class Foo {
        const BAR = 1;
        public method bar() {
            echo BAR;           // <-- did you mean self::BAR / Foo::BAR?
        }
    }
  • ($this) 在实例/类作用域外使用 (严重程度:错误,代码:xr031)

    示例代码

    // COUNTEREXAMPLE
    class Foo {
        public static function bar() {
            return $this->bar;      // <-- error: no $this in static method
        }
    }
  • 可能在全球范围内使用 ($this) (严重程度:警告,代码:xr032)

    类似于上面,由在不含其他类和/或方法的文件中全局范围内使用 $this 引起,因此可以将其包含在类方法的主体中。例如,参见 Joomla 项目中的这种编码风格。

// COUNTEREXAMPLE

// file: main.php
class Foo {
    public function bar() {
        include "method_body.php";
    }
}

// file: method_body.php

    return $this->bar;      // <-- warning here
If your project depends on code like this, disable this error.
  • 类关键字 (%s) 在实例/类作用域外使用 (严重程度:错误,代码:xr033)

    类似于 xr031,由在类结构外使用任何类上下文关键字(self::、parent:: 或 static::)引起。示例代码

    // COUNTEREXAMPLE
    function bar() {
        return new self();  // <-- error: no self class in regular function
    }
  • 可能在全球范围内使用类关键字 (%s) (严重程度:错误,代码:xr034)

    类似于 xr032xr033,由在全球范围内使用任何类上下文关键字(self::、parent:: 或 static::)引起

  • 条件表达式中赋值 (%s) (严重程度:警告,代码:xr041)

    示例代码

    // COUNTEREXAMPLE
    if ($foo = 0) { ... }       // <-- should it be ($foo == 0) here?
    if ($bar = $baz) { ... }    // <-- ($bar == $baz) ?

    // examples below are ok and don't raise warning:
    if ($handle = fopen("file", "w") { ... }    // ok
    if ($ch = curl_init(...)) { ... }           // ok
  • 在打开标签前有空格 (%s) (严重程度:警告,代码:xr051)

    由在打开 php 标签 (<?php) 前的空格(换行、字节顺序标记等)引起。如果输出是基于文本的(例如 HTML 或 XML),则这不是重要的事情,但可能会破坏二进制输出。如果此警告对您的项目不重要,请使用 lint.ignore-error 选项禁用它。

  • 不必要的关闭标签 (%s) (严重程度:警告,代码:xr052)

    类似于上面,关闭标签 (?>) 之后的所有文本都将包含在脚本的输出中。最佳实践是完全省略关闭标签。

  • 类 (%s) 被定义了多次 (严重程度:警告,代码:xr061)

    由具有相同名称的两个类定义引起。

  • 方法 (%s) 在类 (%s) 中未定义 (严重程度:错误,代码:xr062)

    由尝试调用既不在类中也不在其基类中定义的方法的代码引起。

  • 常量 (%s) 在类 (%s) 中未定义 (严重程度:错误,代码:xr063)

    由尝试访问类常量的代码引起,而该类(或其基类)未定义此常量。

  • 属性 (%s) 在类 (%s) 中未声明 (严重程度:警告,代码:xr064)

    这是一个警告,因为如果代码将值分配给未声明的属性,则将创建该属性。然而,这通常是一个错误

    // COUNTEREXAMPLE
    class A {
        protected $myPropery = null;
        public function setProperty($value) {
            $this->my_property = $value;        // <-- warning
                                                // property 'my_property' will be created here
                                                // is it ok or prop 'myPropery' was meant?
        }
    }
  • 无法检查类(%s)的成员,因为其定义缺失 (严重程度:警告,代码:xr065)

    由对某个缺失定义的类(属性、方法或常量)的任何成员的引用引起。如果启用了选项 xref.project-check 正在检查单个文件,所有在其他地方定义的引用的用户定义类将触发此警告;在这种情况下,建议关闭此选项。

    echo A::MY_CONST;   // warning
                        // there is not definition of class A, so
                        // it's not possible to check if there is a constant MY_CONST
To disable this warning for a specific class, use **lint.ignore-missing-class** option.
  • 无法检查类(%s)的成员,因为其基类(%s)的定义缺失 (严重程度:警告,代码:xr066)

    类似于上述情况,但由基类定义缺失引起。

    class B extends A {
    }

    echo B::MY_CONST;       // warning
                            // there is no const B::MY_CONST, but it may be inherited from class A,
                            // which definition is missing.
  • 属性(%s)是静态的,不是实例 (严重程度:错误,代码:xr067)

    由尝试将静态类属性当作实例属性访问引起。

    // COUNTEREXAMPLE
    class A {
        public static $name = "my name";

        public function test() {
            echo $this->name;       // <-- error
                                    // there is no $this->name, it's self::$name

        }
    }
  • 成员(%s)是实例的,不是静态的 (严重程度:错误,代码:xr068)

    由尝试将实例成员(属性或方法)当作静态成员访问引起。

    // COUNTEREXAMPLE
    class A {
        public function test() {}
    }

    echo A::test();     // <-- error
                        // method test() is not static!
  • 类(%s)的成员(%s)是私有的 (严重程度:错误,代码:xr069)

    由在类外部尝试访问私有成员(属性或方法)引起。

    // COUNTEREXAMPLE
    class A {
        private $prop = null;
    }
    class B extends A {
        public function test() {
            echo $this->prop;       // <-- error
                                    // class B doesn't declare it's own property 'prop'
                                    // and can't access private prop of its parent class
        }
    }
  • 类(%s)的成员(%s)是受保护的 (严重程度:错误,代码:xr070)

    类似于xr069,但由访问受保护的成员(属性或方法)引起。

  • 类(%s)没有调用其基类(%s)的构造函数 (严重程度:警告,代码:xr081)

    如果子类声明了一个不调用父类构造函数的'__construct'方法,则父类的字段可能不会被初始化。如果1)子类没有声明构造函数(然后PHP将创建一个默认的构造函数并调用父类)或2)父类没有构造函数,则不会报告此警告。

    // COUNTEREXAMPLE
    class A {
        public $prop;
        function __construct() { $this->prop = 42; }
    }

    class B extends A {
        function __construct() {
            // warning: there is no call to parent::__construct(),
            // so property A::$prop is uninitialized
            // when instance of class B is created.
        }
    }

    $b = new B();
    echo $b->prop;  // undefined!
  • 未知函数(%s) (严重程度:警告,代码:xr091)

    要么函数名拼写错误,要么它在xref可访问范围之外定义。使用 lint.add-function-signature[] 配置指令使函数已知。如果启用了选项 xref.project-check 正在检查单个文件,所有在其他地方定义的引用的用户定义函数将触发此警告;在这种情况下,建议关闭此选项。

  • 可能将类(%s)的方法(%s)当作函数调用 (严重程度:警告,代码:xr092)

    在调用类方法时没有使用相应的前缀(实例方法为 $this->,静态方法为 self:: 或 ClassName::)。

  • 函数/方法(%s)的参数数量错误:( %s) 而不是 (%s) (严重程度:警告,代码:xr093)

  • 类(%s)构造函数的参数数量错误:( %s) 而不是 (%s) (严重程度:警告,代码:xr094)

  • 类(%s)的默认构造函数不接受参数 (严重程度:警告,代码:xr095)

    尝试创建一个没有定义构造函数的类的对象,并将一些参数传递给默认构造函数。由于默认构造函数不接受任何参数,这些参数将会丢失。

    // COUNTEREXAMPLE
    class MyMessage { public $text; }
    $m = new MyMessage("text");     // <-- warning - the text will be lost.
                                    // Class MyMessage doesn't define constructor,
                                    // and default constructor doesn't accept arguments

CONFIG FILE

XRef工具将按以下顺序在这些位置中寻找配置文件

  • 由命令行选项 -c (--config) 设置的路径,仅影响命令行脚本
  • 环境变量XREF_CONFIG
  • 在当前目录下或其父目录中的文件 .xref/xref.ini

如果找不到文件,将使用默认值 - 这些值足以运行命令行 lint 工具,但不能运行 xref-doc 或 xref-ci。

以下每个值都可以通过命令行选项 -d (--define) 覆盖,例如:

xref-lint -d lint.check-global-scope=false -d lint.ignore-error=xr010 ...

配置文件参数列表

  • project.name (字符串; 可选)

    您项目的名称,将在生成的文档中提及

  • project.source-code-dir[] (路径数组; 必须项)

    查找项目源代码的路径集合,用于创建文档/lint 报告

  • project.exclude-path (路径数组; 可选)

    排除给定的文件或目录进行 lint 检查

  • xref.data-dir (路径,必须项)

    XRef 将项目数据保存在该目录的路径

  • xref.smarty-class (路径,必须项)

    安装的 Smarty 模板引擎主类的路径,例如 /home/igariev/lib/smarty/Smarty.class.php

  • xref.template-dir (路径,可选)

    包含(您自定义的)模板文件的目录的路径。如果未设置,将使用默认模板。

  • xref.script-url (URL,可选)

    可以从 XRef/web-scripts 目录访问 PHP 脚本的 URL;如果存在,xref-ci 通知将包含到它们的链接

  • xref.storage-manager (类名;可选)

    XRef 数据持久存储的插件类名;默认为 XRef_Storage_File

  • xref.plugins-dir[] (路径数组;可选)

    查找插件的路径;即使未指定,也会搜索默认的 XRef 库目录。

  • xref.project-check (布尔值;可选)

    选择在项目(即检查文件中声明/引用的交叉引用一致性)中检查所有文件,还是仅独立检查每个文件。默认启用,这会导致运行时间更长,但 lint 报告更准确。

  • doc.output-dir (路径;必须项)

    将生成的文档放在哪里

  • lint.color (true/false/auto;可选)

    仅针对命令行 lint 工具 - 控制台输出是否应为彩色;默认为 auto。

  • lint.report-level (errors/warnings/notices;可选)

    lint 应该报告哪种严重级别的消息;默认为 warnings。

  • lint.add-constant[] (字符串数组;可选)

    如果您收到有关实际上在其他地方定义的全局常量的小写字符串字面量的警告,您可以在此处列出这些常量。

  • lint.add-global-var[] (字符串数组;可选)

    如果您检查全局作用域中变量的使用(lint.check-global-scope 选项设置为 true),并且您的代码依赖于在其他文件中定义的全局变量,您可以通过将它们列在此列表中通知 lint。

  • lint.add-function-signature[] (字符串数组;可选)

    到目前为止,lint 还不知道在其他文件中定义的用户函数,也不知道是否有可以分配引用传递变量的值的函数。您可以在此列表中列出此类函数(和类方法)。

    语法

      add-function-signature[] = "my_function($a, $&b)"
      add-function-signature[] = "MyClass::someMethod($a, $b, &c)"
    
  • lint.check-global-scope (布尔值;可选)

    lint 是否应该警告全局作用域中的未知变量。对于某些项目,这些变量是真实错误;对于其他项目,这是可以的,因为全局作用域变量可以在其他包含的文件中初始化。选择最适合您项目的选项;默认为 true(检查全局空间)。

  • lint.ignore-error[] (字符串数组;可选)

    不报告此项目的错误代码列表。

  • lint.ignore-missing-class[] (类名数组;可选)

    不要报告给定类的缺失定义。警告:此选项会导致跳过大多数关于派生(子)类的测试,因为子类可以继承(缺失的)基类中的任何方法/属性。

  • lint.parsers[] (类名数组;可选)

    lint 应该使用哪些解析器;默认为 XRef_Parser_PHP

  • lint.plugins[](类名数组;可选)

    lint 应使用哪些插件;默认情况下,它们是 XRef_Lint_UninitializedVars, XRef_Lint_LowerCaseLiterals 和 XRef_Lint_StaticThis。

  • ci.source-code-manager(类名;可选)

    用于与仓库一起工作的插件。默认情况下是 XRef_SourceCodeManager_Git。

  • ci.update-repository(布尔值;必需)

    CI 工具是否应该自己更新仓库;如果不是,其他人应该做

  • ci.incremental(布尔值;必需)

    此选项影响 CI 工具报告哪些错误 - 所有错误或仅新的。推荐值为开启(true),这可以显著减少垃圾邮件数量。

  • git.repository-dir(路径;必需)

    项目 git 仓库目录的本地路径

  • git.update-method(pull/fetch;必需)

    如何更新 git 仓库 - 通过 pull 或 fetch 方法

  • git.ignore-branch[](字符串数组;可选)

    列出不应报告的分支名称

  • mail.from(字符串;必需)

    连续集成电子邮件应从哪个名称/电子邮件地址发送

  • mail.reply-to(电子邮件地址;必需)

    CI 发送电子邮件的回复字段

  • mail.to[](电子邮件地址数组;必需)

    CI 电子邮件的收件人是谁。您可以在此处指定多个地址,并/或使用带有由提交信息填充的字段的电子邮件模板。%an - 提交者姓名,%ae - 提交者的电子邮件地址等。有关受支持字段的说明,请参阅您的仓库管理器文档。

    示例

      to[] = you@your.domain
      to[] = "{%ae}"
    

如何扩展 XREF

在 XRef 工具中,大部分工作都是由可加载的插件完成的,配置文件决定了为任何操作加载哪个插件。因此,这里有检查清单

  1. 找到您的插件应实现的接口所有接口都在 lib/interfaces.php 文件中定义;目前有三种接口用于文件处理插件(XRef_IDocumentationPlugin, XRef_ILintPlugin, XRef_IProjectLintPlugin),一个接口用于缓存/存储(XRef_IPersistentStorage),用于源版本控制系统(XRef_ISourceCodeManager)和解析器(XRef_IFileParser)。

  2. 创建您的实现;您可以从 XRef 类继承并仅覆盖所需的方法。如果类名为 My_Plugin,则将其放入名为 My/Plugin.class.php 的文件中。

  3. 在配置文件的 xref.plugins-dir[] 变量中指定查找您的插件的根本目录。

  4. 在配置文件中告诉 XRef 您的插件应加载。感兴趣的参数:lint.parsers[], lint.plugins[], doc.parsers[], doc.plugins[], xref.storage-manager 和 ci.source-code-manager。

作者

伊戈尔·加里耶夫 gariev@hotmail.com

贡献者

蒂姆·奥滕 https://github.com/totten