哈Ha! 我提请您注意亚历山大·库兹曼科 ( Alexander Kuzmenko )最近(2019年6月14日至15日)香港开源会议的报告的译文 。

在加入Haxe基金会作为Haxe编译器的开发人员之前,Alexander专门从事PHP编程大约10年,因此他知道该报告的主题。

关于Haxe是什么的小介绍是一套用于创建软件的跨平台工具,其中包括:
- 取决于目标平台的编译器,可以将Haxe源代码转换为其他编程语言(C ++,PHP,Java,C#,JavaScript,Python,Lua)的源代码,或者直接编译成虚拟机的字节码(JVM,neko) ,HashLink,Flash)
- 为所有支持的平台实现的标准库
- Haxe还提供了与以目标平台的语言编写的代码进行交互的工具。
- 标准库管理器-haxelib

Haxe中的PHP支持早就出现了-早在2008年。 在Haxe最高为3.4.7(含)以下的版本中,支持PHP 5.4和更高版本,从第四版本开始,Haxe支持PHP 7.0或更高版本。

您可能会问:为什么PHP开发人员应该使用Haxe?
这样做的主要原因是能够在服务器和客户端上使用相同的逻辑。
考虑以下示例:您有一个使用Laravel框架用PHP编写的服务器,以及几个用JavaScript,Java和C#编写的客户端。 在这种情况下,要处理服务器和客户端之间的网络交互(其逻辑基本相同),您将需要为所使用的每种语言编写4种实现。 但是使用Haxe,您可以编写一次网络协议代码,然后在不同平台上进行编译/翻译,从而节省了大量时间。

这是另一个示例-游戏应用程序-部落冲突。 Alexander参与了此类游戏的开发:它的服务器是用PHP编写的,移动客户端是C#(Xamarin)的,浏览器客户端是使用Phaser框架的JavaScript的。 在客户端和服务器上,处理了一种逻辑-所谓的“战斗”,它计算了玩家单位在该位置上的行为。 最初,战斗代码是分别为每个平台编写的。 但是随着时间的推移(该项目已经进行了大约5年的开发),其在服务器和客户端上的行为积累了差异。 这是因为它是由不同的人编写的,每个人都以自己的方式意识到它。 因此,没有可靠的方法来检测作弊者,因为服务器上的逻辑行为与客户端上的逻辑完全不同,结果,诚实的玩家也遭受了打击。 服务器可以将其视为作弊者,而不考虑诚实战斗的结果。
最后,决定将战斗转移到Haxe,这样就可以彻底解决作弊者的问题,因为 现在,战斗逻辑在所有平台上均表现相同。 此外,该决定还降低了战斗系统进一步开发的成本,因为 现在,只需要一名熟悉Haxe的程序员就足够了,而不是三个,每个程序员都要为其平台负责。

Haxe和PHP有何不同? 通常,Haxe语法对于PHP程序员而言似乎并不陌生。 是的,它有所不同,但是在大多数情况下,它将与PHP非常相似。
幻灯片显示了用于在控制台中显示字符串的代码比较。 在Haxe中,代码看起来几乎相同,除了要使用PHP函数,您需要导入它们(请参见第一行)。

与手动编写的代码相比,这就是生成的PHP代码的外观。
Haxe编译器会自动在代码中添加一段注释,以描述函数参数和返回类型的类型,因此可以将生成的代码连接到PHP项目,并且自动完成功能可以很好地工作。
让我们看一下Haxe和PHP的语法上的一些显着差异。

让我们从匿名函数的语法差异开始(以它们为排序数组的示例)。
幻灯片显示了一个匿名函数,该函数捕获并更改了局部变量desc
的值。 在PHP中,您需要明确指定匿名函数主体中可用的变量。 另外,为了能够更改变量的值,必须在变量名前添加&
。
在Haxe中,这不再是必需的。 编译器本身确定要访问的变量。 另外,箭头函数(描述匿名函数的简写形式)出现在Haxe 4中,使用该函数可以将数组仅排序为一行,从而缩短示例。

语法上的另一个差异是switch
控制结构的描述中的差异。 在PHP中,此switch
作用与在C中相同。 在Haxe中,其工作方式不同:
- 首先,交换机不使用
break
关键字 - 其次,您可以使用
|
组合多个条件 (而不是复制case
关键字) - 第三,Haxe对
switch
使用模式匹配。 因此,例如,您可以将switch
应用于数组,并且在条件下可以根据数组的内容定义操作( _
号表示此值不会打扰我们,并且可以是任何值)。 如果数组由三个元素组成,则满足条件[1, _, 3]
,而第一个元素为1,第三个元素为3,第二个元素的值为any。

在Haxe中,一切都是表达式,这使您可以编写更紧凑的代码。 您可以从try
/ catch
, if
或switch
返回一个值if
而无需在这些结构内使用return
关键字。 在上面的带有try
/ catch
示例中catch
编译器“知道”您要返回一些值,并可以从此构造中传递它。

PHP正在逐渐向更强的类型化发展,但是Haxe已经具有强大的静态类型!
在上面的示例中,我们为变量s
分配了从functionReturnsString()
获得s
值,该functionReturnsString()
返回一个字符串。 因此,变量s
的类型是字符串。 并且,如果您尝试将其传递给giveMeInteger()
函数,该函数giveMeInteger()
一个整数作为参数,则Haxe编译器将引发有关类型不匹配的错误。
此示例还演示了Haxe的另一个重要功能-类型推断-编译器根据为其分配什么值来独立确定变量类型的能力。

对于程序员来说,这意味着在大多数情况下,明确指定变量的类型是可选的。 因此在上面的isSmall()
函数中,编译器确定参数a
的类型为整数,因为 在函数主体的第一行中,我们将a的值与整数进行比较。 此外,基于在函数主体的第二行中我们返回true
的事实,编译器确定返回类型为布尔值。 而且因为 由于编译器已经确定了返回值的类型,因此在进一步尝试从函数中返回任何其他类型时,它将引发类型不匹配错误。

在Haxe中,与PHP不同,它没有自动类型转换。 例如,在PHP中,可以从表示返回整数的函数返回字符串,在这种情况下,执行脚本时,字符串将转换为数字(并非总是成功)。 在Haxe中,类似的代码根本无法编译-编译器将引发类型不匹配错误。

Haxe的另一个功能是其高级类型系统,其中包括:
- 函数类型,由函数参数类型和返回类型组成
- 广义(参数化)类型(例如,这些类型包括将存储值的类型用作参数的数组)
- 枚举类型
- 广义代数数据类型
- 匿名结构的类型允许您声明对象的类型而无需声明类
- 抽象数据类型 (对现有类型的抽象,但在运行时不会损失性能)
所有这些类型在编译期间都将转换为PHP类。

Haxe的主要区别特征之一是元编程(在Haxe中,它称为宏),即自动生成程序源代码的能力。
宏在程序编译时执行,并用常规Haxe编写。
宏具有对抽象语法树的完全访问权限,也就是说,它们可以读取(在其中搜索所需的表达式)并对其进行修改。
宏可以生成表达式,修改现有类型并创建新的表达式。

在PHP的上下文中,例如可以将宏用于路由。 假设您有一个简单的路由器类,该类实现了一种通过其标识符显示页面的方法以及一种注销方法。 使用宏,可以为route()
方法生成代码,该代码将基于http请求将其重定向到Router
类的相应方法。 因此,不需要手动编写ifs来调用此类的每个方法(在PHP中编译项目时,宏会自动执行此操作)。 请注意,生成的代码不使用反射,不需要任何特殊的配置文件或任何其他附加技巧,因此它将非常快速地工作。

使用宏的另一个示例是用于解析和JSON验证的代码生成。 例如,您有一个Data
类,其对象应基于从JSON获得的数据来创建。 可以使用宏来完成此操作,但是由于宏可以访问Data
类的结构,因此除了解析外,您还可以通过在缺少字段或数据类型不匹配的情况下添加异常引发来生成用于JSON验证的代码。 因此,您可以确保您的应用程序不会丢失从用户或第三方服务器收到的错误数据。
还值得一提的是,PHP平台的某些数据类型的实现的重要功能如下: 如果您不考虑它们,那么您可能会遇到不愉快的后果。

在PHP中,字符串是二进制安全的 ,因此在PHP中,如果不需要Unicode支持,则使用字符串的方法将非常快速地工作。
在Haxe中,从第四个版本开始,字符串支持Unicode,因此在PHP中对其进行编译时,模块中的方法将用于处理mbstring多字节字符串,这意味着对字符串中任意字符的访问速度缓慢,对字符串长度的计算速度较慢。
因此,如果不需要Unicode支持,则可以使用php.NativeString
类方法来处理字符串,该方法将使用PHP中的“本地”字符串。

左侧的幻灯片显示了使用两种都不支持Unicode的方法的PHP代码。
右边是等效的Haxe代码。 如您所见,如果需要Unicode支持,则必须使用String
类的方法,如果不需要,则必须使用php.NativeString
类的方法。

另一个要点是使用数组。
在PHP中,数组按值传递,并且数组还支持数字键和字符串键。
在Haxe中,数组通过引用传递,并且仅支持数字键(如果需要字符串键,则在Haxe中应使用Map
类)。 同样,Haxe数组中不允许索引中的“空洞”(索引必须连续进行)。
还值得注意的是,在Haxe中按索引写入数组非常慢。

这是使用箭头函数映射数组的代码。 如您所见,Haxe编译器正在非常积极地优化在输出处接收的PHP代码:结果代码中没有匿名函数,而是将其代码循环应用于数组的每个元素。 除了map()
此优化还适用于filter()
方法。
另外,如有必要,您可以使用php.NativeArray
类和相应的PHP方法在Haxe中处理数组。

匿名对象使用HxAnon
类实现,该类继承自PHP的StdClass
类。

在PHP中, StdClass
是实例的类,我们尝试将其转换为对象的所有实例都将转换为该类。 而且,如果不是实现匿名对象规范的一种功能,则这将是理想的:在Haxe中,访问匿名对象的不存在字段应返回null
,而在PHP中这将引发警告。 因此,我必须从PHP的标准类继承并向其中添加一个魔术方法,该方法在访问不存在的属性时将返回null
。

Haxe可以与用PHP编写的代码进行交互。 为此,具有以下功能(类似于与JavaScript代码进行交互的可能性):
- 外部
- 将PHP代码直接嵌入到haxe代码中
php.*
包中的特殊类php.*
php.Syntax
用于不在Haxe中的特殊PHP构造php.Global
用于本地PHP全局功能php.Const
用于本机PHP全局常量php.SuperGlobal
用于可从任何地方访问的超全局PHP变量( $_POST
, $_GET
, $_SERVER
等)

因为 PHP使用经典的OOP模型,然后为其编写外部代码是一个相当简单的过程。 实际上,除某些关键字外,PHP类的extern几乎是Haxe中的文字“翻译”。
例如,上面幻灯片中的PHP类的外部代码将如下所示:

PHP类中的常量在Haxe代码中“变成”了静态变量(但添加了特殊的meta标签)。
静态变量$useBuiltinEncoderDecoder
变为静态变量useBuiltinEncoderDecoder
。
这表明可以自动创建PHP类的外部代码 (Alexander今年计划实现一个外部生成器 )。

要插入PHP代码,将使用特殊的php.Syntax
模块。 以这种方式添加的代码不受Haxe编译器的任何转换或优化。
除php.Syntax
,Haxe仍具有使用未php.Syntax
代码的能力。

还值得一提的是Haxe的PHP中没有的这些功能:
- 具有读写方法的真实属性
- 只读字段和变量
- 静态扩展
- 可以用于注释字段且在宏中可读的元标记。 因此,元标记主要用于元编程。
- 空安全性(实验功能)
- 条件编译,它允许您启用/禁用部分可用的代码,例如,在应用程序的调试版本中
- Haxe编译器生成高度优化的PHP代码,其运行速度比手动编写的PHP代码快许多倍。
有关Haxe及其功能的更多信息,请参见其官方指南 。