Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Очень просто о PHP, от элементарных понятий до ООП - часть 6. Создание и вызов собственных функций.


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

Итак, продолжим:


Чуток знаний

Если вы регулярно получали дозу PHP 101, вы знаете достаточно о PHP чтобы писать собственные простые программы. Однаго, они будут "процедурными", или линейными - выражения в них будут исполняться последовательно, одно за другим - просто потому что пока-что вы использовали только этот стиль программирования.

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

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

Человеческим языком

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

Каждый язык программирования имеет свои встроенные функции, и обычно позволяет разработчиками определять свои собственные функции. Например, если у меня на столе есть данные о прибылях и убытках за год, и я хочу увеличить каждую циферку на 35%, мне следует вызвать бухгалтеров из фирмы по соседству чтобы они сделали это для меня... или я могу написать простую функцию на PHP, которая называется obmanAkcionerov(), чтобы она сделала за меня всю работу (так быстрее и PHP не берёт почасовую оплату).

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

Блюз в понедельник утром

Чтобы понять как работает функция, рассмотрите следующий пример:

<?php

// определим стандартную функцию
function myStandardResponse() { 
    echo "Исчезни, придурок!"; 
} 

// в автобусе 
echo "Привет, детка, а дай телефон позвонить?"; 
myStandardResponse(); 

// в офисе 
echo "А сделай работу за Эдика, а то он уехал на Таити на месяцок? Я
думаю ты справишься, хоть и придётся вставать пораньше, а ложиться
заполночь. Правда, заплатить за это мы не сможем, так как у нашей
бухгалтерши ПМС...";
myStandardResponse(); 

// на вечеринке
echo "Киса, хочешь семок?"; 
myStandardResponse();

?>

После запуска мы увидим что-то наподобие:

Привет, детка, а дай телефон позвонить?

Исчезни, придурок!

А сделай работу за Эдика, а то он уехал на Таити на месяцок? Я думаю ты справишься, хоть и придётся вставать пораньше, а ложиться заполночь. Правда, заплатить за это мы не сможем, так как у нашей бухгалтерши ПМС...

Исчезни, придурок!

Киса, хочешь семок?

Исчезни, придурок!

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

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

Конечно, определение функции - это только половина головоломки, чтобы она перестала быть бесполезной, вам следует "вызвать" её. В PHP, как и в миллионе других языков, это осуществляется именем функции. Вызов определённой пользователем функции идентичен вызову встроенной функции PHP, такой как echo() или explode().

Вот типичный вид функции:

function имя функции (возможные дополнительные аргументы) { 
    выражение 1... 
    выражение 2... 
    . 
    . 
    . 
    выражение n... 
}

У меня есть аргумент ...или два!

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

Введите аргументы.

Аргументы работают, замещая определённую переменную внутри функции. Значение этой переменной передаётся функции во время выполнения из основной программы. Так как ввод для функции будет разным каждый раз при вызове, то и вывод будем также разным.

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

<?php

// определим функцию
function getCircumference($radius) { 
    echo "Длина окружности с радиусом $radius равна "
    .sprintf("%4.2f", (2 * $radius * pi())).""; 
} 

// вызов функции с аргументом 
getCircumference(10); 

// вызов функции с другим аргументом
getCircumference(20);

?>

В этом примере, когда функция getCircumference() вызывается с аргументом, аргумент назначается заменителю переменной $radius внутри функции, а затем обрабатывается кодом внутри определения функции.

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

<?php

