Pull to refresh

Использование выражений в PHP

Reading time 4 min
Views 6.7K
В данном тексе я лишь обращаю внимание на некоторые возможности языка PHP. Я не предлагаю их использовать, так как это в некоторых случаях существенно усложняет читабельность кода и приводит к сложностям при отладке. Однако описанные мной подходы в ряде случаев существенно позволяют сократить код.

Выражения — это краеугольный камень PHP. Почти все, что вы пишете в PHP, является выражением. Самое простое и точное определение выражения — «все что угодно, имеющее значение». (с сайта php.net)

UPD: Статья только о том, как МОЖНО делать в некоторых случаях, когда это оправданно. В большинстве случаях (и тем более повсеместно) описанную практику применять не стоит ни в коем случае, так как она сильно усложняет код. Цель статьи — расказать о возможностях языка, не более того.


Как правило 80-90% любого PHP-скрипта состоит из выражений:

$a = 2 + 1;
$a = isset($a) ? $a : false;
$a = func1($a) + func2($a);
$res = mysql_query($query);


Можно игнорировать присваивание значения выражения. Это все равно будет правильным кодом с точки зрения PHP:

2 + 1;
isset($a) ? $a : false;
func1($a) + func2($a);
mysql_query($query);


Оператор присваивания в свою очередь может использоваться в выражениях многократно:

$a = ($b = 2 + 1);
$a = isset($a) ? $b = $a : $b = false;
$a = ($b = func1($a)) + func2($a);
$a = mysql_query($query = ‘SELECT …’);


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

// Если $a == true выполнится func1() в противном случае func2()
$a ? func1() : func2();

// func1() и func2() выполнятся в любом случае:
func1() + func2();

// func1() и func2() выполнятся случае если $a > 0
$a <= 0 or func1() + func2();


Встретив в PHP-коде подобие последней строчки из данного примера, многие программисты впадут в ступор. Поэтому тут я остановлюсь поподробнее.

Дело в том, что выражения с логическими операторами вычисляются до получения однозначного значения. Этот момент может наступить раньше, нежели выражение будет целиком вычислено, например:

$a = true or func1();

func1() никогда не будет вызван, так как при вычислении “true or” результатом уже однозначно будет true, не зависимо от того, что идет дальше. Аналогично с оператором and:

$a = false and func1();

Замечу, что кроме операторов or и and существуют операторы || и &&. Разница между ними только в приоритете выполнения (or и and вычисляются позже). Для побитовых операторов | & и др. данное правило не действует, выражение вычисляется полностью.

Поэтому выражение:
Func1() or func2() or func3();

Может быть трактовано как «выполнять до тех пор, пока какая-нибудь функция не вернет true», и аналогично с оператором and – false. Поэтому эту возможность можно использовать в качестве оператора if:

$res = mysql_connect(…);
if (!$res) die();


Аналогично:

$res = mysql_connect(…) or die();

Разница только в том, что пример ниже более компактен. Ну, и немного менее читабелен. Вот более реальный пример:

mysql_connect(…) and mysql_select_db (…) and mysql_query("SET NAMES utf8") or die(mysql_error());

Всего одна строчка – выполнение соединения с БД, выбор БД, установка кодировки и вывод ошибки, если что-то пошло не так.

Это можно рассматривать и глубже, но тогда код становится абсолютно нечитаемым, и чтоб в нем разобраться другому разработчику потребуется серьезный мозговой штурм. Здесь (как и везде) действует правило «хорошо, но в меру».

Теперь о непосредственном практическом применении. Далее я приведу примеры со своими комментариями, т.к. в данном случае пример лучший учитель.

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

$res = mysql_query(“SELECT …”);
If ($res) while ($v = mysql_fetch_assoc($res))
{
// тут какие-то действия с массивом.
}

Вместо вот этого можно написать:

for($res = mysql_query(“SELECT …”); $res&&($v = mysql_fetch_assoc($res));)
{
// тут какие-то действия с массивом.
}

Не стоит забывать, что оператор цикла for имеет не строго заданную структуру for($i = 0; $i != $k; $i++) а принимает на вход максимум 3 выражения, первое вычисляется в момент старта, второе служит для проверки условия выполнения, и третье вычисляется в момент каждого прохода. Любое из этих выражений можно опустить (в прошлом примере третье выражение отсутствует). Тем самым, если наши «какие-то действия с массивом» не очень сложны, можно написать и так:

for(
$res = mysql_query(“SELECT …”);
$res && ($v = mysql_fetch_assoc($res));
// тут какие-то действия с массивом, например
print_r($v)
);


Кстати, у цикла for начальное и конечное выражение может состоять из многих выражений, которые перечисляются через запятую:

for(
$res = mysql_query(“SELECT …”), $i = 0;
$res && ($v = mysql_fetch_assoc($res));
// тут какие-то действия с массивом, например
print_r($i++), print_r($v)
);

Другие случаи подобного синтаксиса в php мне не знакомы.

Интересные случаи использования оператора присваивания, при котором переменные, входящие в выражение изменяются в процессе его вычисления. Например, у нас есть некий файл, который может содержаться в одном из директориев DIR1 и DIR2 или отсутствовать. Требуется определить путь к этому файлу. Сходу можно написать вот такой код.

if (is_file(DIR1.$file)) $path = DIR1.$file;
elseif (is_file(DIR2.$file)) $path = DIR2.$file;
else $path = false;

Но это можно написать и таким способом:

is_file($path = DIR1.$file) || is_file($path = DIR2.$file) || $path = false;

Выражение будет выполняться до тех пор, пока какой-то is_file не вернет true, причем именно в этом случае установится соответствующий $path. Кстати, к такому виду часто могут быть приведены большие конструкции if-elseif-elseif… и switch-case-case…

Ну, и наконец, как уже было сказано можно избавляться от небольших блоков if. Как это делается, я уже привел примеры, так что повторяться не буду.

Tags:
Hubs:
+46
Comments 156
Comments Comments 156

Articles