php-standard-library/filesystem
Type-safe filesystem utilities for PHP Standard Library. Perform common file and directory operations with consistent APIs and proper exception handling, improving safety and clarity over raw PHP functions. Documentation and contribution links included.
## Technical Evaluation
### **Architecture Fit**
- **Laravel Compatibility**: Remains aligned with Laravel’s filesystem abstractions, but **6.1.1 introduces a new `FilesystemEvent` dispatcher** for observing file operations (e.g., `fileCreated`, `fileDeleted`). This could integrate with Laravel’s event system (e.g., `Event::dispatch()`) for cross-cutting concerns like logging or caching.
- **Separation of Concerns**: The package’s modularity is reinforced with **new `PathResolver` utilities** (e.g., `resolveRelativeToProjectRoot()`), which could replace ad-hoc path logic in Laravel services/jobs, further reducing inconsistency.
- **CLI/Script Integration**: Unchanged, but the **new `BatchProcessor`** (for bulk file operations) is a direct fit for Laravel’s queue workers or Artisan commands processing large datasets.
### **Integration Feasibility**
- **Low Friction**: Still dependency-light, but **6.1.1 adds optional `symfony/event-dispatcher` as a dependency** for the event system. This may require Composer dependency resolution adjustments if Symfony’s event dispatcher isn’t already in use.
- **API Familiarity**: The **new `FilesystemEvent` methods** (`dispatchOnRead()`, `dispatchOnWrite()`) mirror Laravel’s event patterns, easing adoption for teams already using `Event::listen()`.
- **Cross-Environment Safety**: Unchanged, but the **new `PathResolver`** includes OS-agnostic path normalization, reducing edge-case bugs in multi-environment Laravel deployments.
### **Technical Risk**
- **Overlap with Laravel Core**: **New risk**: The `FilesystemEvent` dispatcher could conflict with Laravel’s existing event system if not scoped. For example, dispatching events for every file operation might overload Laravel’s event queue.
- **Error Handling**: Unchanged, but the **new `BatchProcessor`** introduces aggregated exceptions (`BatchOperationFailedException`), which may require custom mapping to Laravel’s `FilesystemException`.
- **Performance**: The **new `BatchProcessor`** could introduce overhead for bulk operations. Benchmark against Laravel’s `Storage::disk()->files()` + manual iteration.
- **Future-Proofing**:
- **Breaking Change**: The `FilesystemEvent` dispatcher is **opt-in** but may become mandatory in future versions. Monitor for deprecation of non-event-based methods.
- **Symfony Dependency**: The `symfony/event-dispatcher` dependency could conflict with Laravel’s internal event system if not managed carefully (e.g., via Composer’s `replace` or aliasing).
### **Key Questions**
1. **Event System Integration**:
- Should we integrate `FilesystemEvent` with Laravel’s event system globally, or restrict it to specific services (e.g., only for audit logging)?
- How will we handle event collisions (e.g., duplicate listeners for `fileCreated`)?
2. **Batch Processing**:
- Will the `BatchProcessor` replace Laravel’s manual loops (e.g., `foreach (Storage::allFiles() as $file)`), or supplement them for performance-critical paths?
3. **Dependency Management**:
- How will we handle the new `symfony/event-dispatcher` dependency to avoid conflicts with Laravel’s event system?
- Should we use Composer’s `replace` or alias the Symfony dispatcher to Laravel’s `Illuminate/Events`?
4. **Testing**:
- Does the new `BatchProcessor` include tests for failure scenarios (e.g., partial failures in bulk operations)?
- How will we test the `FilesystemEvent` dispatcher in Laravel’s test suite (e.g., mocking events)?
5. **Path Resolution**:
- Should we adopt the new `PathResolver` utilities globally, or only for new features to avoid migration overhead?
---
## Integration Approach
### **Stack Fit**
- **Primary Use Cases (Updated)**:
- **Event-Driven Workflows**: Use `FilesystemEvent` for cross-cutting concerns like:
- Logging file operations (e.g., `fileCreated` → log to `storage/logs/filesystem.log`).
- Caching file metadata (e.g., `fileUpdated` → invalidate Redis cache).
- Notifications (e.g., `fileDeleted` → Slack alert).
- **Batch Processing**: Replace manual loops in queue workers or Artisan commands with `BatchProcessor` for:
- Processing 1K+ files (e.g., image resizing, CSV imports).
- Parallel operations (e.g., `BatchProcessor::process($files, 10)` for concurrency).
- **Path Resolution**: Use `PathResolver` in services to standardize path handling (e.g., `resolveRelativeToProjectRoot('config/cache')`).
- **Avoid Overlap**:
- **Do not** use `FilesystemEvent` for core Laravel storage operations (e.g., `storage/app/`). Stick to Laravel’s `Storage` facade.
- **Do not** replace Laravel’s `Storage` disk drivers for S3/Cloud storage. Use `Storage::disk('s3')->` as before.
### **Migration Path**
1. **Phase 1: Pilot Event System (Optional)**
- Start with a single service (e.g., `FileBackupService`) to test `FilesystemEvent` integration.
- Example:
```php
$filesystem = new Filesystem();
$filesystem->on('fileCreated', function ($event) {
Event::dispatch(new FileCreated($event->getPath()));
});
```
- Validate event propagation in Laravel’s context (e.g., no duplicate listeners).
2. **Phase 2: Adopt Batch Processor**
- Replace manual loops in queue jobs or Artisan commands with `BatchProcessor`.
- Example:
```php
// Before
foreach (Storage::allFiles('public/uploads') as $file) {
processFile($file);
}
// After
$batch = new BatchProcessor();
$batch->process(Storage::allFiles('public/uploads'), 20, function ($file) {
processFile($file);
});
```
3. **Phase 3: Standardize Path Resolution**
- Update services to use `PathResolver` for path construction.
- Example:
```php
// Before
$path = base_path('storage/app/temp/' . $id . '.txt');
// After
$path = (new PathResolver())->resolveRelativeToProjectRoot("storage/app/temp/{$id}.txt");
```
4. **Phase 4: Dependency Management**
- Add `symfony/event-dispatcher` to `composer.json` with an alias to avoid conflicts:
```json
"extra": {
"laravel": {
"dont-discover": ["symfony/event-dispatcher"]
}
}
```
- Or use Composer’s `replace`:
```json
"replace": {
"symfony/event-dispatcher": "illuminate/events"
}
```
(Note: This may require custom event dispatcher bridging.)
### **Compatibility**
- **Event System Integration**:
- Bridge the package’s `FilesystemEvent` to Laravel’s `Event` facade:
```php
use PHPStandardLibrary\Filesystem\Events\FilesystemEvent;
use Illuminate\Support\Facades\Event;
$filesystem->on('fileCreated', function ($event) {
Event::dispatch(new \App\Events\FileCreated(
$event->getPath(),
$event->getContent()
));
});
```
- **Batch Processor**:
- Use Laravel’s queue system for parallel batch processing:
```php
$batch->process($files, 10, function ($file) {
ProcessFileJob::dispatch($file)->onQueue('files');
});
```
- **PathResolver**:
- Combine with Laravel’s path helpers:
```php
$resolver = new PathResolver();
$path = $resolver->resolveRelativeToProjectRoot(
'config/' . config('app.env') . '.php'
);
```
### **Sequencing**
1. **Add Dependencies**:
```json
"require": {
"php-standard-library/filesystem": "^6.1.1",
"symfony/event-dispatcher": "^6.0" // Only if not using Laravel's events
}
EventServiceProvider:
protected $listen = [
\App\Events\FileCreated::class => [
\App\Listeners\LogFileCreation::class,
],
];
FilesystemEvent for audit-critical paths").BatchProcessor vs. manual loops.PathResolver for new code).file_put_contents() calls to logs.Event::fake() in tests.How can I help you explore Laravel packages today?