PHPackages                             oro/phpstan-rules - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. oro/phpstan-rules

ActiveLibrary[Testing &amp; Quality](/categories/testing)

oro/phpstan-rules
=================

A set of additional PHPStan rules used in Oro products.

1.12.0(1y ago)1328.0k↓33.3%5[1 PRs](https://github.com/oroinc/phpstan-rules/pulls)1MITPHP

Since Jan 8Pushed 2mo ago9 watchersCompare

[ Source](https://github.com/oroinc/phpstan-rules)[ Packagist](https://packagist.org/packages/oro/phpstan-rules)[ RSS](/packages/oro-phpstan-rules/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (20)Used By (1)

Oro's rules for PHPStan
=======================

[](#oros-rules-for-phpstan)

This package contains a set of additional rules for [PHPStan - PHP Static Analysis Tool](https://github.com/phpstan/phpstan).

We use these rules at Oro, Inc. and ask anyone contributing code to Oro Products to follow them as well.

Rules
-----

[](#rules)

### Unsafe DQL usage analysis

[](#unsafe-dql-usage-analysis)

#### Why DQL and SQL queries should be checked

[](#why-dql-and-sql-queries-should-be-checked)

Using DQL does not protect against injection vulnerabilities. The following APIs are designed to be SAFE from SQL injections:

- For Doctrine\\DBAL\\Connection#insert($table, $values, $types), Doctrine\\DBAL\\Connection#update($table, $values, $where, $types) and Doctrine\\DBAL\\Connection#delete($table, $where, $types) only the array values of $values and $where. The table name and keys of $values and $where are NOT escaped.
- Doctrine\\DBAL\\Query\\QueryBuilder#setFirstResult($offset)
- Doctrine\\DBAL\\Query\\QueryBuilder#setMaxResults($limit)
- Doctrine\\DBAL\\Platforms\\AbstractPlatform#modifyLimitQuery($sql, $limit, $offset) for the $limit and $offset parameters.

Consider ALL other APIs to be not safe for user-input:

- Query methods on the Connection
- The QueryBuilder API
- The Platforms and SchemaManager APIs to generate and execute DML/DDL SQL statements
- Expressions constructed with the help of various Expression Builders

See full article at [Doctrine Security](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)

#### Static code analysis - Execution

[](#static-code-analysis---execution)

As checking the whole codebase requires a lot of time, sql-injection search tool was added to simplify the process. The tool is based on [PHPStan - PHP Static Analysis Tool](https://github.com/phpstan/phpstan)and is implemented as additional Rule.

To check codebase for unsafe DQL and SQL usages perform the following actions:

- change directory to `/tool/` where `` is path to application in the file system
- install dependencies `composer install`
- run check with `./bin/phpstan analyze -c phpstan.neon  --autoload-file=`

To speedup analysis it's recommended to run it in parallel on per package basis. This may be achieved with the help of the `parallel` command:

```
cd my_application/tool/
COMPOSER=composer-sql.json composer install
rm -rf logs;
mkdir logs;
ls ../package/ \
| grep -v "\-demo" | grep -v "demo-" | grep -v "test-" | grep -v "german-" | grep -v "view-switcher" | grep -v "twig-inspector" \
| parallel -j 4  "./bin/phpstan analyze -c phpstan.neon `pwd`/../package/{} --autoload-file=`pwd`/../application/commerce-crm-ee/vendor/autoload.php > logs/{}.log"

```

Note that *commerce-crm-ee* application should have `autoload.php` generated. The results of the analysis should be available within a minute. Each result should be checked carefully. Unsafe variables should be sanitized or escaped as a precaution.

#### HOW TO fix found warnings

[](#how-to-fix-found-warnings)

Unsafe variables are any methods that may depend on external data. Even cached data may be unsafe if an attacker manages to access the cache storage. Any variable that comes from the outside, or contains a value returned by an unsafe function, is also unsafe and should be passed into queries with caution.

You can make a variable safe in a number of ways:

##### ORM

[](#orm)

ORM based queries may contain vulnerable inputs. To keep them clean, follow the next rules:

- Parameter identifier *MUST* be a named placeholder. Passing data directly as the right operand is prohibited. The only possible exception is numbers, `\DateTime` and booleans.
- All identifiers MUST BE \[a-zA-Z0-9\_\] compatible.

If there is a need to pass a variable directly into the query, use `QueryBuilderUtil` safe methods

- *getField* - use it when field is constructed with sprintf or concatenation. For example, `select($alias . '.' . $field)`, `select('alias.', $field)`, `select(sprintf('%s.%s', $alias, $field)` , etc.
- *sprintf* - should be used instead of sprintf when it cannot be replaced with getField. For example, `select('IDENTITY(%s.%s) as %s', $alias, $fieldName, $fieldAlias)`
- *checkIdentifier* - should be used to check identifiers (alphanumeric strings). A variable passed to checkIdentifier is considered safe and is allowed for further use.
- *checkField* - similar to checkIdentifier with exception that it's designed to check strings in format "\\w+.\\w+" (alias.fieldName). A variable passed to checkField is considered safe and is allowed for further use.
- *checkParameter* -similar to checkIdentifier with exception that it's designed to check strings in format ":\\w+" (:parameterName). A variable passed to checkParameter is considered safe and is allowed for further use.
- *getSortOrder* - return ASC or DESC if one of these values is passed. Otherwise, an exception is thrown. Used to clear sort directions passed as parameters.

> NOTE!!! -&gt;select(sprintf(%s as something', $fullName)) may be not quick fixed as $fullName may contain CONCAT(firstName, lastName) or any other statement. Such calls should be checked and marked safe

##### DBAL

[](#dbal)

Use bind parameters or quote them with the connection quote method. Identifiers should be either checked for safety with QueryBuilderUtil or quoted with the quoteIdentifier method of connection.

##### Common warnings and possible ways to fix them

[](#common-warnings-and-possible-ways-to-fix-them)

- Unsafe field is used as a part of query

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq($field, ':parameter'));
    ```

    Fix - use `QueryBuilderUtil::checkField` to check field for safeness

    ```
    QueryBuilderUtil::checkField($field);
    $queryBuilder->andWhere($queryBuilder->expr()->eq($field, ':parameter'));
    ```
- Using composite identifier

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq($alias . '.' . $field, ':parameter'));
    ```

    Or

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq(sprintf('%s.%s', $alias, $field), ':parameter'));
    ```

    Possible ways to fix.

    Fix 1 - use `QueryBuilderUtil::getField`

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq(QueryBuilderUtil::getField($alias, $field), ':parameter'));
    ```

    Fix 2 - check each identifier separately with `QueryBuilderUtil::checkIdentifier`

    ```
    QueryBuilderUtil::checkIdentifier($alias);
    QueryBuilderUtil::checkIdentifier($field);
    $queryBuilder->andWhere($queryBuilder->expr()->eq($alias . '.' . $field, ':parameter'));
    ```

    Fix 3 - use safe `QueryBuilderUtil::sprintf`, also applicable when replacing sprintf

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq(QueryBuilderUtil::sprintf('%s.%s', $alias, $field), ':parameter'));
    ```
- Using composite parameter name

    ```
    $queryBuilder->andWhere($queryBuilder->expr()->eq('table.id', $paramer));
    ```

    Fix - use `QueryBuilderUtil::checkParameter`

    ```
    QueryBuilderUtil::checkParameter($paramer);
    $queryBuilder->andWhere($queryBuilder->expr()->eq('table.id', $paramer));
    ```
- Using sort order passed from outside

    ```
    $queryBuilder->orderBy('table.field', $sortOrder);
    ```

    Fix - use `QueryBuilderUtil::getSortOrder`

    ```
    $queryBuilder->orderBy('table.field', QueryBuilderUtil::getSortOrder($sortOrder));
    ```
- Literal is passed to query

    ```
    $queryBuilder->select(sprintf("'%s' as className", $className));
    ```

    Fix use `literal` expression

    ```
    $queryBuilder->select(
      sprintf((string)$queryBuilder->expr()->literal($className) . ' as className')
    );
    ```

#### Static code analysis - Configuration

[](#static-code-analysis---configuration)

If a variable, a property or a method are considered safe after a detailed manual analysis, they may be added to `trusted_data.neon`. Such items will be marked as safe during further checks and skipped.

Available `trusted_data.neon` configuration sections are:

- `variables` - whitelist of safe variables. Format `class.method.variable: true`
- `properties` - whitelist of safe properties. Format `class.method.property: true`
- `safe_methods` - whitelist of safe class methods. Format `class.method: true`
- `safe_static_methods` - whitelist of safe class static methods. Format `class.method: true`
- `check_methods_safety` - consider method safe if passed variables are safe. Format `class.method: true` when all passed variables should be checked. Includes `__construct` method for new instance creation checks
    or `class.method: [1]` when only certain variables require checks (their positions are listed in array)
- `check_static_methods_safety` - consider static method safe if passed variables are safe. Format `class.method: true` when all passed variables should be checked or `class.method: [1]` when only certain variables require checks (their positions are listed in array)
- `clear_methods` - variable is considered as safe if it is passed as argument into listed method. Format `class.method: true`
- `clear_static_methods` - variable is considered as safe if it is passed as argument into listed static method. Format `class.method: true`
- `check_methods` - contains a list of methods that are checked for safeness. If passed arguments are unsafe, a security warning about such usage is reported by the analysis tool. Format `class.method: true` when all passed variables should be checked or `class.method: [1]` when only certain variables require checks (their positions are listed in array). Use `class.__all__: true` to check all class methods. For example, there is SomeClass and we want to check all its methods, except for `method1`. For `method1`, we want to enable only the first and third argument checks, and for `method2` we want all arguments to be checked: ```
    check_methods:
        SomeClass:
            __all__: true
            method1: [0, 2]
            mrthod2: true
    ```

It is recommended to mark methods as safe. If a variable consists of several parts, it is better to add a minimal unsafe part to the whitelist, rather than the whole expression.

#### Example

[](#example)

```
protected function addWhereToQueryBuilder(QueryBuilder $qb, string $suffix, int $index)
{
    $rootAlias = $qb->getRootAlias();
    $fieldName = $rootAlias . '.field' . $idx . $suffix;

    $qb->andWhere($qb->expr()->gt($fieldName, 10);
}
```

Such code will lead to a security warning, as `$fieldName` variable was constructed using several parts, some of which are not safe. The best solution to make this expression safe is to check `$suffix` with `QueryBuilderUtil::checkIdentifier($suffix)`Another option is to add `$suffix` into the `trusted_data.neon` whitelist if its values are always passed as safe or checked in the caller. The worst solution would be to mark `$fieldName` as safe because its parts may be changed and, after adding a new or an unsafe part, it will be skipped, although it may contain an unchecked vulnerability.

Contribute
----------

[](#contribute)

Please referer to [Oro Community Guide](https://oroinc.com/orocommerce/doc/current/community/contribute) for information on how to contribute to this package and other Oro products.

###  Health Score

49

—

FairBetter than 95% of packages

Maintenance65

Regular maintenance activity

Popularity36

Limited adoption so far

Community22

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 85.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~89 days

Recently: every ~59 days

Total

18

Last Release

442d ago

Major Versions

0.11.2 → 1.8.02022-10-17

1.12.0 → 6.1.x-dev2025-03-02

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/30661063?v=4)[Oro, Inc](/maintainers/orocrm)[@orocrm](https://github.com/orocrm)

---

Top Contributors

[![x86demon](https://avatars.githubusercontent.com/u/196506?v=4)](https://github.com/x86demon "x86demon (78 commits)")[![mbessolov](https://avatars.githubusercontent.com/u/2725121?v=4)](https://github.com/mbessolov "mbessolov (7 commits)")[![dxops](https://avatars.githubusercontent.com/u/1804871?v=4)](https://github.com/dxops "dxops (1 commits)")[![OleksandrProtsiuk](https://avatars.githubusercontent.com/u/44535554?v=4)](https://github.com/OleksandrProtsiuk "OleksandrProtsiuk (1 commits)")[![vmalesh-oro](https://avatars.githubusercontent.com/u/247179140?v=4)](https://github.com/vmalesh-oro "vmalesh-oro (1 commits)")[![advocat](https://avatars.githubusercontent.com/u/2920993?v=4)](https://github.com/advocat "advocat (1 commits)")[![yaroslavbr](https://avatars.githubusercontent.com/u/5465204?v=4)](https://github.com/yaroslavbr "yaroslavbr (1 commits)")[![anyt](https://avatars.githubusercontent.com/u/5183991?v=4)](https://github.com/anyt "anyt (1 commits)")

### Embed Badge

![Health badge](/badges/oro-phpstan-rules/health.svg)

```
[![Health](https://phpackages.com/badges/oro-phpstan-rules/health.svg)](https://phpackages.com/packages/oro-phpstan-rules)
```

###  Alternatives

[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k43.5M5.2k](/packages/larastan-larastan)[phpstan/phpstan-symfony

Symfony Framework extensions and rules for PHPStan

78768.9M1.5k](/packages/phpstan-phpstan-symfony)[phpstan/phpstan-doctrine

Doctrine extensions for PHPStan

66766.6M1.1k](/packages/phpstan-phpstan-doctrine)[phpat/phpat

PHP Architecture Tester

1.2k3.5M32](/packages/phpat-phpat)[spaze/phpstan-disallowed-calls

PHPStan rules to detect disallowed method &amp; function calls, constant, namespace, attribute, property &amp; superglobal usages, with powerful rules to re-allow a call or a usage in places where it should be allowed.

33320.0M375](/packages/spaze-phpstan-disallowed-calls)[ekino/phpstan-banned-code

Detected banned code using PHPStan

2925.6M92](/packages/ekino-phpstan-banned-code)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
