Skip to content
yii2-extensionsPublic

About

PHPStan extension for Yii2 with type inference for ActiveRecord, components, and behaviors.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

131 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Yii Framework

PHPStan


PHPUnit PHPStan Security

Enhanced static analysis for Yii2 applications with PHPStan
Precise type inference, dynamic method resolution, and comprehensive property reflection

Features

Feature Overview

Installation

composer require --dev yii2-extensions/phpstan:^0.5

Quick start

Create config/phpstan.php with an analysis-only description of your application:

<?php

declare(strict_types=1);

return [
    'params' => ['adminEmail' => 'admin@example.com'],
    'components' => [
        'db' => ['class' => yii\db\Connection::class],
    ],
];

Create phpstan.neon in the project root, choosing paths that exist in your application:

includes:
    - vendor/yii2-extensions/phpstan/extension.neon

parameters:
    level: 5
    paths:
        - models
    yii2:
        config_path: config/phpstan.php

Run PHPStan:

vendor/bin/phpstan analyse

Type inference examples

The snippets below assume an app\models\Article model with id and title property tags and a published() query scope. The usage guide describes the analysis configuration for the components, params and behaviors shown here.

Active Record

Yii supplies generic types for findOne(), findAll(), relations and the standard query methods. This extension adds row shapes from model @property tags and precise getAttribute() types:

use app\models\Article;

$article = Article::findOne(1);                   // Article|null
$articles = Article::findAll(['title' => 'Yii']); // array<Article>
$query = Article::find()->where(['id' => 1]);     // ArticleQuery&ActiveQuery<Article>
$published = Article::find()->published()->one(); // Article|null
$rows = Article::find()->asArray()->all();        // array<array{id: int, title: string}>

if ($article !== null) {
    $title = $article->getAttribute('title');     // string
}

A custom query without generic PHPDoc, such as one generated by Gii, is bound to the model at the find() call site. Use static or $this scope return types to preserve that binding. Row shapes include inherited property tags; relation and read-only keys are optional and need guards such as ??. See query limitations when a query is reached through a typed parameter or a widened relation getter.

Application components

Yii::$app uses phpstan.application_type, which defaults to yii\web\Application. Component classes come from the analysis configuration:

$view = Yii::$app->exampleView;           // yii\web\View
$typed = Yii::$app->get('typedView');     // yii\web\View
$untyped = Yii::$app->get('untypedView'); // object
$request = Yii::$app->request;            // yii\web\Request

Class names, class-key arrays, object instances and factories with a single class return type can provide precise types. An untyped factory yields object; adding an accurate return type makes the result specific. Core components configured without a class keep the application's annotated property type.

The built-in user: identityClass mapping also supplies the configured identity class to yii\web\User's generic parameter. Its identity property remains nullable. See generic components and the separate console application configuration.

Application params

Params values become an array shape, including nested arrays and keys containing dots or other punctuation:

$email = Yii::$app->params['adminEmail'];          // string
$maxItems = Yii::$app->params['maxItems'];         // int
$enabled = Yii::$app->params['nested']['enabled']; // bool

Scalars are widened to their types rather than retained as literal values. The same shape applies to inherited module params, including $this->module->params in a controller. With no configured params, the type stays array.

Behaviors

The analysis-only behaviors map exposes members from attached behavior classes. In the example, TaggedComponent is mapped to TagBehavior; the application must also attach that behavior at runtime:

use app\behaviors\TagBehavior;
use app\components\TaggedComponent;

$component = new TaggedComponent();

$component->attachBehavior('tag', new TagBehavior());

$label = $component->label;         // string
$summary = $component->summarize(); // string

Properties and methods are resolved from the behavior's native members and PHPDoc. See behavior configuration.

Dependency injection

Container service definitions and aliases are resolved by their IDs:

$view = Yii::$container->get('view.alias');   // yii\web\View
$shared = Yii::$container->get('sharedView'); // yii\web\View

The example config maps view.alias to view.real and gives sharedView a typed factory. An unresolved definition keeps the result as object. Container factories, invocable objects and aliases have distinct rules; see definition forms.

Header collection

$headers = new yii\web\HeaderCollection();

$host = $headers->get('Host');                         // string|null
$ids = $headers->get('X-Request-ID', [], false);       // array<string>
$requestId = $headers->get('X-Request-ID', 'default'); // string

A non-null default removes null from the return type. The $first argument selects a single value or an array.

Logger messages

Logger and target message buffers have tuple types without local stubs or PHPDoc overrides:

foreach ((new yii\log\Logger())->messages as $message) {
    $level = $message[1];          // int
    $category = $message[2];       // string
    $timestamp = $message[3];      // float
    $memory = $message[5] ?? null; // int|null
}

$empty = yii\log\Target::filterMessages([]); // array{}

Payload types follow the installed Yii version. Filtering preserves the input key and tuple types while allowing an empty result. See the logger examples.

Service locator

A service locator resolves configured component IDs, while container-only IDs belong to the container:

$view = Yii::$app->get('exampleView');            // yii\web\View
$optional = Yii::$app->get('exampleView', false); // yii\web\View|null
$unknown = Yii::$app->get('view.alias');          // object; not a configured component ID

Passing false or a non-constant boolean as $throwException makes a resolved component nullable. An object fallback does not guarantee a successful runtime lookup: unknown component IDs can throw. Use Yii::$container->get('view.alias') for the configured container alias.

Documentation

Package information

PHP Yii 2.0.x Yii 22.0.x Latest Stable Version Total Downloads

Project status

Codecov PHPStan Level Max Quality StyleCI

Our social networks

Follow on X Follow on Facebook Join our Subreddit Join on Telegram

License

License

About

PHPStan extension for Yii2 with type inference for ActiveRecord, components, and behaviors.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

20 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages