Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added phpstan-dba support #32

Merged
merged 2 commits into from Jun 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions FAQ.md
Expand Up @@ -65,6 +65,12 @@ Der Strict-Mode ist für erfahrene PHP Programmierer geeignet und verbietet die
Details dazu sind unter [phpstan-strict-rules](https://github.com/phpstan/phpstan-strict-rules) zu finden.


## Was ist phpstan-dba?

[`phpstan-dba`](https://staabm.github.io/2022/05/01/phpstan-dba.html) ist eine Erweiterung für PHPStan, die die statische Code Analyse von Datenbankabfragen ermöglicht.
Somit werden u.a. Fehler in SQL Abfragen erkannt.


## Wie mit dem Fehler "Instantiated class X not found." umgehen?

Falls die Klasse auf ein AddOn hinweist, das noch nicht aktiviert ist, sollte dieses aktiviert werden.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -4,7 +4,8 @@
"phpstan/phpstan-symfony": "^1.2",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-strict-rules": "^1.2",
"phpstan/phpstan-phpunit": "^1.1"
"phpstan/phpstan-phpunit": "^1.1",
"staabm/phpstan-dba": "^0.2.40"
},
"autoload": {
"classmap": [
Expand Down
138 changes: 137 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions lib/RexSqlDynamicReturnTypeExtension.php
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace redaxo\phpstan;

use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Accessory\AccessoryNonEmptyStringType;
use PHPStan\Type\Accessory\AccessoryNumericStringType;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use rex_sql;
use function count;
use function in_array;

final class RexSqlDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
public function getClass(): string
{
return rex_sql::class;
}

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return in_array(strtolower($methodReflection->getName()), ['escape', 'escapelikewildcards'], true);
}

public function getTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type {
$args = $methodCall->getArgs();
if (0 === count($args)) {
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
}

$argType = $scope->getType($args[0]->value);

return $this->inferType($argType);
}

private function inferType(Type $argType): Type
{
$intersection = [new StringType()];

if ($argType->isNumericString()->yes()) {
// a numeric string is by definition non-empty. therefore don't combine the 2 accessories
$intersection[] = new AccessoryNumericStringType();
} elseif ($argType->isNonEmptyString()->yes()) {
$intersection[] = new AccessoryNonEmptyStringType();
}

if (count($intersection) > 1) {
return new IntersectionType($intersection);
}

return new StringType();
}
}
24 changes: 24 additions & 0 deletions lib/phpstan-dba.neon
@@ -0,0 +1,24 @@
services:
# redaxos setQuery etc. APIs support both, prepared and unprepared statements..
# therefore we need to register them twice, to also cover cases which don't pass parameters.
-
class: staabm\PHPStanDba\Rules\SyntaxErrorInPreparedStatementMethodRule
tags: [phpstan.rules.rule]
arguments:
classMethods:
- 'rex_sql::setQuery'
- 'rex_sql::setDBQuery'
- 'rex_sql::getArray'
- 'rex_sql::getDBArray'

-
class: staabm\PHPStanDba\Rules\SyntaxErrorInQueryMethodRule
tags: [phpstan.rules.rule]
arguments:
classMethods:
- 'rex_sql::setQuery#0'
- 'rex_sql::setDBQuery#0'
- 'rex_sql::getArray#0'
- 'rex_sql::getDBArray#0'
- 'rex_sql::prepareQuery#0'
- 'rex_sql::getQueryType#0'
1 change: 1 addition & 0 deletions pages/settings.php
Expand Up @@ -31,6 +31,7 @@
$select->addOption('Strict-Mode', realpath(__DIR__.'/../vendor/phpstan/phpstan-strict-rules/rules.neon'));
$select->addOption('Deprecation Warnings', realpath(__DIR__.'/../vendor/phpstan/phpstan-deprecation-rules/rules.neon'));
$select->addOption('PHPUnit', realpath(__DIR__.'/../vendor/phpstan/phpstan-phpunit/rules.neon'));
$select->addOption('phpstan-dba', realpath(__DIR__.'/../lib/phpstan-dba.neon'));

$fragment = new rex_fragment();
$fragment->setVar('class', 'edit', false);
Expand Down
29 changes: 29 additions & 0 deletions phpstan-bootstrap.php
@@ -1,5 +1,9 @@
<?php

use staabm\PHPStanDba\QueryReflection\RuntimeConfiguration;
use staabm\PHPStanDba\QueryReflection\MysqliQueryReflector;
use staabm\PHPStanDba\QueryReflection\QueryReflection;

unset($REX);
$REX['REDAXO'] = false;

Expand All @@ -14,3 +18,28 @@
$REX['LOAD_PAGE'] = false;

require __DIR__.'/../../core/boot.php';


// phpstan-dba bootstrapping

$configFile = rex_path::coreData('config.yml');
$config = rex_file::getConfig($configFile);

$db = ($config['db'][1] ?? []) + ['host' => '', 'login' => '', 'password' => '', 'name' => ''];

$mysqli = new mysqli(
$db['host'],
$db['login'],
$db['password'],
$db['name']
);

$config = new RuntimeConfiguration();
// $config->debugMode(true);
// $config->stringifyTypes(true);
// $config->analyzeQueryPlans(true);

QueryReflection::setupReflector(
new MysqliQueryReflector($mysqli),
$config
);
5 changes: 5 additions & 0 deletions phpstan.neon.tpl
Expand Up @@ -61,3 +61,8 @@ services:
class: redaxo\phpstan\RexFunctionsDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicFunctionReturnTypeExtension

-
class: redaxo\phpstan\RexSqlDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
1 change: 1 addition & 0 deletions vendor/composer/autoload_classmap.php
Expand Up @@ -11,5 +11,6 @@
'RexStanUserConfig' => $baseDir . '/lib/RexStanUserConfig.php',
'redaxo\\phpstan\\RexClassDynamicReturnTypeExtension' => $baseDir . '/lib/RexClassDynamicReturnTypeExtension.php',
'redaxo\\phpstan\\RexFunctionsDynamicReturnTypeExtension' => $baseDir . '/lib/RexFunctionsDynamicReturnTypeExtension.php',
'redaxo\\phpstan\\RexSqlDynamicReturnTypeExtension' => $baseDir . '/lib/RexSqlDynamicReturnTypeExtension.php',
'rexstan_command' => $baseDir . '/lib/command.php',
);
4 changes: 3 additions & 1 deletion vendor/composer/autoload_psr4.php
Expand Up @@ -6,5 +6,7 @@
$baseDir = dirname($vendorDir);

