跳转至

Phalcon 容器代码提示解决方案(PhpStorm)

依赖注入以及容器算是现时互联网后端开发的一种非常流行的设计模式,以至于现在用那些不提供DI的框架都有点不知如何去实例化一个类。幸好现在这种框架已经不多,就连那个ThinkPHP都提供了服务容器,而 Symfony 4 则更进一步,跟 Spring 一样通过 AutoWire 将容器的配置进一步简化。

依赖注入的模式可以很大程度地解藕类之间的依赖,所以很多关于最佳实践的文章会特意提醒别将容器直接注入到类中,因为这样的类其依赖是不确定的,并且无法很好的进行单元测试;然而对于业务逻辑相关的类而言,一个提升开发效率的方法就是直接将容器注入其中,这样不需要写一个长长的构造函数定义,以及需要调用新的系统服务的时候又要去改构造函数,改容器配置。

Phalcon 中 Controller 使用的就是这种方式,继承自 Phalcon\Di\Injectable,直接提供 __get() 魔术方法来访问DI中的服务。而我们应用中业务逻辑相关的类也是继承自 Injectable,这样导致的一个问题就是我们获取服务以及后续的使用中就没有了代码提示功能(因为IDE不知道一个不存在的属性,或者某个PHP数组的key下保存的是何种数据类型)。经过一番探索找到中 PhpStorm 中可用的三个方案。

@var 注解

这种方式先将di中获取到的服务赋值给一个变量,然后通过 @var 注解注释变量类型,但这样对于大量使用而言有所繁琐。

1
2
3
4
5
6
7
8
9
/** @var $db \Phalcon\Db\Adapter\AbstractAdapter */
$db = $this->di->get('db');
$db->....

// 或者

/** @var $db \Phalcon\Db\Adapter\AbstractAdapter */
$db = $this->db;
$db->....

.phpstorm.meta.php

可能在PHP这种若类型语言上这种情况太常见了,PhpStorm 提供了 .phpstorm.meta.php 的方案, 在项目的任何目录中添加 .phpstorm.meta.php 文件,然后可以使用 PHPSTORM_META 相关的方法对 各种类型提示进行修改。

.phpstorm.meta.php 提供了对参数、返回值、数组keyvalue类型,函数调用参数key对应返回值value类型等, 功能丰富,并且可以独立于源代码存在,对于修改库等非项目代码而言非常方便;然而却又有一个问题,对于 __get 这种 magic 方法,并不能提示并自动补全该属性,而只能在你写出属性全名后才能匹配上。

1
2
3
4
5
6
7
namespace PHPSTORM_META {
    override(\Phalcon\Di\Injectable::__get(), map([
        "cache" => \Phalcon\Cache::class,
        "logger" => \Monolog\Logger::class,
       //...
    ]));
}

使用 @property 注解

这种方法使用一个中间类继承自 \Phalcon\Di\Injectable,然后将 @property 注解写在类的 phpdoc 上,然后将所有原来继承自 Injectable 的类继承到新加的中间类上。然而这种方法导致的问题是引入了用处不大的中间类,并且对于 Controller 与 Service 两种不同分支的功能要建两个中间类,并且将注解分别写到两边(可能可以通过 Interface 的方式解决,未测试)

最后使用了一个折中的方法,将 Phalcon 的 ide-stubs 库 clone 到内网Git仓库,配置 IDE 使用该目录作为 Phalcon 的代码提示,然后直接修改 \Phalcon\Di\Injectable,加入 @property 注解

好处是在开发过程中接近完美地解决了代码提示功能,坏处是需要同时修改两个 git 库,并且 phalcon 更新后要跟着将 ide-stubs 更新、修改。