liquidrazor / file-locator
Minimal filesystem discovery component for LiquidRazor.
Requires
- php: ^8.3
- liquidrazor/config-loader: ^0.1.1
README
A lightweight, high-performance filesystem discovery component for the LiquidRazor ecosystem.
This library provides a deterministic, memory-efficient, and extensible way to locate PHP files across a project using convention-first defaults with optional project-level overrides.
It is the first step in the LiquidRazor pipeline:
filesystem → files → classes → descriptors → DI registry
Features: Core Capabilities
- Fast, iterator-based filesystem traversal with low memory use
- Convention-first defaults for
include/,lib/, andsrc/ - YAML-based configuration loaded through
liquidrazor/config-loader - Clean separation of concerns with no DI, reflection, or PHP parsing
- Safe traversal with configurable symlink, hidden file, and unreadable path handling
- Config loading delegated to a dedicated LiquidRazor library
Default Behavior: Built-In Roots And Exclusions
Out of the box, the discovery system scans:
include/lib/src/
With automatic exclusions:
vendor/node_modules/.git/.idea/.vscode/var/cache/
Only .php files are considered.
Configuration: Config Sources
1. Default Configuration (Library)
The library ships with an internal configuration:
resources/config/roots.yaml
This defines the baseline discovery rules.
2. Project Override
You can override or extend discovery behavior by adding:
config/roots.yaml
or
config/roots.yml
Config loading is delegated to liquidrazor/config-loader with YAML selected explicitly.
If both config/roots.yaml and config/roots.yml exist, loading fails instead of silently picking one.
Configuration Structure: Supported YAML Shape
discovery: defaults: follow_symlinks: false include_hidden: false on_unreadable: skip extensions: [php] exclude: - vendor - node_modules - .git roots: include: enabled: true path: include recursive: true extensions: [php] exclude: [] lib: enabled: true path: lib recursive: true extensions: [php] exclude: [] src: enabled: true path: src recursive: true extensions: [php] exclude: []
Adding Custom Roots: Extending Discovery
discovery: roots: modules: path: modules recursive: true
Excluding Paths: Global And Root-Specific Rules
Global exclusions:
discovery: exclude: - storage/tmp
Root-specific exclusions:
discovery: roots: src: exclude: - src/Legacy - src/Experimental
Disabling Default Roots: Removing Built-In Paths
discovery: roots: lib: enabled: false
Config Loading Boundary
FileLocator no longer locates config files by full path, reads raw config contents, parses YAML, merges raw config layers, or interpolates environment variables itself.
Those responsibilities now belong to liquidrazor/config-loader.
DiscoveryConfigFactory only:
- asks ConfigLoader for normalized config arrays using the logical name
roots - validates the discovery schema
- normalizes project-relative paths
- builds
DiscoveryConfig,DiscoveryDefaults, andRootConfig
Merge Strategy: How Config Is Built
Configuration is built as:
library defaults + project overrides → final discovery config
The default resource is loaded from resources/config/roots.yaml.
The optional project override is loaded from the project config root using the logical name roots.
Raw config merge behavior now follows liquidrazor/config-loader:
- Associative arrays → merged recursively
- Scalars → overridden by later config
- Indexed arrays → replaced by later config
After loading, FileLocator still validates the discovery schema and removes disabled roots from the runtime config.
Architecture: Main Layers
The system is intentionally split into layers:
1. Config Layer
DiscoveryConfigFactoryDiscoveryConfigValidatorDiscoveryConfig
2. Discovery Layer
FileLocator- streaming traversal using generators
- zero accumulation by default
3. Future Layers
- Class discovery (static parsing)
- DI registry loading
Usage: Basic Flow
use LiquidRazor\FileLocator\Config\DiscoveryConfigFactory; use LiquidRazor\FileLocator\FileLocator; $factory = new DiscoveryConfigFactory(); $config = $factory->create(__DIR__); $locator = new FileLocator($config); foreach ($locator->locate() as $file) { // process file }
The locator yields files lazily, ensuring minimal memory usage.
Performance Principles: Traversal Behavior
- Depth-first traversal
- Early directory pruning
- Generator-based streaming
- No reflection or file inclusion
- No unnecessary allocations
Safety: Failure And Traversal Controls
- Unreadable paths: configurable (
skiporfail) - Symlinks: disabled by default
- Hidden files: excluded by default
- Deterministic behavior (no implicit magic)
Design Philosophy: Implementation Priorities
- Convention over configuration (but never forced)
- Explicit over implicit
- Small, composable components
- No framework lock-in
- Predictable behavior under load
Role in LiquidRazor: Pipeline Position
This component is the foundation of the DI pipeline:
FileLocator
↓
ClassLocator (future)
↓
RegistryLoader (future)
↓
DIRegistry
Notes: Additional Documentation
- Discovery flow documentation
- Architecture overview documentation
- Configuration system documentation
- File locator API documentation
- Config loading integration documentation
- Development setup documentation
- Implementation guidelines documentation
- Testing strategy documentation
- Supported extension points documentation
License: Package Terms
MIT