Все нюансы работы с функциями в PHP. Часть 2

"Все аспекты самостоятельного создания и продвижения сайтов
от практика с многолетним опытом." — блог Рудь Сергея
info@site-on.net
Заметка: активирована адаптивная версия сайта, которая автоматически подстраивается под небольшой размер Вашего браузера и скрывает некоторые детали сайта для удобства чтения. Приятного просмотра!
30.08.2014

Здравствуйте уважаемые читатели блога Site on! В предыдущей статье мы узнали что есть функция в PHP, научились создавать собственные функции, передавать в них аргументы и вызывать их для выполнения. Продолжая тему функций в PHP необходимо подчеркнуть следующие вещи:

Функции, зависящие от условий

Мы можем создавать (определять, описывать) функцию, в зависимости от условия. Например:

<?php
$apply = true;

sayHi(); //вызвали функцию sayHi, её можно вызывать в любом месте

/*здесь нельзя вызвать функцию sayGoodbye, так как мы ещё не проверили
условие и не зашли внутрь конструкции if*/
if($apply){
    function sayGoodbye(){
        echo 'Всем пока! <br>';
    }
}

/*вот теперь можем вызвать sayGoodbye*/
sayGoodbye();

function sayHi(){
    echo 'Всем привет! <br>';
}

Результат:

И взгляните на этот пример:

<?php
$apply = true;

sayHi(); /*вызвали функцию sayHi, её можно вызывать в любом месте*/

/*а вот что будет, если вызвать sayGoodbye здесь*/
sayGoodbye();

if($apply){
    function sayGoodbye(){
        echo 'Всем пока! <br>';
    }
}

function sayHi(){
    echo 'Всем привет! <br>';
}

Результат:

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

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

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

Вложенная функция – это функция, объявленная внутри другой функции. Пример:

<?php

/*Здесь нельзя вызвать sayGoodbye, так как
она появится только после вызова функции sayHi*/

sayHi(); /*вызвали функцию sayHi, её можно вызывать в любом месте*/

/*Теперь можем вызывать sayGoodbye*/
sayGoodbye();

function sayHi(){
    echo 'Всем привет! <br>';

    function sayGoodbye(){
        echo 'Всем пока! <br>';
    }
}

Опять-таки, при первом обходе интерпретатор PHP помечает себе, что он нашёл описание функции sayHi, но не заходит внутрь её тела, он видит только название, а раз интерпретатор не заходит внутрь тела sayHi, то он понятия не имеет, что внутри мы определяем ещё одну функцию – sayGoodbye.

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

Но стоит обратить внимание на очень тонкий момент в ситуации выше: функция sayHi становится одноразовой, потому что если мы ещё раз её вызовем, то PHP опять наткнётся на определение функции sayGoodbye, а в PHP так делать нельзя – нельзя переопределять функции. Об этом и о том, как с этим бороться я писал в предыдущей статье.

В PHP описанные выше приёмы применяются очень редко, чаще их можно увидеть, например, в JavaScript.

Область видимости переменных

В PHP ровно две области видимости: глобальная и локальная. В каждом языке программирования области видимости устроены по-разному. Например, в C++ даже в циклах своя (локальная) область видимости. В PHP, кстати, циклы – это глобальная область видимости. Но сегодня мы говорим о функциях.

У функций в PHP своя, внутренняя область видимости (локальная), то есть все переменные внутри функции видны только внутри этой самой функции.

Итак, ещё раз: все, что вне функций – это глобальная область видимости, все, что внутри функций – локальная область видимости. Пример:

<?php

function sayHi($name){
    echo 'Привет, '.$name.'! <br>';
    $name = 'Рудь Сергей';
}

$name = 'Андрей';
sayHi($name);
echo $name; // ?

Уважаемые знатоки, внимание, вопрос! Что выведет последняя инструкция echo $name; ?

Ответ:

Как вы сами видели, у нас было 2 переменных $name, одна внутри функции (локальная область видимости), другая просто в коде (глобальная область видимости), последнее присвоение в переменную $name было $name = 'Рудь Сергей'; Но так как это было внутри функции, то там оно и осталось. В глобальной же области видимости последним присвоением было $name = 'Андрей'; что мы собственно и видим в результате.

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

