Features Releases

PHP 7 Support in PhpStorm 2016.2

PHP 7 is gaining traction, and we’ve been working hard to try and make PhpStorm 2016.2 the best tool around for working with PHP 7.

This is something I’m really excited about.

One of my biggest problems with PhpStorm’s PHP 7 support was the lack of inspections around scalar type hints and return type hints. HHVM/Hack ships with a static analysis tool that makes working with its type hint system a dream, while PHP has been lacking a viable static analysis tool. PhpStorm’s recent improvements in type support and PHP 7 bring it as close to being a static analysis tool as I’ve seen.

Type Hints???

Note: While I am working with functions for this blog post in the interest of simplicity, this is obviously most useful when working with objects, methods, and unit tests.

If you aren’t sure what I’m talking about when I mention type hinting, it’s nicely simple. Take the following code:

function isIP4Address($ipAddress)
{
	if (!is_string($ipAddress))
	{
		return false;
	}

	$regex = '/^(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})){3}$/';
	$result = preg_match($regex, $ipAddress) === 1;
	return $result;
}

assert(isIP4Address('100.100.100.100'), 'Valid IP Address passes');
assert(isIP4Address('256.0.0.1') === false, 'Invalid IP Address fails');
assert(isIP4Address(100) === false, 'Int passed in short circuits regex');

This is a basic function that checks if a given string is a valid IPv4 IP address, complete with basic tests. We can see that the input is a string, and we should always return a boolean. Before PHP 7, we would have always needed this type check at the start of the function (lines 5-8) to ensure that future code was operating on the type we expected it to. We’d also need to write extra tests for our code checking that what’s returned is a boolean every time and that the type check code gets hit. This is wasted code.

Other languages have had type hinting in functions included in them forever. PHP calls these “type declarations” but the universal name “type hints” has been around a while. Type hinting means that you can define in the function definition exactly what type the parameter should be, and let PHP take care of what happens if you try to pass in something that is not of that type. We’ve had this in PHP for a while for things like objects, callables, and array, but never for the more fundamental scalar types, such as int, string, boolean. PHP 7 gives us this power in the usual way:

function isIP4Address(string $ipAddress)
{
	$regex = '/^(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})){3}$/';
	$result = preg_match($regex, $ipAddress) === 1;
	return $result;
}

assert(isIP4Address('100.100.100.100'), 'Valid IP Address passes');
assert(isIP4Address('256.0.0.1') === false, 'Invalid IP Address fails');
assert(isIP4Address(100) === false, 'Int passed in fails');

Notice how we can remove the whole type checking thing now, as anything that’s passed into the function is cast to a string by the type hint. This will now pass as the integer `100` will be cast to a string `’100’` and will fail the regex validation. This is step 1 of PHP 7’s type hinting, it’s called coercive mode and is quite scary. Do we really want PHP casting things to types when we make a mistake and pass the wrong type in? Some people would argue that PHP has a rich history of silently casting one type to another, but for me, if I make a mistake I’d rather know about it than let PHP do some unknown type casting; usually the wrong type is indicative of a bug somewhere in the application.

Thankfully, PHP 7 has got you covered. You can disable scary-silent-type-casting and force PHP to throw a catchable `TypeError` when you pass in a value that’s the wrong type. Now we can finally code defensively without a ton of boilerplate type checking and unit tests. You can enable strict types by using a define statement at the very top of the file that is calling the function. This is important. Only the calling end defines whether strict typing is used or not. This is a weird concept, but was done to allow reusable libraries that work in both coercive and strict mode:

declare (strict_types=1);

function isIP4Address(string $ipAddress)
{
	$regex = '/^(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})){3}$/';
	$result = preg_match($regex, $ipAddress) === 1;
	return $result;
}

assert(isIP4Address(100), 'Valid IP Address passes');

PHP Fatal error: Uncaught TypeError: Argument 1 passed to isIP4Address() must be of the type string, integer given, called in Validators.php on line 11 and defined in Validators.php:4

The great news is that this also works for whatever you return, too. PHP 7 introduces a brand new return type hint syntax that lets us effectively say “this function should ALWAYS return a boolean.” Again, this removes a lot of boilerplate code and allows us to catch logic bugs where a function may have been mistakenly returning, say, an integer, but we never noticed as it always passed our boolean check. Adding the return type hint is done by using a colon plus the type after the last `)` in our function definition:

declare (strict_types=1);

function isIP4Address(string $ipAddress) : bool
{
	$regex = '/^(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[0-1]?\d{1,2})){3}$/';
	$result = preg_match($regex, $ipAddress) === 1;
	return $result;
}

Now, if we try and return anything other that a boolean, PHP will throw a catchable `TypeError` and tell us exactly what’s gone wrong. It’s amazing: just adding these type hints to legacy projects has allowed me to find many, many bugs that were previously undetected.

What About PhpStorm?

Those who are here because they know all about type hints will be thinking “great, I know this, what about PhpStorm???”. To you people, I apologize, but this is important enough to be covered again before getting around to PHP 7 support in PhpStorm.

PHP 7 support was initially introduced in PhpStorm 9.5, including a PHP 7 level in the interpreter configuration, and modifications to the lexer to allow the PHP 7 specific keywords and syntax. But PhpStorm 2016.2 takes this a step further by adding inspections around type hinting in PHP 7 meaning you’ll catch your type errors right in the IDE before you even run the code.

This is huge.

Let’s consider our above example in PhpStorm 2016.2 with PHP 7 support enabled. (If you aren’t familiar with how this works, there’s a video.) We’ll move to screenshots so that you can see the PhpStorm inspections at work.

php7-inspection-2

Notice how the last assertion has fired a warning because PhpStorm knows you’re passing in an integer and PHP 7 is in strict mode and expects a string. This also works well for the return type hints.

php7-return-type

Here we’ve forgotten that `preg_match` returns an integer, not a boolean, but PhpStorm has warned us of that right in the IDE without needing to run our code. This kind of code inspection is game-changing when you’re using PHP 7 and PhpStorm 2016.2.

PhpStorm 2016.2 has also increased its general understanding of PHP 7. PHP 7 changed the way you address variables making the parsing more uniform, so we’ve improved our uniform variable syntax support.

php7-syntax

Code like this would have raised errors in previous versions of PhpStorm but is now understood. We still have a little way to go to make PhpStorm understand the nuances of PHP’s uniform syntax, but we’re getting there (that’s the problem with writing your own lexer for PHP in your IDE).

We’ve also improved our code generation support to take note of type hints. Previous versions of PhpStorm would not honor type hints defined in, say, the constructor when generating getters and setters; this has now been implemented.

PHP 7.1

While here at JetBrains we’ve been busy improving PHP 7 support in PhpStorm, the amazing people at PHP Internals haven’t been resting on their laurels. PHP 7.1 is close to release, and PhpStorm 2016.2 has already got some initial support.

We already support parsing of:

  • `void` return type, enabling you to tell PHP that this function shouldn’t return anything
  • class constant visibility, allowing you to mark a class constant as private, public or protected
  • the new `list` syntax that allows you to take supply key names in the function call
  • catching multiple exceptions using the new `|` syntax

PHP 7.1 hasn’t been released (at the time of writing), but we’ll cover these features in more detail when 7.1 is officially available.

You might be able to tell that I’m very excited about this new PHP 7 support, particularly the strict mode inspections for type hints. Give them a go yourself and let me know what you think.

– Gary and the PhpStorm Team

image description