Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель PHP 4 - Котеров Д. В

..pdf
Скачиваний:
92
Добавлен:
24.05.2014
Размер:
4.38 Mб
Скачать

198

Часть III. Основы языка PHP

эти директивы обрабатываются во время компиляции, а в PHP — во время выполнения. Что ж, на то он и интерпретатор, чтобы позволять себе интерпретацию.

То, что возможно создавать условно определяемые функции, сильно подрыва- ет веру в PHP как в истинный транслятор. Как вообще можно устроить транс- лятор так, чтобы он правильно обрабатывал подобные вещи? Я не знаю. На- деюсь, что разработчики PHP нашли-таки способ, и условно определяемые функции транслируются вместе со всей программой, а не на этапе исполнения, как это было в PHP версии 3. Однако полной уверенности в этом нет, а доку- ментация по этому поводу молчит (пока).

Передача функций "по ссылке"

Я отнюдь не случайно заключил последние два слова названия этого раздела в кавычки — дело в том, что как таковая, передача функции по ссылке в PHP не поддерживается. Однако, т. к. это слишком часто может быть полезным, в PHP есть понятие "функциональной переменной". Легче всего его можно рассмотреть на

примерах:

function A($i) { echo "a $i\n"; } function B($i) { echo "b $i\n"; } function C($i) { echo "c $i\n"; } $F="A"; // или $F="B" или $F="C"

$F(10); // вызов функции, имя которой хранится в $F

Второй пример носит довольно прикладной характер. В PHP есть такая стандартная функция — uasort(), которая сортирует ассоциативный массив, заданный ее первым параметром, причем критерием сравнения для элементов этого массива служит функция, имя которой передано вторым параметром. Мы уже рассматривали эту функцию в предыдущей главе, но я еще раз приведу простой пример:

// Сравнение без учета регистра символов строк function FCmp($a,$b)

{ return strcmp(tolower($a),tolower($b))

}

