承接 skylence/laravel-interceptor 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

skylence/laravel-interceptor

最新稳定版本:v1.0.0

Composer 安装命令:

composer require skylence/laravel-interceptor

包简介

A compiled proxy-based plugin system for Laravel, inspired by Magento 2 but optimized for performance

README 文档

README

A compiled proxy-based plugin system for Laravel, inspired by Magento 2's interceptor pattern.

What is an Interceptor?

An interceptor allows you to modify, extend, or completely override the behavior of any class method without modifying the original source code. This is achieved by generating proxy classes that wrap the original classes and intercept method calls.

Use Cases

  • Validation - Validate input before a method executes
  • Logging & Auditing - Log method calls, parameters, and results
  • Caching - Cache method results transparently
  • Authorization - Check permissions before executing methods
  • Event Dispatching - Trigger events before/after method execution
  • Error Handling - Wrap methods with try/catch logic
  • Performance Monitoring - Measure execution time
  • Data Transformation - Modify input arguments or return values
  • Feature Flags - Conditionally modify behavior
  • Third-party Package Extension - Extend vendor code without forking

How It Works

flowchart TB
    subgraph build["BUILD TIME (artisan)"]
        scan["Scan #[Plugin]<br>attributes"] --> chain["Build chain<br>map + cache"] --> generate["Generate proxy<br>classes"]
    end

    subgraph runtime["RUNTIME"]
        resolve["Container resolves"] --> proxy["Returns Proxy"] --> execute["Executes plugins"]
    end

    build --> runtime
Loading
  1. At build time, plugins are scanned and proxy classes are generated
  2. At runtime, the container returns the proxy instead of the original class
  3. When a method is called, the proxy executes: before → around → original → after

Features

  • Before plugins - Modify method arguments before execution
  • Around plugins - Wrap method execution with custom logic
  • After plugins - Modify return values after execution
  • Compiled proxies - Zero runtime overhead after compilation
  • HMAC-signed cache - Tamper-proof cache files
  • Circular dependency detection - Prevents infinite loops

Requirements

  • PHP 8.2+
  • Laravel 11+

Installation

composer require skylence/laravel-interceptor

Publish the configuration file:

php artisan vendor:publish --tag=interceptor-config

Quick Start

1. Create a Plugin

Create a plugin class with the #[Plugin] attribute:

<?php

namespace App\Plugins;

use App\Services\OrderService;
use Skylence\LaravelInterceptor\Attributes\Plugin;

#[Plugin(target: OrderService::class, sortOrder: 10)]
class OrderValidationPlugin
{
    public function beforePlace(OrderService $subject, Order $order): ?array
    {
        // Validate order before placing
        if ($order->total < 0) {
            throw new InvalidOrderException('Order total cannot be negative');
        }

        // Return null to keep original arguments
        // Return array to replace arguments
        return null;
    }

    public function afterPlace(OrderService $subject, mixed $result, Order $order): mixed
    {
        // Log after order is placed
        logger()->info('Order placed', ['order_id' => $result->id]);

        return $result;
    }
}

2. Compile Plugins

php artisan interceptor:compile

3. Use the Service Normally

// The proxy is automatically injected
$orderService = app(OrderService::class);
$orderService->place($order); // Plugins execute automatically

Plugin Types

Before Plugins

Executed before the original method. Can modify arguments.

#[Plugin(target: UserService::class)]
class UserValidationPlugin
{
    /**
     * Method name: before + PascalCase(targetMethod)
     *
     * @param UserService $subject The original service instance
     * @param string $email First argument of create()
     * @param string $name Second argument of create()
     * @return array|null Return array to replace args, null to keep original
     */
    public function beforeCreate(UserService $subject, string $email, string $name): ?array
    {
        // Normalize email
        $email = strtolower(trim($email));

        // Return modified arguments
        return [$email, $name];
    }
}

Around Plugins

Wrap the method execution. Must call $proceed to continue.

