Prepare for PHP 7 error messages
In the article “Prepare for PHP 7 error messages (part 1)” from last week, we covered the evolution of error messages in the PHP binary. In order to prepare for prepare for PHP 7 error messages, we are now going to review the new messages that are appearing in PHP 7. We’ll also see some of those that are disappearing : it’s good to know we won’t meet them again.
« Return value of %s%s%s() must %s%s, %s%s returned »
The first extra error messages is linked to the new feature called Return Type Hint. This works like Type Hint, but is applied to returned values. At definition time, it is possible to specify which type of value will be returned. This is achieved by adding a colon and a type hint after the function signature :
function myFunction (\MyClass $anObject): \MyOtherClasss { return new \MyOtherClass($anObject->toOtherConfig()); }
The error message will be thrown anytime the return value doesn’t match the specified return type hint. Both Type Hinting (in- and outgoing) aims at enforcing that correct values are passed in and out in a method. Libraries will probably be keen on adopting them, leading to a transition phase where lots of this error message have to be cleaned.
« Cannot declare a return type of %s outside of a class scope »
Linked to the previous, it is possible to use the special keywords ‘self’ and ‘parent’ when defining a Type Hint (‘static’ is not supported). This error message will be emitted if a function makes use of them : only methods, inside a class, can use them. The error will be raised at execution time : linting can’t spot that.
Class MyClass extend MyParentClass { public function myFunction (self $anObject): parent { return new parent($anObject->toOtherConfig()); } }
« Constructor %s::%s() cannot declare a return type »
« Destructor %s::%s() cannot declare a return type »
« %s::%s() cannot declare a return type »
There are some methods that doesn’t require a Return Type hint : Constructor, Desctructor and __clone. All of them are called at object initialization, and no returning value is actually expected : the most important is the process to build (or unbuild) the new object. As such, Return Type Hint are useless in those methods.
PHP 4 constructors are also supported, and it will be fun to find code that mix emerging Return Type hint feature with legacy PHP 4 constructor. Mail me when you find one.
Note that return inside such method are also unused, but do not generate any error message : one can use a return in a __construct, to interrupt the method early. The returned value, often a null, will just be ignored.
« Constant expression contains invalid operations »
This message will help everyone who is using the recent Constant Scalar expression. This new features allows a certain number of operations to be performed when creating a new constant. For example,
const SQUARE_OF_TWO = 2 * 2;
So, most operators are allowed, including ternary operator, logical, comparison, array pair and math operations but not assignations.Variables are all forbidden but normal, magic and class constants are valid. Functioncall are forbidden except array(). More about this : https://wiki.php.net/rfc/scalar_type_hints_v5
This message is one of the direct advantages of the new AST in PHP : where PHP 5.6 is emitting an fatal error because it has reached an error, the AST allows PHP to check the situations and validate it graciously.
« Constants cannot be recursive arrays »
« Constants may only evaluate to scalar values or arrays »
Those errors are also better messages linked to the creation of constants based on structure that can’t be constants, such as objects, resources or recursive arrays. This last one is an array which contains a reference to itself. PHP 7 accepts arrays with references, though they will be dropped by the constant, and only values will be kept.
$array = array('a' = &$array) ;
« Cannot use temporary expression in write context »
The AST is also at the origin of this new message : with the universal variable syntax, some situations are now possible, like nesting :: ($foo::$bar::$baz) or using immediately some object without storing it (new someClass)().
Some newly supported situations will then generate errors, like ‘foo'[0] = ‘b’. This instruction changes the first character for the string ‘foo’, but since the string is not stored in a variable, the result is lost anyway. The result is not necessarily lost, since such expression could be nested in a parenthesis and assigned like $x = (‘foo'[0] = ‘b’) ;. PHP 5.6 doesn’t accept anything like that (fails with a fatal error), so it old code may not generate such errors.
function a(&$b) { /**/ } a([0, 1][0]); // Trying to pass by reference a non-variable
The list of situations that will lead to this error are not explicit (yet), though there are 34 occurrences of this message in the PHP source. Playing with too many literals will probably lead to it. Read more about this in https://wiki.php.net/rfc/uniform_variable_syntax.
To be continued
That will be all for this week. We’ll have another set of PHP 7 error messages next week.