i@yujinyan.me

Blog

对 PHP 的一些吐槽

最早开始写后台接口的时候,曾看到有观点认为包括 Laravel 在内的框架不适合写大型项目。随着使用框架一段时间有了一些体会,做一些总结。

总体而言,和 Python 的情况可能十分类似,这些所谓的“脚本语言”写稍微大一点的程序,最大的毛病还是缺乏静态类型系统。比如,Laravel 的核心是一个挺不错的 DI 容器,可以方便地通过全局的app(Some\Claz::class)方法得到所需要的对象实例。然而正是由于返回的对象可能是任何类型,在编辑器里无法获得任何的方法提示,只能查阅文档,手动输入,十分不靠谱。后来发现可以通过给变量添加注释让编辑器能够知道类型。

/** @var SomeService $service */
$service = app('some.service');

这样的办法勉强还算凑合,但非常难看,还不如像 Java 那样要求把类型直接写出来。

自己的代码可以通过注释添加类型信息,但对于框架中的代码就无能为力了。Laravel 框架的实现运用了许多__call__get 等魔术方法,比如常用的 Illuminate\Database\Eloquent\Model 类中将查询相关的逻辑通过魔术方法委托给 Illuminate\Database\Eloquent\Builder 类实现。这样便造成调用的方法要等到运行时才能确定,编辑器无法找到相应的方法,报一堆虚线警告,看起来实在非常令人纠结。此外要想看一下调用的方法还得在源码中查阅一番,人脑运行一遍代码,造成了不必要的思维负担。

php ide

新一点的 PHP 版本可以给函数参数以及返回值指定类型提示(type hinting / declarations),例如:

function calculateSomething(int $input): int {}
function doStuff(User $user) {}

在几次尝试之后便对这项新功能不怎么感冒了。加上类型提示未必使得代码变得更加安全、健壮。程序的 bug 更多还是需要借助测试来发现。如果需要更加完善的编辑器提示,通过注释也能达到目的。将静态类型语言的一些特性引入进来反而带来了一些风险。原本动态语言都会有一套类型自动转换的法则,有的时候这个函数返一个字符串或者整型都差不多,在后续运算中类型自动转换,稀里糊涂也就过去了。一加上类型提示就完全规定好了,类型不匹配就是 TypeError。但是 PHP 没有编译校验的过程,在没有测试的情况下反而把错误风险转移到了运行时。如果真的需要更加严谨地编写程序,或许直接选择静态类型的语言会更好一些。

缺乏静态类型的一个引申的后果就是无法可靠地对代码进行重构。即使是强大的 PhpStorm,重构的功能也并不完善。比如将一个类挪动位置,类的命名空间似乎不会自动变过去。这种半截子的重构比没有也好不了太多。有几次重命名甚至莫名其妙地误伤一大片无关的代码。有了几次这样惨痛的教训很容易患上“重构恐惧症”,代码无法新陈代谢,质量每况愈下。

参考资料

http://radify.io/blog/type-hinting-in-php-good-or-bad-practice/