- PVSM.RU - https://www.pvsm.ru -
Общие сведения.
Увеличиваем потребление памяти вдвое.
Увеличиваем потребление памяти втрое.
Ещё раз увеличиваем потребление памяти на ровном месте.
Заключение.
Известно, что PHP активно использует механизм copy-on-write [1]. Это означает, что при попытке внутри функции что-то записать в переданные ей параметры вначале будет сделана копия этой переменной, а уж затем в неё что-то запишется. Такая же ситуация наблюдается с итерацией массива с помощью foreach [2]. Отсюда следует, что вам потребуется увеличить количество памяти для создания копии переменной и времени (ресурсов ЦП), чтобы всё это проделать. Т.е. возникнет пауза, прежде чем PHP перейдёт к следующей строчке вашей программы.
Но прежде чем продолжить дальше по теме, я бы хотел рассказать зачем вообще что-то передаётся по ссылке, а что-то по значению. Честно, говоря, я об этом узнал несколько месяцев назад. Т.е. то, что объекты (и массивы, об этом - далее) в PHP всегда передаются по ссылке, а всё остальное по значению я знал. Но вот зачем - нет. Ответ нашёлся в курсе по Go [3] как ни странно. Это компромис. Если умолчать про массивы (и, как заметили в комментариях, строки, которые тоже массивы [4]), то все остальные типы данных в PHP - это скаляры (чтобы быть точным см. is_scalar [5]). Скаляры не занимают много памяти, поэтому их можно быстро скопировать и передать в функцию копию хранимого значения. При этом на вызывающей стороне значение переменной не измениться. Объекты же могут быть огромными, например DOM-дерево огромного XML-документа. Делать копию такого объекта слишком дорого и по времени и по памяти, поэтому он передаётся по ссылке. Так почему бы не передавать скаляры тоже по ссылке? Дело в том, что передавая что-либо по ссылке мы таким образом теряем контроль над переменными в месте вызова. Представьте себе функцию с кучей параметров:
function doSmth($x1, $y2, $scale, $pojection, $alpha, $type, $reference, $mode): float;
И все они передаются по ссылке. Что случится с локальным контекстом после вызова этой функции? Останутся ли все эти переменные в тех же значениях, что и были до вызова doSmth? Неведомо сие. Всё это остаётся на совести разработчика функции doSmth. Т.о. вы частично или полностью теряете контроль над своей программой. Поэтому и придумали компромис: скаляры всегда передаём по значению, а объекты по ссылке.
Ещё одно уточнение, которое кажется необходимым сделать судя по комментариям. В «Reference Counting Basics [6]» есть следующий текст:
Since PHP allows user-land references, as created by the & operator, a zval container also has an internal reference counting mechanism to optimize memory usage.
Это означает, что в PHP есть как ссылки, которые определяет пользователь через амперсанд, так и внутренние ссылки, которые PHP использует где-то там в своих недрах и для своих нужд. Когда в статье говорится о ссылках и передаче по ссылке, то имеется ввиду такое поведение, когда копия переменной не делается.
Массивы в PHP передаются по ссылке. Но если вы что-то попытаетесь записать в него, то будет создана копия массива со всеми вытекающими по памяти и процессору. Это и есть реализация механизма copy-on-write [1]. Пример:
<?php
function doSmth(array $array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
На моем компе с PHP 8.2.3, кстати, вывод будет таким:
memory: 2616
memory: 5264
Т.е. всего лишь записав нолик в первый элемент массива мы увеличили потребление памяти вдвое! Это ли не чудо! Array assignment always involves value copying. Use the reference operator [7] to copy an array by reference, см. тут [8]. Что делать с этим? Да, нужно поставить амперсанд [7] перед параметром $array, вот так: function doSmth(array &$array, int $memory). Тогда вывод станет таким:
memory: 2648
memory: 2680
2680 - 2648 = 32. 32 - это скорее всего кол-во памяти выделенное на саму переменную $array (но не её значение). Как бы там ни было, это не вдвое. Проблема решена. Сейчас расскажу, как увеличить потребление памяти втрое (да, сам понимаю, что немного странно написана статья: казалось бы нужно рассказывать как уменьшить потребление памяти, но... так показалось проще объяснить).
Затираем амперсанд, возвращаем всё в зад и попробуем сделать что-нибудь с массивом, например увеличить на 1 каждый его элемент (всё тоже самое, только добавили foreach и break, чтобы не мотать весь массив):
<?php
function doSmth(array $array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
foreach ($array as $i => $value) {
$array[$i] ++;
printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
break;
}
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
Вывод:
memory: 2616
memory: 5264
memory: 7880, i: 0
memory: 5264
memory: 2648
Как видите, пик потребляемой памяти происходит внутри цикла. Дело в том, что при попытке что-то записать в массив внутри цикла foreach PHP делает (в нашем случае ещё одну) копию массива. И даже если поставить амперсанд перед $value , то это не поможет никак.
<?php
function doSmth(array $array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
foreach ($array as $i => &$value) {
// $array[$i] ++;
$value ++;
printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
// break;
}
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
memory: 2616
memory: 5264
memory: 5328, i: 0
memory: 5360, i: 1
memory: 5392, i: 2
memory: 5424, i: 3
memory: 5456, i: 4
memory: 5488, i: 5
memory: 5520, i: 6
memory: 5552, i: 7
memory: 5584, i: 8
memory: 5616, i: 9
memory: 5648, i: 10
memory: 5680, i: 11
memory: 5712, i: 12
memory: 5744, i: 13
memory: 5776, i: 14
memory: 5808, i: 15
memory: 5840, i: 16
memory: 5872, i: 17
memory: 5904, i: 18
memory: 5936, i: 19
memory: 5968, i: 20
memory: 6000, i: 21
memory: 6032, i: 22
memory: 6064, i: 23
memory: 6096, i: 24
memory: 6128, i: 25
memory: 6160, i: 26
memory: 6192, i: 27
memory: 6224, i: 28
memory: 6256, i: 29
memory: 6288, i: 30
memory: 6320, i: 31
memory: 6352, i: 32
memory: 6384, i: 33
memory: 6416, i: 34
memory: 6448, i: 35
memory: 6480, i: 36
memory: 6512, i: 37
memory: 6544, i: 38
memory: 6576, i: 39
memory: 6608, i: 40
memory: 6640, i: 41
memory: 6672, i: 42
memory: 6704, i: 43
memory: 6736, i: 44
memory: 6768, i: 45
memory: 6800, i: 46
memory: 6832, i: 47
memory: 6864, i: 48
memory: 6896, i: 49
memory: 6928, i: 50
memory: 6960, i: 51
memory: 6992, i: 52
memory: 7024, i: 53
memory: 7056, i: 54
memory: 7088, i: 55
memory: 7120, i: 56
memory: 7152, i: 57
memory: 7184, i: 58
memory: 7216, i: 59
memory: 7248, i: 60
memory: 7280, i: 61
memory: 7312, i: 62
memory: 7344, i: 63
memory: 7376, i: 64
memory: 7408, i: 65
memory: 7440, i: 66
memory: 7472, i: 67
memory: 7504, i: 68
memory: 7536, i: 69
memory: 7568, i: 70
memory: 7600, i: 71
memory: 7632, i: 72
memory: 7664, i: 73
memory: 7696, i: 74
memory: 7728, i: 75
memory: 7760, i: 76
memory: 7792, i: 77
memory: 7824, i: 78
memory: 7856, i: 79
memory: 7888, i: 80
memory: 7920, i: 81
memory: 7952, i: 82
memory: 7984, i: 83
memory: 8016, i: 84
memory: 8048, i: 85
memory: 8080, i: 86
memory: 8112, i: 87
memory: 8144, i: 88
memory: 8176, i: 89
memory: 8208, i: 90
memory: 8240, i: 91
memory: 8272, i: 92
memory: 8304, i: 93
memory: 8336, i: 94
memory: 8368, i: 95
memory: 8400, i: 96
memory: 8432, i: 97
memory: 8464, i: 98
memory: 8496, i: 99
memory: 8496
memory: 2648
foreach вообще достаточно проблемная конструкция для синтаксического сахара. Без проблем её можно использовать только для "посмотреть" на каждой итерации, "сделать" же что-то обходится слишком дорого:
Альтернативой тут будет использование цикла for.
<?php
function doSmth(array $array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$count = count($array);
for ($i = 0; $i < $count; $i ++) {
$array[$i] = 100;
printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
}
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
memory: 2616
memory: 5264
memory: 5264, i: 0
memory: 5264, i: 1
memory: 5264, i: 2
memory: 5264, i: 3
memory: 5264, i: 4
memory: 5264, i: 5
memory: 5264, i: 6
memory: 5264, i: 7
memory: 5264, i: 8
memory: 5264, i: 9
memory: 5264, i: 10
memory: 5264, i: 11
memory: 5264, i: 12
memory: 5264, i: 13
memory: 5264, i: 14
memory: 5264, i: 15
memory: 5264, i: 16
memory: 5264, i: 17
memory: 5264, i: 18
memory: 5264, i: 19
memory: 5264, i: 20
memory: 5264, i: 21
memory: 5264, i: 22
memory: 5264, i: 23
memory: 5264, i: 24
memory: 5264, i: 25
memory: 5264, i: 26
memory: 5264, i: 27
memory: 5264, i: 28
memory: 5264, i: 29
memory: 5264, i: 30
memory: 5264, i: 31
memory: 5264, i: 32
memory: 5264, i: 33
memory: 5264, i: 34
memory: 5264, i: 35
memory: 5264, i: 36
memory: 5264, i: 37
memory: 5264, i: 38
memory: 5264, i: 39
memory: 5264, i: 40
memory: 5264, i: 41
memory: 5264, i: 42
memory: 5264, i: 43
memory: 5264, i: 44
memory: 5264, i: 45
memory: 5264, i: 46
memory: 5264, i: 47
memory: 5264, i: 48
memory: 5264, i: 49
memory: 5264, i: 50
memory: 5264, i: 51
memory: 5264, i: 52
memory: 5264, i: 53
memory: 5264, i: 54
memory: 5264, i: 55
memory: 5264, i: 56
memory: 5264, i: 57
memory: 5264, i: 58
memory: 5264, i: 59
memory: 5264, i: 60
memory: 5264, i: 61
memory: 5264, i: 62
memory: 5264, i: 63
memory: 5264, i: 64
memory: 5264, i: 65
memory: 5264, i: 66
memory: 5264, i: 67
memory: 5264, i: 68
memory: 5264, i: 69
memory: 5264, i: 70
memory: 5264, i: 71
memory: 5264, i: 72
memory: 5264, i: 73
memory: 5264, i: 74
memory: 5264, i: 75
memory: 5264, i: 76
memory: 5264, i: 77
memory: 5264, i: 78
memory: 5264, i: 79
memory: 5264, i: 80
memory: 5264, i: 81
memory: 5264, i: 82
memory: 5264, i: 83
memory: 5264, i: 84
memory: 5264, i: 85
memory: 5264, i: 86
memory: 5264, i: 87
memory: 5264, i: 88
memory: 5264, i: 89
memory: 5264, i: 90
memory: 5264, i: 91
memory: 5264, i: 92
memory: 5264, i: 93
memory: 5264, i: 94
memory: 5264, i: 95
memory: 5264, i: 96
memory: 5264, i: 97
memory: 5264, i: 98
memory: 5264, i: 99
memory: 5264
memory: 2648
Обратите внимание, что размер массива должен вычисляться только один раз перед массивом, а не на каждой итерации:
for ($i = 0; $i < count($array); $i ++)
По ходу пьесы обнаружил ещё две интересные статья на Хабре:
Сравнение производительности перебора массивов в цикле через for() и foreach() [11]. Так ли это для 8-ой версии не знаю, не проверял.
array_* vs foreach или PHP7 vs PHP5 [12]. Тест на скорую руку показал, что array_map потребляет тоже почти в 3 раза больше памяти (а без амперсанда перед $array ещё больше).
<?php
function doSmth(array &$array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
// foreach ($array as $i => &$value) {
// // $array[$i] ++;
// $value ++;
// printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
// // break;
// }
// $count = count($array);
// for ($i = 0; $i < $count; $i ++) {
// $array[$i] = 100;
// printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
// }
array_map(function($value) use ($memory) {
$value ++;
printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $value, PHP_EOL);
}, $array);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
memory: 2648
memory: 2680
memory: 6160, i: 1
memory: 6160, i: 2
memory: 6160, i: 3
memory: 6160, i: 4
memory: 6160, i: 5
memory: 6160, i: 6
memory: 6160, i: 7
memory: 6160, i: 8
memory: 6160, i: 9
memory: 6160, i: 10
memory: 6160, i: 11
memory: 6160, i: 12
memory: 6160, i: 13
memory: 6160, i: 14
memory: 6160, i: 15
memory: 6160, i: 16
memory: 6160, i: 17
memory: 6160, i: 18
memory: 6160, i: 19
memory: 6160, i: 20
memory: 6160, i: 21
memory: 6160, i: 22
memory: 6160, i: 23
memory: 6160, i: 24
memory: 6160, i: 25
memory: 6160, i: 26
memory: 6160, i: 27
memory: 6160, i: 28
memory: 6160, i: 29
memory: 6160, i: 30
memory: 6160, i: 31
memory: 6160, i: 32
memory: 6160, i: 33
memory: 6160, i: 34
memory: 6160, i: 35
memory: 6160, i: 36
memory: 6160, i: 37
memory: 6160, i: 38
memory: 6160, i: 39
memory: 6160, i: 40
memory: 6160, i: 41
memory: 6160, i: 42
memory: 6160, i: 43
memory: 6160, i: 44
memory: 6160, i: 45
memory: 6160, i: 46
memory: 6160, i: 47
memory: 6160, i: 48
memory: 6160, i: 49
memory: 6160, i: 50
memory: 6160, i: 51
memory: 6160, i: 52
memory: 6160, i: 53
memory: 6160, i: 54
memory: 6160, i: 55
memory: 6160, i: 56
memory: 6160, i: 57
memory: 6160, i: 58
memory: 6160, i: 59
memory: 6160, i: 60
memory: 6160, i: 61
memory: 6160, i: 62
memory: 6160, i: 63
memory: 6160, i: 64
memory: 6160, i: 65
memory: 6160, i: 66
memory: 6160, i: 67
memory: 6160, i: 68
memory: 6160, i: 69
memory: 6160, i: 70
memory: 6160, i: 71
memory: 6160, i: 72
memory: 6160, i: 73
memory: 6160, i: 74
memory: 6160, i: 75
memory: 6160, i: 76
memory: 6160, i: 77
memory: 6160, i: 78
memory: 6160, i: 79
memory: 6160, i: 80
memory: 6160, i: 81
memory: 6160, i: 82
memory: 6160, i: 83
memory: 6160, i: 84
memory: 6160, i: 85
memory: 6160, i: 86
memory: 6160, i: 87
memory: 6160, i: 88
memory: 6160, i: 89
memory: 6160, i: 90
memory: 6160, i: 91
memory: 6160, i: 92
memory: 6160, i: 93
memory: 6160, i: 94
memory: 6160, i: 95
memory: 6160, i: 96
memory: 6160, i: 97
memory: 6160, i: 98
memory: 6160, i: 99
memory: 6160, i: 100
memory: 2680
memory: 2680
Таким образом оптимальным, без всяких дополнительных затрат будет вариант с передачей массива по ссылке и использования for.
<?php
function doSmth(array &$array, int $memory) {
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array[0] = 0;
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$count = count($array);
for ($i = 0; $i < $count; $i ++) {
$array[$i] = 100;
printf('memory: %s, i: %s%s', memory_get_usage() - $memory, $i, PHP_EOL);
}
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
}
$memory = memory_get_usage();
$array = range(0, 99);
doSmth($array, $memory);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
memory: 2648
memory: 2680
memory: 2680, i: 0
memory: 2680, i: 1
memory: 2680, i: 2
memory: 2680, i: 3
memory: 2680, i: 4
memory: 2680, i: 5
memory: 2680, i: 6
memory: 2680, i: 7
memory: 2680, i: 8
memory: 2680, i: 9
memory: 2680, i: 10
memory: 2680, i: 11
memory: 2680, i: 12
memory: 2680, i: 13
memory: 2680, i: 14
memory: 2680, i: 15
memory: 2680, i: 16
memory: 2680, i: 17
memory: 2680, i: 18
memory: 2680, i: 19
memory: 2680, i: 20
memory: 2680, i: 21
memory: 2680, i: 22
memory: 2680, i: 23
memory: 2680, i: 24
memory: 2680, i: 25
memory: 2680, i: 26
memory: 2680, i: 27
memory: 2680, i: 28
memory: 2680, i: 29
memory: 2680, i: 30
memory: 2680, i: 31
memory: 2680, i: 32
memory: 2680, i: 33
memory: 2680, i: 34
memory: 2680, i: 35
memory: 2680, i: 36
memory: 2680, i: 37
memory: 2680, i: 38
memory: 2680, i: 39
memory: 2680, i: 40
memory: 2680, i: 41
memory: 2680, i: 42
memory: 2680, i: 43
memory: 2680, i: 44
memory: 2680, i: 45
memory: 2680, i: 46
memory: 2680, i: 47
memory: 2680, i: 48
memory: 2680, i: 49
memory: 2680, i: 50
memory: 2680, i: 51
memory: 2680, i: 52
memory: 2680, i: 53
memory: 2680, i: 54
memory: 2680, i: 55
memory: 2680, i: 56
memory: 2680, i: 57
memory: 2680, i: 58
memory: 2680, i: 59
memory: 2680, i: 60
memory: 2680, i: 61
memory: 2680, i: 62
memory: 2680, i: 63
memory: 2680, i: 64
memory: 2680, i: 65
memory: 2680, i: 66
memory: 2680, i: 67
memory: 2680, i: 68
memory: 2680, i: 69
memory: 2680, i: 70
memory: 2680, i: 71
memory: 2680, i: 72
memory: 2680, i: 73
memory: 2680, i: 74
memory: 2680, i: 75
memory: 2680, i: 76
memory: 2680, i: 77
memory: 2680, i: 78
memory: 2680, i: 79
memory: 2680, i: 80
memory: 2680, i: 81
memory: 2680, i: 82
memory: 2680, i: 83
memory: 2680, i: 84
memory: 2680, i: 85
memory: 2680, i: 86
memory: 2680, i: 87
memory: 2680, i: 88
memory: 2680, i: 89
memory: 2680, i: 90
memory: 2680, i: 91
memory: 2680, i: 92
memory: 2680, i: 93
memory: 2680, i: 94
memory: 2680, i: 95
memory: 2680, i: 96
memory: 2680, i: 97
memory: 2680, i: 98
memory: 2680, i: 99
memory: 2680
memory: 2680
Ну и сладкое на десерт. В замечательной статье «Массивы в РНР 7: хэш-таблицы [13]» говорится о том, что массив, точнее его внутреннее представление может храниться в упакованном и классическом виде. В упакованном значит в сжатом. Сжатым массив создаётся когда в нем используются только целочисленные ключи и только по порядку, например так:
<?php
$array = [];
$array[] = 'Один';
$array[] = 'Два';
...
Или используя функцию range как это делалось в коде выше, например:
<?php
$memory = memory_get_usage();
$array = range(0, 99);
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
$array['qwe'] = 'new item value';
printf('memory: %s%s', memory_get_usage() - $memory, PHP_EOL);
Вывод:
memory: 2616
memory: 8280
Массив начал занимать в 3 раза больше памяти после того как в него добавили один (один!) элемент. В 3 раза, Карл! На ровном месте. Никто не ждал подвоха, а тут опять и снова!
Для других значений (вместо 100) статистика по увеличению потребления памяти выглядит так:
|
Размер массива, шт |
Кол-во памяти до, байт |
Кол-во памяти после, байт |
Разница, раз |
|
10 |
408 |
728 |
1.8 |
|
100 |
2648 |
8280 |
3.1 |
|
1000 |
20568 |
41048 |
2 |
|
10000 |
266328 |
655448 |
2.5 |
|
100000 |
2101360 |
5242992 |
2.5 |
|
1000000 |
16781424 |
41943152 |
2.5 |
Таким образом, если вдруг вам захочется добавить в ваш массив какую-то информацию об этом же массиве (например среднее значение или min и max), то не надо. Сделайте для этого другой массив или используйте stdClass [14]:
<?php
$bigArray = range(0, 10000000);
$info = new stdClass; // $info = []
$info->data = $bigArray; // $info['data'] = $bigArray;
$info->min = min($bigArray); // $info['min'] = min($bigArray);
$info->max = ...; // $info['max'] = ...;
...
Справедливости ради отмечу, что утверждение о том, что целочисленные ключи должны идти строго по порядку не совсем верно. Они могут идти не по порядку до тех пор пока значение ключа не превысит размер хэш-таблицы.
Аккуратно работайте с массивами, особенно с большими. Их точно лучше передавать по ссылке, как PHP передаёт объекты. Используйте for. Думайте, проверяйте и замеряйте. Держите массивы упакованными. Рассмотрите возможность использования SplFixedArray [15].
Тут вообще нужно быть осторожным и проверять всё именно для вашей версии PHP. Работает ли for быстрее foreach на восьмёрке? А на семёрке? А на пятёрке? Т.е. всё то, что написано, верно для моей версии PHP на моём компьютере, но верно ли для вас - вопрос.
Автор: Александр Зеленин
Источник [16]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/optimizatsiya-koda/385889
Ссылки в тексте:
[1] copy-on-write: https://en.wikipedia.org/wiki/Copy-on-write
[2] foreach: https://www.php.net/manual/ru/control-structures.foreach.php
[3] курсе по Go: https://www.coursera.org/specializations/google-golang
[4] строки, которые тоже массивы: https://www.php.net/manual/en/language.types.string.php#language.types.string.substr
[5] is_scalar: https://www.php.net/manual/en/function.is-scalar.php
[6] Reference Counting Basics: https://www.php.net/manual/en/features.gc.refcounting-basics.php
[7] reference operator: https://www.php.net/manual/en/language.references.php
[8] тут: https://www.php.net/manual/en/language.types.array.php
[9] О тонкостях работы foreach в PHP: https://habr.com/ru/articles/172647/
[10] Подводный камень в foreach: https://habr.com/ru/articles/136835/
[11] Сравнение производительности перебора массивов в цикле через for() и foreach(): https://habr.com/ru/articles/216103/
[12] array_* vs foreach или PHP7 vs PHP5: https://habr.com/ru/articles/340696/
[13] Массивы в РНР 7: хэш-таблицы: https://habr.com/ru/companies/vk/articles/308240/
[14] stdClass: https://www.php.net/manual/ru/class.stdclass.php
[15] SplFixedArray: https://www.php.net/manual/en/class.splfixedarray.php
[16] Источник: https://habr.com/ru/articles/746868/?utm_source=habrahabr&utm_medium=rss&utm_campaign=746868
Нажмите здесь для печати.