return array(
'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-deprecation-rules/src', $vendorDir . '/phpstan/phpstan-strict-rules/src', $vendorDir . '/phpstan/phpstan-symfony/src', $vendorDir . '/phpstan/phpstan-phpunit/src'),
'staabm\\PHPStanDba\\' => array($vendorDir . '/staabm/phpstan-dba/src'),
'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-deprecation-rules/src', $vendorDir . '/phpstan/phpstan-phpunit/src', $vendorDir . '/phpstan/phpstan-strict-rules/src', $vendorDir . '/phpstan/phpstan-symfony/src'),
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
);
23 changes: 20 additions & 3 deletions vendor/composer/autoload_static.php
Expand Up @@ -11,19 +11,35 @@ class ComposerStaticInit7d28987f5749c840ef9609f53969aebc
);

public static $prefixLengthsPsr4 = array (
's' =>
array (
'staabm\\PHPStanDba\\' => 18,
),
'P' =>
array (
'PHPStan\\' => 8,
),
'C' =>
array (
'Composer\\Semver\\' => 16,
),
);

public static $prefixDirsPsr4 = array (
'staabm\\PHPStanDba\\' =>
array (
0 => __DIR__ . '/..' . '/staabm/phpstan-dba/src',
),
'PHPStan\\' =>
array (
0 => __DIR__ . '/..' . '/phpstan/phpstan-deprecation-rules/src',
1 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
2 => __DIR__ . '/..' . '/phpstan/phpstan-symfony/src',
3 => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src',
1 => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src',
2 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
3 => __DIR__ . '/..' . '/phpstan/phpstan-symfony/src',
),
'Composer\\Semver\\' =>
array (
0 => __DIR__ . '/..' . '/composer/semver/src',
),
);

Expand All @@ -33,6 +49,7 @@ class ComposerStaticInit7d28987f5749c840ef9609f53969aebc
'RexStanUserConfig' => __DIR__ . '/../..' . '/lib/RexStanUserConfig.php',
'redaxo\\phpstan\\RexClassDynamicReturnTypeExtension' => __DIR__ . '/../..' . '/lib/RexClassDynamicReturnTypeExtension.php',
'redaxo\\phpstan\\RexFunctionsDynamicReturnTypeExtension' => __DIR__ . '/../..' . '/lib/RexFunctionsDynamicReturnTypeExtension.php',
'redaxo\\phpstan\\RexSqlDynamicReturnTypeExtension' => __DIR__ . '/../..' . '/lib/RexSqlDynamicReturnTypeExtension.php',
'rexstan_command' => __DIR__ . '/../..' . '/lib/command.php',
);

Expand Down