利用PHP的流,使用書簡式的Zun-Doko-Kiyoshi遊戲
“ズンドコキヨシまとめ” – 丘塔
我感觉错过了时机,但我尝试使用流来实现,因为我还没有看到这种实现方法。
“Stream是什么意思?”PHP中的流是处理大型二进制数据的机制。虽然有类似的迭代器和生成器的机制,但流专门针对二进制序列,具有伪装文件系统和与套接字通信结合等独特功能。
嗯,这和C语言的流非常相似。接口也继承了C语言的风格,并不像node.js的API。
使用流媒体的沉默打击计划
-
生成一个无限输出“ズン”和“ドコ”的流。
- 生成一个无限输出“ズン”和“ドコ”的流。
-
- 使用流过滤器监视输出,并在满足条件时将“キヨシ”添加到输出中。
- 在“キヨシ”后停止输出任何内容,结束循环。
这样的感觉怎么样呢?虽然看起来有些复杂,但我认为对学习流的人来说正合适。
创建一个无限的Zundoko Stream。如果要在PHP端实现流(stream)的话,可以使用streamWrapper。
在这里(http://jp2.php.net/manual/ja/class.streamwrapper.php)可以找到相关方法的实现。我们将一直去实现这些方法,但是这次我们只需要实现读取功能,所以会尽可能地省略其他方法的实现。
class Zundoko
{
function stream_open($path, $mode, $options, &$opened_path) {
return true;
}
function stream_read(int $count) {
return (mt_rand(0, 1) ? 'ズン' : 'ドコ') . PHP_EOL;
}
function stream_eof() {
return false;
}
}
stream_wrapper_register('zundoko', 'Zundoko');
class Zundoko
{
function stream_open($path, $mode, $options, &$opened_path) {
return true;
}
function stream_read(int $count) {
return (mt_rand(0, 1) ? 'ズン' : 'ドコ') . PHP_EOL;
}
function stream_eof() {
return false;
}
}
stream_wrapper_register('zundoko', 'Zundoko');
通过这个操作,zundoko:// schema 和 Zundoko stream 成功关联起来了。你可以通过 fopen 来创建 zundoko stream。
$zd = fopen('zundoko://', 'r');
for ($i = 0; $i < 10; ++$i) {
echo fgets($zd);
}
如果执行,应该会显示“嘭嘭嘭嘭嘭嘭嘭嘭嘭嘭嘭”,如果设为无限循环,就可以无限次地进行“嘭嘭嘭嘭嘭嘭嘭嘭嘭嘭嘭”。
您还可以使用file_get_contents在指定长度下获取“ズンドコ”字符串。由于必须指定字节长度,因此您需要将所需的“ズンドコ”个数乘以7。
echo file_get_contents('zundoko://', false, null, 0, 70); //ズンドコ10個出力
创建Kiyoshi流过滤器
为了处理流,您可以创建另一个流并连接它们,但是由于存在流过滤器机制,我决定试试看这种方法是否多余。
流过滤器是一种在流中进行数据转换的机制。在简单的情况下,可以使用类似string.rot13这样的方法来执行rot13转换。通过继承内置类php_user_filter,可以创建流过滤器。
class Kiyoshi extends php_user_filter
{
private $zundoko = [];
private $finished = false;
const KIYOSHI = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'];
public function filter($in, $out, &$consumed, $closing)
{
if ($this->finished) {
while ($bucket = stream_bucket_make_writeable($in));
return PSFS_ERR_FATAL;
}
while ($bucket = stream_bucket_make_writeable($in)) {
$this->zundoko[] = rtrim($bucket->data);
if (count($this->zundoko) > 5) {
array_shift($this->zundoko);
}
if (self::KIYOSHI === $this->zundoko) {
$bucket->data .= "キ・ヨ・シ!\n";
$bucket->datalen += 19;
$this->finished = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
stream_filter_register('kiyoshi', 'Kiyoshi');
在php_user_filter中实现的是一个名为filter的方法。
传递给filter的$in和$out是称为Bucket Brigade的特殊资源,类似于流处理的二进制数组。每个二进制数组对应一个Bucket,多个Bucket集合在一起称为Bucket Brigade。
使用stream_bucket_make_writeable函数可以将一个个Bucket取出来。
$bucket = stream_bucket_make_writeable($in);
您只需对$bucket执行var_dump操作就可以了解到它是一个简单的stdClass对象。
class stdClass#3 (3) {
public $bucket =>
resource(68) of type (userfilter.bucket)
public $data =>
string(7) "ドコ
"
public $datalen =>
int(7)
}
在filter方法中,我们可以从传入的Bucket Brigade中逐个取出Bucket,并对其进行处理,然后使用stream_bucket_append方法将其传递给$out。这样就可以实现所需的功能。
完成版 – Final version
<?php
// streamでズンドコキヨシ
class Zundoko
{
function stream_open($path, $mode, $options, &$opened_path) {
return true;
}
function stream_read(int $count) {
return (mt_rand(0, 1) ? 'ズン' : 'ドコ') . PHP_EOL;
}
function stream_eof() {
return false;
}
}
stream_wrapper_register('zundoko', 'Zundoko');
class Kiyoshi extends php_user_filter
{
private $zundoko = [];
private $finished = false;
const KIYOSHI = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'];
public function filter($in, $out, &$consumed, $closing)
{
if ($this->finished) {
while ($bucket = stream_bucket_make_writeable($in));
return PSFS_ERR_FATAL;
}
while ($bucket = stream_bucket_make_writeable($in)) {
$this->zundoko[] = rtrim($bucket->data);
if (count($this->zundoko) > 5) {
array_shift($this->zundoko);
}
if (self::KIYOSHI === $this->zundoko) {
$bucket->data .= "キ・ヨ・シ!\n";
$bucket->datalen += 19;
$this->finished = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
stream_filter_register('kiyoshi', 'Kiyoshi');
$zd = fopen('zundoko://', 'r');
stream_filter_append($zd, 'kiyoshi');
while ($result = fgets($zd)) {
echo $result;
}
<?php
// streamでズンドコキヨシ
class Zundoko
{
function stream_open($path, $mode, $options, &$opened_path) {
return true;
}
function stream_read(int $count) {
return (mt_rand(0, 1) ? 'ズン' : 'ドコ') . PHP_EOL;
}
function stream_eof() {
return false;
}
}
stream_wrapper_register('zundoko', 'Zundoko');
class Kiyoshi extends php_user_filter
{
private $zundoko = [];
private $finished = false;
const KIYOSHI = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'];
public function filter($in, $out, &$consumed, $closing)
{
if ($this->finished) {
while ($bucket = stream_bucket_make_writeable($in));
return PSFS_ERR_FATAL;
}
while ($bucket = stream_bucket_make_writeable($in)) {
$this->zundoko[] = rtrim($bucket->data);
if (count($this->zundoko) > 5) {
array_shift($this->zundoko);
}
if (self::KIYOSHI === $this->zundoko) {
$bucket->data .= "キ・ヨ・シ!\n";
$bucket->datalen += 19;
$this->finished = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
stream_filter_register('kiyoshi', 'Kiyoshi');
$zd = fopen('zundoko://', 'r');
stream_filter_append($zd, 'kiyoshi');
while ($result = fgets($zd)) {
echo $result;
}
或许在不使用无意义的流过滤器的情况下,只需通过Zundoko流来完成清宫判定会更简单吧。