Я нашел следующее решение здесь, в StackOverflow, чтобы получить массив определенного свойства объекта из массива объектов: PHP – Извлечение свойства из массива объектов
Предлагаемое решение – использовать array_map
и внутри создать функцию с create_function
следующим образом:
$catIds = array_map(create_function('$o', 'return $o->id;'), $objects);
Что происходит ?: array_map
пробегает каждый элемент массива, в этом случае объект stdClass
. Сначала он создает такую функцию:
function($o) { return $o->id; }
Во вторых он вызывает эту функцию для объекта в текущей итерации. Он работает, он работает почти так же, как это аналогичное решение:
$catIds = array_map(function($o) { return $o->id; }, $objects);
Но это решение работает только в PHP версии> = 5.3, поскольку использует концепцию Closure
=> http://php.net/manual/de/class.closure.php
Теперь настоящая проблема:
Первое решение с create_function
увеличивает память, потому что созданная функция будет записана в память и не будет повторно использована или уничтожена. Во втором решении с Closure
это будет.
Таким образом, решения дают одни и те же результаты, но имеют различное поведение по отношению к памяти.
Следующий пример:
// following array is given $objects = array ( [0] => stdClass ( [id] => 1 ), [1] => stdClass ( [id] => 2 ), [2] => stdClass ( [id] => 3 ) )
ПЛОХО
while (true) { $objects = array_map(create_function('$o', 'return $o->id;'), $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235616 4236600 4237560 4238520 ...
ХОРОШО
while (true) { $objects = array_map(function($o) { return $o->id; }, $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235136 4235168 4235168 4235168 ...
Я трачу так много времени, чтобы узнать это, и теперь я хочу знать, если это ошибка с сборщиком мусора или я сделал ошибку? И почему имеет смысл оставить уже созданную и вызванную функцию в памяти, когда она никогда не будет использоваться повторно?
Вот пример: http://ideone.com/9a1D5g
Обновлено : Когда я рекурсивно просматриваю свой код и его зависимости, например PEAR и Zend, я слишком часто нашел этот BAD- путь.
Обновлено : Когда две функции вложены, мы исходим изнутри, чтобы оценить это выражение. Другими словами, он сначала create_function
(один раз), а возвращаемое имя функции является аргументом для одиночного вызова array_map
. Но поскольку GC забывает удалить его из памяти (никакой указатель не оставлен для функции в памяти), и PHP не сможет повторно использовать функцию, уже находящуюся в памяти, позвольте мне подумать, что есть ошибка, а не только вещь с «плохой производительностью», , Эта конкретная строка кода является примером в PHPDoc и используется во многих больших средах, например Zend и PEAR, и многое другое. С одной строкой вы можете обойти эту «ошибку», проверьте. Но я не ищу решения: я ищу правду. Это ошибка, или это мой подход. И последнее я еще не мог решить.
В случае create_function()
функция лямбда-стиля создается с помощью eval()
, и возвращается строка, содержащая его имя. Затем это имя передается как аргумент функции array_map()
.
Это отличается от анонимной функции типа закрытия, когда ни одна строка, содержащая имя, не используется вообще. function($o) { return $o->id; }
function($o) { return $o->id; }
Является функцией, или, скорее, экземпляром класса Closure.
Функция eval()
внутри create_function()
выполняет часть кода PHP, которая создает create_function()
функцию. Примерно так:
function create_function($arguments,$code) { $name = <_lambda_>; // just a unique string eval('function '.$name.'($arguments){$code}'); return $name; }
Обратите внимание, что это упрощение.
Итак, как только функция будет создана, она будет сохраняться до конца скрипта, как и обычные функции в скрипте. В приведенном выше примере BAD на каждой итерации цикла создается новая функция, занимающая все больше памяти.
Вы можете, однако, умышленно уничтожить функцию лямбда-стиля. Это довольно просто, просто измените цикл на:
while (true) { $func = create_function('$o', 'return $o->id;'); $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); }
Строка, содержащая ссылку (= имя) функции, была сделана expliciet и доступна здесь. Теперь, каждый раз, когда create_function()
, старая функция перезаписывается новой.
Итак, нет, нет «утечки памяти», он должен работать таким образом.
Конечно, приведенный ниже код более эффективен:
$func = create_function('$o', 'return $o->id;'); while (true) { $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); }
И его следует использовать только тогда, когда анонимная функция закрытия не поддерживается вашей версией PHP.
Не используйте create_function()
если вы можете избежать этого. В частности, не раз. В большом желтом окне « Предупреждение» в руководстве по PHP :
… он имеет плохую производительность и характеристики использования памяти.
Хорошо, я думаю, проблема в том, что первое решение с create_function
работает на старых версиях PHP, а второе решение не увеличивает ненужную память. Но давайте посмотрим на первое решение. Метод create_function
вызывается внутри array_map
, а именно для каждого while
повторении. Если мы хотим, чтобы решение работало со старыми версиями PHP и без увеличения объема памяти, мы должны делать следующее для более старого экземпляра функции на каждом этапе, while
итерация:
$func = create_function('$o', 'return $o->id;'); $catIds = array_map($func, $objects);
Это все. Так просто.
Но он также не отвечает на вопрос вообще. Остается вопрос, является ли это ошибкой с PHP или функцией. Для моего понимания, что путь записи результата create_function
в переменной ДОЛЖЕН быть таким же, как поставить его непосредственно в качестве параметра в array_map
, не так ли?