今年(2016)年初來到A公司了,剛?cè)肼毜臅r(shí)候比較缺前端fe,就過來頂了三個(gè)月,這段時(shí)間學(xué)到了好多前端開發(fā)的知識(shí),這些都是題外話了。期間接觸了一個(gè)很好用的前端自動(dòng)化部署工具 —— fis,其中有個(gè)小功能特別好用,實(shí)時(shí)監(jiān)控文件,然后同步到服務(wù)端,大大提高了開發(fā)的效率。之后轉(zhuǎn)到后端,發(fā)現(xiàn)大家開發(fā)測(cè)試相同模塊的時(shí)候經(jīng)常會(huì)出現(xiàn)互相覆蓋的情況,要么就一個(gè)個(gè)的復(fù)制文件到服務(wù)器上,效率非常低下,之前為這個(gè)也開發(fā)過一個(gè)純shell版本的自動(dòng)化監(jiān)控同步工具文件同步監(jiān)控工具后來發(fā)現(xiàn)純shell版本的使用體驗(yàn)和擴(kuò)展性能非常差,就開發(fā)了這篇文章所要介紹的php版本。

這次開發(fā)了一個(gè)規(guī)范的php composer包,使用的時(shí)候直接require即可。

實(shí)現(xiàn)

php版本的實(shí)現(xiàn)沿用了shell版本的思想,通過管道命令傳遞shell命令的結(jié)果,然后對(duì)結(jié)果做各種處理,達(dá)到我們監(jiān)控文件的目的。

為了完成這個(gè)功能,首先要實(shí)現(xiàn)一個(gè)php版本的管道命令,這里我對(duì)resource popen ( string $command , string $mode )做了封裝,可以通過很友好的處理命令的返回值。

完成了上面的模塊,接下來就是具體的實(shí)現(xiàn)了,實(shí)現(xiàn)這里有多重友好的方式,添加多路徑遞歸監(jiān)控、包含正則匹配、排除正則匹配。

pipe的開發(fā)

<?php/** * php對(duì)對(duì)popen的封裝,通過回調(diào)的方式模擬管道命令 * */namespace Aizuyan\Pipe;class Pipe{    /**     * 要通過管道執(zhí)行的命令     */
    protected $command = "";
    /**     * 回調(diào)函數(shù),將管道數(shù)據(jù)傳遞給該函數(shù)     */
    protected $callback = null;
    /**     * 數(shù)據(jù)之間的分隔符     */
    protected $delimiter = "\n";
    /**     * 設(shè)置命令     *     * @param cmd string 要運(yùn)行的命令     */
    public function setCmd($cmd)
    {        $this->command = $cmd;
        return $this;
    }    /**     * 設(shè)置回調(diào)函數(shù),處理管道輸出的命令     */
    public function setCallback(callable $cb)
    {        $this->callback = $cb;
        return $this;
    }    /**     * 設(shè)置數(shù)據(jù)片段之間的分隔符     */
    public function setDelimiter($delimiter)
    {        $this->delimiter = $delimiter;
        return $this;
    }    /**     * 開始運(yùn)行     */
    public function run()
    {        $fp = popen($this->command, "r");
        if (false === $fp) {            throw new \RuntimeException("popen execute command failed!");
        }        $item = "";
        while (!feof($fp)) {            $char = fgetc($fp);
            if ($this->delimiter == $char) {                call_user_func($this->callback, $item);
                $item = "";
            } else {                $item .= $char;
            }
        }        pclose($fp);
    }
}

下面是測(cè)試程序

<?phpinclude "vendor/autoload.php";$test = new \Ritoyan\Pipe\Pipe();$test->setCmd("tail -f /root/t.txt")->setCallback(function($item){        echo "獲取內(nèi)容:" . $item . "\n";})->setDelimiter("\n")->run();

對(duì)tail -f /root/t.txt這個(gè)shell命令的返回結(jié)果進(jìn)行實(shí)時(shí)處理,setCallback(callable func)設(shè)置了回調(diào)函數(shù),func的參數(shù)是shell命令的標(biāo)準(zhǔn)輸出,setDelimiter($delimiter)設(shè)置了傳入回調(diào)函數(shù)參數(shù)的分隔符,這樣就可以很容易、很任性的將輸出傳遞給回調(diào)函數(shù)了。

inotify開發(fā)

這部分代碼就不提出來了,主要就是依賴上面的Pipe然后對(duì)inotifywait -mrq --format '%w,%e,%f'命令的輸出做處理,正向逆向過濾,下面是對(duì)Inotify的調(diào)用

<?phprequire "vendor/autoload.php";$obj = new Aizuyan\Inotify\Inotify();$obj->addExclude([
    "/swp$/",
    "/swpx$/",
    "/~$/",
    "/\d$/",
    "/swx$/"])->setCallback(function ($item){    echo $item["event"] . " 文件 " . $item["file"] . "\n";})->addPaths("/datas/git/")->start();

這樣運(yùn)行之后,到我們修改/datas/git目錄下的文件的時(shí)候會(huì)輸出下面的內(nèi)容,可以很方便的對(duì)修改文件做定制化的處理

CREATE 文件 /datas/git/inotify/README.md
MODIFY 文件 /datas/git/inotify/README.md
MOVED_TO 文件 /datas/git/aizuyan/pinyin-1/README.md
DELETE 文件 /datas/git/aizuyan/pinyin-1/LICENSE
......

inotify-tools安裝

整個(gè)功能依賴于一個(gè)linux軟件 —— inotify-tools
centos安裝yum install inotify-tools,或者通過源碼直接安裝(文檔)嘗試在OS中安裝,發(fā)現(xiàn)失敗了~

如何使用

我已經(jīng)將他發(fā)布到了composer倉庫中,可以輕松安裝:
composer require aizuyan/inotify,之后就可以像上面的例子一樣使用了

另外這是開發(fā)的兩個(gè)組件的github地址:Aizuyan\Pipe\Pipe , Aizuyan\Inotify\Inotify