Давайте я проиллюстрирую области видимости на рисунке:

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

Обращение к глобальным переменным из локальной области видимости

Но что если нам всё-таки нужно из функции обратиться к той самой переменной $name из глобальной области видимости, да не просто обратиться, а изменить её? Для этого есть 3 основных варианта. Первый из них использование ключевого слова global:

<?php

function sayHi($name){
    echo 'Привет, '.$name.'! <br>';
    global $name; /*начиная с этого момента мы имеем ввиду
    глобальную переменную $name*/
    $name = 'Рудь Сергей';
}

$name = 'Андрей';
sayHi($name);
echo $name; // ?

Результат:

Но у этого способа есть минус, с тех пор как мы обратились к глобальной переменной $name мы потеряли (переписали) локальную переменную $name.

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

$name = 'Андрей'; //Тоже самое что и
$GLOBALS['name'] = 'Андрей';

Следовательно:

<?php

function sayHi($name){
    echo 'Привет, '.$name.'! <br>';
    $GLOBALS['name'] = 'Рудь Сергей';
}

$name = 'Андрей';
sayHi($name);
echo $name; // ?

Результат тот же, что и при использовании ключевого слова global:

Только в этот раз мы не переписали локальную переменную, то есть переменная $name внутри функции осталась прежней и равна "Андрей", а не "Рудь Сергей".

Передача аргументов по ссылке

Третий способ – это передача адреса (ссылки) переменной, а не её значения. Ссылки в PHP не очень удались, в отличие от других языков программирования. Тем не менее, я расскажу вам единственный правильный вариант передачи аргумента по ссылке в функцию, который нормально поддерживается в PHP 5.3 и выше. Есть и другие способы работы со ссылками, но они работали в PHP 5.2 и ниже, в итоге сами разработчики PHP решили от них отказаться, поэтому не будем о них.

Так вот, ПРАВИЛЬНАЯ передача аргумента по ссылке в PHP 5.3 и выше осуществляется следующим образом:

