There are tactical reasons to adopt strong typehint: easy validation, less code, fashionable. Besides, the first typehints blend in effortlessly with the current application: it is as if typehint was already there. Later, it appears that scalar types paved the way to more substantial code refactoring. Classes emerge from the initial scalar types, code congregate around important values, types gets more complex. Finally, systemic typehint arrives. Type hints become systemic when they help tame the class dependency hell, and help us plan for the new code. During the session, we'll cover the various stages of using typehints, with their advantages, and when not to overuse them.
7. Typehints as debug tactics
• Set up a typehint
• Wait for the 'Fatal error'
• Fix the calling code, now that you know where it is
• Remove it in production
• Leave it in development
<?php
foo("a");
foo(1);
foo(new x);
foo(array());
function foo(string $s) {
echo $s;
}
class x {
function __toString()
{ return "a"; }
}
?>
8. Your code is already typed
• Typehints are already used in the code
• PHP, and strict_types
• is_string(), is_array(), instanceof…
• Coding conventions
9. Coding conventions
• The type is in the name of the argument
• $string, $array
• $user = new User();
<?php
function shorten($string) {
return substr($string, 0, 5);
}
function getPattern(array $ints) {
}
function (StringClass $string) {
}
?>
10. Moving type checks to the signature
• is_a(), is_subclass_of(), instanceof, === null
• is_string(), is_array()
• (int), (string)…
<?php
function bar($user) {
if ($user === null) {
throw new TypeError();
}
if (!$user instanceof User)) {
throw new TypeError();
}
return $user->validate();
}
?>
11. Moving type checks to the signature
• is_a(), is_subclass_of(), instanceof, === null
• is_string(), is_array()
• (int), (string)…
<?php
function bar(Usager $user) {
return $user->validate();
}
?>
12. Moving type checks to the signature
• is_a(), is_subclass_of(), instanceof, === null
• is_string(), is_array()
• (int), (string)…
<?php
function bar($price) {
return floor((float) $price) * 1.21;
}
?>
13. Follow the lead
<?php
function bar($argument) {
return substr($argument, 0, 10);
}
?>
14. Follow the lead
<?php
function bar(string $argument) {
return substr($argument, 0, 10);
}
?>
15. Follow the lead
<?php
function bar($argument) : string {
return substr($argument, 0, 10);
}
?>
16. Follow the lead
<?php
function bar(string $argument) : string {
return substr($argument, 0, 10) ?: '';
}
?>
Substr() returns string OR false
N
O
T
I
N
P
H
P
8
.
0
R
C
1
17.
18. Backward with the return types
<?php
function bar($argument) {
return foo($argument);
}
function foo($x) {
return barbar($x);
}
function barbar($y) : string {
//...
}
?>
19. Forward with caution
<?php
function bar3($argument) {
return foo(null);
}
?>
<?php
function bar($argument) {
return foo($argument);
}
function foo($x) {
return barbar($x);
}
function barbar(string $y) {
//...
}
?>
<?php
function bar2($argument) {
return foo(22);
}
?>
20. Wait for PHP 8.0
<?php
function foo($x) {
if (rand(1, 3)) {
return 1;
} else {
return barbar($x);
}
}
function barbar($y) : string {
//...
}
?>
21. Adoption
• Use it for debugging purposes
• Follow the types that are already there
• PHP
• Your framework
• Propagate your own types
• Return types are easy, argument types need caution
33. Evolution
• Scalar types cannot evolve
• Classes can evolve
• Class types don't need to be complex
• A price is not an int/
fl
oat
• A path may be a string while a string is not a path
37. Evolution
f1 ($a) : s
f2 ($a) : s
f3 ($a) : s
f5 ($a) : s
f4 ($a) : s
($a) : s
f
38. • Type hints reduces the number of possible paths in
the code
• Scalar are versatile
• They have a lot of connectivity
• Upgrading scalar types to class types reduce
complexity even more
Scalar types to classes
Evolution
f1 ($a) : s
f2 ($a) : s
f3 ($a) : s
f5 ($a) : s
f4 ($a) : s
($a) : s
f
40. Organisation
• Typehinting has impact on the signature of a method
• This impact spreads all over the application
• It introduces a hierarchy of the classes
41. Classes order
Organisation
• A method returns one type
• A method has dependencies on
its arguments's type
• That object must be created
fi
rst
• A method has dependencies on
its constructor too
C1
M1()
__construct()
42. Classes order
Organisation
• A method returns one type
• A method has dependencies on
its arguments's type
• That object must be created
fi
rst
• A method has dependencies on
its constructor too
C1
M1()
45. Classes order
Organisation
• Topological sorting of the classes
• One class has priority over another class when it is used as typehint
• There is no garantee for a single top class :
• Probably several top classes
• With scalar as argument, because they are so easy to tweak
C5
C1
C3
C2
48. Subsidiary questions
Organisation
• Why are there multiple methods with the same typehints?
• Potential double work
• How testable are the classes?
• How many objects do you need to create an instance?
• Can a method reach a speci
fi
c type of class?
• The needed types may not be available
49. Adding typehints to your code base
• Adoption
• Follow the current code
• Evolution
• Refactor the code, use classes as typehints
• Organisation
• Use typehints to reduce complexity and plan evolutions
50. Subsidiary questions
Static analysis helps you type
• Exakat
• Typehint suggestion report, type inconsistencies and typehint report
• Phpdoctor
• Reports missing types
• Psalm
• Infers typehints on the
fl
y
51. Subsidiary questions
Static analysis helps you type
• Exakat
• Typehint suggestion report, type inconsistencies and typehint report
• Phpdoctor
• Reports missing types
• Psalm
• Infers typehints on the
fl
y
53. Classes order
Organisation
M1
M2
C1
M1()
__construct() M2()
• M1 needs a property
• M2 provides that property
• M2 > M1()
• True in PHP 7.4+
• x::$a must not be accessed before
initialization
• True in PHP 7.0
• Call to a member function bar() on
null