Функции и "области видимости" + "Генераторы"
Primary tabs
ТЕЗИСЫ:
- все переменные, которые объявляются и используются в функции, по умолчанию локальны для этой функции.
Пример функции
<!DOCTYPE html> <html lang="ru"> <head> <title>Пример функции и ее использования</title> <meta charset='utf-8'> </head> <body> <?php // Функция принимает ассоциативный массив и создает несколько // тегов <option value="$key">$value, где $key - очередной // ключ массива, а $value - очередное значение. Если задан // также и второй параметр, то у соответствующего тега option // проставляется атрибут selected. function selectItems($items, $selected = 0) { $text = ""; foreach ($items as $k => $v) { if ($k === $selected) $ch = " selected"; else $ch = ""; $text .= "<option$ch value='$k'>$v</option>\n"; } return $text; } // Предположим, у нас есть массив имен и фамилий $names = [ "Weaving" => "Hugo", "Goddard" => "Paul", "Taylor" => "Robert", ]; // Если был выбран элемент, вывести информацию if (isset($_REQUEST['surname'])) { $name = $names[$_REQUEST['surname']]; echo "Вы выбрали: {$_REQUEST['surname']}, {$name} "; } ?> <!-- Форма для выбора имени человека. --> <form action="<?=$_SERVER['SCRIPT_NAME']?>" method="POST"> Выберите имя: <select name="surname"> <?=selectItems($names, $_REQUEST['surname'])?> </select><br /> <input type="submit" value="Узнать фамилию"> </form> </body> </html>
-Имя функции не зависит о регистра
-Функция return возвращает в том числе объекты (массивы)
<?php function mySqr($n) { return $n * $n; } $value = mySqr(4); echo $value; // выводит 16 echo mySqr(10); // выводит 100 ?>
<?php ## Возврат массива function silly() { return [1, 2, 3]; } // Присваивает массиву значение array(1,2,3) $arr = Silly(); var_dump($arr); // выводим массив // Присваивает переменным $a, $b, $c первые значения из списка list ($a, $b, $c) = Silly(); // Допустимо, начиная с PHP 5.4 echo Silly()[2]; // 3 ?>
## Неявный возврат null
function f() { }
var_dump(f());
// null
?>
Объявление и вызов функции
- Вызывать функции нужно после того как они будут определены
Это означает, что если мы подключяем определение функции во внешнем файле через инструкцию require_once , а только потом вызываем функцию, то это корректное.
Параметры по умолчанию
Передача параметров по ссылке
<?php ## Передача параметров по значению function increment($a) { echo "Текущее значение: $a<br />"; $a++; echo "После увеличения: $a<br />"; } # ... $num = 10; echo "Начальное значение: $num<br />"; increment($num); echo "После вызова функции: $num<br />"; ?>
Здесь создается локальная переменная $a , которая представляет собой копию оригинала $num. Изменение копии не отражается на изменение оригинала. Чтобы оригинал поменялся, мы должны указать символ амперсанда перед переменной a: "&$a" - тогда это ссылочная переменная.
Переменное число параметров
?php ## Переменное число параметров функции (устаревший способ) function myecho() { for ($i = 0; $i < func_num_args(); $i++) { echo func_get_arg($i)."<br />\n"; // выводим элемент } } // Отображаем строки одну под другой myecho("Меркурий", "Венера", "Земля", "Марс"); ?>
<?php ## Использование func_get_args() function myecho() { foreach (func_get_args() as $v) { echo "$v<br />\n"; // выводим элемент } } // Отображаем строки одну под другой myecho("Меркурий", "Венера", "Земля", "Марс"); ?>
<?php ## Переменное число параметров функции (современный способ) function myecho(...$planets) { foreach ($planets as $v) { echo "$v<br />\n"; // выводим элемент } } // Отображаем строки одну под другой myecho("Меркурий", "Венера", "Земля", "Марс"); ?>
<?php ## Использование ... function toomanyargs($fst, $snd, $thd, $fth) { echo "Первый параметр: $fst<br />"; echo "Второй параметр: $snd<br />"; echo "Третий параметр: $thd<br />"; echo "Четвертый параметр: $fth<br />"; } // Отображаем строки одну под другой $planets = ["Меркурий", "Венера", "Земля", "Марс"]; toomanyargs(...$planets); ?>
Типы аргументов и возвращаемого значения
## Типы аргументов и возвращаемого значения
function sum(int $fst, int $snd) : int
{
return $fst + $snd;
}
echo sum(2, 2); // 4
echo sum(2.5, 2.5); // 4
?>
<?php ## Строгая типизация declare(strict_types = 1); function sum(int $fst, int $snd) : int { return $fst + $snd; } echo sum(2, 2); // 4 echo sum(2.5, 2.5); // Fatal Error в PHP < 7,Exception TypeError в PHP >=7 ?>
Локальные переменные
<?php $a = 100; // глобальная переменная, равная 100 function test($a) { echo $a; // выводим значение параметра $a // Параметр $a не имеет к глобальной переменной $a никакого отношения! $a++; // изменяется локальная копия значения, переданного в $a } test(1); // выводит 1 echo $a; // выводит 100 - глобальная переменная $a не изменилась ?>
<?php ## Локальные переменные function silly() { $i = mt_rand(); // записывает в $i случайное число echo "$i<br />"; // выводит его на экран // Эта $i не имеет к глобальной $i никакого отношения! } // Выводит в цикле 10 случайных чисел for ($i = 0; $i != 10; $i++) silly(); ?>
Глобальные переменные
## Глобальные переменные в функции
$monthes = [
1 => "Январь",
2 => "Февраль", // ...
12 => "Декабрь" ];
// Возвращает название месяца по его номеру. Нумерация начинается с 1!
function getMonthName($n)
{
global $monthes;
return $monthes[$n];
}
// Применение
echo getMonthName(2);
// выводит "Февраль"
?>
Массив $GLOBALS
function getMonthName($n) { return $GLOBALS["monthes"][$n]; }
Самовложенность
Как работает инструкция global
function test() { global $a; $a = 10; }
То же самое:
function test() { $a = &$GLOBALS['a']; $a = 10; }
<?php ## Особенности инструкции global $a = 100; function test() { global $a; unset($a); } test(); echo $a; // выводит 100, т. е. настоящая $a не была удалена в test()! ?>
Статические переменные
<?php ## Статические переменные function selfcount() { static $count = 0; $count++; echo $count; } for ($i = 0; $i < 5; $i++) selfcount(); ?>
Рекурсия
Факториал
<?php ## Статические переменные function factor($n) { if ($n <= 0) return 1; else return $n * factor($n - 1); } echo factor(20); ?>
Пример функции: dumper()
<?php // Распечатывает дамп переменной на экран function dumper($obj) { echo "<pre>",htmlspecialchars(dumperGet($obj)),"</pre>"; } // Возвращает строку - дамп значения переменной в древовидной форме (если это массив или объект). В переменной $leftSp хранится строка с пробелами, которая будет выводиться слева от текста. function dumperGet(&$obj, $leftSp = "") { if (is_array($obj)) { $type = "Array[".count($obj)."]"; } elseif (is_object($obj)) { $type = "Object"; } elseif (gettype($obj) == "boolean") { return $obj? "true" : "false"; } else { return "\"$obj\""; } $buf = $type; $leftSp .=" "; for (Reset($obj); list($k, $v) = each($obj);) { if ($k === "GLOBALS") continue; $buf .= "\n$leftSp$k => ".dumperGet($v, $leftSp); } return $buf; } dumper($GLOBALS); ?>
Вложенные функции
<?php ## Вложенные функции function father($a) { echo $a, "<br />"; function child($b) { echo $b + 1, "<br />"; return $b * $b; } return $a * $a * child($a); // Фактически возвращает $a * $a * ($a+1) * ($a+1) } father(10); child(30); // Попробуйте теперь ВМЕСТО этих двух вызовов поставить такие // же, но только в обратном порядке. Что, выдает ошибку? // Почему, спрашиваете? Читайте дальше! ?>
Условно определяемые функции
Эмуляция функции virtual()
<?php ## Эмуляция virtual() в CGI-версии PHP // Функция virtual() не поддерживается? if (!function_exists("virtual")) { // Тогда определяем свою echo "virtual"; function virtual($uri) { $url = "http://".$_SERVER["HTTP_HOST"].$uri; echo file_get_contents($url); } } // Пример - выводит корневую страницу сайта virtual("/"); ?>
Передача функций по ссылке
<?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(303); // вызов функции, имя которой хранится в $F ?>
<?php // Сравнение без учета регистра символов строк function fCmp($a, $b) { return strcmp(strtolower($a), strtolower($b)); } $riddle = ["g" => "Not", "o" => "enough", "d" => "ordinariness"]; uasort($riddle, "fCmp"); // Сортировка без учета регистра символов ?>
Использование call_user_func()
Использование call_user_func_array()
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" call_user_func($f, 101); // вызов функции, имя которой хранится в $f
\n"; // выводим элемент } } // То же самое, но предваряет параметры указанным числом пробелов function tabber($spaces, ...$planets) { // Подготавливаем аргументы для myecho() $new = []; foreach ($planets as $planet) { $new[] = str_repeat(" ", $spaces).$planet; } // Вызываем myecho() с новыми параметрами call_user_func_array("myecho", $new); } // Отображаем строки одну под другой tabber(10, "Меркурий", "Венера", "Земля", "Марс"); ?>
Анонимные функции
<?php ## Анонимная функция $myecho = function (...$str) { foreach ($str as $v) { echo "$v<br />\n"; // выводим элемент } }; // Вызов функции $myecho("Меркурий", "Венера", "Земля", "Марс"); ?>
<?php ## Передача анонимной функции в качестве параметра function tabber($spaces, $echo, ...$planets) { // Подготавливаем аргументы для myecho() $new = []; foreach ($planets as $planet) { $new[] = str_repeat(" ", $spaces).$planet; } // Пользовательский вывод задается извне $echo(...$new); } // Массив для вывода $planets = ["Меркурий", "Венера", "Земля", "Марс"]; // Отображаем строки одну под другой tabber(10, function(...$str) { foreach ($str as $v) { echo "$v<br />\n"; } }, ...$planets); ?>
Замыкания
Замыкание — это функция, которая запоминает состояние окружения в момент своего создания
"; $check = function(array $errors) use ($message) { if (isset($errors) && count($errors) > 0) { echo $message; foreach($errors as $error) { echo "$error
"; } } };
$check([]); // ... $erorrs[] = "Заполните имя пользователя"; $check($erorrs); // ... $message = "Список требований"; // Уже не изменить $erorrs = ["PHP", "MySQL", "memcache"]; $check($erorrs); ?>
Возврат функцией ссылки
<?php $a = 100; function r() { global $a; // объявляет $a глобальной return $a; // возвращает значение, а не ссылку! } $b = r(); $b = 0; // присваивает $b, а не $a! echo $a; // выводит 100 ?>
Технология отложенного копирования
// Тестируем разные функции на скорость test("takeVal"); test("takeRef"); test("takeValAndModif"); test("takeRefAndModif");
function test($func) { // Создаем большой массив $a = []; for ($i = 1; $i $func took %d itr/sec
", $N); } ?>
----Итераторы----
Стандартное поведение foreach
Генератор позволяет вам писать код, использующий foreach для перебора набора данных без необходимости создания массива в памяти, что может привести к превышению лимита памяти, либо потребует довольно много времени для его создания. Вместо этого, вы можете написать функцию-генератор, которая, по сути, является обычной функцией, за исключением того, что вместо возврата единственного значения, генератор может возвращать (yield) столько раз, сколько необходимо для генерации значений, позволяющих перебрать исходный набор данных.
<?php ## Стандартное поведение foreach class Monolog { public $first = "It's him."; protected $second = "The Anomaly."; private $third = "Do we proceed?"; protected $fourth = "Yes."; private $fifth = "He is still..."; public $sixth = "...only human."; } $monolog = new Monolog(); foreach ($monolog as $k => $v) { echo "$k: $v<br>"; } ?>
<?php ## Пример определения итератора /** * Каталог. При итерации возвращает свое содержимое. */ class FSDirectory implements IteratorAggregate { public $path; // Конструктор public function __construct($path) { $this->path = $path; } // Возвращает итератор - "представителя" данного объекта public function getIterator() { return new FSDirectoryIterator($this); } } /** * Класс-итератор. Является представителем для объектов FSDirectory * при переборе содержимого каталога. */ class FSDirectoryIterator implements Iterator { // Ссылка на "объект-начальник" private $owner; // Дескриптор открытого каталога private $d = null; // Текущий считанный элемент каталога private $cur = false; // Конструктор. Инициализирует новый итератор. public function __construct($owner) { $this->owner = $owner; $this->d = opendir($owner->path); $this->rewind(); } //* //* Далее идут переопределения виртуальных методов интерфейса Iterator //* // Устанавливает итератор на первый элемент public function rewind() { rewinddir($this->d); $this->cur = readdir($this->d); } // Проверяет, не закончились ли уже элементы public function valid() { // readdir() возвращает false, когда элементы каталога закончились return $this->cur !== false; } // Возвращает текущий ключ public function key() { return $this->cur; } // Возвращает текущее значение public function current() { $path = $this->owner->path."/".$this->cur; return is_dir($path)? new FSDirectory($path) : new FSFile($path); } // Передвигает итератор к следующему элементу в списке public function next() { $this->cur = readdir($this->d); } } /** * Файл */ class FSFile { public $path; // Конструктор public function __construct($path) { $this->path = $path; } // Возвращает информацию об изображении public function getSize() { return filesize($this->path); } // Здесь могут быть другие методы } ?>
Определение собственного итератора
Как PHP обрабатывает итераторы
Множественные итераторы
Виртуальные массивы
Библиотека SPL.
Класс DirectoryIterator
Класс FilterIterator
Класс LimitIterator
Рекурсивные итераторы
---Отражения-----
Неявный доступ к классам и методам
Неявный вызов метода
Неявный список аргументов
Инстанцирование классов
Использование неявных аргументов
Аппарат отражений
Функция: ReflectionFunction
Параметр функции: ReflectionParameter
Класс: ReflectionClass
Наследование и отражения
Свойство класса: ReflectionProperty
Метод класса: ReflectionMethod
Библиотека расширения: ReflectionExtension .
Различные утилиты: Reflection .
Исключение: ReflectionException ..
Иерархия 2
IteratorAggregate - это расширение класса
Изучим код:
interface A { public function S(); public function C(); } class B implements A { public $n; public function S() { return 4; } public function C($n) { return $n; } } $obj = new B; $f = $obj->C(5); echo $f;
Если запустить код, то можно увидеть ошибку: "Fatal error: Declaration of B::C($n) must be compatible with A::C()" . Означает, что объявление метода в расширенном классе, не должно противоречить объявлению метода и свойств суперкласса ( в данном случае интерфейса). Для этого нам нужно поставить "заглушку" напишем в методе, что значение переменной null:
interface A { public function S(); public function C(); } class B implements A { public $n; public function S() { return 4; } public function C($n=null) { return $n; } } $obj = new B; $f = $obj->C(5); echo $f;
- Log in to post comments
- 1220 reads