pixielity/laravel-discovery
Composer 安装命令:
composer require pixielity/laravel-discovery
包简介
Powerful class discovery system for Laravel with attribute-based scanning, directory traversal, and monorepo support
关键字:
README 文档
README
Configuration File
config/discovery.php:
<?php
return [
/*
* Cache Configuration
*/
'cache' => [
'enabled' => env('DISCOVERY_CACHE_ENABLED', env('APP_ENV') !== 'local'),
'path' => base_path('bootstrap/cache/discovery'),
'ttl' => null, // Forever in production
],
/*
* Monorepo Patterns
*/
'monorepo' => [
'packages' => [
'path' => 'packages/*',
'namespace' => 'Pixielity\{package}',
],
'modules' => [
'path' => 'modules/*',
'namespace' => 'Modules\{module}',
],
],
/*
* Pre-configured Paths
*/
'paths' => [
'settings' => [
'packages/*/src/Settings',
'modules/*/src/Settings',
'app/Settings',
],
'health' => [
'packages/*/src/Health',
'modules/*/src/Health',
],
'commands' => [
'packages/*/src/Console/Commands',
'modules/*/src/Console/Commands',
],
],
];
Using Pre-configured Paths
$paths = config('discovery.paths.settings');
$settings = Discovery::directories($paths)->get();
Monorepo Configuration
The package automatically resolves namespaces for monorepo structures:
packages/
├── Auth/src/Settings/AuthSettings.php → Pixielity\Auth\Settings\AuthSettings
├── Billing/src/Settings/BillingSettings.php → Pixielity\Billing\Settings\BillingSettings
└── Users/src/Settings/UserSettings.php → Pixielity\Users\Settings\UserSettings
🎯 Real-World Examples
Example 1: Auto-Register Settings Classes
Automatically discover and register all settings classes in your application.
// In your service provider
public function boot(): void
{
$settings = Discovery::attribute(AsSetting::class)
->directories(config('discovery.paths.settings'))
->extending(Settings::class)
->instantiable()
->cached('settings')
->get();
foreach ($settings as $class => $metadata) {
$this->app->singleton($class);
}
}
Attribute Definition:
#[Attribute(Attribute::TARGET_CLASS)]
class AsSetting
{
public function __construct(
public string $key,
public string $group = 'general',
public bool $encrypted = false,
) {}
}
Settings Class:
#[AsSetting(key: 'app.settings', group: 'application')]
class AppSettings extends Settings
{
public string $siteName;
public string $siteUrl;
public bool $maintenanceMode = false;
}
Example 2: Dynamic Route Registration
Automatically register routes from controller methods with attributes.
// In your RouteServiceProvider
public function boot(): void
{
$routes = Discovery::methods(Route::class)
->directories('app/Http/Controllers')
->cached('routes')
->get();
foreach ($routes as $identifier => $metadata) {
[$class, $method] = explode('::', $identifier);
$route = $metadata['attribute'];
Route::{strtolower($route->method)}($route->path, [$class, $method])
->name($route->name)
->middleware($route->middleware);
}
}
Route Attribute:
#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
public function __construct(
public string $method,
public string $path,
public ?string $name = null,
public array $middleware = [],
) {}
}
Controller:
class UserController extends Controller
{
#[Route(method: 'GET', path: '/users', name: 'users.index', middleware: ['auth'])]
public function index()
{
return User::paginate();
}
#[Route(method: 'POST', path: '/users', name: 'users.store', middleware: ['auth', 'admin'])]
public function store(Request $request)
{
return User::create($request->validated());
}
#[Route(method: 'GET', path: '/users/{user}', name: 'users.show')]
public function show(User $user)
{
return $user;
}
}
Example 3: Plugin System
Build a flexible plugin system with automatic plugin discovery.
// Plugin Interface
interface Plugin{
public function register(): void;
public function boot(): void;
public function getName(): string;
public function getVersion(): string;
}
// Plugin Attribute
#[Attribute(Attribute::TARGET_CLASS)]
class Plugin
{
public function __construct(
public string $name,
public string $version,
public bool $enabled = true,
public array $dependencies = [],
) {}
}
// Example Plugin
#[Plugin(name: 'Analytics', version: '1.0.0', enabled: true)]
class AnalyticsPlugin implements Plugin{
public function register(): void
{
// Register plugin services
}
public function boot(): void
{
// Boot plugin
}
public function getName(): string
{
return 'Analytics';
}
public function getVersion(): string
{
return '1.0.0';
}
}
// Plugin Manager
class PluginManager
{
public function loadPlugins(): void
{
$plugins = Discovery::attribute(Plugin::class)
->implementing(PluginInterface::class)
->where('enabled', true)
->instantiable()
->cached('plugins')
->get();
foreach ($plugins as $class => $metadata) {
$plugin = app($class);
// Check dependencies
$this->checkDependencies($metadata['attribute']->dependencies);
// Register and boot plugin
$plugin->register();
$plugin->boot();
Log::info("Plugin loaded: {$plugin->getName()} v{$plugin->getVersion()}");
}
}
}
Example 4: Health Check System
Automatically discover and run health checks.
// Health Check Interface
interface HealthCheck{
public function check(): HealthCheckResult;
public function getName(): string;
}
// Health Check Attribute
#[Attribute(Attribute::TARGET_CLASS)]
class HealthCheck
{
public function __construct(
public string $name,
public int $priority = 0,
public bool $critical = false,
) {}
}
// Example Health Check
#[HealthCheck(name: 'Database', priority: 10, critical: true)]
class DatabaseHealthCheck implements HealthCheck{
public function check(): HealthCheckResult
{
try {
DB::connection()->getPdo();
return HealthCheckResult::healthy('Database connection successful');
} catch (\Exception $e) {
return HealthCheckResult::unhealthy('Database connection failed: ' . $e->getMessage());
}
}
public function getName(): string
{
return 'Database';
}
}
// Health Check Controller
class HealthController extends Controller
{
public function check()
{
$checks = Discovery::attribute(HealthCheck::class)
->implementing(HealthCheckInterface::class)
->instantiable()
->cached('health-checks')
->get();
$results = [];
$allHealthy = true;
foreach ($checks as $class => $metadata) {
$check = app($class);
$result = $check->check();
$results[] = [
'name' => $check->getName(),
'status' => $result->status,
'message' => $result->message,
'critical' => $metadata['attribute']->critical,
];
if (!$result->isHealthy() && $metadata['attribute']->critical) {
$allHealthy = false;
}
}
return response()->json([
'status' => $allHealthy ? 'healthy' : 'unhealthy',
'checks' => $results,
], $allHealthy ? 200 : 503);
}
}
Example 5: Event Listener Discovery
Automatically register event listeners.
// Listener Attribute
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
class Listens
{
public function __construct(
public string $event,
public int $priority = 0,
) {}
}
// Example Listener
#[Listens(event: UserCreated::class, priority: 10)]
#[Listens(event: UserUpdated::class, priority: 5)]
class SendWelcomeEmail
{
public function handle(UserCreated|UserUpdated $event): void
{
Mail::to($event->user)->send(new WelcomeEmail($event->user));
}
}
// In EventServiceProvider
public function boot(): void
{
$listeners = Discovery::attribute(Listens::class)
->directories('app/Listeners')
->cached('event-listeners')
->get();
foreach ($listeners as $class => $metadata) {
// Note: Listens is repeatable, so we get an array of attributes
$attributes = is_array($metadata['attribute'])
? $metadata['attribute']
: [$metadata['attribute']];
foreach ($attributes as $attribute) {
Event::listen($attribute->event, $class);
}
}
}
Example 6: Validation Rules Discovery
Discover custom validation rules.
// Validation Rule Attribute
#[Attribute(Attribute::TARGET_CLASS)]
class ValidationRule
{
public function __construct(
public string $name,
public ?string $message = null,
) {}
}
// Example Rule
#[ValidationRule(name: 'phone', message: 'The :attribute must be a valid phone number.')]
class PhoneRule implements ValidationRule
{
public function passes($attribute, $value): bool
{
return preg_match('/^[0-9]{10}$/', $value);
}
public function message(): string
{
return 'The :attribute must be a valid phone number.';
}
}
// In ValidationServiceProvider
public function boot(): void
{
$rules = Discovery::attribute(ValidationRule::class)
->implementing(ValidationRule::class)
->instantiable()
->cached('validation-rules')
->get();
foreach ($rules as $class => $metadata) {
$ruleName = $metadata['attribute']->name;
Validator::extend($ruleName, function ($attribute, $value, $parameters, $validator) use ($class) {
$rule = app($class);
return $rule->passes($attribute, $value);
});
if ($message = $metadata['attribute']->message) {
Validator::replacer($ruleName, function ($message, $attribute, $rule, $parameters) {
return str_replace(':attribute', $attribute, $message);
});
}
}
}
Example 7: API Resource Discovery
Automatically register API resources.
// API Resource Attribute
#[Attribute(Attribute::TARGET_CLASS)]
class ApiResource
{
public function __construct(
public string $model,
public ?string $collection = null,
) {}
}
// Example Resource
#[ApiResource(model: User::class, collection: UserCollection::class)]
class UserResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at,
];
}
}
// Resource Registry
class ResourceRegistry
{
protected array $resources = [];
public function discover(): void
{
$resources = Discovery::attribute(ApiResource::class)
->extending(JsonResource::class)
->cached('api-resources')
->get();
foreach ($resources as $class => $metadata) {
$modelClass = $metadata['attribute']->model;
$this->resources[$modelClass] = $class;
}
}
public function getResourceForModel(string $modelClass): ?string
{
return $this->resources[$modelClass] ?? null;
}
}
Example 8: Middleware Discovery
Discover and register middleware automatically.
// Middleware Attribute
#[Attribute(Attribute::TARGET_CLASS)]
class Middleware
{
public function __construct(
public string $alias,
public array $groups = [],
public int $priority = 0,
) {}
}
// Example Middleware
#[Middleware(alias: 'check.subscription', groups: ['web', 'api'], priority: 100)]
class CheckSubscription
{
public function handle(Request $request, Closure $next)
{
if (!auth()->user()?->hasActiveSubscription()) {
return response()->json(['error' => 'Subscription required'], 403);
}
return $next($request);
}
}
// In Kernel or ServiceProvider
public function registerMiddleware(): void
{
$middlewares = Discovery::attribute(Middleware::class)
->directories('app/Http/Middleware')
->cached('middlewares')
->get();
foreach ($middlewares as $class => $metadata) {
$attribute = $metadata['attribute'];
// Register alias
$this->app['router']->aliasMiddleware($attribute->alias, $class);
// Register in groups
foreach ($attribute->groups as $group) {
$this->app['router']->pushMiddlewareToGroup($group, $class);
}
}
}
📚 API Reference
DiscoveryManager
Main service class for discovery operations.
Methods
attribute(string $attributeClass): DiscoveryBuilder
Discover classes by attribute.
Discovery::attribute(AsCard::class)->get();
directories(string|array $directories): DiscoveryBuilder
Discover classes in directories.
Discovery::directories('packages/*/src/Settings')->get();
implementing(string $interface): DiscoveryBuilder
Discover classes implementing an interface.
Discovery::implementing(HealthCheckInterface::class)->get();
extending(string $parentClass): DiscoveryBuilder
Discover classes extending a parent class.
Discovery::extending(Command::class)->get();
methods(string $attributeClass): DiscoveryBuilder
Discover methods with an attribute.
Discovery::methods(Route::class)->get();
properties(string $attributeClass): DiscoveryBuilder
Discover properties with an attribute.
Discovery::properties(Validate::class)->get();
clearCache(?string $key = null): void
Clear discovery caches.
Discovery::clearCache('settings');
finder(string|array $directories): Finder
Get Symfony Finder for advanced discovery.
$finder = Discovery::finder('packages/*/src')
->name('*.php')
->date('since yesterday');
DiscoveryBuilder
Fluent builder for configuring discovery.
Methods
where(string $property, mixed $operator, mixed $value = null): self
Filter by attribute property.
->where('enabled', true)
->where('priority', '>', 5)
filter(callable $callback): self
Add custom filter.
->filter(fn($class, $meta) => $meta['attribute']->priority > 5)
instantiable(): self
Filter to instantiable classes only.
->instantiable()
extends(string $parentClass): self
Validate parent class.
->extends(Command::class)
implements(string $interface): self
Validate interface implementation.
->implements(ServiceInterface::class)
cached(string $key): self
Enable caching.
->cached('settings')
get(): array
Execute discovery and return results.
->get()
⚡ Performance
Benchmarks
Performance tests on a monorepo with 500 classes:
| Operation | Without Cache | With Cache | Improvement |
|---|---|---|---|
| Attribute Discovery | 50ms | 2ms | 25x faster |
| Directory Scan (100 files) | 120ms | 3ms | 40x faster |
| Interface Discovery | 80ms | 2ms | 40x faster |
| Method Discovery | 60ms | 2ms | 30x faster |
Optimization Tips
- Use Attribute Discovery - Fastest method, leverages composer cache
- Enable Caching - Essential for production
- Scope Directory Scans - Use specific paths instead of scanning everything
- Combine Filters - Apply filters early to reduce processing
// ✅ Optimized
Discovery::attribute(AsCard::class)
->where('enabled', true) // Filter early
->instantiable() // Validate early
->cached('cards') // Cache results
->get();
// ❌ Not optimized
Discovery::directories('.') // Scans everything
->get(); // No caching
🧪 Testing
Running Tests
# Run all tests
composer test
# Run with coverage
composer test-coverage
# Run static analysis
composer analyse
Writing Tests
use Pixielity\Discovery\Tests\TestCase;
class MyDiscoveryTest extends TestCase
{
public function test_discovers_classes_with_attribute(): void
{
$classes = Discovery::attribute(MyAttribute::class)->get();
$this->assertNotEmpty($classes);
$this->assertArrayHasKey(MyClass::class, $classes);
}
}
🔒 Security
Security Considerations
- File System Access - Discovery scans the file system. Ensure paths are properly configured.
- User Input - Never use user input directly in directory paths.
- Class Loading - Always validate classes before instantiation.
- Cache Security - Protect cache directory from web access.
Best Practices
// ✅ GOOD - Validate before use
$classes = Discovery::attribute(MyAttribute::class)
->instantiable()
->implementing(MyInterface::class)
->get();
// ❌ BAD - No validation
$classes = Discovery::directories($_GET['path'])->get();
Reporting Vulnerabilities
Please report security vulnerabilities to: security@pixielity.com
See SECURITY.md for our security policy.
🤝 Contributing
We welcome contributions! Please see CONTRIBUTING.md for details.
Development Setup
# Clone repository
git clone https://github.com/pixielity-co/laravel-discovery.git
cd discovery
# Install dependencies
composer install
# Run tests
composer test
# Run static analysis
composer analyse
Coding Standards
- Follow PSR-12
- Use strict types
- Add PHPDoc blocks
- Write tests for new features
- Update documentation
💖 Credits
This package stands on the shoulders of giants. Special thanks to:
Core Dependencies
- Laravel Framework - The PHP framework for web artisans
- Created by Taylor Otwell
- Provides the foundation and container system
- composer-attribute-collector by Olivier Laviale
- Blazing-fast attribute collection via Composer
- Makes attribute discovery incredibly performant
- Symfony Finder - The Symfony PHP framework
- Created by Fabien Potencier
- Provides powerful file system traversal
Inspiration
- Laravel's Package Auto-Discovery - Inspired the automatic registration pattern
- Spatie's Laravel Packages - Set the standard for Laravel package quality
- PHP 8 Attributes - Made modern, declarative PHP possible
Community
- All our contributors
- The Laravel community for feedback and support
- Everyone who reported issues and suggested improvements
Tools & Services
- PHPStan - PHP Static Analysis Tool
- PHPUnit - PHP Testing Framework
- GitHub Actions - CI/CD Platform
- Packagist - PHP Package Repository
📄 License
The MIT License (MIT). Please see LICENSE for more information.
MIT License
Copyright (c) 2026 Pixielity Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
🆘 Support & Resources
Documentation
Community
- 💬 GitHub Discussions - Ask questions, share ideas
- 🐛 GitHub Issues - Report bugs, request features
- 📧 Email Support - Direct support
Stay Updated
**[⬆ Back to Top](#top)** Made with ❤️ by the [Pixielity Team](https://github.com/pixielity-co)pixielity/laravel-discovery 适用场景与选型建议
pixielity/laravel-discovery 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 866 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 08 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「reflection」 「composer」 「attribute」 「dependency-injection」 「laravel」 「discovery」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 pixielity/laravel-discovery 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 pixielity/laravel-discovery 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 pixielity/laravel-discovery 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Library emulating the PHP internal reflection using just the tokenized source code.
A package for automatically encrypting and decrypting Eloquent attributes in Laravel 5.5+, based on configuration settings.
Parse use statements for a reflection object
This composer plugin enables installation of GravityForms WordPress plugin and its addons.
Generate UML class diagrams by reflection for your PHP projects
统计信息
- 总下载量: 866
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 4
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-08