Функции и "области видимости" + "Генераторы"
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
- 1312 reads