ThinkPHP 5 프레임 워 크 도입 Go AOP,PHP AOP 프로 그래 밍 프로젝트 상세 설명
프로젝트 배경
현재 개 발 된 WEB 소프트웨어 에는 PHP 가 API 조작 데이터 창 고 를 방문 하 는 기능 이 있 는데 처음에는 데이터 창고 가 작 아 문제 가 발견 되 지 않 았 고 데이터 가 많아 지면 서 API 호출 시간 이 항상 초과 되 었 다(60s).그래서 비동기 요청 을 사용 하여 60s 로 데 이 터 를 되 돌 릴 수 있 으 면 되 돌 릴 수 있 고,그렇지 않 으 면 비동기 ID 로 돌아 간 다음 에 통계 작업 을 완성 할 수 있 는 지 물 어보 기로 했다.프로젝트 가 빡빡 하고 일손 이 부족 하기 때문에 반드시 최소한 의 대가 로 현재 문 제 를 해결 해 야 한다.
프로젝트 선택
AOP 방식 으로 기 존 코드 논 리 를 바 꾸 지 않 고 백 엔 드 만 있 으 면 대부분의 작업 을 수행 할 수 있다.백 엔 드 는 AOP 로 요청 API 를 자 르 는 방법 으로 API 가 되 돌아 오 는 결 과 를 감청 함으로써 기 존 논 리 를 계속 실행 시 킬 지(API 가 60s 에서 데 이 터 를 되 돌려 주 었 음),오프라인 작업 기능 에 들 어 갈 지(API 보고 통계 작업 은 60s 내 에서 수행 할 수 없 음)를 제어 한다.
이전에
AOP-PHP
으로 확장 한 적 이 있 는데 시작 이 간단 했다.그러나 나중에 어떤 큰 프로젝트 에서 이 확장 을 도입 한 후에 out of memory
을 직접 폭발 시 켰 다.그 다음 에 그 소스 코드 를 연구 한 결과 문법 트 리 를 바 꾸 었 고 Hook 은 모든 호출 방법 을 사용 했다.즉,모든 방법 이 AOP-PHP
으로 바 뀌 었 는데 이 방법 은 절단면 방법 이 있 는 지 물 었 다.그래서 효율 적 인 손실 이 비교적 크다.그리고 이 프로젝트 는 현재 8 년 동안 업데이트 되 지 않 았 다.그래서 이 해결 방안 을 추천 하지 않 습 니 다.실제 환경
Debian,php-fpm-7.0,ThinkPHP-5.10。
AOP 도입
주 이의 좋 은 언어 로 서 PHP 는 스스로 AOP 를 가지 고 있 지 않다.그럼 AOP-PHP 확장 을 설치 해 야 합 니 다.제 가 pecl 을 열 고 다운로드 하려 고 할 때 눈 이 멀 었 습 니 다.모두 bate 버 전 으로 php 7 지원 에 대한 설명 이 표시 되 지 않 았 습 니 다.하지만 저 는 요행 을 바라 고 git 를 찾 았 습 니 다.4-5 년 동안 업데이트 되 지 않 았 습 니 다.업 데 이 트 를 기다 리 시 겠 습 니까?아,작 가 는 issue 에서 시간 이 있 으 면 phop 7 을 호 환 하기 시작 하 겠 다 고 말 했 습 니 다.
좋 습 니 다.독 한 말 은 많이 하지 않 습 니 다.다음 방안 은 Go!AOP 입 니 다.git 를 보 니 작 가 는 흰 티 셔츠 를 입고 산 봉 우 리 를 좋아 하 는 멋 진 남자 입 니 다.기본적으로 모든 issue 가 열심히 대답 합 니 다.
composer require goaop/framework
ThinkpHP 5 는 coposer 와 호 환 되 는 것 이 좋 습 니 다.나 도 그대로 써 서 contrller 에 들 어 갔다.
<?PHP
namespace app\tests\controller;
use think\Controller;
class Test1 extends Controller
{
public function test1()
{
echo $this->aspectAction();
}
public function aspectAction()
{
return 'hello';
}
}
정의 aspect
<?PHP
namespace app\tests\aspect;
use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;
use app\tests\controller\Test1;
class MonitorAspect implements Aspect
{
/**
* Method that will be called before real method
*
* @param MethodInvocation $invocation Invocation
* @Before("execution(public|protected app\tests\controller\Test1->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>
";
}
}
aspect 사용 하기
<?PHP
// file: ./application/tests/service/ApplicationAspectKernel.php
namespace app\tests\service;
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
use app\tests\aspect\MonitorAspect;
/**
* Application Aspect Kernel
*
* Class ApplicationAspectKernel
* @package app\tests\service
*/
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-ap 핵심 서비스 설정
<?PHP
// file: ./application/tests/behavior/Bootstrap.php
namespace app\tests\behavior;
use think\Exception;
use Composer\Autoload\ClassLoader;
use Go\Instrument\Transformer\FilterInjectorTransformer;
use Go\Instrument\ClassLoading\AopComposerLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use app\tests\service\ApplicationAspectKernel;
use app\tests\ThinkPhpLoaderWrapper;
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 갈 고 리 를 설정 하여 go-ap 을 시작 합 니 다.
<?PHP
// file: ./application/tests/tags.php
// thinkphp5.10 , module tags.php
return [
//
'app_init' => [],
//
'app_begin' => [],
//
'module_init' => [
'app\\tests\\behavior\\Bootstrap'
],
//
'action_begin' => [],
//
'view_filter' => [],
//
'log_write' => [],
//
'app_end' => [],
];
호환성 테스트자,방문.http://127.0.0.1/tests/test1/test1 표시:
hello
이것 은 예상 한 효과 가 아 닙 니 다.aspect 에서 정의 되 었 습 니 다.이 방법 에 접근 하기 전에 더 많은 정 보 를 출력 할 것 입 니 다.다음 과 같은 내용 이 예상 입 니 다.
Calling Before Interceptor for method: app\tests\controller\Test1->aspectAction() with arguments: []
그 에 게 올 라 가서 공식 Doc 을 보 세 요.더 높 은 용법 입 니 다.go-ap 의 운영 체 제 를 말 하지 않 았 습 니 다.
git 에서 도 비슷 한 issue 를 보지 못 했 습 니 다.어,작가 가 자주 issue 에 답 하 는 것 을 발 견 했 습 니 다.demo 를 시도 해 보 세 요.아마도 나 는 demo 를 시험 해 봐 야 할 것 같다.
Run Demos
제 가 사용 하 는 것 은 LNMP 기술 창고 입 니 다.
# 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)(/?.+)$;
}
}
다음은 코드 를 조정 해 야 합 니 다.vendor/lisachenko/go-aop-php/demos/autoload.php
파일 을 보 았 습 니 다.
<?PHP
・・・
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
/** @var Composer\Autoload\ClassLoader $loader */
$loader = include __DIR__ . '/../vendor/autoload.php';
$loader->add('Demo', __DIR__);
}
이 코드 의 첫 줄 을 볼 수 있 습 니 다.vendor 에서 autoload 를 찾 지 못 했 습 니 다.우 리 는 아래 와 같이 조정 한다.
<?PHP
$re = __DIR__ . '/../../../vendor/autoload.php';
if (file_exists(__DIR__ . '/../../../autoload.php')) {
/** @var Composer\Autoload\ClassLoader $loader */
$loader = include __DIR__ . '/../../../autoload.php';
$loader->add('Demo', __DIR__);
}
다시 시도 해 보 세 요.demo 가 실행 되 기 시 작 했 습 니 다.시도 해 보 았 습 니 다.실행 에 성공 하 였 습 니 다.
이상 의 출력 을 통 해 demo 에서 방법 이 실행 되 기 전에 성공 적 으로 캡 처 되 었 음 을 알 수 있 습 니 다.왜 thinkphp controller 에서 실행 하면 성공 하지 못 합 니까?나 는 정지점 을 채택 하여 디 버 깅 을 진행 하기 로 결정 했다.
정지점 을 통 해 나 는 이 파일 을 발견 했다.
<?PHP
// file: ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.php
public 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));
}
}
이것 은 autoload 입 니 다.모든 종류의 불 러 오 는 것 은 이 를 거 쳐 내부 클래스 인지 아 닌 지 판단 하고 후속 작업 에 들 어 갑 니 다.인 터 럽 트 를 통 해 FilterInjector Transformer 에 들 어가 면 load 파일 을 문법 적 으로 분석 하고 등 록 된 annotation 에 따라 관련 클래스 에 proxy 클래스 를 생 성 합 니 다.고-op 이 프로그램 에 어떻게 들 어 가 는 지 알 겠 죠?생 성 된 proxy 류 는 당신 이 설정 한 cache-dir(제 가 설정 한 것 은 ./runtime/aop_cache/
)에서 볼 수 있 습 니 다.동시에./runtime/aopcache/폴 더 아래 에 도 많은 것 이 생 성 되 었 습 니 다.opcache 파일 에 Test 1 파일 이름과 같은 파일 이 생 성 되 었 습 니 다.파일 을 열 어 보 니 기 존의 Test 1 컨트롤 러 를 대리 하고 있 습 니 다.이 일련의 정 보 는'Go!'를 얻 을 수 있다.AOP 는'납치'composer autoload 를 통 해 모든 종 류 를 들 여 보 냈 으 며,aspect 의 정의 에 따라 프 록 시 클래스 를 만 들 지 여 부 를 결정 하고 advice 를 심 었 습 니 다.
어,ThinkpHP 5 는 composer autoload 에 있 는 물건 을 복사 해서 자신의 autoload 에 넣 은 다음 에 composer 는 아무 일 도 없 었 다.그리고 go-aop 은 coposer autoload 가 내 린 명령 을 기다 리 지 못 하면 자 연 스 럽 게 역할 을 하지 못 합 니 다.so,다음 단계.
ThinkPHP 5 개선
ThinkpHP 5 에 서 는 기본적으로 TP5 내부 의 Loader 만 등록 되 어 있 으 며,include 요청 을 composer 의 autoload 에 보 내지 않 습 니 다.따라서 go-aop 에 역할 을 하기 위해 서 는 include class 의 요청 이 AopComposerLoad 을 거 쳐 야 합 니 다.
이 서류 좀 봅 시다.
<?PHP
// ./vendor/lisachenko/go-aop-php/src/Instrument/ClassLoading/AopComposerLoader.php:57
public 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);
}
}
이 파일 에는 autoload callback 이 Classloader 형식 인지 아 닌 지 를 검사 하 는 형식 이 있 습 니 다.그러나 ThinkpHP 5 는 정지점 을 통 해 ThinkpHP 5 가 문자열 배열 이라는 것 을 알 수 있 습 니 다.so,여기 서도 go-op 을 class loader 의 callback 에 등록 할 수 없습니다.여기 서 PHP autoload 체 제 를 언급 해 야 합 니 다.이것 은 현대 PHP 의 매우 중요 한 기능 입 니 다.이것 은 우리 가 클래스 를 사용 할 때 이름 을 통 해 파일 을 자동 으로 불 러 올 수 있 습 니 다.우 리 는 일정한 클래스 규칙 과 파일 구조 디 렉 터 리 를 정의 하고 상기 규칙 을 실현 할 수 있 는 함 수 를 더 하면 자동 으로 불 러 올 수 있 습 니 다.spl_autoload_register 함수 의 세 번 째 매개 변 수 를 통 해
prepend
을 true 로 설정 하면 TP5 의 loader 앞 에 서서 먼저 호출 될 수 있 습 니 다.위의 원리 에 따라 다음 과 같은 개선 을 할 수 있다.
이것 은 go-aop 을 위 한 새로운 autoload 로 본질 적 으로 원래 의 ThinkpHP 5 loader 에 케이스 를 추가 한 것 일 뿐 입 니 다.
<?PHP
// file: ./application/tests
namespace app\tests;
require_once __DIR__ . './../../vendor/composer/ClassLoader.php';
use think\Loader;
use \Composer\Autoload\ClassLoader;
use Go\Instrument\Transformer\FilterInjectorTransformer;
use Go\Instrument\ClassLoading\AopComposerLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
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 = [
'app\tests\controller'
];
$isAllowed = false;
foreach ($allowedNamespace as $ns) {
if (strpos($class, $ns) === 0) {
$isAllowed = true;
break;
}
}
// AOP , 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
// \app\tests\behavior\Bootstrap::moduleInit
// 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;
여기 서 우 리 는 autload 를 만 들 고 그것 을 맨 앞 에 직접 삽입 했다.마지막.
지금 다시 한 번 방문 해 보도 록 하 겠 습 니 다.http://127.0.0.1/tests/test1/test1
aspect
에서 출력 된 정 보 를 볼 수 있 습 니 다.마지막 으로 우 리 는 총 결 을 한다.
PHP7
현재 확장 되 지 않 은 AOP
.ThinkPHP5
은 자신의 Autoloader
을 가지 고 있다.Go!AOP
의 AOP 는 Class Autoload
에 의존 하 는 callback
을 실현 하고
을 통 해 Proxy
을 지향 한다.ThinkPHP5
통합 Go!AOP
은 autoload
을 조정 해 야 한다.본 고 는 ThinkPHP 프레임 워 크 를 기반 으로 한 PHP 프로 그래 밍 에 도움 이 되 기 를 바 랍 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
windows 환경 에서 Composer 를 사용 하여 ThinkPHP 5 를 설치 합 니 다.1.환경 검사,환경 에 Composer 가 설치 되 어 있 는 지 확인 하 십시오.Composer 는 PHP 의 의존 관리 도구 입 니 다.아래 명령 을 통 해 미 러 를 설정 합 니 다. 명령:composer co...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.