#[Plugin(target: PaymentService::class)]
class PaymentLoggingPlugin
{
    /**
     * @param PaymentService $subject The original service
     * @param \Closure $proceed Call to execute next plugin or original method
     * @param Payment $payment Original method arguments follow
     */
    public function aroundProcess(
        PaymentService $subject,
        \Closure $proceed,
        Payment $payment
    ): mixed {
        $startTime = microtime(true);

        try {
            // Call the original method (or next around plugin)
            $result = $proceed($payment);

            logger()->info('Payment processed', [
                'duration' => microtime(true) - $startTime,
            ]);

            return $result;
        } catch (\Throwable $e) {
            logger()->error('Payment failed', ['error' => $e->getMessage()]);
            throw $e;
        }
    }
}

After Plugins

Executed after the original method. Can modify the result.

#[Plugin(target: ProductRepository::class)]
class ProductCachePlugin
{
    public function __construct(
        private CacheManager $cache
    ) {}

    /**
     * @param ProductRepository $subject
     * @param mixed $result The return value from the method
     * @param int $id Original method arguments follow
     */
    public function afterFind(ProductRepository $subject, mixed $result, int $id): mixed
    {
        // Cache the result
        if ($result !== null) {
            $this->cache->put("product.{$id}", $result, 3600);
        }

        return $result;
    }
}

Plugin Attribute Options

#[Plugin(
    target: TargetClass::class,  // Required: Class to intercept
    sortOrder: 10,                // Optional: Execution order (lower = first)
    disabled: false,              // Optional: Disable this plugin
    methods: ['save', 'delete']   // Optional: Only intercept these methods
)]

Commands

Compile Plugins

Scan for plugins and generate proxy classes:

php artisan interceptor:compile

# Force recompilation
php artisan interceptor:compile --force

# Skip caching (for debugging)
php artisan interceptor:compile --no-cache

List Plugins

View all registered plugins:

php artisan interceptor:list

# Filter by target class
php artisan interceptor:list --target=UserService

# Scan fresh instead of using cache
php artisan interceptor:list --scan

Clear Compiled Data

Remove all generated proxies and cache:

php artisan interceptor:clear

# Skip confirmation
php artisan interceptor:clear --force

# Only clear proxies
php artisan interceptor:clear --proxies

# Only clear cache
php artisan interceptor:clear --cache

Configuration

// config/interceptor.php

return [
    // Where to store generated proxy classes
    'proxy_path' => storage_path('framework/interceptor/proxies'),

    // Directories to scan for plugins
    'scan_paths' => [
        app_path('Plugins'),
    ],

    // Classes that should never be intercepted
    'exclude' => [
        // App\Services\CriticalService::class,
    ],

    // Auto-compile on boot (development only!)
    'auto_compile' => env('INTERCEPTOR_AUTO_COMPILE', false),

    // Cache driver (null = default)
    'cache_driver' => env('INTERCEPTOR_CACHE_DRIVER'),

    // Include debug info in proxies
    'debug' => env('INTERCEPTOR_DEBUG', false),

    // HMAC signature verification for cache files
    'verify_cache_signatures' => env('INTERCEPTOR_VERIFY_SIGNATURES', true),

    // Cache TTL in seconds (null = 10 years)
    'cache_ttl' => env('INTERCEPTOR_CACHE_TTL'),

    // Pretty print cache files (slower, for debugging)
    'pretty_print_cache' => env('INTERCEPTOR_PRETTY_PRINT', false),

    // Max plugin instances to cache in memory
    'max_plugin_instances' => env('INTERCEPTOR_MAX_INSTANCES', 100),
];

Using in Laravel Packages

When building a Laravel package that uses the interceptor system:

1. Package Structure

your-package/
├── src/
│   ├── Plugins/
│   │   └── YourPlugin.php
│   ├── Services/
│   │   └── YourService.php
│   └── YourPackageServiceProvider.php
├── config/
│   └── your-package.php
└── composer.json

2. Register Plugin Scan Path

In your service provider, add your plugin directory to the scan paths:

<?php

namespace YourVendor\YourPackage;

