成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ThinkPHP5 引入 Go AOP

shadowbook / 2032人閱讀

摘要:由于項(xiàng)目緊,人手不足,必須以最小的代價(jià)解決當(dāng)前問題。引入作為一門好的語言,是不自帶的。通過斷點(diǎn)進(jìn)入,發(fā)現(xiàn)會(huì)對(duì)的文件進(jìn)行語法解析,并根據(jù)注冊(cè)的對(duì)相關(guān)的類生成類。

項(xiàng)目背景

目前開發(fā)的WEB軟件里有這一個(gè)功能,PHP訪問API操作數(shù)據(jù)倉庫,剛開始數(shù)據(jù)倉庫小,沒發(fā)現(xiàn)問題,隨著數(shù)據(jù)越來越多,調(diào)用API時(shí)常超時(shí)(60s)。于是決定采用異步請(qǐng)求,改為60s能返回?cái)?shù)據(jù)則返回,不能則返回一個(gè)異步ID,然后輪詢是否完成統(tǒng)計(jì)任務(wù)。由于項(xiàng)目緊,人手不足,必須以最小的代價(jià)解決當(dāng)前問題。

方案選擇

從新分析需求,并改進(jìn)代碼

采用AOP方式改動(dòng)程序

從新做需求分析,以及詳細(xì)設(shè)計(jì),并改動(dòng)代碼,需要產(chǎn)品,架構(gòu),前端,后端的支持。會(huì)驚動(dòng)的人過多,在資源緊張的情況下是不推薦的。
采用AOP方式,不改動(dòng)原有代碼邏輯,只需要后端就能完成大部分任務(wù)了。后端用AOP切入請(qǐng)求API的方法,通過監(jiān)聽API返回的結(jié)果來控制是否讓其繼續(xù)運(yùn)行原有的邏輯(API在60s返回了數(shù)據(jù)),或者是進(jìn)入離線任務(wù)功能(API報(bào)告統(tǒng)計(jì)任務(wù)不能在60s內(nèi)完成)。

實(shí)際環(huán)境

Debian,php-fpm-7.0,ThinkPHP-5.10。

引入AOP

作為一門zui好的語言,PHP是不自帶AOP的。那就得安裝AOP-PHP拓展,當(dāng)我打開pecl要下載時(shí),傻眼了,全是bate版,沒有顯示說明支持php7。但我還是抱著僥幸心理,找到了git,發(fā)現(xiàn)4-5年沒更新了,要不要等一波更新,哦,作者在issue里說了有時(shí)間就開始兼容php7。
好吧,狠話不多說,下一個(gè)方案:Go!AOP.看了下git,作者是個(gè)穿白體恤,喜歡山峰的大帥哥,基本每個(gè)issue都會(huì)很熱心回復(fù)。

composer require goaop/framework

ThinkPHP5 對(duì)composer兼容挺不錯(cuò)的哦,(到后面,我真想揍ThinkPHP5作者)這就裝好了,怎么用啊,git上的提示了簡(jiǎn)單用法。我也就照著寫了個(gè)去切入controller。

aspectAction();
    }
    
    public function aspectAction()
    {
        return "hello";
    }
}

定義aspect

aspectAction(*))")
     */
    public function beforeMethodExecution(MethodInvocation $invocation)
    {
        $obj = $invocation->getThis();
        echo "Calling Before Interceptor for method: ",
             is_object($obj) ? get_class($obj) : $obj,
             $invocation->getMethod()->isStatic() ? "::" : "->",
             $invocation->getMethod()->getName(),
             "()",
             " with arguments: ",
             json_encode($invocation->getArguments()),
             "
"; } }

啟用aspect

registerAspect(new MonitorAspect());
    }
}

go-aop 核心服務(wù)配置

init([
            "debug" =>  true,
            "appDir"    =>  __DIR__ . "./../../../",
                "cacheDir"  =>  __DIR__ . "./../../../runtime/aop_cache",
                "includePaths"  =>  [
                    __DIR__ . "./../../tests/controller",
                    __DIR__ . "./../../../thinkphp/library/think/model"
                ],
                "excludePaths"  =>  [
                    __DIR__ . "./../../aspect",
                ]
            ]);
        return $params;
    }
}

配置模塊init鉤子,讓其啟動(dòng) go-aop

 [],
    // 應(yīng)用開始
    "app_begin"    => [],
    // 模塊初始化
    "module_init"  => [
        "app	estsehaviorBootstrap"
    ],
    // 操作開始執(zhí)行
    "action_begin" => [],
    // 視圖內(nèi)容過濾
    "view_filter"  => [],
    // 日志寫入
    "log_write"    => [],
    // 應(yīng)用結(jié)束
    "app_end"      => [],
];
兼容測(cè)試

好了,訪問 http://127.0.0.1/tests/test1/... 顯示:

hello

這不是預(yù)期的效果,在aspect定義了,訪問該方法前,會(huì)輸出方法的更多信息信息。
像如下內(nèi)容才是預(yù)期

Calling Before Interceptor for method: app	estscontrollerTest1->aspectAction() with arguments: []

上他官方Doc看看,是一些更高級(jí)的用法。沒有講go-aop的運(yùn)行機(jī)制。
上git上也沒看到類似issue,額,發(fā)現(xiàn)作者經(jīng)常在issue里回復(fù):試一試demo。也許我該試試demo。

Run Demos

我采用的是LNMP技術(shù)棧。

假設(shè)這里有臺(tái)Ubuntu你已經(jīng)配置好了LNMP環(huán)境

下載代碼

配置nginx

# file: /usr/share/etc/nginx/conf.d/go-aop-test.conf
server {
    listen 8008;
#    listen 443 ssl;
    server_name 0.0.0.0;
    root "/usr/share/nginx/html/app/vendor/lisachenko/go-aop-php/demos";
    index index.html index.htm index.php;
    charset utf-8;

    access_log /var/log/nginx/go-aop-access.log;
    error_log  /var/log/nginx/go-aop-error.log notice;

    sendfile off;
    client_max_body_size 100m;

    location ~ .php(.*)$ {
        include                         fastcgi_params;
        fastcgi_pass                     127.0.0.1:9000;
        fastcgi_index                     index.php;

        fastcgi_param                      PATH_INFO      $fastcgi_path_info;
#        fastcgi_param                   SCRIPT_FILENAME /var/www/html/app/vendor/lisachenko/go-aop-php/demos$fastcgi_script_name;  #docker的配置
        fastcgi_param                      SCRIPT_FILENAME /usr/share/nginx/html/api/vendor/lisachenko/go-aop-php/demos$fastcgi_script_name;
        fastcgi_param                      PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_split_path_info          ((?U).+.php)(/?.+)$;
    }
}

接下來要調(diào)整下代碼

訪問 http://127.0.0.1:8008 試試,(估計(jì)大家都遇到了這個(gè))

這個(gè)報(bào)錯(cuò)信息提示找不到這個(gè)類。來到報(bào)錯(cuò)的文件里。這文件使用了use找不到類,就是autoload出問題了,看到 vendor/lisachenko/go-aop-php/demos/autoload.php 這個(gè)文件。

add("Demo", __DIR__);
}

可以看到這個(gè)代碼第一行沒找到vendor下的autoload。我們做如下調(diào)整

add("Demo", __DIR__);
}

再試試,demo運(yùn)行起來了。


嘗試了下,運(yùn)行成功


通過以上的輸出,可以得出demo里是對(duì)方法運(yùn)行前成功捕獲。為什么在thinkphp的controller里運(yùn)行就不成功呢。我決定采用斷點(diǎn)進(jìn)行調(diào)試。

通過斷點(diǎn)我發(fā)現(xiàn)了這個(gè)文件

original->findFile($class)) {
        $isInternal = false;
        foreach ($this->internalNamespaces as $ns) {
            if (strpos($class, $ns) === 0) {
                $isInternal = true;
                break;
            }
        }

        include ($isInternal ? $file : FilterInjectorTransformer::rewrite($file));
    }
}

