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

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

Готовая методика создания простых и сложных динамичных сайтов, с использованием PHP и MySQL.

Использование веб-редактора Adobe Dreamweaver в разработке сайтов.

Использование графических редакторов Adobe Flash, Adobe Photoshop, Adobe Fireworks в подготовке веб-графики.

Разработка веб-сайтов под "ключ".

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

Массив GLOBALS. Статические переменные PHP. Рекурсия. Вложенные функции PHP. Возврат функцией ссылки

Массив

Массив GLOBALS

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

Пример:

<?php
$Monthes[1]="Январь";
$Monthes[2]="Февраль";
//... и т. д.
$Monthes[12]="Декабрь";
// Возвращает название месяца по его номеру. Нумерация начинается с 1!
function GetMonthName($n) {
return $GLOBALS["Monthes"][$n];
}
//. . .
echo GetMonthName(2); // выводит "Февраль"
?>

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

  • присвоить этот массив какой-либо переменной целиком, используя оператор =;
  • как следствие, передать его функции "по значению" — можно передавать только по ссылке.

Однако остальные операции допустимы. Мы можем при желании, например, по одному перебрать у него все элементы и, скажем, вывести их значения на экран. И, наконец, третье: добавление нового элемента в $GLOBALS равнозначно созданию новой глобальной переменной (конечно, предваренной символом $ в начале имени, ведь в самом массиве ключи — это имена переменных без символа доллара), а выполнение операции unset() для него равносильно уничтожению соответствующей переменной.

Вооружившись механизмом создания ссылок, мы можем теперь наглядно продемонстрировать, как работает инструкция global, а также заметить один ее интересный нюанс. Как мы знаем, global $a говорит о том, что переменная $a является глобальной, т. е., является синонимом глобальной $a. Синоним в терминах PHP — это ссылка. А вот как это воспринимается транслятором:

<?php
function test(){
global $a;
$a=10;
}
test();
echo $a;
?>

Приведенное описание функции test() полностью эквивалентно следующему описанию:

<?php
function test(){
$a=&$GLOBALS['a'];
$a=10;
}
test() ;
echo $a;
?>

Оператор unset($a) в теле функции не уничтожит глобальную переменную $a, а лишь "отвяжет" от нее ссылку $a. Точно то же самое происходит и в первом случае.

Пример:

<?php
$a=100;
function test(){
global $a;
unset($a);
}
test();
echo $a; // выводит 100, т. е. настоящая $a не была удалена в test()!
?>

Как же нам удалить глобальную $a из функции? Существует только один способ: ис пользовать для этой цели $GLOBALS['a']. Вот как это делается:

<?php
function test() {
unset($GLOBALS['a']);
}
$a=100;
test();
echo $a; // Ошибка! Переменная $a не определена!
?>

Статические переменные PHP

Рассмотрим пример:

<?php
function myFync() {
static $a=0;
echo $a;
$a++;
}
for($i=0; $i<10; $i++) myFync();
?>

В окне веб-браузера это будет выглядеть ТАК.

После запуска будет выведена строка 0123456789, как мы и хотели. Давайте теперь уберем слово static. Мы увидим: 0000000000. Это и понятно, ведь переменная $a стала локальной, и ей при каждом вызове функции присваивается одно и то же значение — 0.

<?php
function myFync(){
$a=0;
echo $a;
$a++;
}
for($i=0; $i<10; $i++) myFync();
?>

В окне веб-браузера это будет выглядеть ТАК.

Итак, конструкция static говорит компилятору о том, что уничтожать указанную переменную для нашей функции между вызовами не надо. В то же время присваивание $a=0 сработает только один раз, а именно — при самом первом обращении к функции (так уж устроен static).

Рекурсия

Конечно, в PHP поддерживаются рекурсивные вызовы функций, т. е. вызовы функцией самой себя (разумеется, не до бесконечности, а в cоответствии с определенным условием). Это бывает чрезвычайно удобно для таких задач, как, например, обход всего дерева каталогов вашего сервера (с целью подсчитать суммарный объем, который занимают все файлы), или для других задач. Рассмотрим для примера функцию, которая рекурсивно вычисляет факториал из некоторого числа n (обозначается n!).

Алгоритм стандартный: если n=0, то n!=1, а иначе n!=n*((n-1)!).

<?php
function Factor($n){
for($f=1; $n>1; $n--) $f*=$n;
return $f;
}
echo Factor(6);
?>

В окне веб-браузера это будет выглядеть ТАК.

Вложенные функции PHP

Стандарт PHP не поддерживает вложенные функции. Однако он поддерживает нечто, немного похожее на них. Итак, "вложенные" функции выглядят следующим образом:

<?php
function Parent($a){
echo $a;
function Child($b){
echo $b+1;
return $b*$b;
}
return $a*$a*Child($a); // фактически возвращает $a*$a*($a+1)*($a+1)
}
// Вызываем функции
Parent(10);
echo "<br>";
Child(30);
// Попробуйте теперь ВМЕСТО этих двух вызовов поставить такие
// же, но только в обратном порядке. Что, выдает ошибку?
// Почему, спрашиваете? Читайте дальше!
?>

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

Parent(10);
Parent(20);

Последний вызов породит ошибку: функция Child() уже определена. Это произошло потому, что Child() определяется внутри Parent(), и до ее определения управление программы фактически доходит дважды (при первом и втором вызовах Parent()). Поэтому-то интерпретатор и "протестует": он не может второй раз добавить Child() в таблицу функций.

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

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

Пример:

<?php
$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 воспринимает & только в правой части оператора присваивания сразу после знака =). Но выход есть. Воспользуемся специальным синтаксисом описания функции, возвращающей ссылку:

<?php
$a=100;
function &R() // & — возвращает ссылку
{ global $a; // объявляет $a глобальной
return $a; // возвращает значение, а не ссылку!
}
$b=&R(); // не забудьте & !!!
$b=0; // присваивает переменной $a!
echo $a; // выводит 0. Это значит, что теперь $b — синоним $a
?>

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