Code example: https://onlinephp.io/c/0cd2c
Explainer (not mine): https://thephp.website/en/issue/php-type-system/
Wow, this is so cool. I should have looked into this years ago -- loose types have been my #1 complaint about PHP / the only reason I ever feel tempted to switch to Python.
This seems pretty fucking killer at reducing the passive rate of bugs introduced per new feature! (I learned about this concept from John Carmack's interview on the Lex Fridman Podcast.)
If you use PHP Type Hinting Declarations, are there any caveats / edge-cases I should be aware of?
I imagine I'll take a performance hit, so that's what I'll test next...
Type hinting isn't required, but it can allow you to catch certain types of mistakes. For example, you might have a function or method which requires an integer. PHP will happily convert "number looking strings" into integers, and this can cause hard to debug behaviour. If you specify in your code that you specifically need an integer, this can prevent those kinds of bugs in the first place. Many programmers consider protecting their code in this way to be a best practice.
As a concrete example of this in action, let's look at an updated version of your index.php file:
index.php
<?php
include 'Song.php';
include 'Test.php';
$song_object = new Song;
$test_object = new Test;
$song_object->title = "Beat it!";
$song_object->lyrics = "It doesn't matter who's wrong or right... just beat it!";
$test_object->title = "Test it!";
$test_object->lyrics = "It doesn't matter who's wrong or right... just test it!";
function sing(Song $song)
{
echo "Singing the song called " . $song->title;
echo "<p>" . $song->lyrics . "</p>";
}
sing($song_object);
sing($test_object);
As well as the new Test.php file I added:
Test.php
<?php
class Test
{
public $title;
public $lyrics;
}
When I run index.php now, I get the following error:
Output:
Singing the song called Beat it!<p>It doesn't matter who's wrong or right...
just beat it!</p>PHP Catchable fatal error: Argument 1 passed to sing() must
be an instance of Song, instance of Test given, called in test/index.php on
line 22 and defined in test/index.php on line 15
Catchable fatal error: Argument 1 passed to sing() must be an instance of
Song, instance of Test given, called in test/index.php on line 22 and defined
in test/index.php on line 15
This is PHP letting me know that I tried to use the wrong type of class when I called the sing() function.
This is useful because, even though the above example worked, the Test class could be different than the Song class. This could lead to hard to debug errors later on. Using hinting in this way gives developers a way to prevent type errors before they cause issues. This is especially useful in a language like PHP which is often eager to auto-convert between types.
Type hinting is a natural process. At first it may seem like extra work but it is very helpful as your project grows in PHP. It allows for better readability and makes error control and strict programming conventions easier to apply.
Initially, you must implement the ‘contract’, where the contract is a php interface which can ‘lock’ constants and the key public methods and their arguments, as such:
interface SongInterface {
//...
}
class Song implements SongInterface
{
public $title;
public $lyrics;
//...
}
Then continue with the actual execution part:
$song = (object) new Song;
$song->title = (string) "Beat it!";
$song->lyrics = (string) "It doesn't matter who's wrong or right... just beat it!";
function sing(SongInterface $song): string
{
$html = (string) "Singing the song called " . $song->title
. "<p>" . $song->lyrics . "</p>";
return htmlentities($html, ENT_QUOTES, 'utf-8');
}
echo sing($song);
Using an interface, you only define the functions and then implement it in a song class. This way you can always inject another song class (with newer functionalities and changes) without breaking your application. Check out oop interfaces: http://php.net/manual/en/language.oop5.interfaces.php
Since php7, you can also define the return type of a function. https://wiki.php.net/rfc/return_types
stdClass is NOT a base class! PHP classes do not automatically inherit from any class. All classes are standalone, unless they explicitly extend another class. PHP differs from many object-oriented languages in this respect.
The best way to enforce this would be to create a degenerate interface called Object. A degenerate interface means it has no defined methods.
interface Object {
// leave blank
}
Then in your base classes, you can implement Object.
class SomeBase implements Object {
// your implementation
}
You can now call your function as you wanted to
function myFunc (Object $obj);
myFunc($someBase);
If you pass any object which inherits from your Object interface, this type hint will pass. If you pass in an array, int, string etc, the type hint will fail.