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

Props Dic Laravel Package

mrclay/props-dic

A lightweight PHP DI container that exposes services as typed properties and factory methods ($c->foo, $c->new_foo()), letting you add @property/@method PHPDoc for IDE autocomplete and static analysis. Supports lazy resolution, caching, and factories.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the Package:

    composer require mrclay/props-dic
    
  2. Create a Container Subclass: Define a container class extending Props\Container with @property-read and @method PHPDoc annotations.

    <?php
    namespace App\Services;
    
    use Props\Container;
    
    /**
     * @property-read UserRepository $users
     * @method        User new_user()
     */
    class AppContainer extends Container
    {
        public function __construct()
        {
            $this->users = function (AppContainer $c) {
                return new UserRepository();
            };
    
            $this->setFactory('user', User::class);
        }
    }
    
  3. Use the Container: Access dependencies via properties or methods with IDE autocompletion.

    $container = new AppContainer();
    
    // IDE knows this is a UserRepository instance
    $userRepo = $container->users;
    
    // Get a fresh User instance
    $user = $container->new_user();
    
  4. Integrate with Laravel (Optional): Bind the container to Laravel’s service container for hybrid use:

    $app->singleton(AppContainer::class, function () {
        return new AppContainer();
    });
    

First Use Case

Replace a generic DI call in a Laravel controller with a typed property:

// Before (generic)
public function index()
{
    $users = app()->get('App\Services\UserRepository');
    // No IDE hints, magic string
}

// After (Props)
public function index(AppContainer $container)
{
    $users = $container->users; // IDE autocompletes UserRepository
}

Implementation Patterns

Workflows

  1. Domain-Specific Containers: Create modular containers for different layers (e.g., AuthContainer, PaymentContainer).

    /**
     * @property-read AuthService $auth
     * @method        Token new_token()
     */
    class AuthContainer extends Container { ... }
    
  2. Hybrid Laravel Integration: Use Props for domain logic while keeping Laravel’s container for framework services.

    // In a service class
    public function __construct(private AuthContainer $auth) {}
    
    public function login()
    {
        $token = $this->auth->new_token(); // Fresh instance
    }
    
  3. Factory Extensions: Modify resolved instances before returning them.

    $container->extend('user', function ($user, AppContainer $c) {
        $user->setLogger($c->logger);
        return $user;
    });
    
  4. Pimple Migration: Replace Pimple’s array access with property access:

    // Pimple
    $container['user'] = new User();
    
    // Props
    $container->user = new User();
    

Integration Tips

  • Leverage Laravel’s Container for Framework Services: Keep Laravel’s container for routes, views, and middleware, but use Props for application logic.

    $app->bind('auth.container', function () {
        return new AuthContainer();
    });
    
  • Use new_* Methods for Stateless Services:

    // Always get a fresh database connection
    $connection = $container->new_connection();
    
  • Combine with Laravel’s Service Providers: Register Props containers in providers:

    public function register()
    {
        $this->app->singleton(AppContainer::class, function () {
            return new AppContainer();
        });
    }
    
  • Static Analysis with Psalm/PHPStan: Annotate containers for type checking:

    /**
     * @property-read UserRepository $users
     * @property-read LoggerInterface $logger
     */
    class AppContainer extends Container { ... }
    

Gotchas and Tips

Pitfalls

  1. Caching Behavior:

    • Properties are cached by default. Use new_* methods for fresh instances.
    $container->user; // Cached
    $container->new_user(); // Fresh
    
  2. Missing Factory Checks:

    • Avoid calling new_* on non-factory properties:
    $container->ccc = new CCC(); // Not a factory
    $container->hasFactory('ccc'); // false
    $container->new_ccc(); // Throws exception
    
  3. IDE Annotation Lag:

    • Some IDEs (e.g., PHPStorm) may not immediately recognize PHPDoc annotations. Restart the IDE or trigger a full analysis.
  4. Laravel Container Conflicts:

    • Avoid binding the same service to both Laravel’s container and Props. Use one as the source of truth.
  5. Circular Dependencies:

    • Props does not detect circular dependencies. Use Laravel’s container or Symfony DI for this.

Debugging

  1. Check Factory Existence:

    if (!$container->hasFactory('service')) {
        throw new \RuntimeException("No factory for 'service'");
    }
    
  2. Inspect Resolved Values:

    $factory = $container->getFactory('service');
    dump($factory); // Debug the closure
    
  3. Clear Cache Manually:

    $container->clear(); // Reset all cached values
    

Tips

  1. Use setFactory for Static Methods:

    $container->setFactory('user', [UserFactory::class, 'create']);
    
  2. Extend for Post-Processing:

    $container->extend('user', function ($user) {
        $user->boot();
        return $user;
    });
    
  3. Leverage Props\Pimple for Pimple Users:

    $pimple = new Props\Pimple();
    $pimple['db'] = function () { return new DB(); };
    // Now $pimple->db is IDE-friendly
    
  4. Combine with Laravel’s app():

    $container = app(AppContainer::class);
    
  5. Type-Hint Container in Constructors:

    public function __construct(private AppContainer $container) {}
    
  6. Use for Configuration:

    /**
     * @property-read string $app_name
     */
    class ConfigContainer extends Container {
        public function __construct() {
            $this->app_name = config('app.name');
        }
    }
    
  7. Avoid Overusing extend:

    • Extensions are called on every access, which can impact performance in tight loops.
  8. Document Non-IDE-Friendly Properties:

    • Use @property-read even for simple values to keep IDE hints consistent:
    /**
     * @property-read string $version
     */
    class AppContainer extends Container {
        public function __construct() {
            $this->version = '1.0.0';
        }
    }
    
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.
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium