LINUX.ORG.RU

Мысли о PHP typecast вообще и про преобразования строк в числа в PHP в частности.

 , ,


0

2

Привет!

Меня всегда напрягало то, как PHP преобразует строки в числа. Классический пример:

$value = (int)"32px"; // 32
$value = (int)"kg32"; // 0

Я понимаю, что такое поведение намеренно и в некоторых случаях бывает полезным.

Но… в результате можно получить вполне корректное целое число, при этом информация о том, что исходная строка содержала что-то ещё (важное или не очень), бесследно теряется.

Я думал над тем, как можно было бы решить эту проблему, и пришёл к одной идее.

Что, если добавить к значениям признак reliable / unreliable (надёжное / ненадёжное)?

Представим, что наш PHP-движок УМЕЕТ автоматически устанавливать специальные флаги времени выполнения для любых значений.

Например, помечать значение как unreliable, если при его преобразовании возникли сомнения.

Этот флаг будет сохраняться при присваивании и вычислениях:

$a = (int)"32kg";  // 32, unreliable
$b = $a * 2;       // 64, всё ещё unreliable
$c = $b + 10;      // 74, всё ещё unreliable

Флаг можно устанавливать и снимать явно:

$a = (unreliable)42;
$a = (reliable)$a;

В обычном режиме этот флаг ни на что не влияет. Код продолжает работать как прежде. При необходимости можно проверить значение с помощью is_reliable($value) и принять соответствующие меры:

if (!is_reliable($x = (int)$UserInput)) {
 ....
}

Но можно предусмотреть и более строгий режим - с помощью языковой конструкции reliable / catch, по аналогии с try/catch:

$a = (int)"32kg"; // 32, unreliable

reliable {

    echo $a; // Попытка использовать unrel значение - исключение!

} catch (Error $e) {
    // Обрабатываем исключение
}

Или включить строгую проверку для всего последующего кода. Такой режим работает по принципу этакого fail-fast: никаких блоков catch() - просто выбрасывается необработанное исключение, которое прерывает выполнение.

$a = (int)'hello'; // 0, unreliable
$b = $a + 1;

reliable on;  // языковая конструкция, включить строгий режим
...
$c = $b; // Исключение: попытка использовать unrel значение
...

Примеры выше - это все про строки. Но ureliable может происходить и в других сценариях, например, мы конвертим объект в число, а объект не имеет соответствующего магического метода __toInt. Или мы ковертим массив во float, (т.е. (float)[‘a’ => 0, 2]) и так далее.

А ТЕПЕРЬ, ВНИМАНИЕ, ВОПРОС:

Мне было бы интересно узнать, что думают об этом те, кто пишет на PHP.

Как вам такая идея? Кажется ли она полезной? Или это просто добавление сложности там, где на самом деле нет никакой проблемы?

Спасибо!

Меня всегда напрягало то, как PHP преобразует строки в числа. Классический пример:

Я с PHP вообще не знаком и знакомство не планирую. Из этого примера, похоже что под капотом этого преобразования типа лежит функция atoi() из стандартной сишной библиотеки, в которой нет механизма определения ошибки.

Если нужно отслеживать ошибку есть функция strtol(), можете посмотреть как она работает.

Jullyfish ★★
()

Надо чинить преобразование строки в число, а не подпирать костылями.

$value = (int)«32px»; // 32

32px - не число и то, что при преобразовании получается 32 уже ошибка.

hippi90 ★★★★★
()
Ответ на: комментарий от Jullyfish

Да, там исторически был atoi(), и я знаю про strtol().

Наверно, следовало пояснить: я занимаюсь разработкой php-подобного языка. Фича, про которую я написал выше - была легкореализуема, поэтому я ее и реализовал.

Меня интересует мнение сообщества программистов на PHP - была бы такая фича им интересна.

Старый код, который про эту фичу ничего не знает, будет работать как и работал.

vvb333007
() автор топика

Ну, вряд ли возможно сделать универсальный код, который смесь букв и цифр превратит в число. Кто его знает, что в той смеси и для чего она была сделана изначально. Но если тебе нужно выбрать первое число из такой смеси (например), ты можешь использовать простую строчку

$v = (int) (preg_match('/\d+/', 'kg32', $matches) ? $matches[0] : 0);
PeleWin ★
()

Зачем оно в движке? Сделай свой класс ReliableIntCast в конструктор суй строку и все возможные нужные тебе методы каста и предикаты

cobold ★★★★★
()
Ответ на: комментарий от cobold

Для поиска ошибок в процессе разработки и как строгий assert() в релизе. Иногда выгоднее написать код, который просто аварийно завершается, вместо того чтобы городить обработку ошибок. Ввел пользователь мусор - скрипт просто упал.

Второй момент - этот (вышеописанный) механизм же работает не только для «string->numeric». Он работает для всех типов: например каст массива в строку (например: $a = (string)[1,2,3,4]; ) или каст экземпляра класса в строку ($a = (string)(new MyClass))

В Zend PHP, если класс не имеет метода __toString(), то каст к строке вернет «Object». Отличить после каста - не возможно: то-ли объект не имел метода __toString() и поэтому «Object» то-ли имел, но вернул «Object».

Где оно может пригодиться:

Например, написали вы код, который работает, все случаи мусорного ввода вы обработали. Теперь можно добавить в самое начало скрипта reliable on; и виртуальная машина вывалит исключение , если где-то ошибка пролезла несмотря на то, что вы обмазались проверками.

вариант reliable {} позволяет включать проверку на конкретный код (или вызов). Исключение можно поймать в блоке catch

Программист может сам помечать значения как unreliable: $a = (unreliable)1; для того, чтобы их потом как-то специально обработать.

vvb333007
() автор топика
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария