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

Psalm Plugin Laravel Package

php-standard-library/psalm-plugin

Psalm plugin for PHP Standard Library (PSL) that improves type inference for PSL Type specifications (e.g., shape/optional), producing more precise array shapes and safer analysis. Install via Composer and enable with psalm-plugin.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Plugin:
    composer require --dev php-standard-library/psalm-plugin
    
  2. Enable the Plugin:
    vendor/bin/psalm-plugin enable php-standard-library/psalm-plugin
    
  3. Update psalm.config.php: Ensure your config includes PSL types:
    <?php
    return [
        'plugins' => [
            'Psl\Psalm\Plugin',
        ],
        'types' => [
            __DIR__ . '/vendor/php-standard-library/psl/src/Type',
        ],
    ];
    

First Use Case: Type-Safe Data Coercion

Use PSL’s Type\shape() to define strict input validation and let Psalm infer precise types:

use Psl\Type;

$spec = Type\shape([
    'name' => Type\string(),
    'age' => Type\int(),
]);

$data = $spec->coerce($_GET['user']);
// Psalm now knows `$data` is `array{name: string, age: int}` (not a generic array)

Implementation Patterns

1. PSL + Psalm in Laravel

  • Request Validation: Replace Laravel’s Validator with PSL shapes in controllers:

    use Psl\Type;
    
    $userShape = Type\shape([
        'email' => Type\email_address(),
        'password' => Type\string()->min_length(8),
    ]);
    
    $validated = $userShape->coerce(request()->all());
    // Psalm enforces types at dev time; runtime coercion handles edge cases.
    
  • Form Requests: Extend Illuminate\Foundation\Http\FormRequest to integrate PSL:

    public function rules(): array
    {
        $this->validatePsalmShape(); // Custom method to run PSL + Psalm checks.
        return [];
    }
    
    protected function validatePsalmShape(): void
    {
        $shape = Type\shape([...]);
        $shape->coerce($this->all());
    }
    

2. API Contracts

Define API payloads as PSL shapes and enforce them in:

  • Middleware:
    public function handle($request, Closure $next)
    {
        $shape = Type\shape(['token' => Type\string()->min_length(32)]);
        $shape->coerce($request->all());
        return $next($request);
    }
    
  • Service Classes:
    public function processWebhook(array $payload): void
    {
        $webhookShape = Type\shape([
            'event' => Type\literal('charge.succeeded'),
            'data' => Type\shape([...]),
        ]);
        $webhookShape->coerce($payload);
        // Psalm guarantees `$payload` matches the shape.
    }
    

3. Iterables and Collections

Leverage PSL’s Iter functions with precise Psalm types:

use Psl\Iter;

$users = ['Alice', 'Bob'];
$firstName = Iter\first($users); // Psalm infers `string` (not `mixed` or `null`).
$count = Iter\count($users);     // Psalm infers `positive-int`.

4. String Manipulation

PSL’s Str functions now return type-accurate results:

use Psl\Str;

$email = 'user@example.com';
$localPart = Str\before($email, '@'); // Psalm infers `non-empty-string`.

5. Testing

Use PSL shapes in PHPUnit to validate test data:

public function testUserCreation()
{
    $userShape = Type\shape([
        'name' => Type\string(),
        'email' => Type\email_address(),
    ]);
    $userShape->coerce($this->userData); // Fails fast if data is invalid.
}

Gotchas and Tips

Pitfalls

  1. Psalm Version Mismatch:

    • Issue: Plugin may fail if Psalm version isn’t compatible (e.g., PSL 2.x requires Psalm 5+).
    • Fix: Pin versions in composer.json:
      "require-dev": {
          "psalm/psalm": "^5.0",
          "php-standard-library/psalm-plugin": "^2.1"
      }
      
  2. Overly Strict Types:

    • Issue: Psalm may flag legitimate PSL coercion edge cases (e.g., null inputs).
    • Fix: Use @psalm-suppress sparingly or adjust shapes:
      $shape = Type\shape(['name' => Type\optional(Type\string())]);
      
  3. Performance in CI:

    • Issue: Psalm + PSL can slow down CI if not cached.
    • Fix: Cache Psalm results:
      vendor/bin/psalm --init-cache
      vendor/bin/psalm --cache-results
      

Debugging

  1. Enable Verbose Output:

    vendor/bin/psalm --no-cache --output-format=json > psalm.json
    

    Use jq to inspect errors:

    jq '.errors[] | {file, message}' psalm.json
    
  2. Isolate PSL Issues: Temporarily disable other Psalm plugins to test PSL-specific errors:

    // psalm.config.php
    return [
        'plugins' => ['Psl\Psalm\Plugin'], // Only PSL plugin enabled.
    ];
    

Extension Points

  1. Custom Return Type Providers: Extend the plugin by adding new return type logic for PSL functions:

    // src/Psl/Psalm/Plugin/ReturnTypeProvider.php
    public function getFunctionReturnType(
        FunctionLikeInterface $functionLike,
        ArrayType $argumentType,
        string $callingCode
    ): ?Type {
        if ($functionLike->getName() === 'Psl\Str\lowercase') {
            return Type::string();
        }
        return null;
    }
    
  2. PSL Shape Validation in IDE: Use psalm-plugin with PHPStorm’s Psalm integration for real-time feedback:

  3. Laravel Service Provider: Auto-load PSL shapes in a Laravel service provider:

    public function boot(): void
    {
        Psalm::registerPlugin('Psl\Psalm\Plugin');
        Psalm::addTypeLocation(__DIR__ . '/vendor/php-standard-library/psl/src/Type');
    }
    

Tips

  1. Prioritize High-Impact Areas: Start with PSL in:

    • API request/response handling.
    • Database model validation.
    • Payment/webhook processing.
  2. Gradual Adoption: Use @psalm-ignore for legacy code and migrate incrementally:

    /** @psalm-ignore-next-line */
    $oldCodeThatBreaksPsalm();
    
  3. Combine with Laravel Valet: Add Psalm to Valet’s after hook in ~/.valet/Laravel/valet.php:

    $after[] = function () {
        if (file_exists(__DIR__ . '/composer.json')) {
            passthru('composer require --dev php-standard-library/psalm-plugin');
            passthru('vendor/bin/psalm-plugin enable php-standard-library/psalm-plugin');
        }
    };
    
  4. Document PSL Shapes: Use PHPDoc to annotate PSL shapes for better IDE support:

    /**
     * @psalm-type UserShape = array{
     *     id: positive-int,
     *     name: string,
     *     email: email-address,
     * }
     */
    $userShape = Type\shape([...]);
    
  5. Leverage psalm-plugin CLI:

    • List enabled plugins:
      vendor/bin/psalm-plugin list
      
    • Disable the plugin temporarily:
      vendor/bin/psalm-plugin disable php-standard-library/psalm-plugin
      
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.
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
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope