hiqdev/hoa-stream

Hiqdev Hoa\Stream 库。

1.0 2022-09-14 12:46 UTC

This package is auto-updated.

Last update: 2024-09-14 17:32:14 UTC


README

由于 Hoa 项目已归档,因此不提供升级或补丁,也不会接受合并请求。

HOA 包包括一些针对 PHP 8.0 和 PHP 8.1 已弃用的代码,但我们需要这些包才能在现代 PHP 版本上运行。

从哪些方面发生了变化?

更改主要影响了方法声明中的返回数据类型提示以及未初始化属性的访问。

如何使用

我们目前已经分叉了以下包,主要是为了让 hoa/stream 与 PHP 8.1 兼容

您只需将 composer.json 中的 hoa 包的要求替换为相应的分叉包:不需要更改代码库中的任何内容。

如果您使用某个人的包,该包需要 hoa,请简单地将分叉添加到您的项目根 composer.json 中:我们已经将分叉标记为替代品,因此 composer 将安装它们而不是原始包。

版本

我们从最新的 hoa 包版本分叉,并从 1.0 开始提升自己的版本。

测试

在运行测试套件之前,必须安装开发依赖项

$ composer install

然后,运行所有测试套件

$ vendor/bin/hoa test:run

有关更多信息,请阅读贡献指南

快速使用

作为一个快速概述,我们建议了解 Hoa\Stream 在接口方面提供了什么,即流能力。这是本库中最重要的部分。然后,如何定义流,接着是使用流上下文。事件、监听器和通知将在下一节中详细说明。最后,在最后几节中将详细说明封装器和过滤器。

接口,也称为流能力

此库定义了几个表示重要流能力的接口。在设计一个与流一起工作的函数或库时非常有用。它确保流是类型的,并提供某些能力。接口在 Hoa\Stream\IStream 命名空间中声明

  • In,从流中读取,提供 readreadIntegerreadLinereadAlleof 等。
  • Out,写入流,提供 writewriteArraywriteLinetruncate 等。
  • Bufferable,对于具有至少一个内部缓冲区的流,提供 newBufferflushgetBufferLevel 等。
  • Touchable,对于“可触摸”的流,提供 touchcopymovedeletechangeGroup 等。
  • Lockable,锁定流,提供 lock 和代表不同类型锁的几个常量,如 LOCK_SHAREDLOCK_EXCLUSIVELOCK_NO_BLOCK 等。
  • Pathable,对于基于路径的流,提供 getBasenamegetDirname
  • Pointable,如果有的话,移动流的内部指针,提供 rewindseektell
  • Statable,获取有关流的统计信息,提供 getSizegetStatisticsgetATimegetCTimeisReadable 等。
  • Structural,对于结构化流,即充当树的流,提供 selectRootselectAnyElementsselectElementsselectAdjacentSiblingElementquerySelector 等。

因此,如果只需从流中读取,则可以使用 Hoa\Stream\IStream\In 来类型化流。它还允许实现者选择其流将提供哪些或哪些不提供的能力。

最后,最高级的接口是Stream,定义了getStream方法,就这么多。这是最未定义的流。所有功能都必须扩展此接口。

定义一个具体的流

Hoa\Stream\Stream主类是抽象的。留给用户的两个方法实现是:_open_close,分别用于打开特定流和关闭此特定流,例如

class BasicFile extends Hoa\Stream\Stream
{
    protected function &_open($streamName, Hoa\Stream\Context $context = null)
    {
        if (null === $context) {
            $out = fopen($streamName, 'rb');
        } else {
            $out = fopen($streamName, 'rb', false, $context->getContext());
        }

        return $out;
    }

    protected function _close()
    {
        return fclose($this->getStream());
    }
}

然后,最常见的用法将是

$file = new BasicFile('/path/to/file');

就这么多。这个流还没有能力。让我们实现In能力

class BasicFile extends Hoa\Stream\Stream implements Hoa\Stream\IStream\In
{
    // …

    public function read($length)
    {
        return fread($this->getStream(), max(1, $length));
    }

    // …
}

其他方法留给读者作为练习。因此,我们现在能够

$chunk = $file->read(42);

Stream能力已经由Hoa\Stream\Stream类实现。

上下文流

上下文由Hoa\Stream\Context类表示。它表示流的一组选项和参数。以http://的选项和参数作为可能的例子。多亏了上下文,我们可以添加HTTP头,或指定代理、最大重定向数等。所有这些信息都是流的选项/参数。

要使用它们,首先让我们定义上下文

$contextId = 'my_http_context';
$context   = Hoa\Stream\Context::getInstance($contextId);
$context->setOptions([
    // …
]);

因此,我们可以要求流根据选择的上下文ID使用此上下文,如下所示

$basicFile = new BasicFile('/path/to/file', $contextId);

对于流实现者,Hoa\Stream\Context类上的getOptionsgetParameters方法将非常有用,分别用于检索选项和参数,并根据它们执行操作。

“选项”和“参数”的概念由PHP本身定义。

事件、监听器和通知

流有一些事件和几个监听器。到目前为止,监听器主要代表“流通知”。

注册了2个事件:hoa://Event/Stream/<streamName>hoa://Event/Stream/<streamName>:close-before。因此,例如,为了在/path/to/file流关闭之前执行一个函数,我们将编写

Hoa\Event\Event::getEvent('hoa://Event/Stream//path/to/file:close-before')->attach(
    function (Hoa\Event\Bucket $bucket) {
        // do something!
    }
);

记住,流不一定是文件。它可以是一个套接字、一个WebSocket、一个stringbuffer、你定义的任何流……因此,这个事件可以在各种不同的场景中用于不同的目的,比如记录事物、关闭相关资源、触发另一个事件……没有规则。观察到的流仍然是打开的,理论上仍然可以继续使用。

当调用Hoa\Stream\Stream::close方法时,将触发此事件。

现在让我们转向监听器。要注册一个监听器,我们必须创建一个我们的流实例,而不打开它。这个动作被称为“延迟打开”。我们可以通过默认Hoa\Stream\Stream构造函数的第三个参数来控制打开时间;设置为true以延迟打开,如下所示

$file = new BasicFile('/path/to/file', null, true);
// do something
$file->open();

将第二个参数传递为null意味着:没有上下文。注意,我们必须手动调用open方法来打开流。在流实例化和流打开之间,我们可以附加新的监听器。

根据流实现的不同,将触发不同的监听器。术语“监听器”在Hoa中到处都在使用,但PHP——在流的环境中——将它们称为通知。让我们用一个HTTP流举例

$basic = new BasicFile(
    'https://hoa-project.net/', // stream name
    null,                       // context ID
    true                        // defere opening
);
$basic->on(
    'connect',
    function (Hoa\Event\Bucket $bucket) {
        echo 'Connected', "\n";
    }
);
$basic->on(
    'redirect',
    function (Hoa\Event\Bucket $bucket) {
        echo 'Redirection to ', $bucket->getData()['message'], "\n";
    }
);
$basic->on(
    'mimetype',
    function (Hoa\Event\Bucket $bucket) {
        echo 'MIME-Type is ', $bucket->getData()['message'], "\n";
    }
);
$basic->on(
    'size',
    function (Hoa\Event\Bucket $bucket) {
        echo 'Size is ', $bucket->getData()['max'], "\n";
    }
);
$basic->on(
    'progress',
    function (Hoa\Event\Bucket $bucket) {
        echo 'Progressed, ', $bucket->getData()['transferred'], ' bytes downloaded', "\n";
    }
);

// Then open.
$basic->open();

你可能会看到这样的东西

Connected
MIME-Type is text/html; charset=UTF-8
Redirection to /En/
Connected
MIME-Type is text/html; charset=UTF-8
Progressed, … bytes downloaded
Progressed, … bytes downloaded

监听器的详尽列表如下

  • authrequire,当需要认证时,
  • authresult,当认证结果已知时,
  • complete,当流完成时(这里的含义可能变化很大),
  • connect,当流连接时(这里的含义可能变化很大),
  • failure,当发生意外情况时,
  • mimetype,当流的MIME类型已知时,
  • progress,当有显著进展时,
  • redirect,当流被重定向到另一个流时,
  • resolve,当流被解析(这里的含义可能很大),
  • size,当知道流的尺寸时。

所有监听器桶数据是一个包含以下对的数组

  • code,一个是STREAM_NOTIFY_*常量之一,这基本上是监听器名称(参见文档),
  • severity,一个是STREAM_NOTIFY_SEVERITY_*常量
    • STREAM_NOTIFY_SEVERITY_INFO,正常,非错误相关的通知,
    • STREAM_NOTIFY_SEVERITY_WARN,非临界错误条件,处理可以继续,
    • STREAM_NOTIFY_SEVERITY_ERR,发生了临界错误,处理不能继续。
  • message,包含大多数有用信息的字符串,
  • transferred,已传输的字节数,
  • max,要传输的总字节数。

流实现者可以添加更多的监听器。请查看Hoa\Event。并非所有监听器都会在所有类型的流上触发。

包装器

流包装器允许声明方案,如hoa://fortune://。您可以想象添加您最喜欢的在线存储,cloud://。任何流包装器都可以与原生标准PHP函数一起使用,如fopenfile_get_contentsmkdirtouch等。对于用户来说是透明的。

Hoa\Stream\Wrapper\Wrapper类包含所有用于registerunregisterrestore包装器的方法。`isRegistered`和`getRegistered`方法也很有用。包装器由一个类表示

Hoa\Stream\Wrapper\Wrapper::register('tmp', Tmp::class);

包装器必须实现Hoa\Stream\Wrapper\IWrapper\IWrapper接口。它是同一命名空间中两个其他接口的组合:`Stream`和`File`。

`Stream`接口要求实现与流相关的几个方法,例如

  • stream_open,
  • stream_close,
  • stream_cast,
  • stream_eof,
  • stream_flush,
  • stream_lock,
  • stream_metadata,
  • stream_read,
  • stream_write,
  • stream_seek,
  • stream_tell,
  • stream_stat,
  • 等。

API提供了所有必需的信息。

`File`接口要求实现与作为文件操作的流相关的其他方法,例如

  • mkdir,
  • dir_opendir,
  • dir_closedir,
  • dir_readdir,
  • rename,
  • unlink,
  • 等。

实现示例是Hoa\Protocol库中的hoa://方案。它不依赖于这个库以避免依赖,但代码可能会有所帮助。

过滤器

流就像一个管道,有输入和输出。这可能通过切割这个管道成两段,并插入一小段:一个过滤器。有三种类型的过滤器,由`Hoa\Stream\Filter\Filter`类上的常量识别

  1. Filter::READ,当过滤器应用于读取操作时,
  2. Filter::WRITE,当过滤器应用于写入操作时,
  3. Filter::READ_AND_WRITE,当两者都适用。

此类允许使用`register`或`remove`过滤器。一个过滤器采用一个扩展自`Hoa\Stream\Filter\Basic`过滤器的类的形式,以及一个相关联的名称。这不是强制性的,但强烈推荐。

一旦注册了过滤器,我们就可以通过使用其名称,通过`append`或`prepend`方法将其应用于流。您可能已经猜到,可以在流上应用多个过滤器,并按特定顺序进行,如“解密”、“解压缩”、“转换到...”。在这种情况下,顺序很重要。

最后,我们像往常一样使用流。流不一定是`Hoa\Stream`的实例,它可以任何PHP流资源。传递一个`Hoa\Stream`实例将明显展开为其底层的PHP流资源。

让我们实现一个过滤器,将流的内容转换为大写。我们首先定义我们的过滤器

class ToUpper extends Hoa\Stream\Filter\Basic
{
    public function filter($in, $out, &$consumed, $closing)
    {
        $iBucket = new Hoa\Stream\Bucket($in);
        $oBucket = new Hoa\Stream\Bucket($out);

        while (false === $iBucket->eob()) {
            $consumed += $iBucket->getLength();

            $iBucket->setData(strtoupper($iBucket->getData()));
            $oBucket->append($iBucket);
        }

        unset($iBucket);
        unset($oBucket);

        return parent::PASS_ON;
    }
}

太好了。现在让我们将我们的过滤器注册到一个特定的名称下

$filterName = 'toupper';
Hoa\Stream\Filter::register($filterName, ToUpper::class);

然后,我们必须在特定的流上应用过滤器,所以让我们打开一个流,并添加过滤器

$file = new Hoa\File\Read(__FILE__);
Hoa\Stream\Filter::append($file, $filterName, Hoa\Stream\Filter::READ);

此过滤器仅适用于读取操作。因此,当我们在流上读取时,我们将看到它的效果,让我们试试

echo $file->readAll();

您将看到所有内容都是ASCII大写。

过滤器是一个低级流API。它与所有类型的流集成。这是一个非常强大的工具。我们提到了一些用法,如解密、转换到、解压缩……实际上,PHP附带了一些标准过滤器,如:string.toupperstring.tolowerdechunkzlib.*bzip2.*convert.iconv.*等。使用Hoa\Stream\Filter\Filter::getRegistered方法将提供所有注册过滤器的列表。

Hoa\Stream\Filter\LateComputed类是一个特殊的过滤器。它在流到达末尾时调用其公共compute方法。因此,通过扩展此过滤器,您可以重写compute方法并处理_buffer属性。此缓冲区包含流的所有内容。这真的是一个缓冲区。为什么它会很有用?例如,如果您正在读取PHP文件,您可以使用解析器(例如)等即时转换源代码,并重新写入文件的一部分。这项技术对于向代码(添加一些探针)进行仪器化特别有用。

使用包装器也可以自动应用过滤器!例如,instrument://包装器可以使用stream_open方法(来自Hoa\Stream\Wrapper\IWrapper\Stream接口)向要打开的流前缀一个过滤器。

可能性是无穷无尽的。

其他操作

还有更多要介绍。Hoa\Stream支持复合流(使用Hoa\Stream\Composite抽象类),即嵌入其他流的流,例如Hoa\Xml。XML流从另一个内部流(文件、套接字或任何其他内容)读取和写入。Hoa\Stringbuffer允许使用流API操作字符串,因此流内容将写入磁盘。与您可能猜到的Hoa\File不同,流的能力不是相同的。

文档

Hoa\Stream的黑客手册包含了有关如何使用此库以及它如何工作的详细信息。

要本地生成文档,请执行以下命令

$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open

更多文档可以在项目的网站上找到:hoa-project.net

获取帮助

主要有两种方式可以获得帮助

贡献

您想贡献吗?谢谢!详细的贡献指南解释了您需要知道的一切。

许可证

Hoa是在新BSD许可证(BSD-3-Clause)下。请参阅LICENSE以获取详细信息。

相关项目

以下项目正在使用此库

  • Marvirc,一个简单、极具模块化和速度极快的IRC机器人
  • WellCommerce,基于Symfony 3全栈框架的现代电子商务引擎
  • 当然还有许多Hoa的库。