日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

ThinkPHP5 框架引入 Go AOP,PHP AOP編程項(xiàng)目詳解

瀏覽:266日期:2022-09-10 13:30:28

本文實(shí)例講述了ThinkPHP5 框架引入 Go AOP,PHP AOP編程。分享給大家供大家參考,具體如下:

項(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)完成)。

之前用過AOP-PHP拓展,上手很簡單,不過后來在某一個(gè)大項(xiàng)目中引入該拓展后,直接爆了out of memory,然后就研究其源碼發(fā)現(xiàn),它改變了語法樹,并Hook了每個(gè)被調(diào)用的方法,也就是每個(gè)方法被調(diào)用是都會(huì)去詢問AOP-PHP,這個(gè)方法有沒有切面方法。所以效率損失是比較大的。而且這個(gè)項(xiàng)目距離現(xiàn)在已經(jīng)有8年沒更新了。所以不推薦該解決方案。

實(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上的提示了簡單用法。我也就照著寫了個(gè)去切入controller。

<?PHPnamespace apptestscontroller;use thinkController;class Test1 extends Controller{ public function test1() { echo $this->aspectAction(); } public function aspectAction() { return ’hello’; }}

定義aspect

<?PHPnamespace apptestsaspect;use GoAopAspect;use GoAopInterceptFieldAccess;use GoAopInterceptMethodInvocation;use GoLangAnnotationAfter;use GoLangAnnotationBefore;use GoLangAnnotationAround;use GoLangAnnotationPointcut;use apptestscontrollerTest1;class MonitorAspect implements Aspect{ /** * Method that will be called before real method * * @param MethodInvocation $invocation Invocation * @Before('execution(public|protected apptestscontrollerTest1->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()), '<br>n'; }}

啟用aspect

<?PHP// file: ./application/tests/service/ApplicationAspectKernel.phpnamespace apptestsservice;use GoCoreAspectKernel;use GoCoreAspectContainer;use apptestsaspectMonitorAspect;/** * Application Aspect Kernel * * Class ApplicationAspectKernel * @package apptestsservice */class ApplicationAspectKernel extends AspectKernel{ /** * Configure an AspectContainer with advisors, aspects and pointcuts * * @param AspectContainer $container * * @return void */ protected function configureAop(AspectContainer $container) { $container->registerAspect(new MonitorAspect()); }}

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

<?PHP// file: ./application/tests/behavior/Bootstrap.phpnamespace apptestsbehavior;use thinkException;use ComposerAutoloadClassLoader;use GoInstrumentTransformerFilterInjectorTransformer;use GoInstrumentClassLoadingAopComposerLoader;use DoctrineCommonAnnotationsAnnotationRegistry;use apptestsserviceApplicationAspectKernel;use apptestsThinkPhpLoaderWrapper;class Bootstrap{ public function moduleInit(&$params) { $applicationAspectKernel = ApplicationAspectKernel::getInstance(); $applicationAspectKernel->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

<?PHP// file: ./application/tests/tags.php// 由于是thinkphp5.10 沒有容器,所有需要在module下的tags.php文件里配置調(diào)用他return [ // 應(yīng)用初始化 ’app_init’ => [], // 應(yīng)用開始 ’app_begin’ => [], // 模塊初始化 ’module_init’ => [ ’apptestsbehaviorBootstrap’ ], // 操作開始執(zhí)行 ’action_begin’ => [], // 視圖內(nèi)容過濾 ’view_filter’ => [], // 日志寫入 ’log_write’ => [], // 應(yīng)用結(jié)束 ’app_end’ => [],];兼容測試

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

hello

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

Calling Before Interceptor for method: apptestscontrollerTest1->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.confserver { 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è))

ThinkPHP5 框架引入 Go AOP,PHP AOP編程項(xiàng)目詳解

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

<?PHP···if (file_exists(__DIR__ . ’/../vendor/autoload.php’)) { /** @var ComposerAutoloadClassLoader $loader */ $loader = include __DIR__ . ’/../vendor/autoload.php’; $loader->add(’Demo’, __DIR__);}

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

<?PHP$re = __DIR__ . ’/../../../vendor/autoload.php’;if (file_exists(__DIR__ . ’/../../../autoload.php’)) { /** @var ComposerAutoloadClassLoader $loader */ $loader = include __DIR__ . ’/../../../autoload.php’; $loader->add(’Demo’, __DIR__);}

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

ThinkPHP5 框架引入 Go AOP,PHP AOP編程項(xiàng)目詳解

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

ThinkPHP5 框架引入 Go AOP,PHP AOP編程項(xiàng)目詳解

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

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

<?PHP// file: ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.phppublic function loadClass($class){ if ($file = $this->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è)文件

<?PHP// ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.php:57public static function init(){ $loaders = spl_autoload_functions(); foreach ($loaders as &$loader) { $loaderToUnregister = $loader; if (is_array($loader) && ($loader[0] instanceof ClassLoader)) { $originalLoader = $loader[0]; // Configure library loader for doctrine annotation loader AnnotationRegistry::registerLoader(function ($class) use ($originalLoader) { $originalLoader->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è)類型檢測,檢測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è)殼而已。

<?PHP// file: ./application/tests namespace apptests;require_once __DIR__ . ’./../../vendor/composer/ClassLoader.php’;use thinkLoader;use ComposerAutoloadClassLoader;use GoInstrumentTransformerFilterInjectorTransformer;use GoInstrumentClassLoadingAopComposerLoader;use DoctrineCommonAnnotationsAnnotationRegistry;class ThinkPhpLoaderWrapper extends ClassLoader{ static protected $thinkLoader = Loader::class; /** * Autoload a class by it’s name */ public function loadClass($class) { return Loader::autoload($class); } /** * {@inheritDoc} */ public function findFile($class) { $allowedNamespace = [ ’apptestscontroller’ ]; $isAllowed = false; foreach ($allowedNamespace as $ns) { if (strpos($class, $ns) === 0) { $isAllowed = true; break; } } // 不允許被AOP的類,則不進(jìn)入AopComposer if(!$isAllowed) return false; $obj = new Loader; $observer = new ReflectionClass(Loader::class); $method = $observer->getMethod(’findFile’); $method->setAccessible(true); $file = $method->invoke($obj, $class); return $file; }}

<?PHP// file: ./application/tests/behavior/Bootstrap.php 在剛剛我們新添加的文件當(dāng)中// 這個(gè)方法 apptestsbehaviorBootstrap::moduleInit 的后面追加如下內(nèi)容// 組成AOPComposerAutoLoader$originalLoader = $thinkLoader = new ThinkPhpLoaderWrapper();AnnotationRegistry::registerLoader(function ($class) use ($originalLoader) { $originalLoader->loadClass($class); return class_exists($class, false);});$aopLoader = new AopComposerLoader($thinkLoader);spl_autoload_register([$aopLoader, ’loadClass’], false, true);return $params;

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

最后

現(xiàn)在我們?cè)僭L問一下 http://127.0.0.1/tests/test1/test1 你就能看到來自 aspect 輸出的信息了。

最后我們做個(gè)總結(jié):

PHP7 目前沒有拓展實(shí)現(xiàn)的 AOP。 ThinkPHP5 有著自己的 Autoloader。 Go!AOP 的AOP實(shí)現(xiàn)依賴Class Autoload的 callback,通過替換原文件指向Proxy類實(shí)現(xiàn)。 ThinkPHP5 整合 Go!AOP 需要調(diào)整 autoload。

更多關(guān)于thinkPHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《ThinkPHP入門教程》、《thinkPHP模板操作技巧總結(jié)》、《ThinkPHP常用方法總結(jié)》、《codeigniter入門教程》、《CI(CodeIgniter)框架進(jìn)階教程》、《Zend FrameWork框架入門教程》及《PHP模板技術(shù)總結(jié)》。

希望本文所述對(duì)大家基于ThinkPHP框架的PHP程序設(shè)計(jì)有所幫助。

標(biāo)簽: PHP
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日本久久二区| 日韩视频网站在线观看| 97精品97| 国产一区二区三区四区五区| 亚州精品视频| 日本少妇精品亚洲第一区| 97久久超碰| 麻豆极品一区二区三区| 久久精品国产一区二区| 天堂а√在线最新版中文在线| 97精品一区| 影音先锋国产精品| 日韩和欧美的一区| 国产精品15p| sm捆绑调教国产免费网站在线观看| 中文在线中文资源| 99在线精品视频在线观看| 日韩精品视频一区二区三区| 国产精品久久久久久模特| 欧美精品99| 国产综合色产| 91九色综合| 日韩不卡在线| 国产精品一区二区美女视频免费看| 麻豆国产精品| 精品91久久久久| 国产精品久久久久9999高清| 久久久久久久久丰满| 日本不卡不码高清免费观看 | 精品国产亚洲一区二区三区大结局| 92国产精品| 色狠狠一区二区三区| 国产成人精品一区二区三区免费| 在线日韩中文| 精品国产一级| 蜜臀久久99精品久久久久宅男 | 久久国产日韩| 久久国产精品美女| 免费成人性网站| 久久三级视频| 精品视频一二| 国产精品美女午夜爽爽| 亚洲网址在线观看| 午夜久久免费观看| 国产中文在线播放| 日本午夜精品久久久久| 国产精品嫩草99av在线| 日韩一区二区三区在线看| 日韩一区二区免费看| 欧美日中文字幕| 久久国产亚洲| 国产麻豆久久| 欧洲一区二区三区精品| 色欧美自拍视频| 国产成人77亚洲精品www| 国产精品第一| 久久99蜜桃| 国产成人免费| 国产一区丝袜| 亚洲成人av观看| 97视频热人人精品免费| 亚洲永久av| 最新亚洲一区| 亚洲精品三级| 国产亚洲一区二区三区不卡| 久久国产三级| 国产中文欧美日韩在线| 日韩精品中文字幕第1页| 日韩国产欧美| 久久高清免费观看| 97久久精品| 亚洲欧洲美洲av| 在线视频日韩| 欧美午夜三级| 色88888久久久久久影院| 久久久久久黄| 亚洲精品极品少妇16p| 一区二区不卡| 成人在线超碰| 制服诱惑一区二区| 免费一级欧美在线观看视频| 高清一区二区| 激情久久婷婷| 久久激情综合网| 久久精品国产68国产精品亚洲| 日产午夜精品一线二线三线| 欧美一级一区| 亚洲欧美网站| 999久久久精品国产| 国产欧美亚洲精品a| 在线一区二区三区视频| 欧美日一区二区在线观看| 丝袜脚交一区二区| 国产日韩三级| 都市激情国产精品| 国产毛片久久| 91午夜精品| 欧美日韩99| 国产日韩三级| 日韩国产在线观看| 国产成人精品一区二区三区视频 | 鲁大师成人一区二区三区| 在线成人直播| 午夜亚洲精品| 不卡一区综合视频| 久久久夜精品| 久久激情一区| 播放一区二区| 国产欧美日韩综合一区在线播放| 欧美日韩在线播放视频| 国产精品av一区二区| 日韩精品成人| 日韩欧美一区二区三区免费看| 在线一区免费| 国内一区二区三区| 老牛国内精品亚洲成av人片| 亚洲一区成人| 美日韩精品视频| 三级欧美韩日大片在线看| 亚洲男女自偷自拍| 亚洲欧美激情诱惑| 国产精品香蕉| 国产精品视区| 国产亚洲精品v| 亚洲涩涩av| 激情婷婷亚洲| 麻豆精品视频在线观看| 91精品国产经典在线观看| 精品在线网站观看| 蜜臀av性久久久久蜜臀aⅴ四虎| 亚洲欧美日本视频在线观看| 久久午夜影院| 视频在线观看一区| 日韩一区欧美| 久久99精品久久久野外观看| 欧美精品日日操| 视频一区中文字幕精品| а√天堂中文在线资源8| 麻豆91小视频| 中文字幕一区二区三区四区久久 | 日本欧美一区| 精品一区av| 欧美福利在线| 亚洲福利国产| 欧美天堂一区| 日韩免费久久| 久久福利毛片| 99在线观看免费视频精品观看| 日韩影院免费视频| 国产成人精品一区二区三区免费 | 日韩中文字幕亚洲一区二区va在线 | 亚洲一区久久| 欧美专区一区| 欧美精品一区二区三区精品| 天堂va在线高清一区| 日韩av专区| 日本精品另类| 亚洲精品91| 91精品国产成人观看| 视频在线不卡免费观看| 国产一区国产二区国产三区| 国产精品久久久久av电视剧| av免费不卡国产观看| 国产亚洲毛片| 国产精品二区影院| 精品一区毛片| 国产欧美视频在线| 99久久亚洲精品蜜臀| 日本亚州欧洲精品不卡| 国产一区二区视频在线看| 91精品1区| 日本久久综合| 日韩精品成人| 好吊一区二区三区| 美女久久精品| 丝袜美腿一区二区三区| 国产精品久久久久久久久免费高清 | 日韩欧美一区二区三区免费看| 老牛影视一区二区三区| 国产精品久久久久久久免费观看| 美女久久网站| 久久精品亚洲欧美日韩精品中文字幕| 日韩精品一级| 国产精品美女| 久久婷婷av| 福利一区和二区| 日本亚州欧洲精品不卡| 激情综合自拍| 精品捆绑调教一区二区三区| 久久99久久人婷婷精品综合| 亚洲丝袜美腿一区| 日韩亚洲国产欧美| 亚洲性视频h| 亚洲黑丝一区二区| 久久国产直播| 三上悠亚国产精品一区二区三区 | 欧美+日本+国产+在线a∨观看| 国产伦精品一区二区三区千人斩| 免费视频一区三区| 免费视频亚洲|