// определим функцию
function changeCase($str, $flag) { 
    /* проверим флаг (переменную-индикатор) и выберем ветвь кода */ 
    switch($flag) { 
        case 'U': 
            print strtoupper($str)."
"; break; case 'L': print strtolower($str)."
"; break; default: print $str."
"; break; } } // вызовем функцию changeCase("Корова прыгает через ковбойца", "U"); changeCase("Превед, корова и ковбой", "L"); ?>

Здесь, в зависимости от значения второго аргумента, программа проходит по нужной ветви и изменяет первый аргумент.

Круги на песке

Рассмотренные ранее функции просто печатали свой вывод на экран. Но что если нам надо функция, делающая что-нибудь с результатом? Ну, в PHP вы можете определить функцию, которая возвращает значение, такое как результат вычислений, вызвавшему её выражению. Это делается при помощи выражения return в функции, как показано ниже:

<?php

// определим функцию
function getCircumference($radius) { 
    // возвращаем значение
    return (2 * $radius * pi()); 
} 

/* вызовем функцию с аргументом сохраним результат в переменной */ 
$result = getCircumference(10); 

/* вызовем эту-же функцию с другим аргументом и
   напечатаем возвращённое значение */ 
print getCircumference(20);

?>

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

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

<?php

// определим функцию
function getCircumference($radius) { 
// возвращаем значение 
    return (2 * $radius * pi()); 
} 

// печатаем возвращаемое значение после его форматирования
print "Ответ: ".sprintf("%4.2f", getCircumference(20)); 

?>

Возвращаемые значения не должны быть числами или строками, функция может легко вернуть массив (помните?), как показано в следующем примере:

<?php

/* определим функцию, могущую принимать список email-адресов*/ 
function getUniqueDomains($list) { 
    /* проходим по списку, разделяем адресатов и домены в другой массив */ 
    $domains = array(); 
    foreach ($list as $l) { 
        $arr = explode("@", $l); 
        $domains[] = trim($arr[1]); 
    } 
    // удаляем повторы и возвращаем массив 
    return array_unique($domains); 
} 

// читаем адреса email из файла в массив
$fileContents = file("data.txt"); 

/* передаём содержимое файла функции и получаем результат в массиве */ 
$returnArray = getUniqueDomains($fileContents); 

// обрабатываем полученный массив 
foreach ($returnArray as $d) { 
    print "$d, "; 
}

?>

Предположим, что файл data.txt выглядел так:

test@test.com
a@x.com
zooman@deeply.bored.org
b@x.com
guess.me@where.ami.net
testmore@test.com

, тогда вывод программы будет такой:

test.com, x.com, deeply.bored.org, where.ami.net,

Обратите внимание, выражение return прерывает выполнение программы внутри функции.

Походный порядок

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

<?php

// определим функцию 
function introduce($name, $place) { 
    print "Привет, я $name с горада $place"; 
} 

// call function 
introduce("Лёлик", "Борщаговка");

?>

Вывод такой:

Привет, я Лёлик с горада Борщаговка

Здесь, если вы поменяете местами аргументы при передаче их функции, вы увидите бред:

Привет, я Борщаговка с горада Лёлик

А если вы вообще забудете передать аргументы:

Warning: Missing argument 2 for introduce() in xx.php on line 3
Привет, я с горада

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

<?php

// определим функцию 
function introduce($name="John Doe", $place="Лондон") { 
    print "Привет, я $name с горада $place"; 
} 

// вызовем функцию
introduce("Лёлик");

?>

В этом случае вывод будет таким:

Привет, я Лёлик с горада Лондон

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

Чудесное сокращение списка аргументов

Все примере выше имеют одно сходство: число аргументов постоянно. Однако, PHP 4.x поддерживает переменные списки аргументов, используя команды func_num_args() и
func_get_args(). За неимением лучшего названия они именуются "функциями функций". Не ломайте свой язык, а лучше гляньте как они могут использоваться:

<?php

// определим функцию 
function someFunc() { 
   // получим аргументы 
   $args = func_get_args(); 
     
   // напечатаем их 
   print "Вы передали мне такие аргументы:";
 foreach ($args as $arg) { 
 print " $arg "; 
 } 
print "<br />"; 
}

?>

Хм... если вы любопытны, можете попробовать функции someFunc() массив и увидите, что вместо печати элементов массива будет напечатано слово "Array". Это можно исправить, добавив тест на аргумент-массив внутри функции:

<?php

// определим функцию 
function someFunc() { 
    // получисо число переданных аргументов
    $numArgs = func_num_args(); 
     
// получим аргументы
    $args = func_get_args(); 
     
    // напечатаем аргументы 
    print "Вы передали мне такие аргументы: "; 
    for ($x = 0; $x < $numArgs; $x++) { 
        print "<br />Аргумент $x: "; 
        /* проверка, не передан-ли массив, и если да,
           то проходим по его содержимому */ 
        if (is_array($args[$x])) { 
            print " МАССИВ "; 
            foreach ($args[$x] as $index => $element) { 
                print " $index => $element "; 
            } 
        } 
        else { 
            print " $args[$x] "; 
        } 
    } 
} 

// вызываем функцию с разными аргументами
someFunc("зелёный", "чёрный", "синий", array(4,5), "жёлтый");

?>

Действуем глобально

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

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

<?php

// определим переменную в основной программе
$today = "вторник"; 

// определим функцию 
function getDay() {
    // определим переменную в функции
    $today = "суббота"; 
    // напечатаем переменную 
    print "День внутри функции: $today<br />"; 
} 

// вызовем функцию
getDay(); 

// напечатаем переменную
print "День вне функции: $today";

?>

После запуска скрипта вы увидите нечто подобное:

День внутри функции: суббота

День вне функции: вторник

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

Обратное утверждение также верно: переменные, определённые внутри функции, не могут быть "видимы" вне её. Для иллюстрации этого посмотрите это пример и его вывод (или отсутствие):

<?php

// определим функцию
function getDay() { 
    // определим переменную внутри функции
    $today = "суббота"; 
} 

getDay(); 
print "Сегодня $today";

?>

Вот вывод:

Сегодня

В зависимости, во что вы установили error_reporting в вашем php.ini, вы можете увидеть сообщение об ошибке:

Notice: Undefined variable: today in x1.php on line 10

Однако, я не говорил что это нельзя обойти. Чтобы сделать переменные функции доступными вне её (и наоборот), всё что вам надо, это определить их как "глобальные" словом global (Ты знал! Ты знал! ).

Вот переписанный выше пример, в этот раз мы определяем переменную $today как глобальную:

<?php

// определим переменную в основной программе
$today = "вторник"; 

// определим функцию
function getDay() { 
    // создадим глобальную переменную
    global $today; 
     
    // определим переменную внутри функции
  $today = "суббота"; 
    // напечатаем переменную
    print "Сегодня $today - внутри функции <br />"; 
} 

// напечатаем переменную 
print "Сегодня $today - перед вызовом функции <br />"; 

// вызовем функцию 
getDay(); 

// напечатаем переменную
print "Сегодня $today - после вызова функции";

?>

И вывод:

Сегодня вторник - перед вызовом функции
Сегодня суббота - внутри функции
Сегодня суббота - после вызова функции

Таким образом, если переменная объявлена глобальной, то она будет доступна на глобальном уровне, и ею можно манипулировать - как внутри, так и за пределами функции. В PHP существуют так называемые суперглобальные переменные, которые видими независимо от того внутри вы функци или вне её. Вы уже видели, некоторые из этих специальных переменных в действии: переменные $ _SERVER, $ _POST и $ _GET - суперглобальные, поэтому вы можете получить такие значения как имя выполняемого в настоящее время скрипта даже внутри функции. Суперглобальные функции - это хорошо, поскольку они всегда рядом когда вам надо, и вам не требуется прыгать сквозь всякие обручи как в цирке, чтобы получить сохранённые в них данные. Прочтите больше о суперглобальных переменных и об области видимости переменных на страницах http://www.php.net/manual/en/language.variables.predefined.php и http://www.php.net/manual/en/language.variables.scope.php.

Проверяя ссылки

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

Запутано? Ну, это, вероятно, легче понять на примере. Давайте начнем с такого:

<?php

// создадим переменную 
$today = "суббота"; 

// функция для печати значения переменной
function setDay($day) { 
    $day = "вторник"; 
    print "Сегодня $day - внутри функции <br />"; 
} 

// вызовем функцию 
setDay($today); 

// напечатаем значение переменной 
print "Сегодня $today - вне функции";

?>

Это мы уже видели раньше, и вы уже знаете что будет напечатано:

Сегодня вторник - внутри функции
Сегодня суббота - вне функции

Это потому, что когда вызывается функция getDay(), она получает значение "суббота" - передача по значению. Оригинальная переменная остаётся неизменённой, функции пересылается только её содержимое. Функция обрабатывает это содержимое, изменяет и показывает его.

А теперь посмотрим как работает передача по ссылке:

<?php

// создадим переменную
$today = "суббота"; 

// функция для печати значения переменной
function setDay(&$day) { 
    $day = "вторник"; 
    print "Сегодня $day - внутри функции <br />"; 
} 

// вызовем функцию
setDay($today); 

// напечатаем значение переменной
print "Сегодня $today - вне функции";

?>

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

Сегодня вторник - внутри функции
Сегодня вторник - вне функции

Теперь вы понимаете почему я сказал, что обсуждение переменных было-бы неполным без упоминания о двух способах передачи переменных. Это, конечно, то, что делает ключевое слово global внутри функции: использует ссылку чтобы изменения переменной в функции также отражали её изменения вовне. Руководство по PHP отлично это поясняет: "..когда вы декларируете переменную $var как global $var, фактически вы создаёте ссылку на глобальную переменную". Подробне читайте всё о ссылках на странице http://www.zend.com/manual/language.references.php.

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

В седьмой части я покажу вам как объединить связанные функции в классы, а также расскажу о крутой возможности PHP 5 - объектной модели. Вы определённо не захотите пропустить эту часть!

Оригинал перевода статьи: http://www.figli-migli.org.ua/books/2634-php101-13a


В избранное