這是一個(gè)autoload,每個(gè)類的載入都會(huì)經(jīng)過它,并且會(huì)對(duì)其判斷是否為內(nèi)部類,不是的都會(huì)進(jìn)入后續(xù)的操作。通過斷點(diǎn)進(jìn)入 FilterInjectorTransformer,發(fā)現(xiàn)會(huì)對(duì)load的文件進(jìn)行語法解析,并根據(jù)注冊(cè)的annotation對(duì)相關(guān)的類生成proxy類。說道這,大家就明白了go-aop是如何做到切入你的程序了吧,生成的proxy類,可以在你配置的cache-dir(我配置的是./runtime/aop_cache/)里看到。

同時(shí)./runtime/aop_cache/ 文件夾下也生成了很多東西,通過查看aop_cache文件內(nèi)產(chǎn)生了與Test1文件名相同的文件,打開文件,發(fā)現(xiàn)它代理了原有的Test1控制器。這一系列信息,可以得出,Go!AOP 通過"劫持" composer autoload 讓每個(gè)類都進(jìn)過它,根據(jù)aspect的定義來決定是否為其創(chuàng)建一個(gè)代理類,并植入advice。
額,ThinkPHP5是把composer autoload里的東西copy出來,放到自己autoload里,然后就沒composer啥事了。然后go-aop一直等不到composer autoload下發(fā)的命令,自然就不能起作用了,so,下一步

改進(jìn)ThinkPHP5

在ThinkPHP5里,默認(rèn)有且只會(huì)注冊(cè)一個(gè)TP5內(nèi)部的 Loader,并不會(huì)把include請(qǐng)求下發(fā)給composer的autoload。所以,為其讓go-aop起作用,那么必須讓讓include class的請(qǐng)求經(jīng)過 AopComposerLoad.
我們看看這個(gè)文件

loadClass($class);

                return class_exists($class, false);
            });
            $loader[0] = new AopComposerLoader($loader[0]);
        }
        spl_autoload_unregister($loaderToUnregister);
    }
    unset($loader);

    foreach ($loaders as $loader) {
        spl_autoload_register($loader);
    }
}

這個(gè)文件里有個(gè)類型檢測(cè),檢測(cè)autoload callback是否為Classloader類型,然而ThinkPHP5不是,通過斷點(diǎn)你會(huì)發(fā)現(xiàn)ThinkPHP5是一個(gè)字符串?dāng)?shù)組,so,這里也就無法把go-aop注冊(cè)到class loader的callback當(dāng)中了。

這里就要提一下PHP autoload機(jī)制了,這是現(xiàn)代PHP非常重要的一個(gè)功能,它讓我們?cè)谟玫揭粋€(gè)類時(shí),通過名字能自動(dòng)加載文件。我們通過定義一定的類名規(guī)則與文件結(jié)構(gòu)目錄,再加上能實(shí)現(xiàn)以上規(guī)則的函數(shù)就能實(shí)現(xiàn)自動(dòng)加載了。在通過 spl_autoload_register 函數(shù)的第三個(gè)參數(shù) prepend 設(shè)置為true,就能讓其排在在TP5的loader前面,先一步被調(diào)用。

依照如上原理,就可以做如下改進(jìn)
這個(gè)是為go-aop包裝的新autoload,本質(zhì)上是在原來的ThinkPHP5的loader上加了一個(gè)殼而已。

getMethod("findFile");
        $method->setAccessible(true);
        $file = $method->invoke($obj, $class);
        return $file;
    }
}
loadClass($class);

    return class_exists($class, false);
});
$aopLoader = new AopComposerLoader($thinkLoader);
spl_autoload_register([$aopLoader, "loadClass"], false, true);

return $params;

在這里我們做了一個(gè)autload 并直接把它插入到了最前面(如果項(xiàng)目?jī)?nèi)還有其他autloader,請(qǐng)注意他們的先后順序)。

最后

現(xiàn)在我們?cè)僭L問一下 http://127.0.0.1/tests/test1/... 你就能看到來自 aspect 輸出的信息了。
最后我們做個(gè)總結(jié):

PHP7 目前沒有拓展實(shí)現(xiàn)的 AOP。

ThinkPHP5 有著自己的 Autoloader

Go!AOP 的AOP實(shí)現(xiàn)依賴Class Autoloadcallback,通過替換原文件指向Proxy類實(shí)現(xiàn)。