function sayHi(&$name){

Мы в самом описании функции добавили значок амперсанд (&) – этот значок означает, что мы принимаем не значение переменной, а ссылку (адрес) на это значение в памяти. Ссылки в PHP позволяют создать две переменные, указывающие на одно и то же значение. Это означает, что при изменении одной из этих переменных, меняются обе, так как в памяти они обращаются к одному и тому же значению.

И в итоге имеем:

<?php

function sayHi(&$name){ //принимаем не значение, а ссылку на значение
    echo 'Привет, '.$name.'! <br>';
    $name = 'Рудь Сергей';
}

$name = 'Андрей';
sayHi($name);
echo $name; // ?

Результат:

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

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

<?php

function sayHi($name){
    $c = 0; // счётчик
    echo 'Привет, '.$name.'! <br>';
    $c++; // увеличиваем счётчик на 1
    echo 'Всего поздоровались ' . $c . ' раз.<hr>';
}

sayHi('Рудь Сергей');
sayHi('Андрей');
sayHi('Дмитрий');

Результат:

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

<?php

function sayHi($name){
    static $c = 0; // счётчик, сделали статическим
    echo 'Привет, '.$name.'! <br>';
    $c++; // увеличиваем счётчик на 1
    echo 'Всего поздоровались ' . $c . ' раз.<hr>';
}

sayHi('Рудь Сергей');
sayHi('Андрей');
sayHi('Дмитрий');

Результат:

Возврат значений

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

<?php

function getSquare($number){
    $result = $number*$number;
    echo $result;
}

getSquare(5);

Результат:

Сделаем так, чтобы вместо вывода на экран она возвращала результат выполнения. Для этого используется ключевое слово return:

<?php

function getSquare($number){
    $result = $number*$number;
    return $result;
}

getSquare(5);

Результат:

Теперь мы можем использовать это различными способами:

<?php

function getSquare($number){
    $result = $number*$number;
    return $result;
}

echo getSquare(5); //выводит результат
echo '<br>';
$num = getSquare(5); // присвоили результат в переменную
echo $num; // вывели переменную на экран

Результат:

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

<?php

function getSquare($number){
    $result = $number*$number;
    return $result;
    echo 'До меня PHP никогда не дойдёт :(';
}

echo getSquare(5); //выводит результат
echo '<br>';
$num = getSquare(5); // присвоили результат в переменную
echo $num; // вывели переменную на экран

Результат:

То есть return – это ещё и выход из функции. Его можно использовать и без возвращаемого значения, только ради выхода.

Рекурсивная функция

Рекурсивная функция – это функция, которая вызывает сама себя. Рекурсия используется не часто и считается ресурсоёмкой (медленной) операцией. Но бывает, что использование рекурсии самый очевидный и простой вариант. Пример:

<?php

function countPlease($number){
    echo $number . '<br>';
    if($number < 20){ // чтобы рекурсия не стала бесконечной
        countPlease(++$number); // функция countPlease вызвала сама себя
    }
}

countPlease(1);

Результат:

Если вы знаете, как обойтись без рекурсии, то лучше так и сделать.

Строгая типизация в PHP (уточнение типа)

В PHP сделаны мелкие шаги к строгой типизации, благодаря этому мы можем заранее указать, какой тип должна принимать функция (это называется type-hint):

<?php

function countPlease(array $numbers){
    // какой-то код
}

countPlease(1);

Результат:

Catchable fatal error: Argument 1 passed to countPlease() must be an array, integer given, called in /home/index.php on line 7 and defined in /home/index.php on line 3

Ошибка нам говорит, что функция ожидает принять массив, а вместо этого мы ей передаём число. К сожалению, пока что мы можем уточнять тип только для массивов (array), а с PHP 5.4 ещё добавился такой вариант как callable:

<?php

function countPlease(callable $v){
    $v();
}

countPlease("getEcho");

function getEcho(){
    echo 'Вызвали функцию getEcho;
}

Callable проверяет, может ли переданное значение быть вызвано в качестве функции. Callable может быть как именем функции, заданным строковой переменной, так и объектом и именем вызываемого метода. Но про объекты и методы мы поговорим позже (это раздел объектно-ориентированного программирования), а с функциями вы уже знакомы. Результат работы я вам показать не могу, так как у меня сейчас стоит PHP 5.3, но он был бы:

Вызвали функцию getEcho

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

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

<?php

function getEcho(){
    echo 'Вызвали функцию getText';
}

$age = 22;
getEcho('Рудь Сергей', $age);

Результат:

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

func_num_args() - Возвращает количество аргументов, переданных функции
func_get_arg(порядковый номер) - Возвращает элемент из списка аргументов
func_get_args() - Возвращает массив, содержащий аргументы функции

Пример:

<?php

function getEcho(){
    echo 'Вызвали функцию getText <br>';
    echo func_get_arg(0);
}

$age = 22;
getEcho('Рудь Сергей', $age);

Результат:

Заключение

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

Если у кого-то есть желание набить руку, но нет идей как это сделать – лучшим способом будет написание уже готовых (встроенных) функций PHP, например, можно написать собственную функцию count() или любую другую.

Благодарю всех за внимание и до новых встреч! Если что-то не понятно, смело задавайте ваши вопросы в комментариях!

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

Ваш email:
Вы можете помочь развитию проекта, сделав всего 1 клик:
Спасибо!
Пожалуйста, прокомментируйте, как Вам моя статья?
Имя:
Комментарий:

Если Вы хотите вставить код, пожалуйста, заключайте его в [code][/code]

Подписаться на новые комментарии:

E-mail:


Защита от спама: пожалуйста, напишите слово "сел" справа налево
Ответ:
Подписаться на новые комментарии без комментирования - Email:
Защита от спама: пожалуйста, напишите слово "сел" справа налево
Ответ:

04.02.2015 11:50:00 Илья:
В мануале прочитал, что в php 5.6 появилась возможнасть передовать аргументы в таком виде - my_funk(...[1, 2]) .
Обьясните ,пожалуйста ,что это значит и особенно не понятно троеточие.
04.02.2015 20:02:46 Сергей отвечает:
Не вникал в новшества PHP 5.6, но [1, 2] - это так задаётся массив начиная с PHP 5.4. То есть тоже самое что и:
$data = array(1,2);
my_funk(...$data);


Либо:
my_funk(...array(1,2));

Что значит троеточие, не знаю, нужна ссылка на источник, где так написано. Думаю там в контексте понятно.
14.11.2015 17:44:26 Roman отвечает:
"..." - означает, что в функцию передается переменное количество аргументов.
14.11.2015 21:24:14 Сергей отвечает:
Да, уже ознакомился :)

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


