Weave Code
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

Thrower Laravel Package

camelot/thrower

Utility wrappers that replace PHP’s inconsistent error/warning/notice behavior with exception-based throwing. Simplifies common tasks by ensuring functions fail predictably via exceptions (excluding deprecation warnings).

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require camelot/thrower
    

    No additional configuration is needed—autoloading handles the rest.

  2. First Use Case: Replace a function call that emits PHP errors with its Thrower counterpart. For example, wrap file_get_contents to handle errors as exceptions:

    use Camelot\Throwable\Thrower;
    
    try {
        $content = Thrower::file_get_contents('path/to/file.txt');
    } catch (\RuntimeException $e) {
        // Handle the exception (e.g., log, return HTTP response)
        return response()->json(['error' => $e->getMessage()], 500);
    }
    
  3. Where to Look First:

    • Source Code: Thrower.php lists all wrapped functions.
    • Laravel Integration: Focus on functions used in routes/, app/Http/Controllers/, and app/Providers/ where errors might slip through.

Implementation Patterns

Usage Patterns

  1. Function Wrapping: Replace core PHP functions with Thrower equivalents in:

    • File Operations: file_get_contents(), file_put_contents(), include_once(), require_once().
    • Data Parsing: json_decode(), unserialize().
    • Database Operations: Wrap PDO or Eloquent calls if they emit errors (though Laravel’s ORM typically throws exceptions natively).

    Example:

    // Before (may emit errors)
    $data = json_decode($jsonString);
    
    // After (throws exception on error)
    $data = Thrower::json_decode($jsonString);
    
  2. Error Handling in Controllers: Use Thrower wrappers in controllers to ensure consistent exception-based responses:

    public function uploadFile(Request $request) {
        try {
            $content = Thrower::file_put_contents(
                storage_path('app/uploads/' . $request->file('file')->getClientOriginalName()),
                $request->file('file')->get()
            );
            return response()->json(['success' => true]);
        } catch (\RuntimeException $e) {
            return response()->json(['error' => 'Upload failed'], 500);
        }
    }
    
  3. Global Error Standardization: Enable Thrower globally in bootstrap/app.php to wrap all errors by default:

    Thrower::enable(); // Converts all PHP errors to exceptions
    

    Pair this with Laravel’s App\Exceptions\Handler to render consistent responses.

  4. Custom Wrappers: Extend Thrower for domain-specific needs. For example, wrap a third-party library:

    class LegacyLibraryWrapper {
        public static function safeCall($method, ...$args) {
            return Thrower::wrapError(function() use ($method, $args) {
                return call_user_func_array($method, $args);
            });
        }
    }
    
  5. CLI and Artisan Commands: Use Thrower in CLI scripts to ensure exceptions are caught and logged:

    use Camelot\Throwable\Thrower;
    
    try {
        $result = Thrower::exec('ls /nonexistent');
    } catch (\RuntimeException $e) {
        Log::error('Command failed: ' . $e->getMessage());
        exit(1);
    }
    

Integration Tips

  • Laravel Exception Handler: Ensure App\Exceptions\Handler catches Throwable\ErrorException to log or render errors consistently:

    public function render($request, Throwable $exception) {
        if ($exception instanceof \Throwable\ErrorException) {
            // Custom logic for wrapped errors
        }
        return parent::render($request, $exception);
     }
    
  • Testing: Mock Thrower wrappers in PHPUnit to test error scenarios:

    public function testFileReadFails() {
        $this->expectException(\RuntimeException::class);
        Thrower::file_get_contents('nonexistent.txt');
    }
    
  • Performance: Benchmark wrapped functions in high-traffic endpoints (e.g., APIs) to ensure minimal overhead.


Gotchas and Tips

