When PHP 7 was released at the end of 2015, it had a huge impact on the community. It came out with one of the most anticipated features we were waiting for: scalar type hints. As if this wasn't big enough, return types were also added to this new major version and the possibility to make PHP handle these types even in a strict mode, similar to other typed languages. Even for those who don't really care about features, PHP 7 brought a better performance for their applications and was hyped just like the new types. Some of the PHP critics recognized this as a step to become more mature as a language, but this wasn't the end of new types, because in PHP 7.1 the PHP team released the new iterable and void types and add them to the existing scalar ones. How can this be topped? By releasing the object type in PHP 7.2 and a new feature called Parameter Type Widening in which you can omit the type hints, even when you extend an interface or class, that uses types in its arguments.
Wait what? We introduced more and more new types and now we can remove the type hints and make our code inconsistent to an interface's type hints? This is a step back from what we achieved since PHP 7.0. What were they thinking?
<?php
interface FooInterface
{
public function bar($str, $r);
}
class FooClass implements FooInterface
{
public function bar($str)
{
return $str;
}
}
Fatal error: Declaration of FooClass::bar($str) must be compatible with FooInterface::bar($str, $r)
The same happened when you didn't use the same type hints specified by the interface or if you didn't used the defined type at all. The same fatal error occurred on overriding a method on class and abstract class extension the wrong way. It had to be exactly the same type hint for every argument, so this example, even though it has the same amount of arguments, would also lead to a fatal error:<?php
interface FooInterface
{
public function bar(string $str);
}
class FooClass implements FooInterface
{
public function bar($str)
{
return $str;
}
}
There are some exceptions in PHP arguments, but I don't want to go deeper into those topics as I want to show that in PHP 7.2, the same typed example above will be a valid implementation. Yes, removing string
in your implementation of FooInterface
is totally okay now and you are free to inject any possible type into the FooClass::bar()
method. While it's not really a feature for custom interfaces, abstract classes or regular classes, it might have a bigger impact when it comes to third party ones you're trying to implement. Those can get type hints in newer releases you'll usually have to add in your projects.__toString()
magic method too. This may break your code in other places where a string was expected or where the string type hint was set to a strict mode with declare(strict_types=1);
. You have to handle the arguments like in the pre-PHP7 days again and in a worst case scenario, PHP 7.2 opened a door for a lot of possible bugs. We're back to use type casts.composer update
in the console. Some methods changed, others were removed or different arguments were expected. These backward compatibility breaks were a hindrance for projects, especially customer projects for companies, who had to convince customers why they need to pay those expenses just to have their product work the same way as before, plus a few bugfixes and security patches, which were also hard to explain. So some libraries and frameworks introduced Semantic Versioning or a similar versioning, that took care of BC breaks for minor and/or patch versions. Some projects decided to prevent bigger BC breaks even in major versions to make a migration easier for the users of the software. It's hard to tell about backward compatibility without mentioning Symfony, which components became an essential part in the PHP projects environment, because it handles compatibility between versions so well. This also might be a reason for its success becoming a first choice for other projects like Spryker, Sylius, Laravel and others. If you don't need to adapt your libraries/frameworks because of some changes in your dependencies, the chances of introducing bugs in your next release are much lower and you have less extra work to do. You are able to concentrate your work on the business instead on old implementations.<?php
interface FooInterface
{
public function bar(string $str) : string;
}
class FooClass implements FooInterface
{
public function bar(string $str)
{
return $str;
}
}
Extending a class or implementing an interface, that has a return type specified, forces you to keep this return type in your implementation or you'll still get the fatal error for being incompatible with the parent element.Fatal error: Declaration of FooClass::bar(string $str) must be compatible with FooInterface::bar(string $str): string
If the dependency of choice introduced return types, you still have to adapt your code to match with the methods. At this point, you can also use type hints in your implementation if the same types are expected as parameters. The return type isn't a part of the Parameter Type Widening feature.ArrayAccess
is expected, but otherwise there will be only edge cases for you. In case you maintain your own open source projects and you're looking for your next major version update, you should consider PHP 7.2 instead of 7.1 as a minimum requirement.