Download the PHP package mspirkov/yii2-phpstan-rules without Composer

On this page you can find all versions of the php package mspirkov/yii2-phpstan-rules. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package yii2-phpstan-rules

Yii2 PHPStan rules

A set of PHPStan rules for Yii2 projects that I put together for my own day-to-day work. Yii2 leans heavily on loosely-typed config arrays and magic properties/methods that PHPStan can't see through on its own, and on conventions — like keeping business logic and database access out of controllers and views — that are easy to drift from without anyone noticing. These rules catch both: they validate Yii2-specific config and structure statically, and they enforce the architectural boundaries and other code-quality checks I try to keep in a codebase. In my experience they help keep a Yii2 codebase a bit cleaner and more maintainable, but they're just my opinions turned into checks, not a universal standard — use what's useful, ignore or disable the rest.

PHP Yii2 PHPStan Total Downloads Tests Coverage PHPStan Level Max

Support

If you like this project, give it a ⭐ on GitHub — it helps others discover it.

Installation

[!IMPORTANT]

It works better with the latest versions of PHP, Yii2, and PHPStan. The more up-to-date the versions are, the more accurate the analysis is.

If your project uses phpstan/extension-installer, the rules are picked up automatically — nothing else to do.

Otherwise, include them manually in your phpstan.neon:

Configuration

All rules are on by default. Turn the whole set off, turn off just one of the two rule groups, or tune individual rules, under parameters.mspirkovYii2Rules:

Rules at a glance

Validation rules

Statically validate Yii2's loosely-typed config arrays and array-driven conventions — shapes PHPStan can't check on its own because they only take effect at runtime. Toggle all of them at once with enableValidationRules.

Rule Catches
activeFormFieldValidation ActiveForm::field() calls targeting an attribute that is missing, read-only, or write-only on the given model
activeQueryWithValidation with() / joinWith() / innerJoinWith() calls referencing a relation that doesn't exist on the queried ActiveRecord model
activeRecordConditionValidation findOne() / findAll() / deleteAll() / updateAll() / updateAllCounters() WHERE conditions with an unknown attribute or a mismatched value type
activeRecordRelationValidation Invalid hasOne() / hasMany() link properties that do not exist on the current or related ActiveRecord model
activeRecordUpdateValuesValidation updateAll() / updateAllCounters() attribute or counter values with an unknown attribute or a mismatched value type
baseObjectInstantiationValidation new on a yii\base\BaseObject subclass whose last constructor argument is a $config array, with bad config keys and bad option types
behaviorAttributesValidation TimestampBehavior/BlameableBehavior/SluggableBehavior/AttributeTypecastBehavior/DateTimeBehavior options naming an unknown model attribute
componentBehaviorsValidation Malformed or invalid behaviors() in yii\base\Component — unknown behavior classes, bad config keys, and bad option types
controllerActionsValidation Malformed or invalid actions() in yii\base\Controller — unknown action classes, bad config keys, and bad option types
htmlActiveAttributeValidation Html::activeInput() / activeTextInput() / etc. calls referencing an attribute that does not exist on the given model
modelAttributeHintsValidation attributeHints() entries in yii\base\Model that target attributes that don't exist, or use an empty attribute name
modelAttributeLabelsValidation attributeLabels() entries in yii\base\Model that target attributes that don't exist, or use an empty attribute name
modelRulesValidation Malformed or invalid rules() in yii\base\Model — unknown validators, missing required options, bad regexes, unknown attributes, and more
modelScenariosValidation scenarios() entries in yii\base\Model with an empty name, a non-array attribute list, or an unknown attribute
queryConditionValidation where() / andWhere() / orWhere() operator-format conditions (in, between, like, etc.) with the wrong number of operands
uploadedFileInstanceValidation UploadedFile::getInstance() / getInstances() calls referencing an attribute that does not exist on the given model
widgetPropertiesValidation Unknown or mistyped option keys and bad option types in Widget::begin() / Widget::widget() config arrays
yiiCreateObjectValidation Yii::createObject() config arrays missing class/__class, bad config keys, and bad option types

Code quality rules

Catch architectural drift, complexity, and other code-quality issues that are easy to miss without anyone noticing — business logic and database access staying out of controllers and views, actions calling other actions directly, superglobals, dynamic SQL, an Application object that anything can read from or write to, and calls that are provably redundant.

Rule Catches
noComplexActionClasses Standalone yii\base\Action classes with too much branching/looping — logic that belongs in a service
noComplexControllerActions The same, for controller actions
noControllerActionCallsViaThis $this->actionFoo() inside a controller instead of a redirect or shared method
noDbQueriesInActions Direct DB/ActiveRecord access in Action classes
noDbQueriesInControllers Direct DB/ActiveRecord access in controllers
noDbQueriesInViews Direct DB/ActiveRecord access in view files
noDirectSuperglobals Direct use of $_GET, $_POST, $_SESSION, etc.
noDynamicQueryWhere String-concatenated conditions passed to Query::where() / andWhere() / orWhere()
noForbiddenYiiAppProperties Reads of arbitrary yii\base\Application components, including Yii::$app->*
noRedundantExistenceCheck Query::one() !== null / Query::count() compared against 0 or 1 where Query::exists() suffices
noRedundantHtmlEncode Html::encode() calls whose argument is always a numeric-string
noYiiAppPropertyMutation Writes to yii\base\Application properties, including setComponents()

Rule reference

Validation rules

Active Form field validation

ActiveForm::field($model, $attribute) binds an editable input to the attribute: it reads the current value to render the input, and writes the submitted value back to the model on load(). This rule checks that the attribute is both readable and writable — a declared (non-readonly) property, a PHPDoc @property, or a matching getter/setter pair — and reports it whether it's missing entirely or only exists as read-only or write-only. yii\base\DynamicModel instances (and subclasses) are skipped entirely, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

ActiveQuery with() validation

with(), joinWith(), and innerJoinWith() take relation names as plain strings, so a typo (or a relation that got renamed) silently returns no related data instead of failing. This rule checks that every relation name passed to these methods — including a joinWith()/innerJoinWith() alias ('orders o' or 'orders AS o') and a dotted sub-relation path ('orders.items') — resolves to an actual relation (a getXxx() method returning something compatible with yii\db\ActiveQueryInterface) on the queried model.

Validating a sub-relation requires knowing which model the parent relation points to. This rule can work that out two ways: from the relation getter's own @return ActiveQuery<T> PHPDoc, or from a @property-read T / @property-read T[] PHPDoc property of the same name on the model (the same resolution activeFormFieldValidation and friends already rely on). A relation whose target model can't be determined either way is still checked for existence at its own level, but any further sub-relation path past it is left unchecked rather than guessed at.

Active Record condition validation

findOne(), findAll(), and deleteAll() take a plain array condition (['attribute' => value], with an array value matched as an IN (...) condition) — and so does the second, condition argument of updateAll() / updateAllCounters(). Like attributeLabels() and scenarios(), this is never checked against the model until the query actually runs. This rule checks that every attribute name in a condition array exists on the queried ActiveRecord model (the same @property-aware resolution as activeRecordRelationValidation) and that its value's type is compatible with the attribute's declared type. Only array literals with a resolvable string key are checked; primary-key-only lookups (findOne(1), findOne([1, 2])) and dynamically-built condition arrays are left alone. A value implementing yii\db\ExpressionInterface (e.g. new Expression('NOW()')) is accepted for any attribute regardless of its declared type — yii\db\conditions\HashConditionBuilder builds it as raw SQL instead of type-casting it, and does so per-value inside an IN (...) array too.

Active Record relations validation

hasOne() and hasMany() relation links are plain string arrays: the array keys belong to the related AR class, and the values belong to the current AR class. This rule checks that those properties exist, including properties declared through PHPDoc @property.

Active Record update values validation

updateAll()'s attribute values and updateAllCounters()'s counter values are the other plain array these two methods take — the values written into the row, as opposed to the WHERE condition activeRecordConditionValidation checks. This rule checks that every attribute name exists on the ActiveRecord model and that its value's type is compatible with the attribute's declared type; unlike a condition, these values are written as-is, so (unlike activeRecordConditionValidation) an array value is not treated as an IN (...) shorthand and is always a type mismatch. As with a condition, a value implementing yii\db\ExpressionInterface is accepted for any attribute regardless of its declared type — yii\db\QueryBuilder::prepareUpdateSets() builds it as raw SQL instead of type-casting it.

BaseObject instantiation validation

yii\base\BaseObject::__construct($config = []) applies $config via Yii::configure($this, $config), the same mechanism Yii::createObject() uses to apply its own config array — so a typo'd key or wrong-typed value in a plain new SomeObject([...]) call is just as invisible to PHPStan as it is in a createObject() config array. This rule checks a new call the same way yiiCreateObjectValidation checks Yii::createObject(): config keys against the target class's writable properties, and literal values against their declared types. It only looks at classes extending yii\base\BaseObject, and only at a literal array passed as the constructor's last argument when that argument is exactly the one named $config — the Yii2 convention for opting into array-config construction. A subclass whose last parameter isn't named config (or is variadic) doesn't follow that convention, so its last argument is left alone.

Behavior attributes validation

TimestampBehavior, BlameableBehavior, SluggableBehavior, AttributeTypecastBehavior, and mspirkov/yii2-db's DateTimeBehavior all fill in specific model attributes on their own — createdAtAttribute/updatedAtAttribute, createdByAttribute/updatedByAttribute, attribute/slugAttribute, attributeTypes, and the attributes event map every AttributeBehavior subclass inherits — and none of that is checked against the model until the behavior actually runs. This rule checks that every attribute name these options reference (a literal string, or an array of them) actually exists on the model declaring behaviors(), the same @property-aware resolution modelRulesValidation and modelAttributeLabelsValidation use. yii\base\DynamicModel instances are skipped, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

TimestampBehavior, BlameableBehavior, SluggableBehavior, and DateTimeBehavior all extend yii\behaviors\AttributeBehavior, so instead of (or alongside) their own shorthand options, any of them can be configured directly through the inherited attributes event map — and this rule checks that map's attribute names the same way, on whichever of the four (or a custom AttributeBehavior subclass) it appears on:

Component behaviors validation

Component::behaviors() uses Yii object configs, so typos usually wait until runtime. This rule checks statically visible behavior definitions on yii\base\Component subclasses, including models: classes that do not extend yii\base\Behavior, bad config keys, unknown config options, and option value types inferred from public properties or setters.

Controller actions validation

Controller::actions() shares the same object-config shape as Component::behaviors() — this rule checks statically visible action definitions on yii\base\Controller subclasses: classes that do not extend yii\base\Action, an empty action ID, bad config keys, unknown config options, and option value types inferred from public properties or setters.

Html active attribute validation

Html::activeInput(), activeTextInput(), and the rest of the active*() family (activeHiddenInput, activePasswordInput, activeFileInput, activeTextarea, activeRadio, activeCheckbox, activeDropDownList, activeListBox, activeCheckboxList, activeRadioList, activeLabel, activeHint) all take a model and a plain attribute-name string, the same as ActiveForm::field(). This rule checks that the attribute exists on the given model, the same @property-aware resolution used by activeFormFieldValidation and uploadedFileInstanceValidation. yii\base\DynamicModel instances are skipped, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

Model attribute hints validation

Model::attributeHints() is just as easy to get wrong as attributeLabels() — a typo'd key silently means the hint is never shown for the intended attribute. This rule checks that every key is an existing property on the model (as a declared property or a PHPDoc @property, same resolution as modelRulesValidation) and isn't left empty — though the existence check alone is skipped for yii\base\DynamicModel subclasses, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically:

Model attribute labels validation

Model::attributeLabels() is just as easy to get wrong as rules() — a typo'd key silently falls back to the default humanized attribute name instead of showing your label. This rule checks that every key is an existing property on the model (as a declared property or a PHPDoc @property, same resolution as modelRulesValidation) and isn't left empty — though the existence check alone is skipped for yii\base\DynamicModel subclasses, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically:

Model validation rules validation

Model::rules() is just a plain array — PHP will never tell you that you forgot a validator's required option, wrote an invalid regex, misconfigured one of its options, or targeted an attribute that doesn't even exist. For every rule entry the validator type resolves to (a built-in alias like required/string/number/compare/date/match/in/unique/exist/file/image/ip/url, a custom Validator subclass, a configured project alias, or an inline closure/method), this rule statically checks the option array against what that validator actually accepts and requires. A validator name it can't resolve is reported as an error; add project-specific aliases under modelRulesValidation.customValidators:

This rule also checks that the attribute names at index 0 of each rule (including array lists of attributes) actually exist on the model, the same way activeRecordRelationValidation checks relation links — as a declared property or a PHPDoc @property. It only reports on attribute names it can resolve to a literal or constant string; anything built dynamically at runtime is left alone. This check alone is skipped for yii\base\DynamicModel subclasses, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

Model scenarios validation

Model::scenarios() maps scenario names to the attributes active in them, and PHP won't tell you that a scenario name is empty, an attribute list isn't actually an array, or an attribute doesn't exist on the model — the same way modelAttributeLabelsValidation checks attributeLabels(). An attribute prefixed with ! (Yii's "unsafe" marker) is checked under its unprefixed name. The attribute-existence check alone is skipped for yii\base\DynamicModel subclasses, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

Query condition validation

Query::where() / andWhere() / orWhere() accept an "operator format" array ([operator, operand1, operand2, ...]), and Yii only discovers a missing operand at query-build time — each yii\db\conditions\*Condition::fromArrayDefinition() throws an InvalidArgumentException if its required operands aren't present. This rule checks the operand count against those same rules: not, between / not between, in / not in, like and its variants, and exists / not exists each need a specific minimum (or, for not, an exact) number of operands, and the standard comparison operators (=, !=, <>, >, >=, <, <=) need exactly 2, Yii's documented "arbitrary operator" case. and / or operands are recursed into, since they typically wrap further operator-format sub-conditions; yii\db\conditions\ConjunctionCondition itself never validates their count, but a zero-operand and/or can never produce a meaningful condition, so this rule still requires at least one. Any other operator string — a genuinely custom one registered via QueryBuilder::setConditionClasses() — is left unchecked rather than guessed at, and so is anything built dynamically or in hash format (['status' => 1], never operator-format to begin with).

