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

All Laravel Package

psr-discovery/all

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require psr-discovery/all
    

    Add to composer.json under require-dev if only needed for testing.

  2. First Use Case: Discover a PSR-18 HTTP Client (most common in Laravel):

    use Psr\Discovery\DiscoveryInterface;
    use Psr\Discovery\DiscoveryException;
    use Psr\Http\Client\ClientInterface;
    
    $discovery = new \Psr\Discovery\Discovery();
    $client = $discovery->discover(ClientInterface::class);
    
  3. Where to Look First:

    • PSR Discovery Documentation for implementation-specific guides.
    • Laravel’s config/app.php for default PSR bindings (e.g., HttpClient, Cache).

Implementation Patterns

Core Workflows

  1. Dependency Injection (DI) Integration: Bind discovered services in Laravel’s AppServiceProvider:

    public function register()
    {
        $discovery = new \Psr\Discovery\Discovery();
        $this->app->singleton(ClientInterface::class, fn() => $discovery->discover(ClientInterface::class));
    }
    
  2. Conditional Discovery: Useful for optional features (e.g., caching):

    try {
        $cache = $discovery->discover(\Psr\SimpleCache\CacheInterface::class);
    } catch (DiscoveryException $e) {
        // Fallback to in-memory cache
        $cache = new \Symfony\Component\Cache\Adapter\ArrayAdapter();
    }
    
  3. Testing: Mock discovery in PHPUnit:

    $discovery = $this->createMock(DiscoveryInterface::class);
    $discovery->method('discover')->with(\Psr\Log\LoggerInterface::class)->willReturn($mockLogger);
    

Laravel-Specific Patterns

  1. Service Provider Bootstrapping: Discover and bind PSR services in boot():

    public function boot()
    {
        $discovery = new \Psr\Discovery\Discovery();
        $this->app->when(\App\Services\ApiService::class)
            ->needs(ClientInterface::class)
            ->give(fn() => $discovery->discover(ClientInterface::class));
    }
    
  2. Config-Driven Discovery: Override defaults via config/psr-discovery.php:

    return [
        'http_client' => env('PSR_HTTP_CLIENT', \GuzzleHttp\Client::class),
        'cache' => env('PSR_CACHE', \Stash\Pool::class),
    ];
    

    Then extend Discovery:

    class AppDiscovery extends \Psr\Discovery\Discovery
    {
        public function discover($interface)
        {
            $config = config('psr-discovery');
            if (isset($config[$interface])) {
                return new $config[$interface];
            }
            return parent::discover($interface);
        }
    }
    
  3. Event Dispatcher Integration: Replace Laravel’s event dispatcher with PSR-14:

    $dispatcher = $discovery->discover(\Psr\EventDispatcher\EventDispatcherInterface::class);
    $this->app->singleton(\Illuminate\Contracts\Events\Dispatcher::class, fn() => new \Illuminate\Events\Dispatcher($dispatcher));
    

Gotchas and Tips

Pitfalls

  1. No Guaranteed Implementations:

    • Discovery fails if no compatible PSR implementation is installed (e.g., no guzzlehttp/guzzle for PSR-18).
    • Fix: Use try-catch or provide fallbacks (e.g., MockHttpClient in tests).
  2. Class Loading Order:

    • The first matching class in composer autoload wins. Prioritize dependencies explicitly:
      composer require guzzlehttp/guzzle:^7.0 --sort-packages
      
  3. Laravel’s Built-in PSRs:

    • Laravel already binds some PSRs (e.g., Cache, Log). Overriding may cause conflicts.
    • Tip: Check vendor/laravel/framework/src/Illuminate/Foundation/Providers/FoundationServiceProvider.php for defaults.
  4. Performance:

    • Discovery scans autoloaded classes on every call. Cache results in production:
      $discovery = new \Psr\Discovery\Discovery();
      $client = $discovery->discover(ClientInterface::class);
      $this->app->singleton(ClientInterface::class, fn() => $client);
      

Debugging

  1. Verify Installed Implementations:

    composer show --installed | grep -E 'guzzle|symfony/cache|monolog'
    
  2. Check Autoload:

    composer dump-autoload
    
  3. Discovery Logs: Enable verbose logging to debug missing classes:

    $discovery = new \Psr\Discovery\Discovery();
    $discovery->setLogger(new \Monolog\Logger('discovery', [new \Monolog\Handler\StreamHandler('php://stderr')]));
    

Extension Points

  1. Custom Discovery Strategies: Extend Discovery to support non-PSR interfaces or custom logic:

    class CustomDiscovery extends \Psr\Discovery\Discovery
    {
        public function discover($interface)
        {
            if ($interface === \App\Contracts\CustomInterface::class) {
                return new \App\Services\CustomService();
            }
            return parent::discover($interface);
        }
    }
    
  2. Priority-Based Discovery: Override findImplementation() to enforce priority:

    protected function findImplementation(string $interface): ?string
    {
        $priorities = [
            \Psr\Http\Client\ClientInterface::class => [
                \GuzzleHttp\Client::class,
                \Symfony\Contracts\HttpClient\HttpClientInterface::class,
            ],
        ];
        return $priorities[$interface][0] ?? null;
    }
    
  3. Integration with Laravel Packages: Use discovery in package development to avoid hard dependencies:

    // In your package's service provider
    $this->app->bind(
        \YourPackage\Contracts\ApiClient::class,
        fn() => $this->app->make(\Psr\Discovery\Discovery::class)->discover(\Psr\Http\Client\ClientInterface::class)
    );
    

Laravel-Specific Tips

  1. Artisan Commands: Discover services dynamically in commands:

    protected $httpClient;
    public function __construct()
    {
        $this->httpClient = app(\Psr\Discovery\Discovery::class)->discover(\Psr\Http\Client\ClientInterface::class);
    }
    
  2. Testing with Mocks: Replace discovery in tests:

    $this->app->instance(\Psr\Discovery\DiscoveryInterface::class, $mockDiscovery);
    
  3. Environment-Specific Discovery: Use config('app.env') to switch implementations:

    $discovery = new \Psr\Discovery\Discovery();
    $client = app()->environment('local')
        ? $discovery->discover(\Psr\Http\Client\ClientInterface::class)
        : new \GuzzleHttp\Client(['base_uri' => 'https://api.prod.com']);
    
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky