Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filesystem Laravel Package

symfony/filesystem

Symfony Filesystem provides basic utilities for working with files and directories: creating, copying, moving, and removing files, dumping content safely, making paths relative, and normalizing paths. It's a lightweight, standalone component usable in any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/filesystem
    

    Add to composer.json under require (pick the line that matches your Symfony/Laravel version):

    "symfony/filesystem": "^8.0"
    
  2. First Use Case: Use the Filesystem class for basic file/directory operations:

    use Symfony\Component\Filesystem\Filesystem;
    
    $fs = new Filesystem();
    
    // Create a directory
    $fs->mkdir('path/to/directory');
    
    // Copy a file (source permissions are preserved as of v5.4.54)
    $fs->copy('source.txt', 'destination.txt');
    
    // Remove a file
    $fs->remove('file.txt');
    
  3. Where to Look First:

    • Official Documentation
    • Symfony\Component\Filesystem\Filesystem class methods (e.g., mkdir, copy, remove, dumpFile, exists, mirror, makePathRelative).
    • Symfony\Component\Filesystem\Path class for path manipulation (e.g., normalize, getFilename, isAbsolute, makeRelative, canonicalize).
  4. Upgrade Note (v5.4.54):

    • This is a [SECURITY] patch release. Bump the constraint on your current Symfony line (^5.4.54, ^6.4.x, ^7.x, or the equivalent ^8.0.x) and run composer update symfony/filesystem before relying on the copy/temp-file behaviour described below.

Implementation Patterns

Core Workflows

1. File and Directory Management

  • Atomic Writes: Use dumpFile() to write files atomically, reducing race conditions:
    $fs->dumpFile('config.json', json_encode($config));
    
  • Recursive Operations: Use mirror() to replicate directory structures:
    $fs->mirror('source_dir', 'destination_dir');
    
  • Copying Preserves Source Mode (v5.4.54+): copy() now keeps the source file's permission bits instead of falling back to umask defaults. If you were relying on the old behaviour, apply an explicit chmod() after the copy:
    $fs->copy($source, $target);
    $fs->chmod($target, 0644); // only if you need to *narrow* what the source had
    

2. Path Manipulation

  • Normalize Paths: Ensure cross-platform compatibility:
    $normalizedPath = Path::normalize('/path/with/../segments');
    
  • Relative Paths: Convert absolute paths to relative using Path::makeRelative() (or Filesystem::makePathRelative() when you need the trailing-slash style used internally by mirror()):
    $relativePath = Path::makeRelative('/var/www/html/storage', '/var/www/html');
    

3. Permissions and Ownership

  • Set Permissions: Use chmod() for secure file access:
    $fs->chmod('file.txt', 0644);
    
  • Change Ownership: Use chown() (Linux/macOS only):
    $fs->chown('file.txt', 1000);
    

4. Temporary Files

  • Create and Clean Up Temp Files:
    $tempFile = tempnam(sys_get_temp_dir(), 'prefix');
    file_put_contents($tempFile, 'data');
    $fs->remove($tempFile); // Cleanup
    
  • Private Temp Files When a Suffix Is Used (v5.4.54+): the component no longer leaves tempnam() output world-readable when a suffix is appended; the resulting file is created with restrictive (0600) permissions. This means a temp file handed to another process/user (e.g. a queue worker running as a different user) may now be unreadable — chmod() it explicitly if that sharing is intentional:
    $fs->chmod($tempFile, 0644); // only when another user must read it
    

5. Integration with Laravel

  • Service Provider Binding:
    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->singleton(Filesystem::class, function () {
            return new Filesystem();
        });
    }
    
  • Artisan Commands:
    use Symfony\Component\Filesystem\Filesystem;
    
    class BackupCommand extends Command
    {
        protected $fs;
    
        public function __construct(Filesystem $fs)
        {
            parent::__construct();
            $this->fs = $fs;
        }
    
        public function handle()
        {
            $this->fs->mirror(storage_path('app'), backup_path('app'));
        }
    }
    

6. Handling Edge Cases

  • Check Existence Before Operations:
    if ($fs->exists('file.txt')) {
        $fs->remove('file.txt');
    }
    
  • Handle Errors Gracefully:
    try {
        $fs->remove('nonexistent_file.txt');
    } catch (\RuntimeException $e) {
        // Log or handle error
    }
    

Gotchas and Tips

Pitfalls

  1. Windows vs. UNIX Path Handling:

    • Issue: Path methods may incorrectly replace backslashes on UNIX systems (fixed in v8.0.9).
    • Fix: Always use Path::normalize() to ensure consistency:
      $path = Path::normalize('C:\Users\file.txt'); // Converts to forward slashes
      
  2. Permission Denied Errors:

    • Issue: Operations may fail silently or throw exceptions on restricted directories.
    • Fix: Use try-catch blocks and log errors:
      try {
          $fs->chmod('protected_file.txt', 0644);
      } catch (\RuntimeException $e) {
          Log::error('Permission denied: ' . $e->getMessage());
      }
      
  3. Temp File Cleanup on Windows:

    • Issue: Temp files may not delete properly on Windows (fixed in v7.1.5).
    • Fix: Use Filesystem::remove() explicitly:
      $fs->remove($tempFile);
      
  4. Temp Files Are Now Private by Default (v5.4.54):

    • Issue: tempnam()-style files created with a suffix are no longer world-readable, so cross-user / cross-container reads that used to "just work" can start failing with Permission denied.
    • Fix: Make the sharing explicit rather than relying on the old permissive mode:
      $fs->chmod($tempFile, 0644);
      
  5. Copy Now Inherits the Source Mode (v5.4.54):

    • Issue: A copy of a 0600 secret file (e.g. .env) is now also 0600 — good for security, but it may break a deploy step that expected umask defaults (0644) on the destination. Conversely, copying a 0777 file propagates that looseness.
    • Fix: Set the destination mode explicitly after copy() when the mode matters.
  6. Atomic Writes and Race Conditions:

    • Issue: Concurrent writes to the same file can corrupt data.
    • Fix: Use dumpFile() for atomic writes:
      $fs->dumpFile('log.txt', $newContent, 0644);
      
  7. Path Injection Risks:

    • Issue: User-provided paths may lead to directory traversal attacks.
    • Fix: Canonicalize and validate paths before use:
      $path = Path::canonicalize($userInputPath);
      
      if (!Path::isAbsolute($path)) {
          throw new \InvalidArgumentException('Invalid path');
      }
      

Debugging Tips

  1. Wrap and Log Every Operation:

    • There is no built-in debug/verbosity switch; log around the call sites instead:
      try {
          $fs->copy($source, $target);
      } catch (\RuntimeException $e) {
          Log::error('copy failed', ['source' => $source, 'target' => $target, 'error' => $e->getMessage()]);
          throw $e;
      }
      
  2. Check Path Normalization:

    • Debug path issues with:
      $normalized = Path::normalize('/path/with/../segments');
      dd($normalized);
      
  3. Inspect Modes After Copy/Temp Operations:

    • Since v5.4.54 both copy() and suffixed tempnam() files carry source-side permissions — verify with fileperms() when a permission bug is suspected:
      dump(decoct(fileperms($target) & 0777));
      
  4. Handle Windows-Specific Quirks:

    • Use str_replace('\\', '/', $path) as a fallback for legacy systems:
      $path = str_replace('\\', '/', $path);
      $normalized = Path::normalize($path);
      

Extension Points

  1. Custom Filesystem Adapter:

    • Extend Filesystem to add custom logic:
      class CustomFilesystem extends Filesystem
      {
          public function copyPrivate(string $origin, string $target): void
          {
              $this->copy($origin, $target);
              $this->chmod($target, 0600);
          }
      }
      
  2. Wrap Operations in a Service:

    • The component dispatches no filesystem events, so centralise instrumentation in your own decorator and bind it in the container:
      class LoggingFilesystem extends Filesystem
      {
          public function copy(string $origin, string $target, bool $overwrite = false): void
          {
              event(new FilesystemOperationEvent('copy', [$origin, $target]));
              parent::copy($origin, $target, $overwrite);
          }
      }
      
  3. Integration with Laravel Events:

    • Dispatch events for critical filesystem operations:
      event(new FilesystemOperationEvent('copy', ['source.txt', 'destination.txt']));
      

Configuration Quirks

  1. No Runtime Symlink Toggle:

    • mirror() has a copy_on_windows option; there is no disableSymlinks() switch. Control symlink handling per call instead:
      $fs->mirror('source_dir', 'destination_dir', null, ['copy_on_windows' => true]);
      
  2. Custom Temp Directory:

    • Override the temp directory for testing:
      putenv('TMPDIR=' . storage_path('framework/tests'));
      
  3. Case-Insensitive Paths (Windows):

    • Normalize paths to avoid case-sensitivity issues:
      $normalized = Path::normalize(strtolower($path));
      

Performance Tips

  1. Batch Operations:

    • Use Filesystem::remove() with an array for multiple files:
      $fs->remove(['file1.txt', 'file2.txt']);
      
  2. Avoid Redundant Checks:

    • Cache existence checks if paths are static:
      if ($fs->exists($path)) {
          // Process file
      }
      
  3. Use mirror() for Large Directories:

    • Efficiently replicate directory structures:
      $fs->mirror('source_dir', 'destination_dir', null, ['override' => true]);
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route