如何使用var_dump +输出缓冲而不会出现内存错误? [英] How can I use var_dump + output buffering without memory errors?

查看:66
本文介绍了如何使用var_dump +输出缓冲而不会出现内存错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用var_dump()和输出缓冲来捕获变量并显示它们的应用程序中使用调试辅助工具.但是,我遇到了一个大对象的问题,这些对象最终占用了缓冲区中过多的内存.

I'm using a debugging aid in an application that uses var_dump() with output buffering to capture variables and display them. However, I'm running into an issue with large objects that end up using up too much memory in the buffer.

function getFormattedOutput(mixed $var) {
  if (isTooLarge($var)) { 
    return 'Too large! Abort!'; // What a solution *might* look like
  }

  ob_start();
  var_dump($var); // Fatal error:  Allowed memory size of 536870912 bytes exhausted
  $data = ob_get_clean();

  // Return the nicely-formated data to use later
  return $data
}

有没有办法可以防止这种情况?还是一种变通方法来检测它即将为特定变量输出大量信息?我实际上没有控制将哪些变量传递到此函数中.可以是任何类型.

Is there a way I can prevent this? Or a work-around to detect that it's about to output a gigantic amount of info for a particular variable? I don't really have control which variables get passed into this function. It could be any type.

推荐答案

如果物理内存有限(您会看到致命错误:)

Well, if the physical memory is limited (you see the fatal error:)

致命错误:允许的内存大小为536870912字节已用尽

Fatal error: Allowed memory size of 536870912 bytes exhausted

我建议在磁盘上进行输出缓冲(请参见 ob_start 上的回调参数).输出缓冲是分块的,也就是说,如果仍有足够的内存将单个块保留在内存中,则可以将其存储到临时文件中.

I would suggest to do the output buffering on disk (see callback parameter on ob_start). Output buffering works chunked, that means, if there still is enough memory to keep the single chunk in memory, you can store it into a temporary file.

// handle output buffering via callback, set chunksize to one kilobyte
ob_start($output_callback, $chunk_size = 1024);

但是,必须记住,这只会防止缓冲时出现致命错误.如果现在要返回缓冲区,则仍需要有足够的内存 或返回文件句柄或文件路径,以便还可以流式传输输出.

However you must keep in mind that this will only prevent the fatal error while buffering. If you now want to return the buffer, you still need to have enough memory or you return the file-handle or file-path so that you can also stream the output.

但是,您可以使用该文件来获取所需的字节大小. PHP字符串的开销不是很大的IIRC,因此,如果仍有足够的可用内存来存储文件大小,则应该可以正常工作.您可以减去偏移量以腾出一点空间并安全地玩耍.只需尝试并稍微弄些错误即可.

However you can use that file then to obtain the size in bytes needed. The overhead for PHP strings is not much IIRC, so if there still is enough memory free for the filesize this should work well. You can substract offset to have a little room and play safe. Just try and error a little what it makes.

一些示例代码(PHP 5.4):

Some Example code (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";

输出:

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"

这篇关于如何使用var_dump +输出缓冲而不会出现内存错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