$a=array("b"=>"bbb", "a"=>"Aaa", "d"=>"ddd); uasort($a,"FCmp"); // Сортировка без учета регистра символов

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

В общем случае, функциональная переменная — это всего лишь переменная-строка, содержащая имя функции, и ничего больше. Поскольку в PHP нет такого понятия, как области видимости для функций (есть только области видимости для локальных переменных), то конфликтов это не порождает — одному имени может соответствовать

Глава 11. Функции и области видимости

199

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

Возврат функцией ссылки

До сих пор я рассматривал лишь функции, которые возвращают определенные значения — а именно, копии величин, использованных в инструкции return. Заметьте, это были именно копии, а не сами объекты. Например:

$a=100; function R()

{ global $a; // объявляет $a глобальной

return $a; // возвращает значение, а не ссылку!

}

$b=R();

$b=0; // присваивает $b, а не $a! echo $a; // выводит 100

В то же время мы бы хотели, чтобы функция R() возвращала не величину, а ссылку на переменную $a, чтобы в дальнейшем с этой ссылкой можно было работать точно так же, как и с $a. Например, это может очень пригодиться в объектноориентированном программировании на PHP (основы которого мы рассмотрим в пятой части книги), когда функция должна возвращать именно объект, а не его копию.

Как же нам добиться нужного результата? Использование оператора $b=&R(), к сожалению, не подходит, т. к. при этом мы получим в $b ссылку не на $a, а на ее копию. Если задействовать return &$a, то появится сообщение о синтаксической ошибке (PHP воспринимает & только в правой части оператора присваивания сразу после знака =). Но выход есть. Воспользуемся специальным синтаксисом описания функции, возвращающей ссылку (листинг 11.15):

Листинг 11.15. Возвращение ссылки

$a=100;

function &R() // & — возвращает ссылку

{ global $a; // объявляет $a глобальной

return $a; // возвращает значение, а не ссылку!

}

$b=&R(); // не забудьте & !!!

$b=0; // присваивает переменной $a!

echo $a; // выводит 0. Это значит, что теперь $b — синоним $a

200

Часть III. Основы языка PHP

Как видим, нужно поставить & в двух местах: перед определением имени функции, а также в правой части оператора присваивания при вызове функции. Использовать амперсанд в инструкции return не нужно.

Лично я не нахожу такой синтаксис удобным. Достаточно по-ошибке всего один раз пропустить & при вызове функции, как переменной $b будет присвоена не ссылка на $a, а только ее копия со всеми вытекающими из этого последствия- ми. При использовании объектно-ориентированного программирования это может породить логические ошибки, выглядящие крайне странно. Поэтому я рекомендую применять возврат ссылки как можно реже, и только в тех случа- ях, когда это действительно необходимо.

Пример функции: Dump()

В отладочных целях часто бывает нужно посмотреть, что содержит та или иная переменная. Однако, если эта переменная — массив, да еще многомерный, с выводом ее содержимого на экран могут возникнуть проблемы. Решить их призвана следующая функция, которую я назвал Dump(). Пользу от этой функции можно реально почувствовать, лишь поработав с ней некоторое время. Уверяю, потом вы не сможете понять, как раньше без нее обходились…

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

В PHP версии 4 для аналогичных целей существуют две стандартных функ- ции print_r() и var_dump(), но листинг, который они выводят, довольно неудобен для восприятия человеком.

Листинг 11.16. Функция Dump()

// Вспомогательная функция, делающая всю "грязную" работу function TextDump(&$Var,$Level=0)

{ if(is_array($Var)) $Type="Array[".count($Var)."]"; else if(is_object($Var)) $Type="Object";

else $Type=""; if($Type) {

echo "$Type\n";

for(Reset($Var),$Level++; list($k,$v)=each($Var);) { if(is_array($v) && $k==="GLOBALS") continue;

Глава 11. Функции и области видимости

201

for($i=0; $i<$Level*3; $i++) echo " ";

echo "<b>".HtmlSpecialChars($k)."</b> => ", TextDump($v,$Level);

}

}

else echo '"',HtmlSpecialChars($Var),'"'."\n";

}

// Основная функция function Dump(&$Var)

{// Подфункция, выводящая практически окончательный результат if((is_array($Var)||is_object($Var)) && count($Var))

echo "<pre>\n",TextDump($Var),"</pre>\n";

else

echo "<tt>",TextDump($Var),"</tt>\n";

}

В реальной жизни следует использовать функцию Dump(). Функция TextDump() (которая, по правде говоря, и делает всю работу) использует только одну неизвестную нам еще функцию — HtmlSpecialChars(), заменяющую в строке символы типа <, > или " на их HTML-эквиваленты (соответственно, <, > и "). Мы применили дополнительную функцию для того, чтобы вывести сам результат, а главная функция занимается только форматированием этого результата (вставка его в тэги <pre> или <tt> в зависимости от размера вывода).

Несколько советов по использованию функций

Хочется напоследок сказать еще несколько слов о функциях.

Первое — не допускайте, чтобы ваши функции разрастались до гигантских размеров. Дробите их на маленькие, по возможности независимые, части, желательно полезные и сами по себе. Это повысит "читабельность", устойчивость и переносимость ваших программ. В идеале каждая функция не должна занимать больше 20—30 строк, возможно, за редким исключением. Этот совет применим вообще ко всем языкам программирования, а не только к PHP.

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

Наконец, последнее: больше используйте встроенные, стандартные функции. Прежде чем писать какую-то процедуру, сверьтесь с документацией — возможно, она уже

202

Часть III. Основы языка PHP

реализована в ядре PHP. Если это так, то не думайте, что сможете написать ее эффективнее на PHP — ведь часто самый неэффективный Си-код работает быстрее, чем самый изящный на PHP. Возможно, лучше пожертвовать объемом за счет быстродействия — например, при работе с базами данных и сложными файлами лучше применять стандартные функции сериализации, чем писать более эффективно упаковывающие, но свои, потому что стандартные работают очень быстро. Правда, из этого правила существуют и исключения: например, я бы не советовал вам использовать Serialize() для формирования строки, сохраняющейся в Cookies браузера — здесь лучше написать свои функции. Опять же, тут действует принцип: чем меньше в программе собственноручно реализованных функций, тем надежнее она будет работать и тем меньше ее придется тестировать.

