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

Php Proxy Builder Bundle Laravel Package

ejsmont-artur/php-proxy-builder-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation Add the bundle to your composer.json:

    composer require ejsmont-artur/php-proxy-builder-bundle
    

    Enable it in config/bundles.php:

    return [
        // ...
        Ejsmont\PhpProxyBuilderBundle\PhpProxyBuilderBundle::class => ['all' => true],
    ];
    
  2. First Use Case: Basic Proxy Generation Inject the ProxyBuilder service into a controller or service:

    use Ejsmont\PhpProxyBuilderBundle\ProxyBuilder;
    
    class MyService
    {
        public function __construct(private ProxyBuilder $proxyBuilder) {}
    
        public function createProxy()
        {
            $proxy = $this->proxyBuilder->build(
                'App\Entity\MyEntity',
                [
                    'method1' => function ($proxy, $method, $args) {
                        // Pre-logic
                        $result = $method($proxy, $args);
                        // Post-logic
                        return $result;
                    },
                ]
            );
            return $proxy;
        }
    }
    
  3. Key Files to Review

    • src/DependencyInjection/PhpProxyBuilderExtension.php (Configuration)
    • src/ProxyBuilder.php (Core logic)
    • tests/ (Usage examples)

Implementation Patterns

Core Workflows

  1. Dynamic Proxy Generation Use ProxyBuilder to wrap services/entities with runtime behavior:

    $proxy = $this->proxyBuilder->build(
        'App\Service\PaymentService',
        [
            'charge' => function ($proxy, $method, $args) {
                // Validate args before calling original method
                if (!isset($args[0]['amount'])) {
                    throw new \InvalidArgumentException('Amount required');
                }
                return $method($proxy, $args);
            },
        ]
    );
    
  2. Dependency Injection Integration Register proxies as services in services.yaml:

    services:
        App\Service\PaymentService:
            class: Ejsmont\PhpProxyBuilderBundle\Proxy
            factory: ['@php_proxy_builder.proxy_builder', 'build']
            arguments:
                - 'App\Service\PaymentService'
                - { 'charge': '@App\Interceptor\ChargeValidator' }
    
  3. Interceptor Pattern Create reusable interceptors (e.g., logging, caching):

    // src/Interceptor/LoggerInterceptor.php
    class LoggerInterceptor
    {
        public function __invoke($proxy, $method, $args)
        {
            \Log::info("Calling {$method} with " . json_encode($args));
            return $method($proxy, $args);
        }
    }
    

    Apply globally via config:

    php_proxy_builder:
        default_interceptors:
            - '@App\Interceptor\LoggerInterceptor'
    
  4. Method-Level Granularity Combine multiple interceptors for specific methods:

    $proxy = $this->proxyBuilder->build(
        'App\Service\UserService',
        [
            'updatePassword' => [
                '@App\Interceptor\LoggerInterceptor',
                '@App\Interceptor\PasswordValidator',
            ],
        ]
    );
    
  5. Proxy Chaining Chain proxies for layered behavior:

    $loggedProxy = $this->proxyBuilder->build(
        'App\Service\OrderService',
        ['@App\Interceptor\LoggerInterceptor']
    );
    $validatedProxy = $this->proxyBuilder->build(
        $loggedProxy,
        ['create': '@App\Interceptor\OrderValidator']
    );
    

Gotchas and Tips

Common Pitfalls

  1. Circular Dependencies

    • Proxies cannot resolve circular references (e.g., ServiceA proxies ServiceB, which proxies ServiceA).
    • Fix: Use lazy-loading or break cycles via interfaces.
  2. Method Signature Mismatches

    • Interceptors must match the proxy’s method signature exactly (including $proxy and $args parameters).
    • Fix: Use var_dump($method, $args) in interceptors to debug.
  3. Configuration Overrides

    • Bundle config (php_proxy_builder) merges with defaults. Overrides may not work as expected.
    • Fix: Use !important in YAML or debug with dump($this->container->getParameter('php_proxy_builder')).
  4. Performance Overhead

    • Runtime proxy generation adds reflection overhead. Cache proxies in production:
      php_proxy_builder:
          cache_proxies: true
          cache_dir: '%kernel.cache_dir%/proxies'
      
  5. Type Safety

    • Proxies bypass type hints. Ensure interceptors handle type juggling:
      // Bad: Assumes $args[0] is always an array
      $args[0]['amount'] = 100;
      
      // Good: Type-check first
      if (!is_array($args[0])) {
          throw new \InvalidArgumentException('Expected array');
      }
      

Debugging Tips

  1. Enable Proxy Logging Add to config/packages/dev/debug.yaml:

    php_proxy_builder:
        debug: true
    

    Logs proxy generation to var/log/dev.log.

  2. Inspect Generated Proxies Dump the proxy class to verify methods:

    var_dump(get_class($proxy)); // Should show "Ejsmont\PhpProxyBuilderBundle\Proxy"
    
  3. Test Interceptors Isolated Use PHPUnit to test interceptors without full proxy setup:

    $interceptor = new MyInterceptor();
    $result = $interceptor($this->createMock('stdClass'), 'testMethod', [1, 2, 3]);
    

Extension Points

  1. Custom Proxy Classes Extend Ejsmont\PhpProxyBuilderBundle\Proxy to add metadata:

    class CustomProxy extends Proxy
    {
        public function getOriginalClass(): string
        {
            return $this->originalClass;
        }
    }
    

    Register via config:

    php_proxy_builder:
        proxy_class: 'App\Proxy\CustomProxy'
    
  2. Dynamic Interceptor Loading Implement Ejsmont\PhpProxyBuilderBundle\Interceptor\InterceptorInterface for custom logic:

    class DynamicInterceptor implements InterceptorInterface
    {
        public function __invoke($proxy, $method, $args)
        {
            if ($method === 'sensitiveOperation') {
                $this->checkPermissions();
            }
            return $method($proxy, $args);
        }
    }
    
  3. Post-Proxy Initialization Use Symfony’s container.post_build to modify proxies:

    services:
        _instanceof:
            App\Service\ProxiedService:
                tags: ['app.proxied_service']
    
    // src/EventListener/ProxyInitializer.php
    class ProxyInitializer implements ContainerAwareInterface
    {
        public function onKernelRequest(GetResponseEvent $event)
        {
            $container = $event->getContainer();
            foreach ($container->findTaggedServiceIds('app.proxied_service') as $id => $tags) {
                $service = $container->get($id);
                // Modify proxy behavior
            }
        }
    }
    
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope