На Хабре часто обсуждается какой язык лучше для написания безопасных приложений. При этом часто отмечается, что PHP спроектирован таким образом, что начинающий разработчик будет допускать ошибки, приводящие к дырам безопасности. В этом хабрапосте я хочу рассказать о, на мой взгляд, нелогичном поведении операций сравнения, операторов if и switch при работе со строками. Опытным разработчикам данные особенности известны, но решил все же собрать их в одном месте. Думаю пост будет полезен начинающим разработчикам и тем, кто работает в PHP, имея опыт программирования на других языках.
echo '"" is '. (''?'true':'false'). "\n";
echo '«0» is '. ('0'?'true':'false'). "\n";
echo '"-0" is '. ('-0'?'true':'false'). "\n";
echo '«0.0» is '. ('0.0'?'true':'false'). "\n";
echo '«00» is '. ('00'?'true':'false'). "\n";
echo '«A» is '. ('A'?'true':'false'). "\n";Имеем:"" is false
«0» is false
"-0" is true
«0.0» is true
«00» is true
«A» is true
Итак истинны все непустые строки, кроме строки «0». Логика такого поведения для меня не ясна. На практике часто с помощью оператора if проверяется заполнено ли поле формы, например:if(isset($_GET['income']) && $_GET['income']) {… }Такая проверка примет поле, в котором указан 0 за незаполненное. Чтобы избежать возможного недоразумения, следует использовать проверку в виде: if(isset($_GET['income']) && strlen($_GET['income'])) {… }
echo '«A»===0 is '. ('A'===0?'true':'false'). "\n";
echo '«A»==0.0 is '. ('A'==0?'true':'false'). "\n";
echo '«A»===0.0 is '. ('A'===0?'true':'false'). "\n";Результат работы скрипта:«A»==0 is true
«A»===0 is false
«A»==0.0 is true
«A»===0.0 is falseЕсли ваша функция сравнивает строку, переданную в качестве аргумента с заданной строкой, то следует предварительно проверить, что в качестве аргумента передана строка, а не число. Пример будет приведен далее, при рассмотрении оператора switch.
function get_salary($name) {
switch($name) {
case 'John':
return 3400;
break;
case 'Mary':
return 4600;
break;
default:
return 0;
}
}
echo «John's salary is ». get_salary('John'). "\n";
echo «Mary's salary is ». get_salary('Mary'). "\n";
echo «Peter's salary is ». get_salary('Peter'). "\n";
echo «0's salary is ». get_salary(0). "\n";
?>Результат работы скрипта:John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 3400На примере видно, что безопасность функции скомпрометирована путем передачи числового значения вместо строкового. Чтобы защитить функцию от нежелательного использования, требуется проверять тип входного значения.<?php
function get_salary($name) {
if(!is_string($name)) return 0;
switch($name) {
case 'John':
return 3400;
break;
case 'Mary':
return 4600;
break;
default:
return 0;
}
}
echo «John's salary is ». get_salary('John'). "\n";
echo «Mary's salary is ». get_salary('Mary'). "\n";
echo «Peter's salary is ». get_salary('Peter'). "\n";
echo «0's salary is ». get_salary(0). "\n";
?>Результат работы скрипта:John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 0Итак, некоторые операции сравнения ведут себя контринтуитивно. Кто предупрежден, тот вооружен.
Кросспост Особенности условных конструкции со строками в PHP с webew.ru.
Что есть истина?
Какие строки истинны? Рассмотрим пример:echo '"" is '. (''?'true':'false'). "\n";
echo '«0» is '. ('0'?'true':'false'). "\n";
echo '"-0" is '. ('-0'?'true':'false'). "\n";
echo '«0.0» is '. ('0.0'?'true':'false'). "\n";
echo '«00» is '. ('00'?'true':'false'). "\n";
echo '«A» is '. ('A'?'true':'false'). "\n";Имеем:"" is false
«0» is false
"-0" is true
«0.0» is true
«00» is true
«A» is true
Итак истинны все непустые строки, кроме строки «0». Логика такого поведения для меня не ясна. На практике часто с помощью оператора if проверяется заполнено ли поле формы, например:if(isset($_GET['income']) && $_GET['income']) {… }Такая проверка примет поле, в котором указан 0 за незаполненное. Чтобы избежать возможного недоразумения, следует использовать проверку в виде: if(isset($_GET['income']) && strlen($_GET['income'])) {… }
Сравнение с участием строк
В PHP два оператора сравнения == и ===. Второй оператор сравнивает совпадение типов и значений, первый производит преобразование типа, а затем выполняет сравнение. Особенность оператора == в том, что при сравнении числа и строки, просходит преобразование строки в число. Если строка не представляет собой число, то она преобразуется в числовое значение 0. Рассмотрим пример:echo '«A»==0 is '. ('A'==0?'true':'false'). "\n";echo '«A»===0 is '. ('A'===0?'true':'false'). "\n";
echo '«A»==0.0 is '. ('A'==0?'true':'false'). "\n";
echo '«A»===0.0 is '. ('A'===0?'true':'false'). "\n";Результат работы скрипта:«A»==0 is true
«A»===0 is false
«A»==0.0 is true
«A»===0.0 is falseЕсли ваша функция сравнивает строку, переданную в качестве аргумента с заданной строкой, то следует предварительно проверить, что в качестве аргумента передана строка, а не число. Пример будет приведен далее, при рассмотрении оператора switch.
Оператор switch
Мы ожидаем, что оператор switch сравнивает переданное значение с заданными константами. Особенность реализации switch в php в том, что он производит сравнение с помощью оператора ==, то есть не производит сравнение типов. В качестве примера рассмотрим вымышленную функцию, которая возвращает зарплату сотрудника, получая его имя на входе:<?phpfunction get_salary($name) {
switch($name) {
case 'John':
return 3400;
break;
case 'Mary':
return 4600;
break;
default:
return 0;
}
}
echo «John's salary is ». get_salary('John'). "\n";
echo «Mary's salary is ». get_salary('Mary'). "\n";
echo «Peter's salary is ». get_salary('Peter'). "\n";
echo «0's salary is ». get_salary(0). "\n";
?>Результат работы скрипта:John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 3400На примере видно, что безопасность функции скомпрометирована путем передачи числового значения вместо строкового. Чтобы защитить функцию от нежелательного использования, требуется проверять тип входного значения.<?php
function get_salary($name) {
if(!is_string($name)) return 0;
switch($name) {
case 'John':
return 3400;
break;
case 'Mary':
return 4600;
break;
default:
return 0;
}
}
echo «John's salary is ». get_salary('John'). "\n";
echo «Mary's salary is ». get_salary('Mary'). "\n";
echo «Peter's salary is ». get_salary('Peter'). "\n";
echo «0's salary is ». get_salary(0). "\n";
?>Результат работы скрипта:John's salary is 3400
Mary's salary is 4600
Peter's salary is 0
0's salary is 0Итак, некоторые операции сравнения ведут себя контринтуитивно. Кто предупрежден, тот вооружен.
Кросспост Особенности условных конструкции со строками в PHP с webew.ru.