Нетривиальный синтаксис в PHP, или Головоломки для кодера::Журнал СА 10.2007
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

 Читать далее...

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

 Читать далее...

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

 Читать далее...

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

 Читать далее...

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

Похоже, что провайдеры и операторы связи начали забывать о требованиях законодательства

 Читать далее...

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

 Читать далее...

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

 Читать далее...

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

 Читать далее...

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

 Читать далее...

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

 Читать далее...

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

 Читать далее...

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

 Читать далее...

Книжная полка  

От создания сайтов до разработки и реализации API

В издательстве «БХВ» недавно вышли книги, которые будут интересны системным администраторам, создателям

 Читать далее...

Разбор полетов  

Ошибок опыт трудный

Как часто мы легко повторяем, что не надо бояться совершать ошибки, мол,

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 6195
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 6903
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4188
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 2989
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3795
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3805
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6299
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3152
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3446
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7263
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10628
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12352
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 13983
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9110
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7064
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5375
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4604
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3414
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3145
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3392
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3013
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Нетривиальный синтаксис в PHP, или Головоломки для кодера

Архив номеров / 2007 / Выпуск №10 (59) / Нетривиальный синтаксис в PHP, или Головоломки для кодера

Рубрика: Программирование /  Программирование

Александр Майоров

Нетривиальный синтаксис в PHP, или Головоломки для кодера

Каждый профессиональный программист должен знать все особенности языка, на котором он пишет. Попробуем разобраться в нестандартных конструкциях PHP и освоить интересные приемы.

Вместо здравствуйте…

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

<?php

function emp( $data = false )

{

    return $data || $data; }($data == true);{;

    $arr = array(0,1,2,3,4,-1,-3, 8,4,3,2,1,0,-1);

}

function x( $data )

{

    $data || $data = '123';

    return $data;

}

define('def456', !!7);

{

    $var1 =

    $var2 =

    $var3 = ~-1;

    $x = 'X';

    $X[] = (array) 'emp';

 

    {

           $var1 = $var2 ? 'abc123' : def456;

           $var1 += true;

    } ( $var2 <<= 1 );

} ( $var3 += true );

def456 && $var1 <<= 2;

${$x[0]}[0][0]($var1) || $var1 -=1;

for ($i=$var=0, $sizeof=sizeof($arr); $i<$count,$arr[$i]<5; $i++,++$var);

{

    $  = ~(int) $var1 .= $var;

}

$var = ~x(--$ );

var_dump($var);

?>

Есть ли в этом примере ошибки? Где? Докажите. Если нет ошибок, то что делает этот скрипт? Что выведет функция var_dump()? Почему? Если вы затрудняетесь ответить, что тут написано, то вам следует прочесть эту статью!

Могу вам дать только одну подсказку. В этом примере нет ни одной синтаксической ошибки, он весь рабочий! А теперь рассмотрим каждую конструкцию подробнее.

Блоки

Блок – группа связанных между собой операторов, рассматриваемых как единое целое. Операторы, образующие блок, логически связаны между собой. Иногда их называют составным оператором. Чаще всего они используются как составная часть других операторов, таких как if, switch, function и прочих. Но блок может являться и самостоятельной единицей программы. Иногда такое их применение целесообразно для улучшения читабельности кода.

<?php

{

   $a = 1;

   $a += 4;

   {

     $b *= $a;

   }

}

?>

Применение блоков особенно удобно, если ваш редактор отслеживает фигурные скобки и позволяет сворачивать блоки.

Тернарный оператор

Тернарный оператор поможет записать несложное условное выражение в одну строку.

Приведенные конструкции:

<?php

if ( $var == 'test' )

{

  $str = "Ok!";

}

else

{

  $str = "No!";

}

print $str;

?>

и

<?php

print $str = $var == 'test' ? "Ok!" : "No!";

?>

эквивалентны. Для кого-то вторая конструкция покажется неудобной, но так короче.

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

Кстати, не стоит думать, что $str присвоит значение $var. В результате интерпретации строка будет прочтена как:

<?php

print $str = ( ($var == 'test') ? "Ok!" : "No!" );

?>

У тернарного оператора очень низкий приоритет. Поэтому следующий код:

<?php

$var = "Я ".(1 == 1) ? "люблю PHP!" : "ненавижу PHP!";

?>

то же самое, что и этот:

<?php

$var = ("Я ".(1 == 1)) ? "люблю PHP!" : "ненавижу PHP!";

?>

Он в любом случае вернёт «люблю PHP!», а вовсе не «Я люблю PHP!». К вопросу, как же все это так работает. Напомню, что строка в PHP читается в так называемой венгерской нотации, то есть справа налево, соответственно попутно выполняя инструкции.

Операции с одновременным присвоением

Операции с одновременным присвоением, такие как /=, *=, -=, +=, %=, .=, очень удобно использовать. Операция присвоения всегда возвращает переменную, стоящую в левой части от знака равенства. Также в PHP разрешено множественное присвоение, что удобно при инициализации переменных.

<?php

$var1 =

$var2 =

$var3 = 1;

?>

Все три переменные будут инициализированы, и им будет присвоена единица.

<?php

$var +=1;

?>

В данном случае такая запись эквивалентна $var++ (или ++$var, в данном примере не играет роли). Где это может быть удобно?

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

Сравните записи цикла, приведенные ниже:

<?php

for ($i=0;;$i=$i+4) { … }

for ($i=0;;$i+=4){ … }

?>

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

Данные операции также можно использовать внутри блоков и прочих объектов:

<?php

$i =

$n = 2;

$var = ($i *= 4) + ++$n;

?>

Эта запись равносильна записи:

<?php

$var = ($i * 4) + ($n = $n + 1);

?>

В результате переменной $var будет присвоено число 11.Согласитесь, что такие записи экономят не только место, но и время на написание и читаются они удобнее. Нужно привыкнуть, а главное, понимать суть всех этих операций.

Преобразование типов

PHP не является жестко типизированным языком. Тип переменной определяется программистом или контекстом её использования. Иногда требуется изменить тип. Привести к нужному типу переменную в PHP можно тремя способами.

Первый – использовать стандартные функции для приведения типов: intval(), floatval(), doubleval(), strval(). Также напомню, что doubleval() является алиасом floatval(), так как, начиная с PHP 4 и выше, используется floatval(), double остался для совместимости. Для приведения к типам существует и универсальная функция settype(). Дополнительную информацию по данным функциям ищите в мануалах.

Второй способ приведения к нужному типу – это использовать так называемые кастинги, такие специальные инструкции, которые позволяют приводить к определенному типу переменные.

<?php

$str = "123.4567a89";

$i = (int) $str; // 123

$d = (double) $str;     // 123.4567

$f = (float) $str;      // 123.4567

?>

Также существует и обратное приведение в строку:

<?php

$i = 123.456;

$str = (string) $i;

?>

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

<?php

$sbuf = "123";

$sbuf = (array) $sbuf;

?>

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

<?php

$sbuf = (array) "123";

echo $sbuf[0];

?>

Привести переменную к объекту можно аналогично. В результате будет создано поле scalar, которое и будет содержать значение:

<?php

$sbuf = (object) "123";

echo $sbuf->scalar;

?>

А теперь настал черед нестандартных приемов, которые позволяют конвертировать переменные из одного типа в другой. Это и есть третий способ. Итак, чтобы привести к числу строку, достаточно с ней произвести математическое действие, тогда PHP автоматически приведет строку к типу float, если в строке встретится точка, или к int, если будут найдены только цифры.

<?php

$s = "123.456a78";

$s += 0;

$s -= 0;

$s *= 1;

$s /= 1;

?>

Любое вышеперечисленное действие приведет строку к типу float(123.456).

<?php

$s = "123a4";

$s += 0;

$s -= 0;

$s *= 1;

$s /= 1;

?>

Любая вышеперечисленная операция над переменной приведет строку к типу int(123).

Из числа в строку можно привести, используя конкатенацию:

<?php

$s = 123;

$s .= '';

?>

Приведет к string(“123”). С логическими типами дела обстоят аналогично:

<?php

true + 0;  // 1

false + 0; // 0