UploadedFile instance validation

UploadedFile::getInstance($model, $attribute) and getInstances($model, $attribute) build the file input's name from $model and a plain attribute-name string, the same way ActiveForm::field() does — so a typo silently returns null (or an empty array) instead of the uploaded file. This rule checks that the attribute exists on the given model, the same @property-aware resolution used elsewhere (e.g. activeFormFieldValidation, modelAttributeLabelsValidation). yii\base\DynamicModel instances are skipped, since their attributes are defined at runtime via defineAttribute() and can't be resolved statically.

Widget properties validation

Widget::begin($config) / Widget::widget($config) configs are just arrays, like behaviors(), so a typo'd key or a wrong-typed value only fails once the widget renders. This rule checks config keys against the called widget's writable properties and literal values against their declared types.

Yii::createObject() validation

Yii::createObject()'s class / __class config array is declared as an open, all-optional PHPStan array shape (array{class?: class-string<T>, __class?: class-string<T>, ...}), so PHPStan itself already flags an unknown or wrong-typed class value — but it stays silent about a missing class/__class key entirely (just an unhelpful "unable to resolve the template type" note) and about every other key in the array, since ... accepts anything. This rule fills exactly those two gaps on calls to createObject() on Yii (or any class extending yii\BaseYii): a clear "must specify class or __class" message, plus config keys checked against the resolved class's writable properties and value types, the same way componentBehaviorsValidation checks behaviors. Callables (a Closure, or a [$target, 'method'] array) are left alone, since they are not object configs.

Code quality rules

Complexity limits

noComplexActionClasses and noComplexControllerActions count if, foreach, for, while, do-while, switch, match, ternaries, and try/catch blocks inside a controller action or Action::run(). Cross any configured threshold and the rule fires, pointing at the exact construct that pushed it over:

No calling actions via $this

No database access outside repositories

Fires on self::find()/findOne()/save(), Yii::$app->db, Yii::$app->db->createCommand(), creating or configuring a Query, transactions, and friends — wherever they turn up in a controller, an Action, or a view file.

noDbQueriesInActions / noDbQueriesInControllers push the same query building into a repository or service instead. Query builder setup counts too: new Query(), $query->where(), and dynamic calls on a Query object are all treated as direct database access in these layers.

No raw superglobals

Covers $_GET, $_POST, $_REQUEST, $_SESSION, $_COOKIE, $_FILES, and $_SERVER, each pointing at the matching yii\web\Request / Session / UploadedFile API.

No dynamic SQL strings

Applies to where(), andWhere(), and orWhere() alike. The check is purely structural — it flags any interpolated or concatenated string passed as the condition, regardless of what the string contains (an IN (...) list is just as flagged as a plain = comparison) — and leaves the array condition syntax, including its ['in', 'column', $values] operator form, untouched.

No forbidden Yii::$app properties

Checks any expression typed as yii\base\Application, not just Yii::$app directly. A short allowlist (id, name, charset, language, timeZone by default) stays available everywhere since those are effectively static configuration, not injectable services.

No redundant existence check

Query::exists() runs a lighter SELECT EXISTS(...) query instead of fetching a row (one()) or counting every matching row (count()). This rule catches the common ways a record-existence check like this ends up written as one of those instead, on any expression typed as yii\db\QueryInterface / yii\db\ActiveQueryInterface — the comparison can be written with the query call on either side, and count() > 0 / count() !== 0 (or, negated, count() < 1 / count() === 0) are flagged the same way as one() !== null / one() === null:

No redundant Html::encode()

PHPStan already flags most nonsensical Html::encode() calls on its own (wrong argument types and the like). The one gap it doesn't cover is a numeric-string argument: a value PHPStan can already prove only ever holds digits, so escaping it can't do anything — htmlspecialchars() never touches a plain number. This rule fires only in that narrow case, on yii\helpers\Html / BaseHtml and their subclasses:

No Yii::$app property mutation

Checks the same yii\base\Application-typed expressions as noForbiddenYiiAppProperties, on the write side.


All versions of yii2-phpstan-rules with dependencies

PHP Build Version
Package Version
Requires php Version >=7.4
phpstan/phpstan Version ^2.2
yiisoft/yii2 Version ^2.0.53
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package mspirkov/yii2-phpstan-rules contains the following files

Loading the files please wait ...