03.01.2016 20:55:54 Николай:
Сергей спасибо за статью в которой все понятно. Напишите пожалуйста почему при переборе массива методом foreach ( $array as &$value) последний элемент сохраняет ссылку и ее нужно убирать unset(), это свойство php для такого цикла. Почему этого не происходит (последний элемент разрывает ссылку сам) методом foreach ($array as $key=>$value)?
04.01.2016 18:37:28 Сергей отвечает:
Потому что в конструкции
foreach ($array as $key=>$value)
ссылки нет. Ссылка создается с помощью знака & программистом, а не PHP.

Поэтому здесь ссылок нет:
foreach ($array as $value)
foreach ($array as $key => $value)


а здесь есть и их нужно удалять в конце:
foreach ($array as &$value)
foreach ($array as $key => &$value)
04.01.2016 18:39:19 Сергей отвечает:
p.s. foreach - это не метод и не функция, это конструкция. Методом в PHP называются другие вещи, поэтому лучше не путать.

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


18.03.2016 18:27:48 Тарас:
Очень доходчиво описаны некоторые неявные особенности функций, спасибо за статью.
Теперь вопрос:
Есть функция вида
function banner() {
$banner = 'Тут могла быть ваша реклама<br>';
return $banner;
}

Есть шаблон вёрстки вида:

<body>
<div id="footer">
{FOOTER}
</div>
</body>

Есть класс для обработки шаблона страницы (привожу чась класса):

class templating {
var $arr_values = array(); // Переменные шаблона
var $html; // HTML - код
// Функция установки значения
function set_value($key, $var) {
$key = '{'.$key.'}';
$this->arr_values[$key] = $var;
}
// Парсинг шаблона
function tpl_parse() {
foreach($this->arr_values as $find => $replace) {
$this->html = str_replace($find, $replace, $this->html);
}
}
}
// Создание экземпляра класса
$tpl = new templating;

Есть вызов функций класса:

$banner = banner();
$tpl->set_value('FOOTER',$banner);
// Запускаем парсинг шаблона
$tpl->tpl_parse();
// Выводим HTML
echo $tpl->php;

Код отрабатывает корректно, до момента вставки функции. Её просто не вставляет в код шаблона. Если её передавать в шаблон как вложение php "<? $banner;?>" или "<? banner();?>" она не выполняется.
Я просто уже не понимаю в какую сторону копать...
18.03.2016 23:33:12 Тарас отвечает:
В итоге методом "научного тыка" проблему победил. Решением оказалась ф-я такого вида:
function menu() {
// Подключаем используемые переменные:
global $page;
if ($page != "blog") {
// Вариант для вывода вставкой вида <?php menu();?_> в html код.
echo "<a href='index.php'>Главная</a><br>";
echo "<a href='index.php?page=news'>Новости</a><br>";
} else {
// Вариант для вывода вставкой вида menu(); в html код через подмену переменных шаблона.
$menu .= "<a href='index.php'>Главная</a><br>";
$menu .= "<a href='index.php?page=news'>Новости</a><br>";
}
return $menu;
}

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

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

Использую для работы
Мои расширения
Свежие статьи
Рекомендую
Горячо обсуждаемые
Подписка
  • Следовать в twitter:
  • Подписаться по RSS:
  • Подписаться по E-mail:
  • Следить ВКонтакте:
  • Следить на Facebook:
Пользовательское соглашение об условиях использования сайта и Политика конфиденциальности
Перепечатывание или копирование материалов сайта (текста, изображений и другого содержимого) для их публичного или коммерческого использования в сети Интернет, либо в печатных изданиях строго запрещены. При нарушении данного правила, с нашей стороны будут предприняты соответствующие меры, вплоть до судебной жалобы.
© site-on.net
Шрифт: +стандартно-