承接 componenta/class-finder 相关项目开发

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

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

componenta/class-finder

Composer 安装命令:

composer require componenta/class-finder

包简介

Lazy class discovery with composable filters and listener notification

README 文档

README

Lazy PHP declaration discovery with composable filters and listener notification.

ClassFinder scans PHP files, extracts named class/interface/trait/enum declarations through componenta/tokenizer, applies filters, and returns a replayable iterator of ClassInfo metadata.

Installation

composer require componenta/class-finder

Requirements

  • PHP 8.4+
  • symfony/finder
  • componenta/tokenizer
  • componenta/filter
  • componenta/arrayable
  • componenta/iterator
  • psr/container
  • psr/log

Related Packages

Package Why it matters here
componenta/tokenizer Parses PHP source and returns ClassInfo declarations.
symfony/finder Walks directories and selects PHP files.
componenta/iterator Provides replayable iteration over discovered declarations.
componenta/filter Provides composable filters used by the finder.
componenta/app and *-app packages Run class discovery while building application cache.

Quick Start

use Componenta\ClassFinder\ClassFinder;
use Componenta\ClassFinder\Filter\InstantiableFilter;
use Componenta\ClassFinder\Filter\PatternFilter;

$finder = new ClassFinder([
    PatternFilter::endsWith('Controller'),
    new InstantiableFilter(),
]);

$controllers = $finder->find(__DIR__ . '/src', exclude: ['tests']);

foreach ($controllers as $file => $classInfo) {
    echo $classInfo->fullyQualifiedName . PHP_EOL;
}

Search Modes

find() accepts the tokenizer search bitmask directly:

use Componenta\Tokenizer\TokenizerInterface;

$classes = $finder->find('src/', mode: TokenizerInterface::SEARCH_CLASSES);
$contracts = $finder->find(
    'src/',
    mode: TokenizerInterface::SEARCH_INTERFACES | TokenizerInterface::SEARCH_TRAITS,
);

Search mode is a per-call argument. It is not a DI configuration key.

ClassIterator

ClassFinder::find() returns ClassIteratorInterface: lazy, replayable, countable, arrayable, and filterable.

$classes = $finder->find('src/');

$classes->count();   // forces traversal once and caches the count
$classes->toArray(); // list<ClassInfo>

$filtered = $classes->withFilter(PatternFilter::namespace('App\\Http'));

The iterator caches traversed declarations so it can be iterated more than once.

Pattern Filters

PatternFilter matches ClassInfo metadata without reflection.

use Componenta\ClassFinder\Filter\PatternFilter;

new PatternFilter('*Controller');        // class name suffix
new PatternFilter('User*');              // class name prefix
new PatternFilter('App\\User');          // exact fully-qualified class name
new PatternFilter('*\\Api\\*Controller'); // fully-qualified wildcard pattern

PatternFilter::exactMatch('UserController');
PatternFilter::namespace('App\\Http');              // namespace and children
PatternFilter::exactNamespace('App\\Http\\Admin');  // exact namespace only
PatternFilter::exactFqn('App\\Http\\UserController');
PatternFilter::fqn('App\\*\\*Controller');
PatternFilter::in(['UserController', 'PostController']);

Use exactNamespace() when the input is a namespace without wildcard. A bare string containing \ is treated as a fully-qualified class name or FQN pattern.

Reflection Filters

Some filters require the declaration to be loaded because they use ClassInfo::$reflector:

  • AttributeSearchFilter
  • AttributePatternFilter
  • AnyAttributeFilter
  • HasAnyAttributesFilter
  • ImplementsFilter
  • ImplementsAnyFilter
  • SubclassFilter

These filters are appropriate when scanned classes are autoloadable. For pure source inspection of unloaded files, prefer metadata-only filters such as PatternFilter, InstantiableFilter, IsAbstractFilter, and IsFinalFilter.

Attribute Filters

use Componenta\ClassFinder\Filter\AttributePatternFilter;
use Componenta\ClassFinder\Filter\AttributeSearchFilter;
use Componenta\ClassFinder\Filter\AnyAttributeFilter;

AttributeSearchFilter::hasAttribute(Route::class);
AttributeSearchFilter::hasAnyAttribute([Route::class, Command::class]);
AttributeSearchFilter::hasAllAttributes([Cache::class, Validate::class]);
AttributeSearchFilter::hasAttribute(Inject::class, deepSearch: true);

new AttributePatternFilter('*Attribute', deepSearch: true);
AttributePatternFilter::attributePrefix('App\\Attribute\\');

new AnyAttributeFilter([Route::class, Command::class], deepSearch: true);

deepSearch: true also checks methods, properties, and constants.

Listeners

Listeners are notified for each accepted declaration. Finalizable listeners are finalized after scanning, even when no declarations were found.

use Componenta\ClassFinder\ClassListenerInterface;
use Componenta\ClassFinder\FinalizableListenerInterface;
use Componenta\Tokenizer\ClassInfo;

final class RouteCollector implements FinalizableListenerInterface
{
    /** @var list<class-string> */
    private array $routes = [];

    public function handle(ClassInfo $info): void
    {
        if ($info->reflector->getAttributes(Route::class) !== []) {
            $this->routes[] = $info->fullyQualifiedName;
        }
    }

    public function finalize(): void
    {
        // Build final registry or cache.
    }
}

ClassListenerNotifier snapshots the provider's listeners once per notify() call, so the same listener instances receive handle() and finalize().

Compile Integration

Packages that collect metadata through listeners can expose compilers without depending on an application runner:

use Componenta\ClassFinder\Compile\CompileResult;
use Componenta\ClassFinder\Compile\ListenerCompilerInterface;

final class RouteCollectorCompiler implements ListenerCompilerInterface
{
    public function supports(object $listener): bool
    {
        return $listener instanceof RouteCollector;
    }

    public function compile(object $listener, string $cacheDir): CompileResult
    {
        return CompileResult::filesOnly([
            $cacheDir . '/routes.cache.php' => '<?php return [];',
        ]);
    }
}

Register compiler class names under Componenta\ClassFinder\Compile\ConfigKey::LISTENER_COMPILERS. The host application decides when discovery runs and where sidecar files are written.

Container Integration

Register the package provider in a PSR-11 compatible container:

$config = (new Componenta\ClassFinder\ConfigProvider())();

Runtime configuration keys are defined in Componenta\ClassFinder\ConfigKey:

Constant Value Description
ConfigKey::FILTERS Componenta\ClassFinder:filters Default FilterInterface instances for ClassFinderFactory.
ConfigKey::LISTENERS Componenta\ClassFinder:listeners Listener service ids or ClassListenerInterface instances.

Listener config is fail-fast: every entry must be a listener instance or a service id string resolving to ClassListenerInterface.

License

MIT

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-14

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固