ThinkPHP 5 프레임 워 크 도입 Go AOP,PHP AOP 프로 그래 밍 프로젝트 상세 설명

16127 단어 ThinkPHP5GoPHPAOP
이 글 의 사례 는 ThinkPHP 5 프레임 워 크 가 Go AOP,PHP AOP 프로 그래 밍 을 도입 하 는 것 을 다 루 었 다.여러분 께 참고 하도록 공유 하 겠 습 니 다.구체 적 으로 는 다음 과 같 습 니 다.
프로젝트 배경
현재 개 발 된 WEB 소프트웨어 에는 PHP 가 API 조작 데이터 창 고 를 방문 하 는 기능 이 있 는데 처음에는 데이터 창고 가 작 아 문제 가 발견 되 지 않 았 고 데이터 가 많아 지면 서 API 호출 시간 이 항상 초과 되 었 다(60s).그래서 비동기 요청 을 사용 하여 60s 로 데 이 터 를 되 돌 릴 수 있 으 면 되 돌 릴 수 있 고,그렇지 않 으 면 비동기 ID 로 돌아 간 다음 에 통계 작업 을 완성 할 수 있 는 지 물 어보 기로 했다.프로젝트 가 빡빡 하고 일손 이 부족 하기 때문에 반드시 최소한 의 대가 로 현재 문 제 를 해결 해 야 한다.
프로젝트 선택
  • 수 요 를 재 분석 하고 코드
  • 을 개선 합 니 다.
  • AOP 방식 으로 프로그램 을 변경 하 다
  • 새로운 수요 분석 과 상세 한 디자인,그리고 코드 를 바 꾸 려 면 제품,구조,전단,백 엔 드 의 지원 이 필요 합 니 다.놀 랄 만 한 사람 이 너무 많아 자원 이 부족 한 상황 에 서 는 추천 하지 않 는 다.
    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 기술 창고 입 니 다.
  • 여기 Ubuntu 가 있다 고 가정 하면 LNMP 환경
  • 이 설정 되 어 있 습 니 다.
  • 다운로드 코드
  • 설정 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)(/?.+)$;
     }
    }
    다음은 코드 를 조정 해 야 합 니 다.
  • 방문http://127.0.0.1:8008 해 봐,
  • 이 잘못된 정보 알림 은 이런 종 류 를 찾 을 수 없다.잘못된 서류 에 들 어 왔 습 니 다.이 파일 은 use 를 사용 하여 클래스 를 찾 을 수 없습니다.바로 autoload 에 문제 가 생 겼 습 니 다.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!AOPautoload 을 조정 해 야 한다.
  • thinkpHP 관련 내용 에 관심 이 있 는 독자 들 은 본 사이트 의 주 제 를 볼 수 있다.
    본 고 는 ThinkPHP 프레임 워 크 를 기반 으로 한 PHP 프로 그래 밍 에 도움 이 되 기 를 바 랍 니 다.

    좋은 웹페이지 즐겨찾기