Torrent文件解码-PHP实现

本文详细介绍了如何使用PHP解析Torrent文件,包括理解Torrent文件结构、处理metadata、解析字典和整数等关键步骤,旨在帮助开发者掌握Torrent文件的解码技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

<? php
/*
功能描述:Torrent文件解码器,使用数组表示文件结构
修改时间:2006-10-21 01:39
修改者:fishtrees
版本: v0.2
备注:与v0.1相比使用了更简单的数据结构
*/
// if (eregi("bdecoder.php",$_SERVER['PHP_SELF'])) die("You can't access this file directly");
include_once ( " debug.fun.php " );
class  Bdecoder{
 
// 定义类型常量
  public   static   $STR = " s " ;
 
public   static   $INT = " i " ;
 
public   static   $LIST = " l " ;
 
public   static   $DIC = " d " ;

 
// 控制模块
 //<String> $str   torrent文件原始串

  public   function  BDecode( $str ) {  
  
$root = $this -> BDec( $str ); // 开始解码
   if ( $root !== false   &&   $root !== null ){
   
return   $root ;
  }
else {
   
return   false ;
  }

 }
 
 
// 解码器
 //<String> &$str   torrent文件原始串

  private   function  BDec( & $str ) {
  
// 解码类型选择
   if  ( preg_match ( ' /^(d+):/ ' ,   $str )) // 字符串类型
    return   $this -> BDec_string( $str );
  
elseif  ( preg_match ( ' /^i(d+)e/ ' ,   $str )) // 整数类型
    return   $this -> BDec_integer( $str );
  
elseif  ( $str [ 0 ==   " l " ) // 表类型
    return   $this -> BDec_list( $str );
  
elseif  ( $str [ 0 ==   " d " ) // 字典类型
    return   $this -> BDec_dictionary( $str );
  
else   trigger_error ( " Error:  " . htmlspecialchars ( substr ( $str , 0 , 50 )) , E_USER_WARNING ); // 出错
    return   null ;
 }

 
// 解码字符串类型
 //<String> $str   torrent文件原始串

  private   function  BDec_string( & $str ) {
  
if  ( ! preg_match ( ' /^(d+):/ ' ,   $str ,   $m )){ // 判断原始串第一个编码模式是否是字符串类型编码模式后截取该模式
             //该类型编码模式=N:长度为N的字符串

    return   false ;
  }
  
$l   =   $m [ 1 ]; // 取得要提取的字符串长度
   $pl   =   strlen ( $l +   1 ; // 取得开始位置
   $v   =   substr ( $str ,   $pl ,   $l ); // 取得要提取字符串
   $str   =   substr ( $str , $pl + $l ); // 从原始串中删除已经提取的字符串
  //$rv=array('value'=>$v,'btdatatype'=>self::$STR);//构造返回值

   $rv = self :: $STR . $v ; // 构造返回值
   return   $rv ;
 }
 
 
// 解码数据项名称,字符串类型
 //<String> $str   torrent文件原始串

  private   function  BDec_name( & $str ) {
  
// 同解码字符串类型,只是不构造返回值
   if  ( ! preg_match ( ' /^(d+):/ ' ,   $str ,   $m ))  return   false ;
  
$l   =   $m [ 1 ];
  
$pl   =   strlen ( $l +   1 ;
  
$v   =   substr ( $str ,   $pl ,   $l );
  
$str   =   substr ( $str , $pl + $l );
  
return   $v ;
 }
 
// 解码整数类型
 //<String> $str   torrent文件原始串

  private   function  BDec_integer( & $str ) {
  
if  ( ! preg_match ( ' /^i(d+)e/ ' ,   $str ,   $m ))  return   false ; // 判断原始串第一个编码模式是否是整数类型编码模式后截取该模式
                //整数类型编码模式=i值e

   $v   =   $m [ 1 ]; // 取得整数值
   if  ( $v   ===   " -0 "  OR ( $v [ 0 ==   " 0 "  AND  strlen ( $v !=   1 ))  return   false ; // 判断整数值是否合法
   $str   =   substr ( $str , strlen ( $v ) + 2 );
  
// $rv=array('value'=>$v,'btdatatype'=>self::$INT);//构造返回值
   $rv = self :: $INT . $v ; // 构造返回值
   return   $rv ;
 }
 
// 解码表类型
 //<String> $str   torrent文件原始串

  private   function  BDec_list( & $str ) {  
  
if  ( $str [ 0 !=   " l " ) // 判断原始串第一个编码模式是否是表类型编码模式
       //表类型编码模式=l任意类型的编码模式e

    return   false ;
  
$str   =   substr ( $str , 1 ); // 将原始串中的第1个代表表模式开始的字符去掉,准备开始提取表模式中的内容
  //初始化返回值

   $children = array ();
  
// $children['value']=array();
   $i = 0 ;
  
do  {
   
if  ( $str [ 0 ==   " e " break ; // 如果当前原始串是表模式的结束字符。则停止提取
   
   
$get = $this -> BDec( $str ); // 提取表模式中包含的其他模式的所代表的内容
   //构造返回值

    $children [] = $get ;
   
unset ( $get );
   
  } 
while  ( true );
  
$str   =   substr ( $str , 1 ); // 在原始串中删除当前解码的表模式的结束字符
   $children [ ' btdatatype ' ] = self :: $LIST ;
  
return   $children ;
 }
 
// 解码字典类型
 //<String> $str   torrent文件原始串

  private   function  BDec_dictionary(  & $str ) {
  
if  ( $str [ 0 !=   " d " return   false ; // 判断原始串第一个编码模式是否是字典类型编码模式
          //表类型编码模式=d任意类型的编码模式e

   $str   =   substr ( $str , 1 ); // 将原始串中的第1个代表字典模式开始的字符去掉,准备开始提取这个字典模式中的内容
  //初始化返回值

   $children   =   Array ();

  
do  {
   
if  ( $str [ 0 ==   " e " break ; // 如果当前原始串是字典模式的结束字符。则停止提取
    $name   =   $this -> BDec_string( $str ); // 取得字典中数据项的名称,如info
    unset ( $get );
   
$get = $this -> BDec( $str ); // 提取当前字典模式中所包含的其他编码模式的内容
   //构造返回值

    $children [ $name ] = $get ;
  } 
while  ( true );
  
$str   =   substr ( $str , 1 ); // 在原始串中删除当前解码的字典模式的结束字符
   $children [ ' btdatatype ' ] = self :: $DIC ;
  
return   $children ;
 }
 
/*
 SYNTAX
Array
(
    [sannounce] => shttp://202.202.120.230:6969/announce
    [screated by] => sBitComet/0.62
    [screation date] => i1159691884
    [sencoding] => sGBK
    [sinfo] => Array
        (
            [slength] => i152788
            [sname] => sBt-protocol-wiki - 維基百科.mht
            [sname.utf-8] => sBt-protocol-wiki .mht
            [spiece length] => i32768
            [spieces] => 

           [btdatatype] => d
        )

    [snodes] => Array
        (
            [0] => Array
                (
                    [0] => s220.139.90.42
                    [1] => i18974
                    [btdatatype] => l
                )

            [1] => Array
                (
                    [0] => s84.43.40.27
                    [1] => i25632
                    [btdatatype] => l
                )

            [2] => Array
                (
                    [0] => s218.254.81.203
                    [1] => i7844
                    [btdatatype] => l
                )

            [3] => Array
                (
                    [0] => s220.160.218.216
                    [1] => i29754
                    [btdatatype] => l
                )

            [4] => Array
                (
                    [0] => s68.62.155.116
                    [1] => i25167
                    [btdatatype] => l
                )

            [5] => Array
                (
                    [0] => s85.84.133.233
                    [1] => i15547
                    [btdatatype] => l
                )

            [6] => Array
                (
                    [0] => s85.75.114.252
                    [1] => i6881
                    [btdatatype] => l
                )

            [7] => Array
                (
                    [0] => s213.180.110.115
                    [1] => i26874
                    [btdatatype] => l
                )

            [8] => Array
                (
                    [0] => s203.59.104.81
                    [1] => i26671
                    [btdatatype] => l
                )

            [9] => Array
                (
                    [0] => s84.3.226.219
                    [1] => i23422
                    [btdatatype] => l
                )

            [btdatatype] => l
        )

    [spublisher] => sfishtrees
    [spublisher-url] => shttp://bbs.swnu.edu.cn/show.php?area=14&aid=1190
    [spublisher-url.utf-8] => shttp://bbs.swnu.edu.cn/show.php?area=14&aid=1190
    [spublisher.utf-8] => sfishtrees
    [btdatatype] => d
)

 
*/
 
 
// 在已经解码的Torrent中查找指定的数据项名称是否存在 返回值<boolean>
 //<Array>$torrent 已经解码的Torrent文件
 //<String>$query 要查询的路径,如/info,第一个'/'代表根路径

  public   function  entry_exists( $torrent ,   $query ) {
  
if ( ! preg_match_all ( " /([^/]+)/ " , $query , $matches , PREG_SET_ORDER)){ // 判断查询路径格式后截取各个路径名
    trigger_error ( " Invalid Query " , E_USER_WARNING );
   
return   false ;   
  }
  
  
// 按路径查找是否存在要查找的路径
   for ( $i = 0 ; $i < count ( $matches ); $i ++ ){
   
if ( $i == 0 ){ // 第一次从根路径开始查找
     $flag = $this -> chkPath( $torrent , ' s ' . $matches [ $i ][ 0 ]); // 判断父路径(即根路径)下的第一个路径名是否存在
     if ( $flag == false ){
     
return   $flag ;
    }
    
$parent = $torrent [ ' s ' . $matches [ $i ][ 0 ]]; // 设置下一次开始查找的父路径
   } else {
    
$flag = $this -> chkPath( $parent , ' s ' . $matches [ $i ][ 0 ]); // 判断父路径下的第一个路径名是否存在
     if ( $flag == false ){
     
return   $flag ;
    }    
    
$parent = $parent [ ' s ' . $matches [ $i ][ 0 ]]; // 设置下一次开始查找的父路径
   }
   
  }
  
return   $flag ;
 }


 
// 在已经解码的Torrent中取得指定的数据项 返回值<Array>
 //<Array>$torrent 已经解码的Torrent文件
 //<String>$query 要查询的路径,如/info,第一个'/'代表根路径

  public   function  entry_get( $torrent ,   $query ) {
  
if ( ! preg_match_all ( " /([^/]+)/ " , $query , $matches , PREG_SET_ORDER)){ // 判断查询路径格式后截取各个路径名
    trigger_error ( " Invalid Query " , E_USER_WARNING );
   
return   false ;  
  }
  
if ( ! $this -> entry_exists( $torrent , $query ))  return   false ;
  
for ( $i = 0 ; $i < count ( $matches ); $i ++ ){
   
if ( $i == 0 ){ // 第一次从根路径开始查找
     $get = $torrent [ ' s ' . $matches [ $i ][ 0 ]]; // 取得父路径(即根路径)中的第一个路径所包含的数据项
   } else {
    
$get = $get [ ' s ' . $matches [ $i ][ 0 ]]; // 从父路径(即上一次的路径)中取得当前路径所包含的数据项
   }
  }
  
return   $get ;
 }

 
// 在已经解码的Torrent中的单个路径中查找指定的数据项名称是否存在 返回值<boolean>
 //<Array>$arr 已经解码的Torrent文件的某个路径中的数据项集合
 //<String>$path 要查询的单个路径,如info,深度为1
 //注:判断返回值是否合法请用绝对等于,===

  private   function  chkPath( $arr , $path ){
  
if (@ is_array ( $arr )){
   
if ( array_key_exists ( $path , $arr )){
    
return   true ;
   }
else {
    
return   false ;
   }
  }
else {
   
return   false ;
  }
 }
}

// 测试
$str = file_get_contents ( ' d:/testfiles.torrent ' );
$dcoder = new  Bdecoder();
$torrent = $dcoder -> BDecode( $str );
// debug_echo('解码后',$torrent);
debug_echo( ' get ' , $isExist = $dcoder -> entry_get( $torrent , ' /info/files/ ' ));

// debug_echo('是否存在',$isExist);


?>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值