Hi, Habr! I bring to your attention the translation of the report of Alexander Kuzmenko from the recently held (June 14-15) conference of the Hong Kong Open Source Conference 2019.
Before joining the Haxe Foundation as a developer of the Haxe compiler, Alexander has been professionally programming in PHP for about 10 years, so he knows the subject of the report.
A small introduction to what Haxe is is a cross-platform toolkit for creating software, which includes:
PHP support in Haxe appeared quite a long time ago - back in 2008. In versions of Haxe up to 3.4.7, PHP 5.4 and higher was supported inclusively, and starting from the fourth version, Haxe supports PHP version 7.0 and higher.
You ask: why does a PHP developer use Haxe?
The main reason for this is the ability to use the same logic on both the server and the client.
Consider this example: you have a server written in PHP using the Laravel framework, and several clients written in JavaScript, Java, C #. In this case, to handle the network interaction between the server and the client (the logic of which is the same), you will need to write 4 implementations for each of the languages used. But with the help of Haxe, you can write the network protocol code once and then compile / translate it for different platforms, saving considerable time on it.
Here is another example - a gaming application - a clone of Clash of Clans. Alexander participated in the development of a similar game: its server was written in PHP, the mobile client in C # (Xamarin), and the browser client in JavaScript using the Phaser framework. On the client and the server, one logic was processed - the so-called "boevka", which calculated the behavior of the players' units at the location. Initially, the code was written for each of the platforms separately. But over time (and the project has been developing for about 5 years), differences in its behavior on the server and on the clients accumulated. This was due to the fact that it was written by different people, each of whom implemented it in its own way. Because of this, there was no reliable way to detect cheaters, because the logic on the server behaved quite differently than on the client, and as a result, honest players suffered as well. the server could count them cheaters and not count the results of a fair fight.
In the end, it was decided to transfer the combat to Haxe, which made it possible to completely solve the problem with the cheaters, since Now the boevka logic behaved equally on all platforms. In addition, this solution has reduced the cost of further development of the combat system, because now there was enough one programmer familiar with Haxe, instead of three, each of whom would be responsible for his platform.
What are the differences between Haxe and PHP? In general, the Haxe syntax does not seem like something alien to a PHP programmer. Yes, it is different, but in most cases it will be very similar to PHP.
The slide shows a comparison of the code for outputting the string to the console. In Haxe, the code looks almost the same, except that to work with PHP functions, you need to import them (see the first line).
And this is how the generated PHP code looks in comparison with the handwritten code.
The Haxe compiler automatically adds a block of comments to the code with a description of the types of function arguments and return types, so the resulting code can be connected to a PHP project and auto-completion will work fine for it.
Consider some significant differences in the syntax of Haxe and PHP.
Let's start with the differences in the syntax of anonymous functions (for example, using them to sort an array).
The slide shows an anonymous function that captures and modifies the value of the local variable desc
. In PHP, it is necessary to explicitly indicate which variables are available in the body of an anonymous function. In addition, to be able to change the value of a variable, you must add &
before its name.
In Haxe, this need is eliminated, because the compiler itself determines which variables you refer to. In addition, in Haxe 4 there are arrow functions (a short form for describing anonymous functions), using which we can reduce our example with sorting the array to just one line.
Another difference in syntax is the differences in the description of the switch
control construct. In PHP, the switch
works the same as in C. In Haxe, it works differently:
break
keyword|
(and not duplicate the case
keyword)switch
in Haxe. For example, you can apply switch
to an array, and in conditions you can define actions depending on the contents of the array (the _
sign means that we don’t care about this value and can be anything). Condition [1, _, 3]
will be satisfied if the array consists of three elements, with the first element being 1, the third is 3, and the value of the second is any.In Haxe, everything is an expression, and it allows you to write more compact code. You can return a value from try
/ catch
, if
or switch
without using the return
keyword inside these constructs. In the above example with try
/ catch
compiler "knows" that you want to return some value, and will be able to pass it from this construct.
PHP is gradually moving towards stricter typing, but Haxe already has strong static typing!
In the example above, we assign the variable s
value obtained from the functionReturnsString()
, which returns a string. Thus, the type of the variable s
is a string. And if you try to pass it to the giveMeInteger()
function, which giveMeInteger()
for an integer as an argument, the Haxe compiler will generate an error about the type mismatch.
This example also demonstrates another important feature of Haxe - type inference (type inference) - the ability of the compiler to independently determine the types of variables, depending on what value it is assigned to.
For a programmer, this means that in most cases it is not necessary to explicitly specify the types of variables. So in the isSmall()
function, the compiler will determine that the type of the argument a
is an integer, since in the first line of the function body, we compare the value of a
with an integer. Further, the compiler, based on the fact that we return true
in the second line of the function body, determines that the return type is a Boolean value. And since Since the compiler has already determined the type of the return value, then when further attempts to return any other type from the function, it will generate a type mismatch error.
In Haxe, unlike PHP, there is no automatic type conversion. For example, in PHP it is possible to return a string from a function for which it is indicated that it returns an integer, in which case the string will be converted to a number (not always successfully) when the script is executed. And in Haxe, the same code simply won't compile - the compiler will generate a type mismatch error.
Another difference with Haxe is its extended type system, including:
All the types listed are compiled into PHP classes when compiled.
One of the main distinguishing features of Haxe is metaprogramming (in Haxe it is called macros), that is, the ability to automatically generate the source code of a program.
Macros are executed during the compilation of the program and are written in the usual Haxe.
Macros have full access to the abstract syntax tree, that is, they can read (search for the required expressions in it) and modify it.
Macros can generate expressions, modify existing types, and also create new ones.
In the context of PHP, macros can for example be used for routing. Let's say you have a simple class router that implements a method for displaying a page by its identifier, as well as a method for logging out of the system. Using a macro, you can generate code for the route()
method, which, based on an http request, will redirect it to the appropriate method of the Router
class. Thus, there is no need to manually write ifs for calls to each of the methods of this class (the macro will do this automatically when compiling the project in PHP). Note that the resulting code does not use reflection, does not require any special configuration files, or any additional tweaks, so it will work very quickly.
Another example of using macros is code generation for parsing and validating JSON. For example, you have a Data
class, whose objects should be created on the basis of data obtained from JSON. This can be done using a macro, but since the macro has access to the structure of the Data
class, in addition to parsing, you can generate code for JSON validation by adding an exception to exclude if there are no fields or data type mismatch. Thus, you can be sure that your application will not miss incorrect data received from users or from a third-party server.
It is also worth mentioning the important features of the implementation of some data types for the PHP platform, since if you do not take them into account, then you can face unpleasant consequences.
In PHP, binary safe ( binary safe ) strings, so in PHP, if you do not need Unicode support, the methods for working with strings work very quickly.
In Haxe, starting with the fourth version, strings support Unicode, so when they are compiled in PHP, the methods from the module for working with multibyte strings mbstring will be used, which means slow access to arbitrary characters in the string, slow calculation of the string length.
Therefore, if you don’t need Unicode support, you can use the methods of the php.NativeString
class to work with strings, which will use native PHP strings.
The slide on the left shows PHP code that uses methods that support Unicode or not.
On the right is the equivalent Haxe code. As you can see, if you need Unicode support, you must use the methods of the String
class, if not, then the methods of the php.NativeString
class.
Another important point is the work with arrays.
In PHP, arrays are passed by value, and arrays support both numeric and string keys.
In Haxe, arrays are passed by reference and only support numeric keys (if string keys are required, then in Haxe, you should use the Map
class for this). Also in the Haxe-arrays are not allowed "holes" in the indices (indices must go continuously).
It is also worth noting that writing to an array by index in Haxe is rather slow.
Here is the code for mapping an array using an arrow function. As you can see, the Haxe compiler is rather actively optimizing the PHP code output: the anonymous function in the resulting code is missing, instead its code is applied in a loop to each element of the array. In addition to map()
this optimization is also applied to the filter()
method.
Also, if necessary, in Haxe you can use the php.NativeArray
class and the corresponding PHP methods for working with arrays.
Anonymous objects are implemented using the HxAnon
class, which is inherited from the StdClass
class from PHP.
In PHP, StdClass
is a class whose instances turn everything we are trying to convert into an object. And it would be ideally suited for the implementation of anonymous objects if it were not for one specification feature: in Haxe, accessing a nonexistent field of an anonymous object should return null
, and in PHP it throws out a warning. Because of this, I had to inherit from the standard class from PHP and add the magic method to it, which returns null
when accessing non-existent properties.
Haxe can interact with code written in PHP. To do this, you have the following options (similar to the possibilities of interacting with JavaScript code):
php.*
package
php.Syntax
- for special PHP constructs that are not in Haxephp.Global
- for native PHP global functionsphp.Const
- for PHP's "native" global constantsphp.SuperGlobal
- for superglobal PHP variables accessible from everywhere ( $_POST
, $_GET
, $_SERVER
, etc.)Because PHP uses the classic OOP model, then writing externs for it is a fairly simple process. In fact, external classes for PHP classes are almost literal "translations" in Haxe, with the exception of some keywords.
As an example, the external code for the PHP class from the slide above will look like this:
Constants from the PHP class are "transformed" into static variables in the Haxe code (but with the addition of special meta tags).
The static variable $useBuiltinEncoderDecoder
becomes the static variable useBuiltinEncoderDecoder
.
This shows that external classes for PHP classes can be created automatically (Alexander plans to implement an external generator this year).
For PHP code inserts, the special module php.Syntax
. The code added in this way is not subject to any transformations and optimizations by the Haxe compiler.
In addition to php.Syntax
in Haxe, the possibility of using untyped-code has remained.
Also worth mentioning are the features of Haxe that are not in PHP:
More information about Haxe and its functions is available in its official manual .
Source: https://habr.com/ru/post/458184/