stratadox/domain-analyser

dev-master 2018-02-19 23:55 UTC

This package is auto-updated.

Last update: 2024-09-29 04:43:14 UTC


README

(徽章)

安装

使用composer安装

composer require stratadox/domain-analyser

目标

领域分析器的最终目标是提供给定上下文中所有类的所有属性类型的详细信息。

当一个属性类型可以有多种类型时,目标是提供该属性可能拥有的所有类型。

挑战

实现这一目标的主要困难在于语言的性质。在动态类型语言中,由于隐式类型转换和允许一切的态度,通常很难确定变量的类型。

在静态类型语言中,这个整个模块将没有任何作用。这个模块的整个目的可能可以用Java表示为

field.getType();

然而,PHP(在写作本文时)没有提供声明属性类型的可能性。

机会

自从PHP5以来,可以为类的接口的函数或方法的参数添加类型提示。PHP7通过添加对标量类型提示的支持扩展了这些可能性。此外,还引入了对返回类型进行类型提示的支持。

剩余的挑战

这些类型提示非常有帮助,但它们并没有解决整个挑战。

首先,它们不适用于属性。属性类型通常通过构造函数或设置器参数上的类型提示来断言,但这些参数与属性之间的链接不是标准功能。

其次,这些属性类型断言是可选的,有时甚至是不可能的。例如,在实现不应用类型提示的接口时(例如,出于向后兼容性或互操作性的原因),语言禁止限制输入类型。

有时,类型提示系统的能力是不够的。当传递一个包含多个元素的数组作为参数时,可以提示数组类型本身,但不能提示数组的内容。

由于上述限制,代码库通常包含额外的检查,以在将输入变量分配给属性之前断言它们的类型。

这个包试图解决这些挑战。

目标

由于PHP的动态类型特性,在某些情况下,即使是静态分析器也很难确定预期的属性(或参数)类型。

这个包假设上述类型提示至少应用到了最低水平。

该模块承认,由于向后兼容性的原因,类型提示系统不能总是发挥其最大潜力。它要求受众在这些情况下执行替代类型检查。

这个分析器的目标是识别那些开发者已做出合理努力限制其影响的输入属性的可能类型。

这些努力可能包括

  • 类型化构造函数参数
  • 类型化设置器(尽管布尔型设置器不好)
  • 使用正的instanceof检查作为赋值条件
  • 使用负的instanceof检查,通过抛出异常(或返回?)
  • 与gettype函数的比较
  • 使用is_string、is_int等函数
  • 将参数作为限制输入的方法的参数
  • 将上述任何一种应用到集合的元素上

范围

未对输入类型进行声明的项目不在此范围内。不去考虑这个问题。

非解释性指示,例如docblock注释。解析器会忽略它们,因此它们可能是不正确的。

在范围内示例

以下示例中,对输入的验证采取了合理的注意。

<?php

class InScope
{
    private $things;

    public function __construct(array $things)
    {
        foreach ($things as $name => $thing) {
            $this->validateName($name);
            $this->validateThing($thing);
        }
        $this->things = $things;
    }

    public function thing($withName)
    {
        return $this->things[$withName];
    }

    private function validateName($name)
    {
        if (!is_string($name)) {
            throw new InvalidArgumentException;
        }
    }

    private function validateThing(Thing $thing) {}
}

域名分析器应正确地将InScope的things属性识别为以string类型值索引的Thing对象关联数组。

范围外示例

以下示例中,开发者仍然需要保护他们的不变性。

<?php

class OutOfScope
{
    private $things;

    public function __construct(array $things)
    {
        $this->things = $things;
    }

    public function thing($withName)
    {
        return $this->things[$withName];
    }
}

尽管域名分析器应指示类型为数组,但无法知道它包含的内容。理论上,可以通过跟踪所有实例来通过跟踪原始值推断类型,但这非常复杂,而且动机本身就不好。

待办事项

检测类型

  • 添加TypeDetector
    • 如果是变量,则询问VariableTypeResolver
    • 否则询问检测器
  • 添加VariableTypeResolver
    • 从赋值开始遍历方法中的语句
    • 在每个节点上询问检测器关于局部变量的类型指示
    • 继续,直到节点耗尽或找到DefiniteType

收集返回类型

  • 添加ReturnCollector
    • 收集返回语句
  • 添加MethodIndex
    • 按方法缓存返回类型
  • 添加MethodIndexer
    • 询问TypeDetector收集方法的返回类型
    • 将返回类型添加到MethodIndex

收集参数类型

  • 添加ParameterCollector
    • 收集方法参数
  • 添加ParameterIndex
    • 按方法和参数缓存参数类型
  • 添加ParameterTypeResolver
    • VariableTypeResolver的逆向版本
    • 另一个方向有分支:分支时排队(有上限吗?)
  • 添加ParameterIndexer
    • 询问ParameterTypeResolver

类型检测

  • 添加InstanceOfDetector
    • 检测是否在instanceOf检查中
    • 检查当前节点是否是instanceOf中的表达式
  • 添加InstanceOfExceptionDetector
    • 检测是否在负instanceOf检查中抛出异常
    • 检查
      • 当前节点是if语句
      • if条件是instanceOf
      • 直接子节点中有throw语句
  • 添加ForeignTypeHintDetector
    • 检测变量是否是调用中的参数
    • 与ParameterIndex检查
  • 添加ReturnTypeDetector
    • 检测变量是否是通过将方法调用赋值给具有已知返回类型的变量声明的
    • 与MethodIndex检查
  • 添加ParameterTypeHintDetector
    • 检测变量是否是在包含方法调用中的类型提示参数
    • 简单情况...

以上文档包含概念目标。其主要目的是组织我的思想,无意做出任何保证或承诺。