本文实例讲述了PHP基于闭包思想实现的torrent文件解析工具。分享给大家供大家参考,具体如下:

PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用哪些外层函数的局部变量。
function count_down($count)
{
return $func = function()
use($count,$func)
{
if(--$count > 0)
$func();
echo "wow\n";
};
}
$foo = count_down(3);
$foo();
我本来是想这样的。但是不行,会在第7行调用$func的时候报错。
错误是Fatal error: Function name must be a string in - on line 7
反复试验后发觉,外部的匿名函数应该通过引用传值传给内部,否则是不行的:
function count_down($count)
{
return $foo = function()
use(&$count,&$foo)
{
echo $count."\n";
if(--$count > 0)
$foo();
};
}
$foo = count_down(4);
$foo();
像上面这样写就对了。
下面是另一种方法:
function count_down_again($count)
{
return function()use($count)
{
printf("wow %d\n",$count);
return --$count;
};
}
$foo = count_down_again(5);
while($foo() >0);
不过,这段代码有点小错误。编译虽然没错,但是$foo函数每次返回的都是4.
也就是use关键字看上去像是支持静态词法域的,在这个例子上,它只是对外层函数使用的变量作了一个简单拷贝。
让我们稍微修改一下,把第3行的use($count)改为use(&$count):
function count_down_again($count)
{
return function()use(&$count)
{
printf("wow %d\n",$count);
return --$count;
};
}
$foo = count_down_again(5);
while($foo() >0);
这样才正确。
我个人使用的方式是基于类的,做成了类似下面的形式:
class Foo
{
public function __invoke($count)
{
if($count > 0)
$this($count - 1);
echo "wow\n";
}
}
$foo = new Foo();
$foo(4);
这样做的行为也是正确的。
这样不会像前一个例子那样失去了递归调用的能力。
虽然这是一个类,但是只不过是在手动实现那些支持闭包和静态词法域的语言中,编译器自动实现的动作。
其实今天早上,我本来准备用类scheme的风格写一个解析器的。可能稍微晚点吧。scheme风格的函数式编程是这样的:
function yet_another_count_down($func,$count)
{
$func($count);
if($count > 0)
yet_another_count_down($func,$count - 1);
}
yet_another_count_down(function($var){echo $var."\n";},6);
它不是很依赖静态词法域,虽然scheme对静态词法域的支持还是很不错的。它主要还是利用了first-class-function。当然,这也是一种典型的闭包。
我实现的torrent解析工具的代码如下:
<?php
$file_name = '1.torrent';
$file = fopen($file_name,'r');
$nil = new Parser($file);//构造解析器
$nil = $nil();//进行解析
$pos = ftell($file);
echo '读取到文件位置'.sprintf('0x%08X',$pos)."\r\n";
fseek($file,0,SEEK_END);
echo '还剩下'.(ftell($file) - $pos).'字节未读取'."\r\n";
if(!feof($file))
{
echo '文件还未结束,再读一个字符:';
$ch = fgetc($file);
if(is_string($ch) && ereg('\w',$ch))
{
echo $ch."\r\n";
}
else
{
printf('0x%02X',$ch);
echo "\r\n";
}
echo '现在的文件位置是'.sprintf('0x%08X',ftell($file))."\r\n";
echo '文件'.(feof($file)?'已结束':'还未结束')."\r\n";
}
fclose($file);//解析器后面不再工作了,此时可以释放文件指针了。
$info = @$nil['value'][0]['info'];
if(!$info)
{
echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';
exit();
}
$name = $info['name.utf-8'] ?$info['name.utf-8']:$info['name'];
if(!$name)
{
echo '这是一个有效的B-Encoding文件,但它不是一个有效的种子文件';
exit();
}
echo $name."\r\n";
if($info['files'])
{
$index = 0;
foreach($info['files'] as $f)
{
$index += 1;
$path = $f['path.utf8'] ?$f['path.utf8'] :$f['path'];
if(!$path)
{
echo '文件列表中的第'.$index."个文件不含目录\r\n";
continue;
}
if(0 === strpos($path[0],"_____padding_file_"))continue;
$under_folder = false;
foreach($path as $item)
{
if($under_folder)
{
echo '/';
}else{
$under_folder = true;
}
echo $item;
}
echo "\r\n";
}
}
else
{
echo "仅有一个文件\r\n";
}
class Parser
{
private $_file;
public function __construct($file)
{
$this ->_file = $file;
}
public function __invoke($parent = array())
{
$ch = $this ->read();
switch($ch)
{
case 'i':
{
$n = $ch;
while(($ch = $this ->read()) != 'e')
{
if(!is_numeric($ch))
{
echo '在';
echo sprintf(
'0x%08X',ftell($this ->_file));
echo '解析数字时遇到错误',"\r\n";
echo '在i和e之间不应该出现非数字字符'."\r\n";
echo '意外的字符'.sprintf('0x%02X',$ch);
exit();
}
else
{
$n .= $ch;
}
}
$n += 0;
$offset = count($parent['value']);
$parent['value'][$offset] = $n;
return $parent;
}
break;
case 'd':
{
$node = array();
//这个$node变量作为字典对象准备加入到$parent的孩子节点中去
//$node['type'] = 'd';
while('e' != ($tmp = $this($node)))
{//每次给$node带来一个新孩子
$node = $tmp;
}
$child_count = count($node['value']);
if($child_count % 2 != 0)
{
echo '解析结尾于';
echo sprintf('0x%08X',ftell($this ->_file));
echo '的字典时遇到错误:'."\r\n";
echo '字典的对象映射不匹配';
exit();
}
$product = array();
for($i = 0; $i < $child_count; $i += 2)
{
$key = $node['value'][$i];
$value = $node['value'][$i + 1];
if(!is_string($key))
{
echo '无效的字典结尾于';
echo sprintf('0x%08X',ftell($this ->_file));
echo ":\r\n";
echo '解析[k => v]配对时遇到错误,k应为字符串';
exit();
}
$product[$key] = $value;
}
/*
* 思想是这样的:子节点想要加入父节点时,
* 往父节点的value数组添加。
* 当父节点收集好所需的信息后,
* 父节点自身再从它的value节点整合内容
* 对于字典和列表统一这样处理会大大降低代码量
*/
$offset = count($parent['value']);
$parent['value'][$offset] = $product;
return $parent;
}
break;
case 'l';
{
$node = array();
while('e' != ($tmp = $this($node)))
{
$node = $tmp;
}
$offset = count($parent['value']);
$parent['value'][$offset] = $node['value'];
return $parent;
}
break;
case 'e':
return 'e';
break;
default:
{
if(!is_numeric($ch))
{
$this ->unexpected_character(
ftell($this ->_file) - 1,$ch);
}
$n = $ch;
while(($ch = $this ->read()) != ':')
{
$n .= $ch;
if(!is_numeric($n))
{
unexpected_character(
ftell($this ->_file) - 1,$ch);
}
}
$n += 0;
$str = '';
for(; $n > 0; --$n)
{
$str .= $this ->read();
}
$offset = count($parent['value']);
$parent['value'][$offset] = $str;
return $parent;
}
break;
}
}
/*
* read函数包裹了$this ->_file变量
*/
function read()
{
if(!feof($this ->_file))
{
return fgetc($this ->_file);
}else{
echo '意外的文件结束';
exit();
}
}
/*
* unexpected_character函数接收2个参数
* 它用于指明脚本在何处遇到了哪个不合法的字符,
* 并在返回前终止脚本的运行。
*/
function unexpected_character($pos,$val)
{
$hex_pos = sprintf("0x%08X",$pos);
$hex_val = sprintf("0x%02X",$val);
echo 'Unexpected Character At Position ';
echo $hex_pos.' , Value '.$hex_val."\r\n";
echo "Analysing Process Teminated.";
exit();
}
}
?>
这里很有趣的是,明明我对文件调用了fseek($file,0,SEEK_END);移动到文件末尾了,但是feof还是报告说文件没有结束,并且fgetc返回一个0,而没有报错。但是此时文件实际上已经到末尾了。
更多关于PHP相关内容感兴趣的读者可查看本站专题:《php curl用法总结》、《php字符串(string)用法总结》、《PHP数组(Array)操作技巧大全》、《php排序算法总结》、《PHP常用遍历算法与技巧总结》、《PHP数据结构与算法教程》、《php程序设计算法总结》、《PHP数学运算技巧总结》及《PHP运算与运算符用法总结》、
希望本文所述对大家PHP程序设计有所帮助。
# PHP
# 闭包
# BT
# torrent
# 文件
# 解析
# PHP闭包(Closure)使用详解
# PHP闭包函数详解
# php的闭包(Closure)匿名函数详解
# PHP闭包函数传参及使用外部变量的方法
# 浅谈PHP 闭包特性在实际应用中的问题
# PHP中的闭包(匿名函数)浅析
# PHP闭包实例解析
# php的闭包(Closure)匿名函数初探
# PHP 闭包详解及实例代码
# php基于闭包实现函数的自调用(递归)实例分析
# PHP 闭包获取外部变量和global关键字声明变量的区别讲解
# 浅析PHP中的闭包和匿名函数
# PHP闭包定义与使用简单示例
# 这是一个
# 是这样
# 还未
# 报错
# 但它
# 递归
# 程序设计
# 的是
# 都是
# 是在
# 是一种
# 成了
# 相关内容
# 在这个
# 让我们
# 则是
# 我对
# 遍历
# 不是很
# 会在
相关文章:
深圳网站制作培训,深圳哪些招聘网站比较好?
制作网站的过程怎么写,用凡科建站如何制作自己的网站?
Thinkphp 中 distinct 的用法解析
网站制作新手教程,新手建设一个网站需要注意些什么?
在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?
宝塔面板创建网站无法访问?如何快速排查修复?
开封网站制作公司,网络用语开封是什么意思?
网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?
相亲简历制作网站推荐大全,新相亲大会主持人小萍萍资料?
建站之星安装后界面空白如何解决?
C++如何将C风格字符串(char*)转换为std::string?(代码示例)
零服务器AI建站解决方案:快速部署与云端平台低成本实践
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
网站广告牌制作方法,街上的广告牌,横幅,用PS还是其他软件做的?
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何通过FTP空间快速搭建安全高效网站?
如何在阿里云购买域名并搭建网站?
如何选择可靠的免备案建站服务器?
长沙做网站要多少钱,长沙国安网络怎么样?
如何快速查询网址的建站时间与历史轨迹?
如何快速搭建二级域名独立网站?
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
如何在云服务器上快速搭建个人网站?
如何安全更换建站之星模板并保留数据?
建站之星伪静态规则如何正确配置?
香港服务器WordPress建站指南:SEO优化与高效部署策略
英语简历制作免费网站推荐,如何将简历翻译成英文?
网站规划与制作是什么,电子商务网站系统规划的内容及步骤是什么?
宝塔建站教程:一键部署配置流程与SEO优化实战指南
建站之星2.7模板快速切换与批量管理功能操作指南
网站代码制作软件有哪些,如何生成自己网站的代码?
广东专业制作网站有哪些,广东省能源集团有限公司官网?
较简单的网站制作软件有哪些,手机版网页制作用什么软件?
武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?
文字头像制作网站推荐软件,醒图能自动配文字吗?
建站之星如何取消后台验证码生成?
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
电影网站制作价格表,那些提供免费电影的网站,他们是怎么盈利的?
宝盒自助建站智能生成技巧:SEO优化与关键词设置指南
导航网站建站方案与优化指南:一站式高效搭建技巧解析
攀枝花网站建设,攀枝花营业执照网上怎么年审?
如何快速搭建响应式可视化网站?
建站之星安装后如何配置SEO及设计样式?
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
建站之星价格显示格式升级,你的预算足够吗?
建站中国官网:模板定制+SEO优化+建站流程一站式指南
台州网站建设制作公司,浙江手机无犯罪记录证明怎么开?
公司网站的制作公司,企业网站制作基本流程有哪些?
5种Android数据存储方式汇总
*请认真填写需求信息,我们会在24小时内与您取得联系。