PHP代码库中的设计问题

Hack由Facebook的语言工具团队开发,2014年3月20日公开发布并开放源代码,运行于HipHop虚拟机HHVM。Julien Verlaguet、Eugene Letuchy、Gabriel Levi、Erik Meijer等人都列在首发说明的核心团队名单中。这是多人协作的工程项目,主要使用场所是Facebook已有的大型PHP代码库。

团队希望更早发现类型错误,同时保留编辑代码后立即观察网页结果的工作方式。已有程序数量庞大,使整体重写代价很高;因此,设计把逐步加入类型信息作为迁移方法。公司在首发公告中表示,此前一年已经转换了几乎全部自身PHP代码。

参考:[1]

静态检查与虚拟机各自负责什么

Hack程序仍需要运行环境执行。2014年3月28日发布的HHVM 3.0.0明确纳入Hack支持;其说明同时强调,提供类型反馈的hh_server、hh_client是与虚拟机分开的工具,虽然随二进制软件包交付,仍须配置并启动。安装运行环境与启用开发期检查,是两个相关但不同的步骤。

类型检查器以驻留本地的服务方式保存程序信息,文件改动后更新分析结果。这使工程师不必在每次修改时从零分析整个项目。Hack的早期实现所追求的是把检查嵌入日常编辑过程,而不只是等网页运行出错后再定位问题。

参考:[2] [1]

给函数接口标出类型

可以用一个简单函数说明这种方法:function bump(int $n): int { return $n + 1; }。参数前的int限定传入整数,冒号后的int说明返回值也是整数;调用bump(4)得到5。如果把字符串传给这个接口,类型检查便有明确的约束可用。

这类注解重点放在函数边界和对象成员上。2015年首版正式规范以严格模式为范围,要求具名函数声明参数及结果类型,而函数内部的局部变量可由表达式推断。规范也明确限定,类型推断不跨越函数边界。这样既能让调用方掌握接口,又无需给每一个临时变量逐项填写类型。

参考:[3]

空值与泛型表达更多约束

可空类型用问号表示。例如?string允许字符串,也允许null;它把“可能没有结果”纳入接口。一个返回?string的查询函数,不能被简单当成始终返回字符串的函数使用。与仅在运行失败后发现空值相比,这种表示让调用者在编写后续处理时就能看到分支条件。

泛型则保留容器与元素之间的关系。规范中的Stack<T>把同一个类型参数用于内部数组、push的参数和pop的结果;实例化为Stack<int>后,取出的元素仍具有整数类型,传入浮点数会遭拒绝。它避免为整数栈、字符串栈各写一套相同的结构,也比把全部元素当成无差别的动态值更便于检查。

参考:[3]

转换工具把设计接到实际项目

2014年4月9日的首届Hack开发者日把语言设计和迁移实践一起展示。Verlaguet介绍语言及检查器,Josh Watzman演示转换流程。Hackificator先帮助修改源文件标记,其他工具尝试加入较宽松的类型注解,再推进到更严格的检查;报告也承认,转换过程中仍有人工步骤。

会议还展示集合操作、匿名函数、async/await以及开发环境FBIDE。后者把类型检查器编译成JavaScript,在浏览器端提供分析和自动补全。这些演示说明,早期推广交付的不只是新语法,也包括程序员把现有代码逐渐转过来的工具和操作路径。

参考:[4]

公开发布之后的正式规范

2015年2月19日,Joel Marcey宣布首份Hack正式语言规范可用。此前已有面向应用程序员的使用文档,新规范则进一步服务于语言实现者和一致性测试的编写者,并借助已有PHP规范整理语法与语义。从语言发布到形成正式规范,团队继续整理实现经验和接口约束。

首版规范聚焦严格模式,运行库也未全部纳入正文;这与2014年允许新旧代码逐步混用的迁移场景应分别理解。Hack早期发展的具体特点,是在已有PHP工程基础上,把明确的函数接口、静态分析和渐进转换连接起来。

参考:[5] [3]