ThinkPHP5 整合 Go!AOP 需要調(diào)整 autoload。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/26291.html

相關(guān)文章

  • wamp環(huán)境安裝thinkphp5.0-wamp環(huán)境配置vhosts

    摘要:環(huán)境集成環(huán)境文件目錄目錄入口文件第一步修改文件,將配置文件包含進(jìn)來點(diǎn)擊,用記事本打開,定位到此處。在第一個(gè)例子中只改動(dòng)了和,這個(gè)配置保證了我們?cè)陂_啟后仍然可以訪問目錄。 環(huán)境windows7 , wampserver集成環(huán)境 , thinkphp5.0文件wamp目錄:D:/wamp/wwwthinkphp目錄:F:/tp5thinkphp入口文件:F:/tp5/public/inde...

    xeblog 評(píng)論0 收藏0
  • ThinkPhp學(xué)習(xí)筆記(一)

    摘要:如上為三個(gè)模塊行為行為是在預(yù)先定義好的一個(gè)應(yīng)用位置執(zhí)行的一些操作。發(fā)生作用的位置稱之為鉤子,當(dāng)應(yīng)用程序運(yùn)行到這個(gè)鉤子的時(shí)候,就會(huì)被攔截下來,統(tǒng)一執(zhí)行相關(guān)的行為。給某一個(gè)鉤子綁定相關(guān)行為就成了一種類編程的思想。 ThinkPhp5.0 Composer: php世界里的包管理器 目錄結(jié)構(gòu) project 應(yīng)用部署目錄 ├─application 應(yīng)用目錄(可設(shè)置) ...

    imtianx 評(píng)論0 收藏0
  • 【好好面試】學(xué)完Aop,連動(dòng)態(tài)代理的原理都不懂?

    摘要:總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來讓我們回答以下幾個(gè)思考題。 【干貨點(diǎn)】 此處是【好好面試】系列文的第12篇文章。文章目標(biāo)主要是通過原理剖析的方式解答Aop動(dòng)態(tài)代理的面試熱點(diǎn)問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進(jìn)而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認(rèn)代理類型是什么 為什么不用靜態(tài)代理 JDK動(dòng)態(tài)代理原理 CGLIB動(dòng)態(tài)代理...

    Keven 評(píng)論0 收藏0
  • 解讀 thinkphp5 源碼(一):自動(dòng)加載

    摘要:索性讀一下它的源碼。行載入類載入類,這個(gè)類比較重要,實(shí)現(xiàn)了自動(dòng)加載。注冊(cè)錯(cuò)誤和異常處理機(jī)制加載慣例配置文件接下來我們看一下自動(dòng)加載的實(shí)現(xiàn)方法。所以借助此函數(shù)可以達(dá)到自動(dòng)加載。博客鏈接解讀源碼一自動(dòng)加載 聽說 TP5 已經(jīng) RC4 了,曾經(jīng)在 RC3 的時(shí)候用它寫過一個(gè)小東西。官方說從 RC4 以后改動(dòng)不是太大。索性讀一下它的源碼。然后順便記錄一下,如有錯(cuò)漏,請(qǐng)路過大神多多指正! 入口 ...

    stormzhang 評(píng)論0 收藏0
  • axios的post請(qǐng)求后臺(tái)(ThinkPHP5)接收不到數(shù)據(jù)

    摘要:最近做項(xiàng)目,做分頁的功能,使用給后臺(tái)發(fā)送數(shù)據(jù),使用接口還是工具都可獲取數(shù)據(jù),唯獨(dú)獲取不到經(jīng)過排除,發(fā)現(xiàn)這與的傳參格式有關(guān)系在使用時(shí),要注意到配置選項(xiàng)中包含和兩者,以為他們是相同的,實(shí)則不然。因?yàn)槭翘砑拥降恼?qǐng)求字符串中的,用于請(qǐng)求。 最近做vue項(xiàng)目,做分頁的功能,使用post給后臺(tái)發(fā)送數(shù)據(jù),使用接口還是工具(postman)都可獲取數(shù)據(jù),唯獨(dú)axios獲取不到;經(jīng)過排除,發(fā)現(xiàn)這與axi...

    nemo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<