3 факта о PHP, которые вы могли не знать

в 14:08, , рубрики: php, Веб-разработка, интересные факты, метки: ,

Не для кого из веб-разработчиков не секрет, что PHP является простым, гибким и не требовательным языком. Но при работе с этим языком можно столкнуться с неожиданными вещами. В этой статье я представлю «странные факты» и объясню, почему PHP дает такие результаты.

Неточности с плавающей точкой

Большинство из вас, наверное, знают, что числа с плавающей точкой не могут реально представить все действительные числа. Кроме того, некоторые операции между двумя вроде бы хорошо заданными числами могут привести к неожиданным ситуациям. Это потому, что точность, с которой компьютер хранит числа, имеет свои особенности. Данное явление сказывается не только на PHP, но и на всех языках программирования. Неточность в операциях с плавающей точкой доставляла немалую головную боль программистам начиная со дня основания дисциплины как таковой.

Об этом факте уже было не раз написано. Одна из наиболее важных статей — «Что каждый компьютерщик должен знать об операциях с плавающей точкой». Если вы никогда не читали ее, я настоятельно рекомендую вам исправить эту ситуацию.

Давайте посмотрим на этот небольшой кусок кода:

<?php<br/>
echo (int) ((0.1 + 0.7) * 10);

Как вы думаете, каким будет результат? Если вы предполагаете, что результатом операции будет 8, то вы ошибетесь. На самом деле 7. Тем, кто имеет сертификат Zend, данный пример уже известен. К слову, вы можете найти данный пример в Руководстве по подготовке к сертификации Zend.

Теперь давайте посмотрим, почему же так происходит:

0.1 + 0.7 = 0.79999999999

Результат данной операции хранится в реальности как 0.79999999999, а не 0.8, как можно было бы подумать. Именно здесь и начинаются проблемы.

Вторая операция, которую мы выполняем:

0.79999999 * 10 = 7.999999999

Эта операция работает как надо, но проблемы остались.

Наконец, третья и последняя операция:

(int) 7.9999999 = 7

Данное выражение использует явное приведение типов. Когда значение приводится к int, PHP обрезает дробную часть и в итоге возвращает 7.

  • Если вы приведете данное выражение к типу float, а не int, или вообще не будете делать приведения типов, то вы получите число 8, как и ожидали
  • Именно из-за того, что существует математический парадокс, что 0.999… равно 1, мы и получили эту ошибку.

Как PHP «инкрементит» строки

Во время работы мы все время используем операции инкремента/декремента, подобные данным:

<?php

$a = 5;

$b = 6;

$a++;

++$b;

Каждый из нас с легкостью понимает, что тут происходит. Но попробуйте прикинуть, что выведет данный код:

<?php

$a = 1;

$b = 3;

echo $a++ + $b;

echo $a + ++$b;

echo ++$a + $b++;

Давайте посмотрим:

4

6

7

Не так уж и сложно, верно? Теперь давайте немного увеличим сложность. Вы когда-нибудь до этого пытались инкременить строки? Попробуйте предположить, что выведет данный код:

<?php

$a = 'fact_2';

echo ++$a;

$a = '2nd_fact';

echo ++$a;

$a = 'a_fact';

echo ++$a;

$a = 'a_fact?';

echo ++$a;
Это задание уже посложнее. Давайте посмотрим, что мы получили:
fact_3

2nd_facu

a_facu

a_fact?

Удивлены? Делая инкремент строки, которая заканчивается на цифру, мы фактически будем увеличивать символ (на следующий символ по алфавиту, т.е. после t следует u). Независимо от того, начинается строка с цифры или нет, последний символ будет изменен. Однако эта операция не имеет никакого смысла в случае, когда строка заканчивается на не буквенно-численный символ.

Этот момент хорошо описан в официальной документации по операциям инкремента/декремента, однако многие не читали этот материал, потому что не ожидали встретить там ничего особенного. Хочу признаться, что до недавнего времени я думал точно так же.

Тайна значений

Вы мастер массивов в PHP. Не стесняйтесь этого. Вы уже знаете все о создании, редактировании и удалении массивов. Тем не менее, следующий пример может удивить вас.

Очень часто при работаете с массивами вам приходится что-либо искать в них. В PHP есть специальная функция для этого in_array(). Давайте посмотрим ее в действии:

<?php

$array = array(

  'isReady' => false,

  'isPHP' => true,

  'isStrange' => true

);

var_dump(in_array('phptime.ru', $array));

Что должно быть выведено?

true

Не правда ли, немного странно. У нас есть ассоциативный массив, в котором содержаться только буленовские значения, и когда мы выполняем поиск строки, получаем true. Действительно ли это волшебство? Давайте посмотрим другой пример:

<?php

$array = array(

  'count' => 1,

  'references' => 0,

  'ghosts' => 1

);

var_dump(in_array('aurelio', $array));

И что мы получаем?

true

И снова in_array() вернула true. Как это возможно?

Только что вы использовали одну из любимых и одновременно ненавистных PHP-функций. Надо сказать, что PHP — не строго типизированный язык. Многие проблемы происходят именно из-за этого. На самом деле, все следующие значения, если их сравнивает PHP, идентичны при использовании одинарного оператора сравнения:

0

false

""

"0"

null

array()

 

По умолчанию, in_array() использует «гибкое» сравненте, поэтому непустая ("") и ненулевая строка («0») эквивалентны true, это же относится и ко всем ненулевым элементам (напр. 1). Следовательно, в нашем первом примере мы получили true, потому что'phpmaster.com' == true, в то время как во втором примере 'aurelio' == 1.

Для решения этой проблемы вы должны использовать третий дополнительный параметр в функции in_array(), который позволяет строгое сравнивание элементов. Если мы теперь напишем:

<?php

$array = array(

  'count' => 1,

  'references' => 0,

  'ghosts' => 1

);

var_dump(in_array('aurelio', $array, true));
мы получим значение false.

Заключение

В этой статье вы видели странное и неожиданное поведение PHP-интерпретатора. Вот что вы могли извлечь из прочитанного:

  • никогда не доверяйте числам с плавающей точкой;
  • дважды проверьте тип данных перед их использованием;
  • будьте в курсе проблем «гибкого» сравнения и «гибких» типов.

Если вы продвинутый программист, то, скорее всего, уже знали о существовании этих странностей, но повторение никогда не бывает бесполезным.

Автор: bagirovs

Источник

Поделиться

* - обязательные к заполнению поля