false + 1; // 1

true + true;     // 2

!123;            // false

!!123;     // true;

!!0;             // false;

!0;        // true;

?>

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

Лучше использовать метод кастинга, чем математические операции. При использовании математических операций интерпретатор сначала конвертирует переменную к нужному типу, а затем производит с ней действие, тем самым совершается лишняя операция.

Циклы

Про циклы говорить много не будем. Тут вопросы не столько нестандартного синтаксиса, сколько оптимизации. Но на практике я чаще встречаю как раз неоптимизированные циклы, что и наводит на мысль, что правильно написанная форма цикла есть нестандартный синтаксис.

Как обычно перечисляют массивы в циклах, например:

<?php

for ($i=0; $i < count($arr); $i++)

{

    print $arr[$i];

}

?>

Почему такой цикл неоптимизирован? Потому что каждая часть (их в операторе for всего три) является произвольным выражением, но второй блок (проверка) выполняется при каждом цикле. Поэтому при каждой итерации в данном случае будет происходить подсчет количества элементов в массиве. Правильно цикл задавать следующим способом:

<?php

for ($i=0, $count = count($arr); $i < $count; $i++)

{

    print $arr[$i];

}

?>

При больших массивах такая запись дает выигрыш в несколько раз перед предыдущей записью.

Теперь поговорим о блоках цикла. В каждом блоке вы можете задать произвольное количество выражений.

<?php

$arr = array(0,1,2,3);

for ($i= $n = 0, $str = '', $count = count($arr); $i < $count; $i++, $n += 2)

{

    $str .= "$n.{$arr[$i]}\n";

}

echo $str;

?>

Иногда в цикле может понадобиться 2-я проверка. Для наглядности поставим задачу и решим её сначала стандартным способом, а затем нетривиальным. Допустим, у нас есть массив целых чисел. Нам надо узнать индекс элемента массива, который будет больше 5, и завершить просмотр массива.

<?php

// Самый простой способ решить описаную нами задачу

$arr = array(0,1,2,3,4,-1,-3,8,4,3,2,1,0,-1);

$var = 0;

for ($i=0, $count = count($arr); $i < $count; $i++)

{

      if ( $arr[$i] > 5 ) break;

          ++$var;

}

?>

В этом варианте задачи ответ будет равен 7, так как индекс первого встретившегося числа, большего 5, это 7 ($arr[7] = 8). Но это же решение можно записать намного короче и даже, по-моему, удобнее:

<?php

$arr = array(0,1,2,3,4,-1,-3,8,4,3,2,1,0,-1);

for ($i=$var=0, $count=count($arr); $i<$count,$arr[$i]<5; $i++,++$var);

?>

Здесь проверку вынесли в блок проверки условия, тем самым отказавшись от оператора break, потому что теперь проверка осуществляется в операторе for. Выражения в блоке проверки указываются также через запятую, а трактуются как «ИЛИ». На этом с условиями можно закончить.

Оператор switch()

Этот оператор многие знают, но немногие используют его вместо многоуровневых проверок ifelse(). А ведь switch() очень даже удобнее использовать в таких случаях. Приведу пример:

<?php

if ( eregi("RegExp0", $str) )

{

    /* ваш код для обработки */

}

elseif ( eregi("RegExp1", $str) )

{

    /* ваш код для обработки */

}

elseif ( eregi("RegExp2", $str) )

{

    /* ваш код для обработки */

}

else

{

    /* ваш код для обработки */

}

?>

Если честно, мне всегда не нравились такие проверки. Намного приятнее читать этот же код вот в такой нетривиальной форме записи:

<?php

switch (1)

{

    case eregi("RegExp0", $str1):

    /* ваш код для обработки */

    break;

    case eregi("RegExp1", $str1):

    /* ваш код для обработки */

    break;

    case eregi("RegExp2", $str1):

    /* ваш код для обработки */

    break;

    default:

    /* ваш код для обработки */

}

?>

Развивать эту тему с условиями и проверками можно долго, со временем вы сами найдете все плюсы и минусы той или иной формы записи. Могу сказать только, что через switch() удобно эмулировать оператор goto (для тех, кто не знал, в PHP нет оператора перехода по меткам goto), который сложно повторить, если использовать только ifelse.

