
int , string and float ."123" to a function that wants an int , and trust PHP, which will do everything “right”. So why then do we need scalar types? int , float , string and bool . They will join already existing array , callable , classes, and interfaces. Let's complete our previous example with the new feature: interface AddressInterface { public function getStreet() : string; public function getCity() : string; public function getState() : string; public function getZip() : string; } class EmptyAddress implements AddressInterface { public function getStreet() : string { return ''; } public function getCity() : string { return ''; } public function getState() : string { return ''; } public function getZip() : string { return ''; } } class Address implements AddressInterface { protected $street; protected $city; protected $state; protected $zip; public function __construct(string $street, string $city, string $state, string $zip) { $this->street = $street; $this->city = $city; $this->state = $state; $this->zip = $zip; } public function getStreet() : string { return $this->street; } public function getCity() : string { return $this->city; } public function getState() : string { return $this->state; } public function getZip() : string { return $this->zip; } } class Employee { protected $id; protected $address; public function __construct(int $id, AddressInterface $address) { $this->id = $id; $this->address = $address; } public function getId() : int { return $this->id; } public function getAddress() : AddressInterface { return $this->address; } } class EmployeeRepository { private $data = []; public function __construct() { $this->data[123] = new Employee(123, new Address('123 Main St.', 'Chicago', 'IL', '60614')); $this->data[456] = new Employee(456, new Address('45 Hull St', 'Boston', 'MA', '02113')); } public function findById(int $id) : Employee { if (!isset($this->data[$id])) { throw new InvalidArgumentException('No such Employee: ' . $id); } return $this->data[$id]; } } $r = new EmployeeRepository(); try { print $r->findById(123)->getAddress()->getStreet() . PHP_EOL; print $r->findById(789)->getAddress()->getStreet() . PHP_EOL; } catch (InvalidArgumentException $e) { print $e->getMessage() . PHP_EOL; } Address class are just strings. Previously, one could only assume that they were lines, not Street objects (consisting of the street number, its name and apartment number) or the city ID from the database. Of course, both of these things are perfectly reasonable in certain circumstances, but they are not considered in this article.findById() $id is a value of type int . Even if it originally came from user input, it will become integer. This means that it cannot contain, for example, SQL injections. Reliance on type checking when working with user input is not the only or even the best defense against attack, but another layer of protection.Address consists of strings and the employee ID is integer, right? It's true; However, not everyone adheres to fanaticism in the matter of documenting their code, or simply forget to update it. With the "active" information from the language itself, you are guaranteed to know that there is no desynchronization, because PHP will throw an exception if it is not. function loadUser(int $id) : User { return new User($id); } function findPostsForUser(int $uid) : array { // Obviously something more robust. return [new Post(), new Post()]; } $u = loadUser(123); $posts = findPostsForUser($u); loadUser() always returns an object of type User , and findPostsForUser() always returns an integer, there is no way to make this code true. This can be said only by looking at the functions and how to use them. And this, in turn, means that the IDE also knows in advance and can warn us about an error before launch. And since the IDE can track a lot more parts than we do, it can also warn of more errors than you can see for yourself ... without executing the code!int to a function that expects a string will work fine, and passing bool at the expected int you will get an integer number 0 or 1, because this is natural behavior expected from the language. For an object passed to a function that is expecting a string , __toString() will be called, the same will happen with the returned values.int or float . Traditionally, when a function expects to get int / float values, and a string is passed, PHP will simply truncate the string to the first non-numeric character, resulting in possible data loss. In the case of scalar types, the parameter will work fine if the string is indeed numeric, but if the value is truncated, this will result in a call to E_NOTICE . Everything will work, but at the moment this situation is considered as a minor error in the condition.strict_types mode. Its use is somewhat subtle and not obvious, but with proper understanding the developer gets an incredibly powerful tool. declare(strict_types=1); strict_types works, let's split our code into several separate files and strict_types it a bit: // EmployeeRespository.php class EmployeeRepository { private $data = []; public function __construct() { $this->data[123] = new Employee(123, new Address('123 Main St.', 'Chicago', 'IL', '60614')); $this->data[456] = new Employee(456, new Address('45 Hull St', 'Boston', 'MA', 02113)); } public function findById(int $id) : Employee { if (!isset($this->data[$id])) { throw new InvalidArgumentException('No such Employee: ' . $id); } return $this->data[$id]; } } // index.php $r = new EmployeeRepository(); try { $employee_id = get_from_request_query('employee_id'); print $r->findById($employee_id)->getAddress()->getStreet() . PHP_EOL; } catch (InvalidArgumentException $e) { print $e->getMessage() . PHP_EOL; } $id inside findById() is an int , not a string. No matter what $employee_id will be, $id will always take an int type, even if E_NOTICE is thrown. If we add the strict_type declaration to EmployeeRepository.php , nothing will happen. We will also all have an int inside findById() .index.php , and then use the findById() call, if $employee_id is a string, or a float or something other than int , this will result in a TypeError .EntityRespository constructor where we create our fake data. The second entry sends the ZIP code as an int, not a string. Most of the time it will not matter. However, in the US, postal codes in the northeast begin with the leading zero. If your int starts with a leading zero, PHP will interpret this as an octal number, that is, a number with a base of 8.EntityRespository.php to strict mode and immediately catch the type mismatch. If we run the code, we get quite specific errors that will tell us the exact lines where to find them. And good utilities (or IDE) can catch them even before launch!int to float . This is safe (except for extremely large or small values ​​when there are consequences of overflow) and logical, since this int by definition also a floating-point value.int , then you know (even if you don’t know the IDE) that a numeric string will always be returned from it and, therefore, you can be sure that there is no data loss when passing them to a function that expects an int .EmployeeRespository , Employee , Address , AddressInterface and EmptyAddress should have strict mode enabled. index.php , on the get_from_request_query() , interacts with incoming requests (via a call to get_from_request_query() ), and thus it will probably be easier for PHP to deal with types rather than manually manually. As soon as the undefined values ​​from the request are passed to the typed function, then it is possible to switch to the strictly typed work.Source: https://habr.com/ru/post/267799/
All Articles