Почему PHP еще далеко то строгой типизации

Почему PHP еще далеко то строгой типизации

NikolasSumrak

Всем привет.


С выходом php 7.1 мы стараемся активно использовать strict types, да и довольно удобно зачастую указывать типы входных параметров и результата. Довольно местами даже код улучшается, согласитесь, теперь не напишешь вот такой метод, принимающий всё подряд:

class A {
    public function foo($param)
    {
        if ($param instanceof Bar) {
            $param = $param->getId();
        }

        if (is_string($param)) {
            $param = $this->someService->getIdByCode($param);
        }

        // some logic
    }
}


А вот так уже выглядит метод со строгим указанием типов:

class A {
    public function foo(int $param)
    {
        // some logic
    }
}


И вот тут нас подстерегают коварные подводные камни языка, который еще не всегда готов работать со strict_types.


К примеру у нас есть такой класс:

declare(strict_types=1);

class B {
    
    private $storage = [];

    public function setUserRating(string $username, int $rating): void
    {
        $this->storage[$username] = $rating;
    }

    public function getUserRating(string $username): ?int
    {
        return $this->storage[$username] ?? null;
    }

    public function getAllUsernames(): array
    {
        return array_keys($this->storage);
    }
}


На первый взгляд все отлично, все типы указаны, что тут еще может сломаться?


Давайте рассмотрим вариант, где username состоит только из цифр. Скажем, 12345.


Мы вызываем метод setUserRating, передавая значения в строгом соответствии с типами:

$b = new B;
$b->setUserRating('12345', 33);


А затем дальше где-то в коде используем метод getAllUsernames()


function hello(string $username): string
{
    return 'Hello, ' . $username;
};

foreach ($b->getAllUsernames() as $username) {
    echo hello($username);
}


И, BOOM! Получаем Fatal error: Uncaught TypeError: Argument 1 passed to hello() must be of the type string, integer given

Что же пошло не так? Все дело в приведении типов языка. Упростив вышесказанное, получим:

$string = '12345';
$array = [$string => 1];
var_dump(array_keys($array));

который выведет нам

array(1) {
    [0] => int(12345)
}

т.е. строка '12345' при использовании ее ключом массива, автоматически была приведена к int.


Будьте бдительны при работе с strict types!

Report Page