ЧАСТЬ IV.

СТАНДАРТНЫЕ ФУНКЦИИ PHP

Глава 12

Строковые функции

Строки в PHP — одни из самых универсальных объектов. Как мы уже видели, любой, сколь угодно сложный объект можно упаковать в строку при помощи функции Serialize() (и обратно через Unserialize()). Строка может содержать абсолютно любые символы с кодами от 0 до 255 включительно. Нет никакого специального маркера "конца строки", как это сделано в Си (там конец строки помечается символом с нулевым кодом). А значит, длина строки во внутреннем представлении PHP хранится где-то отдельно. Для формирования и вставки непечатаемого символа в строку (например, с кодом 1 или 15) используется функция chr(), которую мы рассмотрим ниже.

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

В этой главе я описываю только самые употребительные и удобные функции (около 80%), пропуская все остальные. Какие-то из не вошедших в данную гла- ву функций (например, quotemeta()) мы будем рассматривать в других гла- вах там, где это показалось мне наиболее логичным. Так что, не найдя опи- сание интересующей вас функции здесь, подумайте: возможно, оно лучше подходит для другой темы и его лучше поискать там? И, наконец, последней инстанцией для вас, конечно же, должна являться документация PHP.

Конкатенация строк

Самая, пожалуй, распространенная операция со строками — это их конкатенация, или присоединение к одной строке другой. В ранних версиях PHP для этого, как и для сложения чисел, использовался оператор +, что постоянно приводило к путанице: если к числу прибавляется строка, что должно получиться — число или строка? Если число, то вдруг наша строка содержала на самом деле не число, а какой-то текст? В новой — третьей — версии интерпретатора разработчики отказались от этого механизма и объявили, что + следует применять только для сложения чисел, и никак ина-

Глава 12. Строковые функции

207

че. Что же касается конкатенации строк, то для нее ввели специальный оператор "." (точка).

Оператор "." всегда воспринимает свои операнды как строки и возвращает строку. В случае, если один из операндов не может быть переведен в строковое представление, т. е. если это массив или объект, то он воспринимается как строки array и object соответственно. Вообще говоря, это правило применимо и не только при сцеплении строк, но и при передаче такого операнда в какую-нибудь стандартную функцию, которой требуется строка. Например, следующие команды выведут слово array:

$a=array(10,20,30);

echo $a // Внимание! Неожиданный результат!

Есть и другой, более специализированный, способ конкатенации строк. Он обычно используется, когда значения строковых или числовых переменных перемежаются с обычными словами. Если, к примеру, у нас в $day хранится текущее число, в $month — название месяца и в $year — год, то вывести строку вида "Сегодня 8 мая 2000 года" можно так:

echo "Сегодня $day $month $year года";

При этом в строку, вырабатываемую инструкцией echo, автоматически в нужных местах вставятся значения наших переменных. Это позволяет констатировать тот факт, что в PHP все переменные начинаются с $.

О сравнении строк и инструкции if-else

Теперь я хотел бы рассмотреть одно тонкое место в интерпретаторе PHP, касающееся немного неправильной работы со строками. Заключается оно вот в чем. Если мы используем операторы сравнения == и != (или любые другие, которые могут потребовать перевода строки в число) с операндами-строками, то результат, вопреки ожиданиям, не всегда оказывается верным. Чаще всего это проявляется как раз в инструкции if. Вот примеры (листинг 12.1):

Листинг 12.1. Внимание! Опасное место!

$one=1

// число один

 

$zero=0

// присваиваем число ноль

if($one=="") echo 1

// очевидно, не равно — не выводит 1

if($zero=="") echo 3

// Внимание! Вопреки ожиданиям печатает 3!

if(""==$zero) echo 4

// И это тоже не поможет!..

if("$zero"=="") echo 5

// Не работает в некоторых версиях PHP 3

if(strval($zero)=="") echo 6; // Вот теперь правильно — не выводит 6