Функции и "области видимости" + "Генераторы"

ТЕЗИСЫ:
- все переменные, которые объявляются и используются в функции, по умолчанию локальны для этой функции.

Пример функции


<!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;