Distinctive Features of the PH7 Engine.
Full Type hinting (function signatures).
Introducing the eq and ne operators for strict string comparison.
Function arguments can take any complex expressions as their default values.
Static variables and constants can take any complex expression including function call as their initialization value.
No fatal error, even a call to an undefined function will not abort program execution.
Arrays like object are always passed and returned by reference.
PH7 is 100% hand-coded, written in ANSI C, compile and run unmodified in any platform including restricted embedded devices with a C compiler.
Amalgamation: All C source code for PH7 are combined into a single source file.
Powerful reference mechanism borrowed from the C++ world.
Standard functions can take a single statement without curly braces.
Anonymous functions can be invoked on the fly without any kind of assignment.
Anonymous functions arguments can take default values (Any complex expression).
Anonymous functions can embed static variable.
Language constructs such as echo, print, include, etc. can be used in the context of a function call.
Built-in sorting function such as sort(), usort(), ksort() and many more uses the efficient and stable merge-sort algorithm.
Thread-safe (via compile-time directive) and full re-entrant.
Highly-efficient and platform independent Byte-code compiler.
Built with more 470 function including an XML parser (with namespace support), INI processor, CSV reader/writer, UTF-8 encoder/decoder, zip archive extractor, JSON encoder/decoder, random number/strings generator, native and efficient File IO for Windows and UNIX systems and many more without the need of any external library to link with.
Efficient implementation of arrays using the hashmap data strcuture.
Array cursor is automatically reset at the end of a foreach loop.
const statement can be used outside a class definition.
var statement can be used outside a class definition.
Automatic switch to pass by value when a invalid pass by reference is detected.
Correct and consistent implementation of the ternary operator.
Constants can be redefined using the define() function.
Parameters to call_user_func() can be passed by reference.
Insertion by reference now support function calls.
implode() can take a variable number of arguments (even non-array).
explode() does not return empty (white space stream) entries.
Introducing the implode_recursive() and join_recursive functions().
Introducing the str_glob() function fro string globing.
We describe now, some of the important PH7 features in details.
Function overloading means two or more functions can have the same name but either the number of arguments or the data type of arguments has to be different.
The PH7 engine introduces this powerful mechanism as an extension to the PHP programming language. That is, you define two ore more standard PHP functions with the same name (PH7 is case-sensitive unlike the standard PHP engine) but with different arguments number and/or types and finally you perform a simple function call and let PH7 peek the appropriate function for this call context.
// foo() accepts two arguments and perform their addition.
echo foo(5); // Prints "5"
echo foo(10, 2); // Prints "12"
Here, we define two functions with the same name but with different purposes. That is, the first foo() accepts only a single argument and return its value while the second foo() accepts two arguments, perform their addition and return the result. So if we call foo with a single argument the first foo() will be invoked while if we call foo with two or more arguments,the second foo() will be invoked.
Here, we define three functions with the same name and the same number of arguments but with different signatures. That is, each function accepts a single argument but with a different type than the others. This was possible by the powerful full type hinting extension introduced by the PH7 engine which permit functions to have their own signatures. So if we call foo with a string argument as follows foo('This is a great feature'); The first foo() that expect a string argument will be invoked while if we call foo with a array argument as follows foo(array(14,__TIME__,__DATE__)); the last foo() that expect a array will be invoked.
Again, three functions with the same name but each one expects a different number and different types of arguments, PH7 is smart enough to peek the appropriate function for the call context.
Function overloading is not restricted to standard PHP function, you can also make your class methods candidate for overloading exactly like standard function would do. As a bonus side, overloading apply also to class constructors. That is, define a class with two or more constructors but with different signature and let PH7 peek the appropriate constructor for you.
We recommend to the PH7 enthusiastic users to experiment with this powerful mechanism and to report any unexpected result or inconsistency and/or their feedback to the PH7 public forums or using the PH7 mailing list.
PHP 5 introduces type hinting. Functions are now able to force parameters to be only objects (by specifying the name of the class in the function prototype), interfaces or arrays (since PHP 5.1).
PH7 goes further and extends the type hinting mechanism to be used with scalar types such as string, int, float or even the Boolean type.
With type hinting, arguments are automatically and silently converted to the desired type. By doing so there is no need to add the ugly test statements at the beginning of function prolog to check for the expected argument types using the is_string(), is_float(), etc. functions. All of this is done automatically by the engine freeing the programmer from a boring task.
Example: Type hinting with scalar values (string).
Here if we call the test() function with an int argument, an automatic cast is performed and the given integer value (hexadecimal 0xFFFFFFFF) is converted to a decimal number (4294967295) first, then a string cast is performed. The same operation is done if we call test() with a float argument. The float number is converted to a string and the result is outputted (2,5).
Again, before dumping the result, PH7 internally convert the given argument to the type integer. So if we call our test function with a string argument, this parameter gets converted to the best representation of an integer. That is, the conversion stops at the first character that is not a digit or a hexadecimal character (white space is completely ignored).
Again, another powerful mechanism borrowed from the C/C++ world and introduced as an extension to the PHP programming language.
A comma expression contains two operands of any type separated by a comma and has left-to-right associativity. The left operand is fully evaluated, possibly producing side effects, and its value, if there is one, is discarded. The right operand is then evaluated. The type and value of the result of a comma expression are those of its right operand, after the usual unary conversions.
Any number of expressions separated by commas can form a single expression because the comma operator is associative. The use of the comma operator guarantees that the sub-expressions will be evaluated in left-to-right order, and the value of the last becomes the value of the entire expression.
The following example assign the value 25 to the variable $a, multiply the value of $a with 2 and assign the result to variable $b and finally we call a test function to output the value of $a and $b. Keep-in mind that all theses operations are done in a single expression using the comma operator.
In some contexts where the comma character is used, parentheses are required to avoid ambiguity.
The following table gives some usage example of comma expressions.
An if statement in which, variable $x is incremented, variable $y is assigned the value 100 and variable x is tested against variable $y and finally function foo()gets called. The first two expressions within this comma expression are evaluated before the call to foo(). Regardless of the results of the first three expressions, the fourth is evaluated (call to function foo()) and its result determines whether the if statement is processed.
Introducing the eq and ne operators.
The eq and ne operators are borrowed from the Perl world. They are used for strict string comparison. The reason why they have been implemented by the PH7 engine and introduced as an extension to the PHP programming language is due to the confusion introduced by the standard PHP comparison operators ('==' or '===') especially if you compare strings with numbers.
var_dump( 0xFF == '255' ); // bool(true) ???
That is, if one of the operand looks like a number (either integer or float) then PHP will internally convert the two operands to numbers and then a numeric comparison is performed.
Bummer, if you ask me, this is broken, badly broken. I mean, the programmer cannot dictate it's comparison rule, it's the underlying engine who decides in it's place and perform the internal conversion. In most cases, PHP developers wants simple string comparison and they are stuck to use the ugly and inefficient strcmp() function or it's variants instead. This is the big reason why we have introduced these two operators.
The eq operator is used to compare two strings byte per byte. If you came from the C/C++ world, think of this operator as a barebone implementation of the memcmp() C standard library function. Keep in mind that if you are comparing two ASCII strings then the capital letters and their lowercase letters are completely different and so this example will output false.
The eq operator return a Boolean true if and only if the two strings are identical while the ne operator return a Boolean true if and only if the two strings are different. Otherwise a Boolean false is returned (equal strings).
Note that the comparison is performed only if the two strings are of the same length. Otherwise the eq and ne operators return a Boolean false without performing any comparison and avoid us wasting CPU time for nothing.
Keep in mind that we talk about low level byte per byte comparison and nothing else. Also remember that zero length strings are always equal.
//Operand one is a prefix of the second.
var_dump('TES' eq 'TEST'); //bool(FALSE) What do you expect!
Here, the test array hold three anonymous functions. All of them expects two arguments and all perform an arithmetic operation. The first anonymous function perform an addition and it's associated key in the array is add, the second perform a multiplication and its key is mul while the third perform a division and it's key is div. If we call one if them either on the fly or via variable assignment, you will get the result of the desired arithmetic operation (addition, multiplication or division).
We have outputted the class attribute value without any kind of assignment. The garbage collector subsystem will automatically release this instance since it will not be used anywhere.
Constructors and Destructors are candidate for overloading exactly like any other class methods.
If no constructor is available, attributes will keep their initialization values (any complex expressions) and will not be nullified.
Introducing magic methods __toInt(), __toBool(), __toFloat() which performs the same job as the __toString() magic method but for their types respectively Integer, Boolean and Float.
A class may not implement all the methods declared inside an interface.
PH7 does not handle all the magic methods such __wakeup(), __sleep(), __set_state(), __serialize() and __unserialize().
Object serialization is avoided. If you want to serialize an object, use the built-in json_encode() function.
Under the standard PHP engine, The size of an integer is platform-dependent which mean that when running on 32-bit systems, the size of an integer is stored in 4 bytes and 8 bytes on 64-bit systems. This lead to non portable code especially if you are working with large integers. PH7 have standardized this and integers now are stored in 8 bytes (64 bits) regardless of the host environment. Because there is no cross-platform way to specify 64-bit integer types PH7 includes typedefs for 64-bit signed integers. The ph7_int64 type can store integer values between -9223372036854775808 and +9223372036854775807 inclusive.
While the standard PHP engine would allow simple scalar values for function arguments default values, PH7 goes further and let you use any complex expressions including function calls, math or string expressions and so on as a default values for your function arguments.
The $name parameter takes as it's default value a complex expression which is the concatenation of the string 'PH7' and a random string of length 4 bytes generated by the built-in rand_str() function. The default value for the $age parameter is a constant math expression. If you run this example using the standard PHP engine, you will get a fatal error while under PH7 this example would run perfectly.
PH7 goes further and let you even use anonymous functions as a default value for your function arguments.
That is, the $callback parameter takes as it's default value an anonymous function that echo "hello world" . This anonymous function will be invoked when the user calls the test() function without arguments.
PH7 has native support for UTF-8. That is, you are not restricted to use plain-English to name your variables or functions. Any UTF-8 encoded natural language (i.e: Japanese, Chinese, Arabic, etc.) can do the job.
As you can see, there is no need for ICU or any other internationalization packages, all you need to do is write you PHP variables and function names in your natural language.
Again, PH7 goes further and let you use any complex expressions including function calls as an initialization values for your static variables and constants.
Here, the static variable $salt takes as its initialization value a random number between 0 and 1023 inclusive generated by the built-in rand() function while the constant MY_CONST takes at its initialization value a random string generated by the built-in rand_str() function. Also note that the 'const' construct can be used outside a class definition.
Anonymous function arguments can take a default value exactly like standard function arguments. The default value associated with the function argument can be any complex expression including function calls.
Now the operators precedence table have been improved dramatically, anonymous functions can be invoked on the fly without any kind of assignment, simply delimit the anonymous function declaration in parenthesis and use the call operator '()' to perform the invocation.
Anonymous function can embed static variables exactly the same way a standard PHP function does.
Under PH7, the concept of run-time error is ignored and PH7 will try everything possible to let your program continue it's execution normally. A run-time error is an error that occurs while executing your program. Same example of run-time error includes a call to an undefined function or a instantiation of an undefined class. In both scenario PH7 will throw a simple warning and assume a null return value.
Suppose you have called an undefined function (PH7 does not implements all PHP functions such as crypt() ereg_replace() and so on). Rather than aborting program execution and following the standard PHP engine behavior, PH7 will set a null return value instead so that your program can continue its execution normally giving the illusion that the undefined function have been called and returned null.
Here, $a have been assigned the null value since the undefined_function does not exists and thus cannot be called. The program continue its execution normally, dumping $a and outputting a message.
Here, we have tried to instantiate an undefined class. PH7 will throw a warning and return NULL instead so that the program can continue its execution normally.
The same rule apply, if you access an undefined array entry (I.e: $arr['undefined_index']) or an undefined class attribute (I.e: $class->undefined_attr). In each cases, PH7 throw a warning and return NULL instead.
The only fatal run-time error under PH7 that really could stop program execution is an out of memory. Fortunately PH7 have a tiny memory footprint (depending of course on the executed program an it's proper memory usage) and running out of memory on modern hardware even on modern embedded devices is an extremely rare scenario and we can say that your program is pretty safe.
Remember not to confuse run-time errors with compile-time error that occurs while compiling your PHP script using one of the compile interfaces. These compile-time errors are generally due to malformed PHP programs (that you must fix manually after viewing the compile error log) while run-time errors occurs during execution of the compiled program.
Standard PHP functions (not anonymous) can omit the curly braces if their body comprise only a single statement (This behavior is identical to the if statement).
PH7 pass and return arrays by reference exactly the same way PHP(5) pass objects (class instances) by reference. That is, when assigning an already created instance of an array to a new variable, the new variable will access the same instance as the array that was assigned. This behavior is the same when passing instances to a function. A copy of an already created array can be made by calling the built-in array_copy($src); function. an array variable doesn't contain the array itself as value anymore. It only contains an array identifier which allows array accessors to find the actual array. When an array is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier which points to the same array. If you came from C/C++ thinks of this as two variables pointing to the same instance of a malloc'ed structure.
Here, variable $a and $b point to the same array instance, so if you remove one entry using variable $a, the change is immediately seen in variable $b if you dump it. In other words, $a and $b point to the same instance.
Here, even without including the reference operator & to the function argument, the array is passed by reference and the change is immediately seen (the pop operation) in variable $a.
After calling array_copy(), $a and $b points to completely different instances. This is the solution if you want to pass and/or assign arrays by copy, but remember for performance reason it's recommended that you let PH7 pass array by reference and thus you avoid a heavy copy operation.
The reason why performance rocks under PH7 relies in the fact that is a hand-coded project. That is, PH7 does not use any external tools to generate it's lexical analyzer such as lex or flex nor does not use LALR(1) parsers such as lemon, Bison or YACC to generate it's parser, everything is hand-coded by the Symisc Development Team. By acting so, the lexer and the parser are now thread-safe, full re-entrant, generate better error message, takes less space (100 KB together) in the source tree and are easy to maintain.
PH7 is a compact library. With all features enabled, the library size can be less than 600KiB, depending on compiler optimization settings. (Some compiler optimizations such as aggressive function inlining and loop unrolling can cause the object code to be much larger.) If optional features are omitted, the size of the PH7 library can be reduced below 220KiB. PH7 can also be made to run in very little heap (2MB), making PH7 a popular PHP engine choice on memory constrained gadgets such as cellphones ,tablets, numeric devices and so on.
PH7 is written in ANSI C, compile and run unmodified in any platform including restricted embedded device with a C compiler. PH7 have been extensively tested under Windows and UNIX including Linux (and its' flavours), FreeBSD (main development platform with Windows 7), Oracle Solaris and Mac OS X.
If you have successfully compiled and tested PH7 in an exotic environment or a proprietary UNIX such as HP-UX or AIX, please post a report in the PH7 forums or send an email to devel@symisc.net.
PH7 source tree includes a little script written in PHP that is responsible of building an object called the "amalgamation" after successful compilation of the PH7 project. The amalgamation (Concept introduced by the SQLite project) is a single C code file, named "ph7.c", that contains all C code for the core PH7 library and it's extensions. This file contains about 61K lines of code and is over 1.9 megabytes in size.
The amalgamation contains everything you need to integrate PH7 into a larger project. Just copy the amalgamation into your source directory and compile it along with the other C code files in your project. You may also want to make use of the "ph7.h" header file that defines the programming API for PH7. The ph7.h header file is available separately. The ph7.h file is also contained within the amalgamation, in the first couple of thousand lines. So if you have a copy of ph7.c but cannot seem to locate ph7.h, you can always regenerate the ph7.h by copying and pasting from the amalgamation.
In addition to making PH7 easier to incorporate into other projects, the amalgamation also makes it run faster. Many compilers are able to do additional optimizations on code when it is contained with in a single translation unit such as it is in the amalgamation.
The amalgamation and the ph7.h header file are available on the download page as a file named ph7-amalgamation-X_X_X.zip where the X's are replaced by the appropriate version number.
PH7 includes a powerful HTTP request parser with the ability to extract MIME headers, decode GET/POST raw queries and populate the appropriate arrays such as $_GET, $_POST, $_SERVER and so on. This feature is popular for network oriented application such as web servers that embed the PH7 engine and need to pass HTTP informations to their PHP scripts. Refer to the PH7_VM_CONFIG_HTTP_REQUEST configuration option for additional information.
PH7 uses the hashmap data structure internally to represent the array type. A hashmap is is a data structure that uses a hash function to map identifying values, known as keys, to their associated values. The hashmap implementation is written from scratch by the Symisc Development Team and uses two distinct (Inter-changeable) hash functions for it's operation. The first hash function is used to map 64-bit integer keys (Automatic indexes) to their bucket address in the table while the second hash function (DJB based implementation) is used for string keys. The hasmap implementation is found in the hashmap.c source file from the PH7 source tree.
Built-in sorting functions such as sort(), usort(), ksort(), rsort() and so on uses an efficient implementation of the stable merge-sort algorithm. The PH7 merge-sort implementation is based on the one found in the SQLite source tree.
Thanks to the improved operators precedence table for letting us introduce this smart extension.
The standard PHP engine is reputed for his inconsistency while playing with floating point numbers. Under PH7, you can work and compare floating point numbers exactly the same way you work with integers without any unexpected or inconsistent result.
Unlike the standard PHP engine, the ternary operator under PH7 now evaluates left-to-right, associates right-to-left. That is, there is no need for parenthesis for nested ternary operators.
The following PHP expression will output 't' when running using the standard PHP engine and is an unexpected result, but if your run this code using the PH7 engine this will output 'true' as you might expect.
This will output 'FALSE [bad ternary]' when running using the standard PHP engine but outputs 'TRUE [good ternary]' when running using the PH7 engine.
Array cursor is automatically reset.
The array cursor is automatically reset to the beginning at the end of a running foreach() loop. That is, there in no need to manually call the built-in reset() function to reset the array cursor, this operation is done automatically.
Similarly, the var construct which is used to declare public class attributes can be used now to declare variables outside a class definition.
As of this release, you can pass argument by reference as by copy to the built-in call_user_func() function, this smart extension allow the called function to modify the contents of the passed variable like a standard PHP function would do.
namespace implementation was dropped from the build. In fact Symisc (the developer of the PH7 engine) have an early prototype of PH7 with namespace support but we found that this construct have no particular sense in restricted environments since PH7 tend to be embedded in host application that generally deal with small to medium size PHP scripts. But if you want this features, let us know!.
The declare constrcut is simply a no-op operation in the current release of the PH7 engine. In fact the Symisc development team did not understand the purpose of this constrcut. If you really know its purpose please post a message in the PH7 public forums.
Traits are not implemented as of this version (2.1.4) and will be available in the next release of the PH7 engine.
The serialize() and unserialize() functions are a simple wrapper around the built-in function json_encode() and json_decode().
The list() construct support only one nesting level (No nested list()).
PH7 lack more or less some important functions such preg_replace(), preg_match(), mysql_connect() and so on. Refer to the following page for the list of built-in PHP functions. The Symisc Development team is working very hard to overcome this limit and you are invited to contribute and join the PH7 development process (Read this for more information).
Built-in array_splice() does not support insertions.
And finally, we recommended the PH7 users to report any unexpected result or inconsistencies using the online bug tracker.
Any feedback (We want to hear from you), please send an email to devel@symisc.net.