《PHP編程:PHP基于閉包思想實現(xiàn)的BT(torrent)文件解析工具實例詳解》要點:
本文介紹了PHP編程:PHP基于閉包思想實現(xiàn)的BT(torrent)文件解析工具實例詳解,希望對您有用。如果有疑問,可以聯(lián)系我們。
本文實例講述了PHP基于閉包思想實現(xiàn)的torrent文件解析工具.分享給大家供大家參考,具體如下:PHP實戰(zhàn)
PHP對靜態(tài)詞法域的支持有點奇怪,內(nèi)部匿名函數(shù)必須在參數(shù)列表后面加上use關鍵字,顯式的說明想要使用哪些外層函數(shù)的局部變量.PHP實戰(zhàn)
function count_down($count) { return $func = function() use($count,$func) { if(--$count > 0) $func(); echo "wow\n"; }; } $foo = count_down(3); $foo();
我本來是想這樣的.但是不行,會在第7行調(diào)用$func的時候報錯.PHP實戰(zhàn)
錯誤是Fatal error: Function name must be a string in - on line 7PHP實戰(zhàn)
反復試驗后發(fā)覺,外部的匿名函數(shù)應該通過引用傳值傳給內(nèi)部,否則是不行的:PHP實戰(zhàn)
function count_down($count) { return $foo = function() use(&$count,&$foo) { echo $count."\n"; if(--$count > 0) $foo(); }; } $foo = count_down(4); $foo();
像上面這樣寫就對了.PHP實戰(zhàn)
下面是另一種方法:PHP實戰(zhàn)
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函數(shù)每次返回的都是4.PHP實戰(zhàn)
也就是use關鍵字看上去像是支持靜態(tài)詞法域的,在這個例子上,它只是對外層函數(shù)使用的變量作了一個簡單拷貝.PHP實戰(zhàn)
讓我們稍微修改一下,把第3行的use($count)改為use(&$count):PHP實戰(zhàn)
function count_down_again($count) { return function()use(&$count) { printf("wow %d\n",$count); return --$count; }; } $foo = count_down_again(5); while($foo() >0);
這樣才正確.PHP實戰(zhàn)
我個人使用的方式是基于類的,做成了類似下面的形式:PHP實戰(zhàn)
class Foo { public function __invoke($count) { if($count > 0) $this($count - 1); echo "wow\n"; } } $foo = new Foo(); $foo(4);
這樣做的行為也是正確的.PHP實戰(zhàn)
這樣不會像前一個例子那樣失去了遞歸調(diào)用的能力.PHP實戰(zhàn)
雖然這是一個類,但是只不過是在手動實現(xiàn)那些支持閉包和靜態(tài)詞法域的語言中,編譯器自動實現(xiàn)的動作.PHP實戰(zhàn)
其實今天早上,我本來準備用類scheme的風格寫一個解析器的.可能稍微晚點吧.scheme風格的函數(shù)式編程是這樣的:PHP實戰(zhàn)
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);
它不是很依賴靜態(tài)詞法域,雖然scheme對靜態(tài)詞法域的支持還是很不錯的.它主要還是利用了first-class-function.當然,這也是一種典型的閉包.PHP實戰(zhàn)
我實現(xiàn)的torrent解析工具的代碼如下:PHP實戰(zhàn)
<?php $file_name = '1.torrent'; $file = fopen($file_name,'r'); $nil = new Parser($file);//構(gòu)造解析器 $nil = $nil();//進行解析 $pos = ftell($file); echo '讀取到文件位置'.sprintf('0x%08X',$pos)."\r\n"; fseek($file,0,SEEK_END); echo '還剩下'.(ftell($file) - $pos).'字節(jié)未讀取'."\r\n"; if(!feof($file)) { echo '文件還未結(jié)束,再讀一個字符:'; $ch = fgetc($file); if(is_string($ch) && ereg('\w',$ch)) { echo $ch."\r\n"; } else { printf('0x%02X',$ch); echo "\r\n"; } echo '現(xiàn)在的文件位置是'.sprintf('0x%08X',ftell($file))."\r\n"; echo '文件'.(feof($file)?'已結(jié)束':'還未結(jié)束')."\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 '解析數(shù)字時遇到錯誤',"\r\n"; echo '在i和e之間不應該出現(xiàn)非數(shù)字字符'."\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的孩子節(jié)點中去 //$node['type'] = 'd'; while('e' != ($tmp = $this($node))) {//每次給$node帶來一個新孩子 $node = $tmp; } $child_count = count($node['value']); if($child_count % 2 != 0) { echo '解析結(jié)尾于'; 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 '無效的字典結(jié)尾于'; echo sprintf('0x%08X',ftell($this ->_file)); echo ":\r\n"; echo '解析[k => v]配對時遇到錯誤,k應為字符串'; exit(); } $product[$key] = $value; } /* * 思想是這樣的:子節(jié)點想要加入父節(jié)點時, * 往父節(jié)點的value數(shù)組添加. * 當父節(jié)點收集好所需的信息后, * 父節(jié)點自身再從它的value節(jié)點整合內(nèi)容 * 對于字典和列表統(tǒng)一這樣處理會大大降低代碼量 */ $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函數(shù)包裹了$this ->_file變量 */ function read() { if(!feof($this ->_file)) { return fgetc($this ->_file); }else{ echo '意外的文件結(jié)束'; exit(); } } /* * unexpected_character函數(shù)接收2個參數(shù) * 它用于指明腳本在何處遇到了哪個不合法的字符, * 并在返回前終止腳本的運行. */ 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(); } } ?>
這里很有趣的是,明明我對文件調(diào)用了fseek($file,0,SEEK_END);
移動到文件末尾了,但是feof還是報告說文件沒有結(jié)束,并且fgetc返回一個0,而沒有報錯.但是此時文件實際上已經(jīng)到末尾了.PHP實戰(zhàn)
更多關于PHP相關內(nèi)容感興趣的讀者可查看本站專題:《php curl用法總結(jié)》、《php字符串(string)用法總結(jié)》、《PHP數(shù)組(Array)操作技巧大全》、《php排序算法總結(jié)》、《PHP常用遍歷算法與技巧總結(jié)》、《PHP數(shù)據(jù)結(jié)構(gòu)與算法教程》、《php程序設計算法總結(jié)》、《PHP數(shù)學運算技巧總結(jié)》及《PHP運算與運算符用法總結(jié)》、PHP實戰(zhàn)
希望本文所述對大家PHP程序設計有所幫助.PHP實戰(zhàn)
轉(zhuǎn)載請注明本頁網(wǎng)址:
http://www.snjht.com/jiaocheng/238.html