Type-hint all the things!
03 Juil 0

Type-hint all the things!

  • Time12:35
  • David Négrier
  • 249

If you haven’t seen the video yet, you can have a look at it on Laracast. At some point, the video advocates to remove the type-hints because they are not needed by the program and are « visually polluting » the code.

I really could not disagree more with this statement, but others have reacted before me, like Bruno Skvorc or Marco Pivetta (aka @Ocramius).

Type-hints are good

Type-hints are a good thing. As a CTO at TheCodingMachine, I spend a fair amount of time reading code developed by others. Look at this function definition:

function exportToPdf($html, $file)

Here, at this very moment, my eyes are bleeding.

Ok, the name of the function is exportToPdf. Seems pretty clear. It takes some HTML, and will probably write to some file on the disk. Now… how do I call this code?

Is $file a string representing a path to a file?

exportToPdf('<h1>Foo</h1>', '/app/store/somefile.pdf');

Or maybe it is a resource (a file handler)?

exportToPdf('<h1>Foo</h1>', fopen('/app/store/somefile.pdf', 'w'));

Or maybe a SplFileInfo class?

exportToPdf('<h1>Foo</h1>', new SplFileInfo('/app/store/somefile.pdf'));

 

Without looking at the documentation (assuming there is one) or at the code, I have no way to tell.

On the other hand, if the code was type-hinted, I would know right away.

function exportToPdf(string $html, SplFileInfo $file): void

Now, this is obvious!

 

As a developer consuming this function, I know how to use it. And if I’m using it wrong, I’ll know right away because PHP will crash with a nice error message when the function is called rather than with a cryptic error some time later.

As a developer coding this function, I get autocompletion from my IDE on all the parameters. By the way, if you have no idea what I mean by autocompletion… please do yourself a favor and use a real IDE like PHPStorm, Eclipse PDT or Netbeans. Please! And dump right away Sublime Text, or (God forbid!) Notepad++.

Type-hints have improved

I started coding in PHP with PHP 4, were type-hints where inexistant.

I followed the addition of object type hints in PHP 5, then the addition of « array » typehint.

PHP 7 introduced the ability to typehint scalars (string, int, float…) and finally, PHP 7.1 introduced the « nullable » question mark allowing you to tell a type can be null (« ?string » == string OR null), and also the « iterable » type-hint.

… but there is still a long road to go

There is still a long road to go. PHP 7.2 will add the « object » type-hint (the parameter passed can be any kind of object regardless of its type). The most awaited feature (but also the hardest to implement) is the « generics » (as they are called in Java). Consider this code:

function setProducts(array $products): void

By looking at the signature, it’s impossible to know how to call this function. Is this an array of arrays representing products? An array of objects implementing the « ProductInterface »? Impossible to know.

It would be great to be able to write something like:

function setProducts(array<ProductInterface> $products): void

But so far, this is not supported in PHP and will not be for quite a while. This would require quite a change for the PHP engine, and while changes are being done incrementally towards that goal, you cannot expect that for the next PHP release.

Hopefully, the PHP community has come with workarounds. The most common work-around is to use docblocks to document the type of the array. I believe the « phpDocumentor » project introduced this idea and it was massively used by the community.

/**
 * @param $products ProductInterface[]
 */
function setProducts(array $products): void

 

The square brackets notation means « an array of ». It is supported by the 3 big IDEs (PHPStorm, Eclipse PDT and Netbeans) and will help you have autocompletion in your foreach loops when iterating over an array (so cool!). Obviously, you won’t have language support.

More recently (since PHP 5.6), people have started using variadic functions to make « kind of typed arrays ».

You can use them this way:

function setProducts(ProductInterface ...$products): void

If the language construct looks strange to you, have a look at this great piece of article by Fabian Schmengler.

 

Our best practices

At TheCodingMachine, we keep reading the code from others. We keep trying to improve and that involves being nice to the guy that will read / maintain your code. And of course, type-hinting helps the guy that reads you code (note that this guy might be the future you!)

Regarding type-hinting, our best practice is pretty simple:

  • If you CAN type-hint, then you MUST type-hint
  • If you CANNOT type-hint, then you MUST document it in a docblock
  • If you are type-hinting an array, you MUST document the array type in a docblock

 

A few examples:

<?php
/**
 * Use type hints when you can
 */
function square(float $baz): float;
/**
 * This function takes an array OR an object behaving like an array (so this is not type-hintable in PHP as of 7.1)
 * This function can return anything so we explicitly tell it in the docblock. 
 *
 * @param array|ArrayAccess $arr
 * @return mixed
 */
function pickValue(string $key, $arr);

/**
 * This function takes an array. We specify the type of the array in the docblock.
 * 
 * @param Product[] $products
 */
function setProducts(array $products): void;

 

Reducing visual debt

I don’t like the term « visual debt », but I do fully agree that we need to reduce the information overload in our code.

PHPDoc is probably the first place I would start to tackle this problem.

Look at this piece of code:

/**
 * Sets the product.
 *
 * @param Product $product
 */
public function setProduct(Product $product): void 

 

Does the Docblock brings any value to our code? Guess what… absolutely not! In PHP 4, without any type-hint, it would have been valuable. But in PHP 7, let’s face it, it looses a lot of interest.

I can tell from the method signature that the first argument is an instance of « Product » and honestly, the comment « Sets the product » adds no value to the « setProduct » method name. So simply get rid of all the Docblock! Do not add PHPDoc for the sake of adding PHPDoc.

Instead, only add PHPDoc when you have something to say that matters! This way, you are not polluting your code and since comments are meaningful, there is a bigger chance that developers using your code will actually read them.

Here is a good sample of meaningful comments:

/**
 * @param Product[] $products An array of products **indexed by product ID**
 */
public function setProducts(array $products): void 

If you want to learn more, I strongly recommend reading the blog article « Approaching coding style rationally » by Matthieu Napoli that gives a load of pretty good advices similar to this one.

 

Enforcing type-hints

So you decide that our best practice is good and that you want to type-hint all the things (that are type-hintable).

Yet, you have been developing in PHP 5 for such a long time that you find it hard to get the reflex of adding type-hints (especially return types!). That’s all right. I’m having the exact same problem right now. That’s why I wrote a tool to help me!

At TheCodingMachine, we use continuous integration (most of the time, it’s Gitlab CI, but this does not really matter). One of the tool we use a lot is PHPStan. If you have never heard about it, it is a static analysis tool. It reads your PHP code, analyses it using a set of rules, and will display warnings. The nice thing with PHPStan is that it is Open-source, and thus, extensible. So I decided to write a package containing a set of rules for PHPStan dedicated to type-hint detection!

Say hello to « thecodingmachine/phpstan-strict-rules« !

The package is still relatively new, and quickly evolving, but we are already starting using it and it works great.

It does a number of things like:

  • checking that you did not forgot to add a type-hint
  • if you cannot add a type-hint, it will check that you added a PHPDoc @param or @return annotation to document the expected type
  • it also checks for type mismatch between the PHP type-hint and what’s documented in the docblock

You can view the list of all features in the documentation.

By the way, the package does not only contain rules about type-hinting! It also ships with rules regarding exception handing and more are yet to come. In the future, we will seek to implement in PHPStan most of our best practices. If you are interested in them, we have a dedicated website for those: bestpractices.thecodingmachine.com. Have a look at it, it should be a good read.

 

image description