Эмуляция функции eval()

Хотя бы раз в жизни каждому PHP-программисту приходилось сталкиваться с необходимостью использовать в своей программе eval(). Вообще все стараются отказаться от использования этого небезопасного оператора, но иногда он позволяет облегчить работу программисту.

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

Для наглядности придумаю задачу, которую легче всего решить функцией eval(). Может, это не самый лучший пример, но все же…

Итак, нам надо хранить данные, например, какие-то настройки. Все эти настройки у нас хранятся в массиве. Этот массив приходится хранить в базе. Самый простой вариант – это создать в таблице поле, можно текстовое, в которое будет сбрасываться дамп массива, а затем браться, когда надо, и снова заноситься в переменную.

<?php

$ini = array(

'host'=>'localhost',

'login'=>'root',

'pass'=>'',

'db_name'=>'mysql',

'boolean'=>false,

'int'=>123,

'float'=>123.456,

'array'=>array(0,1,2,3)

);

?>

Чтобы сбросить дамп в виде текста, нужно воспользоваться функцией var_export():

<?php

$ini_dump = var_export($ini, true);

?>

Теперь в переменной содержится дамп, который соответствует синтаксису PHP:

array (

  'host' => 'localhost',

  'login' => 'root',

  'pass' => '',

  'db_name' => 'mysql',

  'boolean' => false,

  'int' => 123,

  'float' => 123.456,

  'array' =>

  array (

    0 => 0,

    1 => 1,

    2 => 2,

    3 => 3,

  ),

)

Мы записываем это в базу или куда нам нужно.

Теперь настал черед загрузки этой структуры. С помощью функции eval() это делается так:

<?php

eval("\$ini = $ini_dump;");

?>

Таким образом мы загрузили нашу структуру обратно в переменную. Но вот что делать, если нет возможности использовать функцию eval()? Решение есть. Первый, самый мощный из всех способов, это функция create_function().

<?php

$ini = null;

$loader = create_function('&$ini', "\$ini = $ini_dump;");

$loader($ini);

?>

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

<?php

call_user_func( create_function ('&$ini', "\$ini = $ini_dump;"), &$ini);

?>

Мне нравится последний вариант. Таким вот образом эмулируется eval().

Есть еще один способ эмулировать eval(), но он пригоден не для всего. Например, создать массив из строки или переменные из строки:

<?php

parse_str("arr[]=1&arr[]=\ndfsdfsdfdsf\nefrewrer \nererew&arr[]=fef");

var_dump($arr);

?>

Дамп переменной приведен ниже:

array(3) {

    [0]=>

    string(1) "1"

    [1]=>

    string(28) "

dfsdfsdfdsf

efrewrer

ererew"

    [2]=>

    string(3) "fef"

  }

Как видите, простой массив можно создать.

Ассоциативные массивы создаются аналогичным образом.

<?php

parse_str("arr[a]=1&arr[b]=2&arr[c]=3");

var_dump($arr);

?>

Будет создан массив следующей структуры:

array(3) {

  ["a"]=>

  string(1) "1"

  ["b"]=>

  string(1) "2"

  ["c"]=>

  string(1) "3"

}

Но, кроме как для инициализации переменных, такой способ больше ни на что не пригоден.

Использование битовых операторов

Многие, даже опытные PHP-программисты слышали о битовых операторах в PHP, но не используют их в своих программах. А зря.

Побитовые операторы позволяют работать с определенными битами. Почему стоит использовать побитовые операторы? Потому что такие операции вычисляются в десятки раз быстрее, чем обычные математические.

Одними из наиболее ресурсоемких операций в PHP, как и во многих других языках, являются операции умножения и деления. Намного быстрее выполняются операции двоичного сдвига.

Немного теории, так сказать, повторение курса информатики, а точнее булевой алгебры. Допустим, есть число 40 (101000). Нам надо разделить число на 2.

В двоичной системе деление происходит сдвигом вправо. Деление на 2 – это сдвиг вправо на 1 позицию. Последнее число отсеивается и остается только 10100, это и есть не что иное, как 20 в десятичной.

Умножение происходит с точностью до наоборот. Умножать, как и делить, можно на числа, кратные двойке.

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

<?php

$a = 40;

$b = $a << 2; // $b = 160

?>

Это код в обычном виде выглядит так:

<?php

$a = 40;

$b *= 4;

?>

Битовые операции, как и математические, можно использовать с непосредственным присвоением. То есть наш пример можно переписать так:

<?php

$a = 40;

$a <<= 2; //$a = 160

?>

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

Логические операторы

Очень интересно можно упрощать условия. Например, есть код:

<?php

define('flag', false);

function foo()

{

       print "Exec!";

}

if ( !flag )

{

   foo();

}

?>

Эту проверку с условием можно упростить до безобразия:

<?php

flag || foo();

?>

Аналогичным образом упрощаем:

<?php

if ( flag )

{

    foo();

}

// Упрощенный вариант предыдущей записи

flag && foo();

?>

Записи получаются очень короткими и удобными.

Иногда требуются функции, которые совершают какие-то действия над данными, а затем возвращают просто true или false, в зависимости от того, есть данные или нет. Классический прототип такой функции:

<?php

function foo ( $data = false )

{

    if ( !empty($data) )

           return true;

    else

           return false;

}

?>

Здесь, правда, можно удалить else, тогда запись будет короче:

<?php

function foo ( $data = false )

{

    if ( $data ) return true;

    return false;

}

?>

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

<?php

function foo ( $data = false )

{

      return $data ? true : false;

}

?>

А можно даже так:

<?php

function foo ( $data = false )

{

        return empty( $data );

}

?>

Но и это не предел аппроксимации, скажу я вам. Настоящие хакеры пишут так:

<?php

function foo ( $data = false )

{

         return $data || $data;

}

?>

Вот это и есть нетривиальная форма записи всего того, что было написано выше. Одна строчка, и ничего лишнего!

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

<?php

function f()

{

        return 'default';

}

function foo( $data = false )

{

      if ( empty($data) ) $data = f();

          return $data;

}

?>

Но эту же проверку можно написать совсем по-другому:

<?php

function f()

{

      return 'default';

}

function foo( $data = false )

{

          $data || $data = f();

      return $data;

}

?>

Вот так – другое дело. Кстати, не стоит путать следующие две записи:

<?php

function foo1( $data = false )

{

        $data || $data = f();

        return $data;

}

function foo2( $data = false )

{

        return $data || $data = f();

}

?>

Функция foo1 вернет значение, а вот foo2 только true. Дело в том, что когда в выражении встречается логический оператор, то результат этого выражения – всегда логическое значение истины или лжи:

<?php

function foo2( $data = false )

{

        return ( $data || $data = f() );

}

?>

Переменные функции

В PHP поддерживаются переменные функции. Это значит, что если в скрипте попадется переменная, за которой следуют скобки, то PHP произведет поиск функции c именем, которое присвоено в качестве значения переменной. Затем будет предпринята попытка выполнить эту функцию:

<?php

function inc( $var )

{

     return ++$var;

}

$foo = 'inc';

echo

$i = $foo(5);

?>

Переменные переменных

Иногда есть необходимость использования имен переменных или самих переменных для переменных. Такие переменные могут быть изменены динамически:

<?php

$var = 'Hello';

$$var = " world!";

echo "$var ${$var}";

?>

В данном случае будет выведена строка «Hello world!». Аналогичным образом можно создавать массивы переменных:

<?php

$var['var1'] = 'Hello';

$$var['var1'] = array(' wo', 'rl');

${$var['var1']}[] = 'd!';

echo "{$var['var1']}{${$var['var1']}[0]}{${$var['var1']}[1]}{${$var['var1']}[2]}";

?>

Результат будет такой же, как и в предыдущем примере. В этом примере показаны все варианты работы с массивами, поэтому вопросов не должно возникнуть. При работе с массивами важно правильно устанавливать порядок имен, к которым относятся индексы. Лучше всего для этого использовать фигурные скобки.

Tips’n’tricks

Приведу несколько полезных советов и приемов.

