PHP基于闭包思想实现的BT(torrent)文件解析工具实例详解
| 本篇章节讲解PHP基于闭包思想实现的torrent文件解析工具。分享给大家供大家参考,具体如下: PHP对静态词法域的支持有点奇怪,内部匿名函数必须在参数列表后面加上use关键字,显式的说明想要使用哪些外层函数的局部变量。0) $func(); echo "wown"; }; } $foo = count_down(3); $foo(); 我本来是想这样的。但是不行,会在第7行调用$func的时候报错。 错误是Fatal error: Function name must be a string in - on line 7 反复试验后发觉,外部的匿名函数应该通过引用传值传给内部,否则是不行的:  0)
      $foo();
  };
}
$foo = count_down(4);
$foo();
 像上面这样写就对了。 下面是另一种方法:0); 不过,这段代码有点小错误。编译虽然没错,但是$foo函数每次返回的都是4. 也就是use关键字看上去像是支持静态词法域的,在这个例子上,它只是对外层函数使用的变量作了一个简单拷贝。 让我们稍微修改一下,把第3行的use($count)改为use(&$count):0); 这样才正确。 我个人使用的方式是基于类的,做成了类似下面的形式:0) $this($count - 1); echo "wown"; } } $foo = new Foo(); $foo(4); 这样做的行为也是正确的。 这样不会像前一个例子那样失去了递归调用的能力。 虽然这是一个类,但是只不过是在手动实现那些支持闭包和静态词法域的语言中,编译器自动实现的动作。 其实今天早上,我本来准备用类scheme的风格写一个解析器的。可能稍微晚点吧。scheme风格的函数式编程是这样的: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 '解析数字时遇到错误',"rn";
            echo '在i和e之间不应该出现非数字字符'."rn";
            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 '的字典时遇到错误:'."rn";
          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 ":rn";
            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."rn";
    echo "Analysing Process Teminated.";
    exit();
  }
}
?>
 这里很有趣的是,明明我对文件调用了 更多关于PHP相关内容感兴趣的读者可查看本站专题:《》、《》、《》、《》、《》、《》、《》、《》及《》、 希望本文所述对大家PHP程序设计有所帮助。 (编辑:源码网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! | 

