Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Aop Bundle Laravel Package

jms/aop-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require jms/aop-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        JMS\AopBundle\JMSAopBundle::class => ['all' => true],
    ];
    
  2. Basic Usage:

    • Define an interceptor (e.g., logging, timing) in a service:
      # config/services.yaml
      App\Service\LoggerInterceptor:
          tags: ['jms.aop.method']
      
    • Annotate a method with @Around to apply the interceptor:
      use JMS\AopBundle\Annotation\Around;
      
      class MyService {
          /**
           * @Around("execution(* App\Service\MyService->*(..))")
           */
          public function logExecution(ProceedingJoinPoint $joinPoint) {
              echo "Before method\n";
              $result = $joinPoint->proceed();
              echo "After method\n";
              return $result;
          }
      }
      
    
    
  3. First Use Case:

    • Log all method calls in a controller to debug API traffic:
      /**
       * @Around("execution(* App\Controller\ApiController->*(..))")
       */
      public function logApiCalls(ProceedingJoinPoint $joinPoint) {
          $method = $joinPoint->getMethod()->getName();
          $args = $joinPoint->getArgs();
          \Log::info("API Call: {$method} with args: " . json_encode($args));
          return $joinPoint->proceed();
      }
      

Implementation Patterns

Common Workflows

  1. Aspect-Oriented Cross-Cutting Concerns:

    • Logging: Wrap method calls to log inputs/outputs.
      /**
       * @Around("execution(* App\Service\*(..)) && !execution(* App\Service\LoggerInterceptor->*(..))")
       */
      public function logServiceCalls(ProceedingJoinPoint $joinPoint) { ... }
      
    • Timing: Measure method execution time.
      public function timeExecution(ProceedingJoinPoint $joinPoint) {
          $start = microtime(true);
          $result = $joinPoint->proceed();
          $duration = microtime(true) - $start;
          \Log::debug("Method {$joinPoint->getMethod()->getName()} took {$duration}s");
          return $result;
      }
      
    • Authorization: Check permissions before proceeding.
      /**
       * @Around("execution(* App\Controller\AdminController->*(..))")
       */
      public function checkAdminAccess(ProceedingJoinPoint $joinPoint) {
          if (!$this->isAdmin()) {
              throw new \RuntimeException("Access denied");
          }
          return $joinPoint->proceed();
      }
      
  2. Pointcut Expressions:

    • Use execution patterns to target methods:
      // Target all public methods in a class
      "execution(* App\Service\UserService->*(..))"
      
      // Target methods with specific annotations
      "execution(@App\Annotation\Cacheable * *(..))"
      
    • Combine with logical operators:
      "execution(* App\Service\*(..)) && !execution(* App\Service\LoggerInterceptor->*(..))"
      
  3. Integration with Symfony Services:

    • Dependency Injection: Inject interceptors as services.
      services:
          app.cache_interceptor:
              class: App\Interceptor\CacheInterceptor
              tags: ['jms.aop.method']
      
    • Event Listeners: Use AOP to replace or extend event listeners.
      /**
       * @Around("execution(* App\EventListener\AuthListener->handle(..))")
       */
      public function extendAuthListener(ProceedingJoinPoint $joinPoint) { ... }
      
  4. Performance Optimization:

    • Lazy Loading: Use AOP to defer expensive operations.
      /**
       * @Around("execution(* App\Service\DataLoader->loadHeavyData(..))")
       */
      public function lazyLoadData(ProceedingJoinPoint $joinPoint) {
          if (!$this->shouldLoad()) {
              return $this->getCachedData();
          }
          return $joinPoint->proceed();
      }
      

Gotchas and Tips

Pitfalls

  1. Performance Overhead:

    • AOP introduces reflection and proxy overhead. Benchmark critical paths.
    • Mitigation: Use sparingly for cross-cutting concerns (e.g., logging, auth), not business logic.
  2. Annotation Parsing Issues:

    • Ensure annotations are parsed by Symfony’s annotation reader:
      php bin/console debug:container --tags="jms.aop.method"
      
    • Fix: Clear cache if interceptors aren’t loading:
      php bin/console cache:clear
      
  3. Circular Dependencies:

    • AOP proxies can break circular references (e.g., ServiceA calls ServiceB, which calls ServiceA).
    • Workaround: Use @Around with ProceedingJoinPoint carefully or refactor dependencies.
  4. Method Signature Mismatches:

    • If an interceptor’s method signature doesn’t match the target, it fails silently.
    • Debug: Check ProceedingJoinPoint arguments:
      var_dump($joinPoint->getMethod()->getParameters());
      
  5. Symfony 4+ Compatibility:

    • The bundle is unmaintained for Symfony 4/5+. Use jms/aop-bundle v1.6 (last stable) or migrate to Doctrine AOP or PHP-AOP.
    • Alternative: For Symfony 5+, consider:
      composer require php-aop/php-aop
      

Debugging Tips

  1. Enable AOP Logging: Add to config/packages/dev/jms_aop.yaml:

    jms_aop:
        proxy_dir: "%kernel.cache_dir%/aop"
        debug: true  # Logs proxy generation
    
  2. Inspect Proxies: Dump proxies to verify interceptors are applied:

    $reflection = new \ReflectionClass($service);
    if ($reflection->isProxy()) {
        var_dump($reflection->getMethod('__invoke')->getClosure()->getClosureThis());
    }
    
  3. Pointcut Testing: Test pointcuts with a dummy interceptor:

    /**
     * @Around("execution(* App\Service\*(..))")
     */
    public function testPointcut(ProceedingJoinPoint $joinPoint) {
        \Log::info("Pointcut matched: " . $joinPoint->getMethod()->getName());
        return $joinPoint->proceed();
    }
    

Extension Points

  1. Custom JoinPoint Interfaces: Extend JMS\AopBundle\Aspect\JoinPoint\JoinPointInterface for domain-specific logic.

  2. Dynamic Pointcuts: Use JMS\AopBundle\Pointcut\ExpressionPointcut to build pointcuts dynamically:

    $pointcut = new ExpressionPointcut("execution(* App\Service\*(..))");
    $this->getAspectInstance()->addPointcut($pointcut);
    
  3. Aspect Inheritance: Extend existing aspects to avoid duplication:

    class ExtendedLoggerAspect extends LoggerAspect {
        /**
         * @Around("execution(* App\Service\AdminService->*(..))")
         */
        public function logAdminCalls(ProceedingJoinPoint $joinPoint) {
            // Custom logic before/after parent
            return parent::logExecution($joinPoint);
        }
    }
    
  4. Weaving Strategies:

    • Runtime Weaving: Default (flexible but slower).
    • Load-Time Weaving: Faster but requires PHP 7.4+ and opcache.jit_buffer_size.
      jms_aop:
          weaving_strategy: "load-time"  # Requires PHP 7.4+
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware