侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解

php  /  管理员 发布于 7年前   237

本文实例讲述了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解析工具的代码如下:

_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闭包(Closure)使用详解
  • PHP闭包函数详解
  • php的闭包(Closure)匿名函数详解
  • PHP闭包函数传参及使用外部变量的方法
  • 浅谈PHP 闭包特性在实际应用中的问题
  • PHP中的闭包(匿名函数)浅析
  • PHP闭包实例解析
  • php的闭包(Closure)匿名函数初探
  • PHP 闭包详解及实例代码
  • php基于闭包实现函数的自调用(递归)实例分析
  • PHP 闭包获取外部变量和global关键字声明变量的区别讲解
  • 浅析PHP中的闭包和匿名函数
  • PHP闭包定义与使用简单示例


  • 上一条:
    php 实现收藏功能的示例代码
    下一条:
    PHP中遍历二维数组_以不同形式的输出操作实例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客