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

Picotainer Laravel Package

mouf/picotainer

Picotainer is a tiny (24 lines) dependency injection container for PHP, inspired by Pimple and compatible with container-interop/PSR-11. Define entries as closures, support delegate lookup, and retrieve services with a minimalist API.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:
    composer require mouf/picotainer
    
  2. Basic Container Initialization:
    use Mouf\Picotainer\Picotainer;
    
    $container = new Picotainer([
        'logger' => function () {
            return new \Monolog\Logger('name');
        },
        'database' => function () {
            return new \Illuminate\Database\Connection();
        }
    ]);
    
  3. First Use Case: Inject dependencies into a class:
    class UserService {
        private $logger;
        private $database;
    
        public function __construct($logger, $database) {
            $this->logger = $logger;
            $this->database = $database;
        }
    }
    
    $userService = $container->get('userService', function ($c) {
        return new UserService($c->get('logger'), $c->get('database'));
    });
    

Where to Look First

  • Picotainer class: Core container logic (24 lines).
  • get() method: Primary entry point for fetching dependencies.
  • Anonymous function signatures: Must accept ContainerInterface for recursive dependency resolution.
  • Container-Interop docs: Delegate lookup for advanced use cases.

Implementation Patterns

Core Workflows

1. Basic Dependency Injection

$container = new Picotainer([
    'userRepository' => function ($c) {
        return new UserRepository($c->get('dbConnection'));
    },
    'dbConnection' => function () {
        return new PDO('mysql:host=localhost;dbname=test');
    }
]);

$userRepo = $container->get('userRepository');

2. Delegate Lookup (Container-Interop)

// Laravel's container as delegate
$laravelContainer = app();
$picotainer = new Picotainer([
    'picotainerService' => function ($c) {
        // $c here is the Laravel container
        return new PicotainerService($c->make('auth'));
    }
], $laravelContainer);

3. Scalar Values and Constants

$container = new Picotainer([
    'appName' => function () {
        return 'MyApp';
    },
    'debugMode' => function () {
        return env('APP_DEBUG', false);
    }
]);

4. Laravel Integration (Hybrid Approach)

// In a service provider
public function register() {
    $this->app->singleton('picotainer', function () {
        $picotainer = new Picotainer([
            'picotainerLogger' => function ($c) {
                return new \Monolog\Logger('picotainer');
            }
        ], $this->app); // Delegate to Laravel's container

        return $picotainer;
    });
}

// Usage in a command
public function handle() {
    $logger = app('picotainer')->get('picotainerLogger');
    $logger->info('Running Picotainer service...');
}

Integration Tips

Avoiding Laravel-Specific Pitfalls

  • No singleton() method: Manually manage singletons via closures:
    $container = new Picotainer([
        'config' => function () {
            return new Config(); // Singleton instance
        }
    ]);
    
  • No bindIf(): Use conditional logic inside closures:
    $container = new Picotainer([
        'cache' => function () {
            return env('CACHE_DRIVER') === 'redis'
                ? new RedisCache()
                : new FileCache();
        }
    ]);
    

Testing with Picotainer

public function testUserService()
{
    $container = new Picotainer([
        'userRepository' => function () {
            return $this->createMock(UserRepository::class);
        }
    ]);

    $service = $container->get('userService');
    // Test logic...
}

Extending Picotainer

class CustomPicotainer extends Picotainer {
    public function singleton($id, Closure $closure) {
        $this[$id] = function () use ($closure) {
            static $instance;
            return $instance ?? ($instance = $closure($this));
        };
        return $this;
    }
}

Gotchas and Tips

Pitfalls

  1. No Built-in Singleton Management

    • Issue: Picotainer does not track singletons by default. Repeated calls to get() with the same key may create duplicate instances.
    • Fix: Manually implement singleton logic in closures or extend Picotainer:
      $container = new Picotainer([
          'config' => function () {
              static $instance;
              return $instance ?? ($instance = new Config());
          }
      ]);
      
  2. Delegate Lookup Ambiguity

    • Issue: When using delegate lookup, the $container parameter in closures refers to the delegate container, not Picotainer itself. This can cause confusion when accessing Picotainer-specific entries.
    • Fix: Use distinct keys or document delegate behavior clearly.
  3. No has() Method for Non-Existent Keys

    • Issue: Calling has() on a non-existent key throws an exception (fixed in v1.0.1 but may still cause confusion).
    • Fix: Always check isset($container[$key]) or wrap get() in a try-catch.
  4. Lack of Laravel-Specific Features

    • Issue: Missing tag(), contextualBinding(), and other Laravel container features.
    • Fix: Implement custom wrappers or avoid Picotainer for Laravel-specific use cases.
  5. No Automatic Wiring

    • Issue: Unlike Laravel’s container, Picotainer does not automatically resolve constructor arguments.
    • Fix: Manually define all dependencies in closures:
      $container = new Picotainer([
          'userService' => function ($c) {
              return new UserService(
                  $c->get('userRepository'),
                  $c->get('logger')
              );
          }
      ]);
      

Debugging Tips

  1. Inspect Container Contents

    print_r($container->get('key')); // Debug values
    var_dump($container->has('key')); // Check existence
    
  2. Override Dependencies for Testing

    $container = new Picotainer([
        'userRepository' => function () {
            return $this->createMock(UserRepository::class);
        }
    ]);
    
  3. Handle Circular Dependencies

    • Issue: Circular dependencies (e.g., A depends on B, which depends on A) will cause infinite recursion.
    • Fix: Restructure dependencies or use lazy-loading patterns.

Extension Points

  1. Custom Container Classes Extend Picotainer to add Laravel-like methods:

    class LaravelPicotainer extends Picotainer {
        public function singleton($id, Closure $closure) {
            $this[$id] = function () use ($closure) {
                static $instance;
                return $instance ?? ($instance = $closure($this));
            };
            return $this;
        }
    }
    
  2. PSR-11 Adapter for Laravel Create a wrapper to use Picotainer as Laravel’s container:

    class PicotainerAdapter implements ContainerInterface {
        protected $picotainer;
    
        public function __construct(Picotainer $picotainer) {
            $this->picotainer = $picotainer;
        }
    
        public function get($id) {
            return $this->picotainer->get($id);
        }
    
        public function has($id) {
            return isset($this->picotainer[$id]);
        }
    }
    
  3. Dynamic Binding Use closures to dynamically bind services:

    $container = new Picotainer([
        'cache' => function () {
            return Cache::store(config('cache.default'));
        }
    ]);
    

Configuration Quirks

  1. Closure Scope

    • Closures in Picotainer do not share scope with the surrounding code. Use use to capture variables:
      $userId = 1;
      $container = new Picotainer([
          'user' => function () use ($userId) {
              return User::find($userId);
          }
      ]);
      
  2. Delegate Lookup Overrides

    • If Picotainer is used as a delegate, its own entries may not be accessible via the delegate container. Always use Picotainer’s get() method for its own keys.
  3. Performance Considerations

    • Picotainer is not optimized for high-scale applications. For Laravel apps, prefer the built-in container for production use.
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
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
imbo/imbo-coding-standard
visualbuilder/filament-lottie
servicioslineaonce/starter-kit
atomcoder/laravel-reorderable