use Illuminate\Support\ServiceProvider;
use Skylence\LaravelInterceptor\InterceptorManager;

class YourPackageServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Add your plugin directory to scan paths
        if ($this->app->bound(InterceptorManager::class)) {
            $this->app->make(InterceptorManager::class)
                ->addScanPath(__DIR__.'/Plugins');
        }
    }
}

3. Package Plugin Example

<?php

namespace YourVendor\YourPackage\Plugins;

use App\Models\User;
use Skylence\LaravelInterceptor\Attributes\Plugin;

#[Plugin(target: User::class, sortOrder: 100)]
class UserAuditPlugin
{
    public function afterSave(User $subject, mixed $result): mixed
    {
        // Audit log for user saves
        activity()
            ->performedOn($subject)
            ->log('User saved');

        return $result;
    }
}

4. Deployment Instructions

Add to your package documentation:

## Post-Installation

After installing this package, recompile the interceptor plugins:

    php artisan interceptor:compile --force

Add this command to your deployment script after `composer install`.

5. Composer Scripts

Add to the host application's composer.json:

{
    "scripts": {
        "post-autoload-dump": [
            "@php artisan interceptor:compile --force --quiet"
        ]
    }
}

Best Practices

DO

  • Compile in deployment - Always run interceptor:compile in your deployment script
  • Use sortOrder - Set explicit sort order when plugin execution order matters
  • Keep plugins focused - One responsibility per plugin
  • Use dependency injection - Plugins support constructor injection
  • Test plugins - Write tests for your plugin logic

DON'T

  • Don't enable auto_compile in production - It causes performance issues
  • Don't intercept framework classes - Add them to the exclude list
  • Don't create circular dependencies - Plugin A targeting Plugin B and vice versa
  • Don't modify $subject state in before plugins - Use return value to modify arguments

Execution Order

When multiple plugins target the same method:

flowchart TB
    subgraph before["Before Plugins"]
        b1["1. BeforePlugin (sortOrder: 10)"]
        b2["2. BeforePlugin (sortOrder: 20)"]
        b1 --> b2
    end

    subgraph around["Around Plugins"]
        a1["3. AroundPlugin (sortOrder: 10)"]
        a2["4. AroundPlugin (sortOrder: 20)"]
        orig["5. Original Method"]
        a2_after["6. AroundPlugin (sortOrder: 20) after proceed"]
        a1_after["7. AroundPlugin (sortOrder: 10) after proceed"]
        a1 --> a2 --> orig --> a2_after --> a1_after
    end

    subgraph after["After Plugins"]
        af1["8. AfterPlugin (sortOrder: 10)"]
        af2["9. AfterPlugin (sortOrder: 20)"]
        af1 --> af2
    end

    before --> around --> after
Loading

Limitations

The following cannot be intercepted:

  • final classes
  • private or protected methods
  • static methods
  • Magic methods (__construct, __get, etc.)
  • Interfaces, traits, enums, abstract classes
  • Internal PHP classes

Troubleshooting

Plugins not executing

  1. Run php artisan interceptor:compile --force
  2. Check php artisan interceptor:list to verify plugins are registered
  3. Ensure the target class is resolved through the container

Class not found errors

  1. Verify the proxy path is writable
  2. Check autoloader is registered (should be automatic)
  3. Run composer dump-autoload

Cache signature verification failed

  1. Run php artisan interceptor:clear --force
  2. Run php artisan interceptor:compile
  3. Ensure APP_KEY is set consistently across environments

Security

  • Cache files are HMAC-signed using your APP_KEY
  • Path traversal attempts are blocked
  • Only public methods can be intercepted
  • Proxy classes have restricted file permissions (0640)

License

MIT License. See LICENSE for details.

skylence/laravel-interceptor 适用场景与选型建议

skylence/laravel-interceptor 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 01 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 skylence/laravel-interceptor 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 skylence/laravel-interceptor 我们能提供哪些服务?
定制开发 / 二次开发

基于 skylence/laravel-interceptor 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 20
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-23