Pitfalls

  1. Double Error Handling:

    • Risk: If Laravel’s default error handler (e.g., App\Exceptions\Handler) and Thrower both process errors, exceptions may be thrown twice.
    • Fix: Disable Laravel’s default error handler during pilot testing or configure Thrower to skip certain errors:
      Thrower::ignore(E_DEPRECATED); // Skip deprecation warnings
      
  2. Lost Error Context:

    • Wrapped exceptions may lose original error details (e.g., file name, line number). Use Throwable::wrapError() with a callback to preserve context:
      $result = Thrower::wrapError(function() {
          return file_get_contents('file.txt');
      }, function($error) {
          return new \RuntimeException("Failed to read file.txt: {$error->getMessage()}", 0, $error);
      });
      
  3. PHP Version Incompatibility:

    • The package assumes PHP 7.0+ features (e.g., Throwable). Test thoroughly on PHP 8.x to avoid issues with deprecated functions.
  4. Static Class Limitations:

    • Thrower is a static class, making it hard to mock in tests. Consider dependency injection for critical paths:
      // Instead of:
      $content = Thrower::file_get_contents('file.txt');
      
      // Use a service:
      $fileService = new FileService();
      $content = $fileService->read('file.txt');
      
  5. Missing Wrappers:

    • Not all error-prone functions are wrapped. For unsupported functions, use Throwable::wrapError():
      $result = Thrower::wrapError(function() {
          return some_unsupported_function();
      });
      

Debugging Tips

  1. Enable Verbose Logging: Configure Thrower to log wrapped errors for debugging:

    Thrower::setLogger(function($error) {
        Log::debug('Wrapped error: ' . $error->getMessage(), ['file' => $error->getFile(), 'line' => $error->getLine()]);
    });
    
  2. Check Error Ignore List: If errors aren’t being caught, verify the ignore list:

    Thrower::ignore(E_NOTICE); // Ensure this isn’t suppressing critical errors
    
  3. Test Edge Cases:

    • Test with E_USER_ERROR, E_USER_WARNING, and E_USER_NOTICE to ensure consistent behavior.
    • Simulate memory limits or timeouts to verify exception handling.

Configuration Quirks

  1. Global Enable/Disable: Use Thrower::enable()/Thrower::disable() to toggle error wrapping globally. Disable in production if using custom error handlers:

    if (app()->environment('production')) {
        Thrower::disable();
    }
    
  2. Custom Exception Classes: Override the default exception class for wrapped errors:

    Thrower::setExceptionClass(\App\Exceptions\CustomErrorException::class);
    
  3. Error Code Mapping: Map PHP error codes to custom exceptions:

    Thrower::mapError(E_USER_WARNING, \App\Exceptions\WarningException::class);
    

Extension Points

  1. Add Custom Wrappers: Extend Thrower by adding new static methods. For example, wrap fopen():

    public static function fopen($path, $mode) {
        return self::wrapError(function() use ($path, $mode) {
            return fopen($path, $mode);
        });
    }
    
  2. Integrate with Laravel Events: Listen for wrapped exceptions in Laravel’s event system:

    Event::listen(\Throwable::class, function($exception) {
        if ($exception instanceof \Throwable\ErrorException) {
            // Custom logic for wrapped errors
        }
    });
    
  3. Custom Error Transformers: Use Throwable::wrapError() with a transformer callback to enrich exceptions:

    $result = Thrower::wrapError(function() {
        return some_risky_operation();
    }, function($error) {
        return new \RuntimeException(
            "Operation failed: {$error->getMessage()}",
            $error->getCode(),
            $error
        );
    });
    
  4. Environment-Specific Behavior: Conditionally enable/disable wrappers based on the environment:

    if (app()->environment(['local', 'staging'])) {
        Thrower::enable();
    }
    
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.
croct/coding-standard
croct/plug-php
nqxcode/phpmorphy
boundwize/pyrameter
testo/facade
headercat/phpstan-extension-ide-helper
yosymfony/parser-utils
innmind/black-box
babenkoivan/elastic-migrations
babenkoivan/elastic-adapter
develia/commons
dmstr/symfony-system-resources-bundle
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
renatomarinho/laravel-page-speed
develia/geo-bundle
austinheap/laravel-database-encryption
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle