Как я изменяю ключи массива и значения при использовании RecursiveArrayIterator?

После некоторого расследования и общего получения по запросу волос решение, кажется, HTML2PDF. DOMPDF сделал ужасное задание с таблицами, границами и даже умеренно сложным макетом и , htmldoc кажется довольно устойчивым, но почти абсолютно не осведомлен CSS, и я не хочу возвращаться к выполнению HTML-разметки без CSS только для той программы.

HTML2PDF смотрел самое многообещающее, но я продолжал иметь эту странную ошибку об аргументах нулевой ссылки node_type. Я наконец нашел решение этого. В основном PHP 5.1.x хорошо работал с заменами regex (preg_replace_ *) на строках любого размера. PHP 5.2.1 представил директиву конфигурации php.ini, названную pcre.backtrack_limit. То, что делает этот параметр конфигурации, является пределами длина строки, для которой сделано соответствие. Почему это было представлено, я не знаю. Значение по умолчанию было выбрано в качестве 100 000. Почему такая низкая стоимость? Снова, никакая идея.

А ошибка была повышена против PHP 5.2.1 для этого , который является, все еще открывают почти два года спустя .

то, Что является ужасающим об этом, является этим, когда предел превышен, замена всего тихо сбои . По крайней мере, если бы ошибка была повышена и зарегистрировалась, у Вас был бы некоторый признак того, что произошло, почему и что измениться для фиксации его. Но нет.

, Таким образом, у меня есть 70k файл HTML для превращений в PDF. Это требует следующих настроек php.ini:

  • pcre.backtrack_limit = 2000000; #, вероятно, больше, чем мне нужно, но это - OK
  • memory_limit = 1024M; # да, один гигабайт ; и
  • max_execution_time = 600; # да, 10 минут .

Теперь проницательный читатель, возможно, заметил, что мой файл HTML меньше, чем 100k. Единственная причина, которую я могу предположить относительно того, почему я поразил эту проблему, состоит в том, что html2pdf делает преобразование в xhtml как часть процесса. Возможно, это приняло меня (хотя почти 50%-е чрезмерное увеличение размера кажется нечетным). Безотносительно случая вышеупомянутое работало.

Теперь, html2pdf является пожирателем ресурсов. Мой 70k файл занимает приблизительно 5 минут и по крайней мере 500-600M из RAM для создания файла 35 страниц PDF. Не достаточно быстрый (безусловно) для загрузки в реальном времени, к сожалению, и использования памяти помещает отношение использования памяти в порядок 1000 к 1 (600M RAM для 70k файла), который совершенно смешон.

, К сожалению, это является лучшим, я придумал.

16
задан John Carter 4 August 2009 в 16:37
поделиться

5 ответов

Похоже, getInnerIterator создает копию суб-итератора.

Может быть, есть другой метод? (следите за обновлениями ..)


Обновление: после некоторого взлома и привлечения трех других инженеров не похоже, что PHP дает вам возможность изменять значения subIterator.

Вы можете всегда используйте старый режим ожидания:

<?php  
// Easy to read, if you don't mind references (and runs 3x slower in my tests) 
foreach($aNestedArray as &$subArray) {
    foreach($subArray as &$val) {
       if ($val < 200) {
            $val = 0;
        }
    }
}
?>

ИЛИ

<?php 
// Harder to read, but avoids references and is faster.
$outherKeys = array_keys($aNestedArray);
foreach($outherKeys as $outerKey) {
    $innerKeys = array_keys($aNestedArray[$outerKey]);
    foreach($innerKeys as $innerKey) {
        if ($aNestedArray[$outerKey][$innerKey] < 200) {
            $aNestedArray[$outerKey][$innerKey] = 0;
        }
    }
}
?>
2
ответ дан 30 November 2019 в 23:00
поделиться

Я знаю, что это не дает прямого ответа на ваш вопрос, но не рекомендуется изменять объект в процессе итерации во время итерации по нему.

0
ответ дан 30 November 2019 в 23:00
поделиться

Может ли дело сводиться к передаче по ссылке или по значению?

Например, попробуйте изменить:

$cArray = new ArrayObject($aNestedArray);

на:

$cArray = new ArrayObject(&$aNestedArray);
0
ответ дан 30 November 2019 в 23:00
поделиться

Похоже, что значения в простых массивах нельзя изменить, потому что они не могут быть переданы по ссылке в конструктор ArrayIterator ( RecursiveArrayIterator наследует его методы offset * () из этого класса, см. SPL Reference ). Таким образом, все вызовы offsetSet () работают с копией массива.

Думаю, они предпочли избегать вызова по ссылке, потому что это не имеет особого смысла в объектно-ориентированной среде ( т.е. при передаче экземпляров ArrayObject , который должен использоваться по умолчанию).

Еще один код для иллюстрации:

$a = array();

// Values inside of ArrayObject instances will be changed correctly, values
// inside of plain arrays won't
$a[] = array(new ArrayObject(range(100, 200, 100)),
             new ArrayObject(range(200, 100, -100)),
             range(100, 200, 100));
$a[] = new ArrayObject(range(225, 75, -75));

// The array has to be
//     - converted to an ArrayObject or
//     - returned via $it->getArrayCopy()
// in order for this field to get handled properly
$a[] = 199;

// These values won't be modified in any case
$a[] = range(100, 200, 50);

// Comment this line for testing
$a = new ArrayObject($a);

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));

foreach ($it as $k => $v) {
    // getDepth() returns the current iterator nesting level
    echo $it->getDepth() . ': ' . $it->current();

    if ($v < 200) {
        echo "\ttrue";

        // This line is equal to:
        //     $it->getSubIterator($it->getDepth())->offsetSet($k, 0);
        $it->getInnerIterator()->offsetSet($k, 0);
    }

    echo ($it->current() == 0) ? "\tchanged" : '';
    echo "\n";
}

// In this context, there's no real point in using getArrayCopy() as it only
// copies the topmost nesting level. It should be more obvious to work with $a
// itself
print_r($a);
//print_r($it->getArrayCopy());
5
ответ дан 30 November 2019 в 23:00
поделиться

Не используя классы Iterator (которые, кажется, копируют данные в RecursiveArrayIterator::beginChildren() вместо передачи по ссылке)

Можно использовать следующее для достижения желаемого

function drop_200(&$v) { if($v < 200) { $v = 0; } }

$aNestedArray = array();
$aNestedArray[101] = range(100, 1000, 100);
$aNestedArray[201] = range(300, 25, -25);
$aNestedArray[301] = range(500, 0, -50);

array_walk_recursive ($aNestedArray, 'drop_200');

print_r($aNestedArray);

или использовать create_function() вместо создания функции drop_200, но ваш пробег может варьироваться в зависимости от использования функции create_function и памяти.

3
ответ дан 30 November 2019 в 23:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: