Bueno, si la memoria física es limitada (ver que el error fatal :)
Fatal error: Allowed memory size of 536870912 bytes exhausted
que sugeriría para hacer el búfer de salida en el disco (véase el parámetro de devolución de llamada en ob_start
). El almacenamiento en búfer de salida funciona fragmentado, es decir, si todavía hay suficiente memoria para mantener el único fragmento en la memoria, puede almacenarlo en un archivo temporal.
// handle output buffering via callback, set chunksize to one kilobyte
ob_start($output_callback, $chunk_size = 1024);
Sin embargo hay que tener en cuenta que esto sólo evitará que el error grave al búfer. Si ahora desea devolver el búfer, aún necesita tener suficiente memoria o devuelve el archivo-handle o file-path para que también pueda transmitir el resultado.
Sin embargo, puede utilizar ese archivo para obtener el tamaño en bytes necesarios. La sobrecarga de las cadenas de PHP no es mucho IIRC, por lo que si todavía hay suficiente memoria libre para el tamaño del archivo, esto debería funcionar bien. Puede restar compensación para tener un poco de espacio y jugar seguro. Solo prueba y comete un error un poco lo que hace.
un código de ejemplo (PHP 5.4):
<?php
/**
* @link http://stackoverflow.com/questions/5446647/how-can-i-use-var-dump-output-buffering-without-memory-errors/
*/
class OutputBuffer
{
/**
* @var int
*/
private $chunkSize;
/**
* @var bool
*/
private $started;
/**
* @var SplFileObject
*/
private $store;
/**
* @var bool Set Verbosity to true to output analysis data to stderr
*/
private $verbose = true;
public function __construct($chunkSize = 1024) {
$this->chunkSize = $chunkSize;
$this->store = new SplTempFileObject();
}
public function start() {
if ($this->started) {
throw new BadMethodCallException('Buffering already started, can not start again.');
}
$this->started = true;
$result = ob_start(array($this, 'bufferCallback'), $this->chunkSize);
$this->verbose && file_put_contents('php://stderr', sprintf("Starting Buffering: %d; Level %d\n", $result, ob_get_level()));
return $result;
}
public function flush() {
$this->started && ob_flush();
}
public function stop() {
if ($this->started) {
ob_flush();
$result = ob_end_flush();
$this->started = false;
$this->verbose && file_put_contents('php://stderr', sprintf("Buffering stopped: %d; Level %d\n", $result, ob_get_level()));
}
}
private function bufferCallback($chunk, $flags) {
$chunkSize = strlen($chunk);
if ($this->verbose) {
$level = ob_get_level();
$constants = ['PHP_OUTPUT_HANDLER_START', 'PHP_OUTPUT_HANDLER_WRITE', 'PHP_OUTPUT_HANDLER_FLUSH', 'PHP_OUTPUT_HANDLER_CLEAN', 'PHP_OUTPUT_HANDLER_FINAL'];
$flagsText = '';
foreach ($constants as $i => $constant) {
if ($flags & ($value = constant($constant)) || $value == $flags) {
$flagsText .= (strlen($flagsText) ? ' | ' : '') . $constant . "[$value]";
}
}
file_put_contents('php://stderr', "Buffer Callback: Chunk Size $chunkSize; Flags $flags ($flagsText); Level $level\n");
}
if ($flags & PHP_OUTPUT_HANDLER_FINAL) {
return TRUE;
}
if ($flags & PHP_OUTPUT_HANDLER_START) {
$this->store->fseek(0, SEEK_END);
}
$chunkSize && $this->store->fwrite($chunk);
if ($flags & PHP_OUTPUT_HANDLER_FLUSH) {
// there is nothing to d
}
if ($flags & PHP_OUTPUT_HANDLER_CLEAN) {
$this->store->ftruncate(0);
}
return "";
}
public function getSize() {
$this->store->fseek(0, SEEK_END);
return $this->store->ftell();
}
public function getBufferFile() {
return $this->store;
}
public function getBuffer() {
$array = iterator_to_array($this->store);
return implode('', $array);
}
public function __toString() {
return $this->getBuffer();
}
public function endClean() {
return ob_end_clean();
}
}
$buffer = new OutputBuffer();
echo "Starting Buffering now.\n=======================\n";
$buffer->start();
foreach (range(1, 10) as $iteration) {
$string = "fill{$iteration}";
echo str_repeat($string, 100), "\n";
}
$buffer->stop();
echo "Buffering Results:\n==================\n";
$size = $buffer->getSize();
echo "Buffer Size: $size (string length: ", strlen($buffer), ").\n";
echo "Peeking into buffer: ", var_dump(substr($buffer, 0, 10)), ' ...', var_dump(substr($buffer, -10)), "\n";
Salida:
STDERR: Starting Buffering: 1; Level 1
STDERR: Buffer Callback: Chunk Size 1502; Flags 1 (PHP_OUTPUT_HANDLER_START[1]); Level 1
STDERR: Buffer Callback: Chunk Size 1503; Flags 0 (PHP_OUTPUT_HANDLER_WRITE[0]); Level 1
STDERR: Buffer Callback: Chunk Size 1503; Flags 0 (PHP_OUTPUT_HANDLER_WRITE[0]); Level 1
STDERR: Buffer Callback: Chunk Size 602; Flags 4 (PHP_OUTPUT_HANDLER_FLUSH[4]); Level 1
STDERR: Buffer Callback: Chunk Size 0; Flags 8 (PHP_OUTPUT_HANDLER_FINAL[8]); Level 1
STDERR: Buffering stopped: 1; Level 0
Starting Buffering now.
=======================
Buffering Results:
==================
Buffer Size: 5110 (string length: 5110).
Peeking into buffer: string(10) "fill1fill1"
...string(10) "l10fill10\n"
se obtiene el mismo problema con 'print_r', por curiosidad? Si no, ¿ves muchos avisos de recursión? – Charles
@Charles, probablemente no. Yo * podría * usar 'print_r' o' var_export' pero realmente me gusta el hecho de que puedo retener el tipo de variable y la información de longitud proporcionada por 'var_dump'. Además, el formato adicional se beneficia cuando xdebug está disponible. –
Probablemente sea una cantidad infinita de salida debido a la recursión. Intente llamarlo usted mismo en la misma var sin usar el almacenamiento en búfer de salida para ver qué sucede. – Jon