Точка с запятой

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

Следующий код вполне работоспособный:

<?php

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;function foo( $data = false ){;;

;;;;;;;$data || $data = 10;;;;;;;;

;;;;;;;;;;return $data;};;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

?>

Нестандартные символы

Однажды я столкнулся с такой темой – Parse error: syntax error, unexpected T_STRING... Ну не впервой ошибки исправлять, полез смотреть: что, где и как… Открыв в том месте, где якобы была ошибка, я ошибки не нашел. И вот я решил посмотреть через HEX-редактор свой сорец (cм. рисунок).

Дамп скрипта

Дамп скрипта

И что бы вы думали? Там, где должна была быть ошибка, был пробел, якобы пробел, но вот ASCII-код его был 0xA0 (160). Это удивило меня, ведь код пробела 0x20 (32). Это знают и взрослые, и дети. Оказалось, что символ с кодом 0xA0 интерпретатором воспринимается как разрешенный символ. Это очень интересная особенность для тех, кто пишет обфускаторы. Такую ошибку (правда это не обязательно может быть ошибка, все зависит от применения данного символа) найти очень непросто, а если человек еще и непрофессионал, то считайте никогда не найдет. Ниже приведенный код работает замечательно:

<?php

$ = 1;

$  += 5;

echo $ ;

?>

Разделители в preg-функциях

Многие программисты, особенно начинающие, заблуждаются в том, что, кроме символа «/», в функциях preg_match, preg_replace и других функциях этого семейства никакого другого использовать нельзя. И во всех регулярных выражениях приходится еще его экранировать:

preg_match(“/<p>.*?<\/p>/i”, $str);

На самом деле это не так. Использовать можно любой служебный печатный символ. То есть «#», «@» и даже «$». Но это нежелательно, ибо такие символы придется экранировать, что очень снижает читабельность регулярного выражения. Но ведь на этом символы не заканчиваются. Почему бы не использовать символы, которых нет на клавиатуре, символы, которые можно получить, зажав <Alt> и набрав номер символа, начиная с нуля:

  • Alt + 0135 = ‡
  • Alt + 0134 = †
  • Alt + 0182 = ¶

Такие символы вряд ли встретятся в тексте просто так, поэтому можно использовать их, и тогда отпадает надобность в экранировании всех остальных символов, кроме этих:

preg_match(“‡<p>.*?</p>‡i”, $str);

Вот такой нетривиальный синтаксис используется в регулярных выражениях.

Массивы символов

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

<?php

$abuf = array('a','b','c','d','e');

for ($i=0,$sizeof=sizeof($abuf); $i < $sizeof; $i++)

{

         $sHex .= '%'.sprintf("%X", ord($abuf[$i]) );

}

echo $sHex;

?>

можно переписать таким вот образом:

<?php

$sbuf = "abcde";

for ($i=0,$strlen=strlen($sbuf); $i < $strlen; $i++)

{

         $sHex .= '%'.sprintf("%X", ord($sbuf[$i]) );

}

echo $sHex;

?>

Числа в экспоненциальной форме

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

<?php

$i1 = 120000000000000000000000000000;

$i2 = 1.2E+29;

?>

Вы видите две записи одного и того же числа, но вторая запись удобнее и короче. Очень многие люди, с которыми я общаюсь, не догадывались, что так можно записывать числа.

А надо ли все это?

Вопрос задан по существу: а надо ли все это? То, что такие сложные конструкции существуют, не значит, что их нужно использовать повседневно. Мой пример в самом начале статьи показывает, как можно писать, но нежелательно (за некоторым исключением отдельных выражений). Разбираться в таком коде очень сложно, но знание такого синтаксиса, я считаю, обязательно!

Во-первых, знание особенностей языка позволяет однозначно трактовать выражения и предсказывать результат. Легче искать ошибки и разбираться в чужом коде. Ну и еще одна положительная сторона – это защита. Иногда хочется защитить код от модификации. Обфускацию никто не отменял, а авторское право защищать каждый волен, как может.

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

P.S.: Кстати, ответ на мой вступительный код: $var = 88. Это мое любимое число.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru