PHP中的共享内存文件

9

我使用 openssl_pkcs7_signopenssl_pkcs7_encrypt 来创建加密数据。这些函数只接受文件名作为输入。我希望将临时文件存储在共享内存中以提高性能。我知道在Linux中,可以使用 file_put_contents('/dev/shm/xxx', data),但在Windows中不可能实现。在PHP中,是否有便携式的方法来解决这个问题?shmop_ 函数能否帮助处理此问题?谢谢。

PS:或者有没有办法使这些函数接受字符串数据作为输入?

PS2:请不要建议从PHP调用 /usr/bin/openssl。这不是可移植的。


你保存它后想用它做什么?您有考虑过使用文件流而不是共享内存吗?请参阅StreamWrapper - ircmaxell
我只是使用 file_put_contents 将数据保存到文件中,并将文件名提供给 OpenSSL 函数。大部分开销来自 OpenSSL 函数和 VFS 操作(读取、写入、删除)。流式传输有什么区别吗? - Reci
2个回答

6

好的,我建议使用文件流包装器来完成此操作。让我举一个快速的例子:

class staticStreamWrapper {
    public $context;
    protected static $data = array();

    protected $path    = '';
    protected $pointer = 0;
    protected $writable = false;

    public function stream_close() {}

    public function stream_eof() {
        return $this->pointer >= strlen(static::$data[$this->path]);
    }

    public function stream_flush() {}

    public function stream_open($path, $mode, $options, &$opened_path) {
        switch ($mode[0]) {
            case 'r':
                if (!isset(static::$data[$path])) return false;
                $this->path = $path;
                $this->writable = isset($mode[1]) && $mode[1] == '+';
                break;
            case 'w':
                static::$data[$path] = '';
                $this->path = $path;
                $this->writable = true;
                break;
            case 'a':
                if (!isset(static::$data[$path])) static::$data[$path] = '';
                $this->path = $path;
                $this->writable = true;
                $this->pointer = strlen(static::$data[$path]);
                break;
            case 'x':
                if (isset(static::$data[$path])) return false;
                $this->path = $path;
                $this->writable = true;
                break;
            case 'c':
                if (!isset(static::$data[$path])) static::$data[$path] = '';
                $this->path = $path;
                $this->writable = true;
                break;
            default:
                return false;
        }
        $opened_path = $this->path;
        return true;
    }

    public function stream_read($count) {
        $bytes = min(strlen(static::$data[$this->path]) - $this->pointer, $count);
        $data = substr(static::$data[$this->path], $this->pointer, $bytes);
        $this->pointer += $bytes;
        return $data;
    }

    public function stream_seek($offset, $whence = SEEK_SET) {
        $len = strlen(static::$data[$this->path]);
        switch ($whence) {
            case SEEK_SET:
                if ($offset <= $len) {
                    $this->pointer = $offset;
                    return true;
                }
                break;
            case SEEK_CUR:
                if ($this->pointer + $offset <= $len) {
                    $this->pointer += $offset;
                    return true;
                }
                break;
            case SEEK_END:
                if ($len + $offset <= $len) {
                    $this->pointer = $len + $offset;
                    return true;
                }
                break;
        }
        return false;
    }

    public function stream_stat() {
        $size = strlen(static::$data[$this->path]);
        $time = time();
        return array(
            0 => 0,
            'dev' => 0,
            1 => 0,
            'ino' => 0,
            2 => 0777,
            'mode' => 0777,
            3 => 1,
            'nlink' => 1,
            4 => 0,
            'uid' => 0,
            5 => 0,
            'gid' => 0,
            6 => '',
            'rdev' => '',
            7 => $size,
            'size' => $size,
            8 => $time,
            'atime' => $time,
            9 => $time,
            'mtime' => $time,
            10 => $time,
            'ctime' => $time,
            11 => -1,
            'blksize' => -1,
            12 => -1,
            'blocks' => -1,
        );
    }

    public function stream_tell() {
        return $this->pointer;
    }

    public function stream_write($data) {
        if (!$this->writable) return 0;
        $size = strlen($data);
        $len = strlen(static::$data[$this->path]);
        if ($this->stream_eof()) {
            static::$data[$this->path] .= $data;
        } else {
            static::$data[$this->path] = substr_replace(
                static::$data[$this->path],
                $data,
                $this->pointer
            );
        }
        $this->pointer += $size;
        return $size;
    }

    public function unlink($path) {
        if (isset(static::$data[$path])) {
            unset(static::$data[$path]);
        }
        return true;
    }

}

现在,您需要注册包装器:

stream_wrapper_register('static', 'staticStreamWrapper');

现在你可以像处理文件一样处理它,即使它从未离开PHP(它被存储为静态变量)!

file_put_contents('static://foo.txt', 'this is my data');
file_get_contents('static://foo.txt'); // "this is my data"
$f = fopen('static://foo.txt', 'r'); // should return a resource
// etc...

感谢提供的流信息。我知道PHP支持自定义流包装器实现。然而,OpenSSL扩展很可能是使用操作系统API编写的,并且这些函数不使用PHP流。使用static流调用openssl_pkcs7_encrypt会导致以下错误消息:error:02001002:system library:fopen:No such file or directory - Reci

3
自从Windows 2000以来,shmop(先前为shm_)方法可用。 shmop_open使用唯一的整数键共享内存区域。 可以使用ftok基于文件路径(通常是脚本文件的完整路径)生成唯一索引。 任何共享相同键的实例都可以共享相同的内存。 http://php.net/manual/en/ref.shmop.php 在Zend Server CE的PHP版本5.3.3上进行了测试 系统 Windows NT CRYPE 6.1 build 7601 (不知名的Windows版本Business Edition Service Pack 1) i586
<?php
$key = ftok(__FILE__, 't');
$memory = shmop_open($key, "c", 0600, 16 * 1024);
$data = array('data' => 'value');
$bytes = shmop_write($memory, serialize($data), 0);
$return = shmop_read($memory, 0, $bytes);
print_r(unserialize($return));
?>

shmop_read/shmop_write会将字符串转换为原始字节进行存储,因此您不需要对其进行序列化,但是您需要在某个地方写入字符串的长度。我的示例创建了一个16KB的共享内存区域,您当然可以根据openssl文件的大小以及存储文件大小所需的空间来调整它的大小。


我理解 shmop_ 函数。我的问题是如何将文件名提供给 OpenSSL?内存块没有文件名。 - Reci
大多数 OpenSSL 函数似乎不接受文件指针,这将使您无法使用“php://memory”内存文件。您必须使用临时文件来读取和写入数据,这会抵消共享内存的任何好处。但是,一些函数(如 openssl_private_decrypt)允许您使用字符串而不是文件,您可以直接从共享内存中加载和卸载它们。还有其他 MIME 函数可能可以代替 pkcs7,但我不太熟悉。建议您查看是否有任何这些函数适合您的需求。 - Josh Brown
此外,您可以使用shmop_size来恢复块大小。 - RafaSashi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接