nish / phpstan-safestring-rule
PHPStan 扩展:警告不安全的字符串
v0.1.1
2024-08-16 02:26 UTC
Requires
- php: ~7.1 || ~8.0
- phpstan/phpstan: ^1.4.6
Requires (Dev)
README
此包是用于检查不安全字符串的 PHPStan 扩展,例如:检查在未调用 htmlspecialchars 的情况下调用 echo,检查在未使用预处理语句的情况下调用数据库查询。
注意
此包不符合“向后兼容承诺”。因为它扩展了核心的基本处理,不能保证与版本差异兼容。
https://phpstan.org/developing-extensions/backward-compatibility-promise
安装
composer require --dev nish/phpstan-safestring-rule
如何使用
添加到 phpstan.neon
includes: - vendor/nish/phpstan-safestring-rule/extension.neon services: - class: Nish\PHPStan\Rules\EchoHtmlRule tags: [phpstan.rules.rule] - factory: Nish\PHPStan\Type\SafeHtmlStringReturnTypeExtension([htmlspecialchars, h, raw]) tags: [phpstan.broker.dynamicFunctionReturnTypeExtension]
composer.json
是
"autoload": { "psr-4": { "App\\": "src" }, "files": [ "src/functions.php" ] },
值对象类 src/ProductDto.php
<?php namespace App; class ProductDto { /** @var int */ public $product_id; /** @var string */ public $name; /** @var ?string */ public $description; }
HTML 模板 src/ProductHtml.php
<?php namespace App; class ProductHtml { public function view(ProductDto $product): void { ?> <div> <div> <?= $product->product_id ?> </div> <div> <?= $product->name ?> </div> <div> <?= $product->description ?> </div> </div> <?php } }
在这种情况下,phpstan 的执行结果如下
3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ ----------------------------------------------------
Line ProductHtml.php
------ ----------------------------------------------------
12 Parameter #1 (string) is not safehtml-string.
15 Parameter #1 (string|null) is not safehtml-string.
------ ----------------------------------------------------
[ERROR] Found 2 errors
然后,不能直接调用 echo 字符串类型。
safehtml-string
是一个虚拟类型,可以通过添加一个辅助函数来解决。
src/functions.php
:
<?php /** * @param int|string|null $input */ function h($input): string { return htmlspecialchars((string)$input); } /** * @param int|string|null $input */ function raw($input): string { return (string)$input; }
phpstan.neon
services: # ... - factory: Nish\PHPStan\Type\SafeHtmlStringReturnTypeExtension([htmlspecialchars, h, raw]) tags: [phpstan.broker.dynamicFunctionReturnTypeExtension]
src/ProductHtml.php
:
<?php namespace App; class ProductHtml { public function view(ProductDto $product): void { ?> <div> <div> <?= $product->product_id ?> </div> <div> <?= h($product->name) ?> </div> <div> <?= h($product->description) ?> </div> </div> <?php } }
运行 phpstan
an/phpstan.neon.
3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
[OK] No errors
OK,没有错误,它很安全!
提示
常量字符串类型不需要转换为 safehtml-string。
<?php namespace App; class TypeHtml { const CURRENT_TYPE_ID = 2; const TYPES = [ 1 => 'TYPE 1', 2 => 'TYPE 2', 3 => 'TYPE 3', ]; public function view(): void { ?> <div> <div> <?= self::CURRENT_TYPE_ID ?> </div> <div> <?= self::TYPES[self::CURRENT_TYPE_ID] ?> </div> </div> <?php } }
这是没有错误的。
当用于方法而不是函数时
- factory: Nish\PHPStan\Type\SafeHtmlStringReturnTypeExtension(DateTimeInterface::format) tags: [phpstan.broker.dynamicMethodReturnTypeExtension] - factory: Nish\PHPStan\Type\SafeHtmlStringReturnTypeExtension(App\FormUtil::makeForm) tags: [phpstan.broker.dynamicMethodReturnTypeExtension]
一次不能指定多个。
使用 safe-string 自定义类型
如果您有以下数据库访问程序
<?php namespace App; use PDO; class ProductDb { /** @var PDO */ private $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } /** * @return array<int,ProductDto> */ public function getProductList(string $where): array { $stmt = $this->pdo->query('select * from products ' . $where); if (!$stmt) return []; $ret = $stmt->fetchAll(PDO::FETCH_CLASS, ProductDto::class); if (!$ret) return []; /** @var array<int,ProductDto> $ret */ return $ret; } }
pdo->query()
是不安全的。
如果类是以下程序
<?php namespace App; use PDO; class ProductPage { /** @return mixed */ public static function index(PDO $pdo, string $where) { $productModel = new ProductDb($pdo); $products = $productModel->getProductList($where); return [ 'templateData' => ['products' => $products], ]; } }
我想显示一个错误。
通过在 phpstan.neon 中写入以下设置来实现。
services: # ... - factory: Nish\PHPStan\Rules\SafeStringCallRule([ 'PDO::query': 0, ]) tags: [phpstan.rules.rule]
0
是参数的索引。
运行 phpstan。
6/6 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ -------------------------------------------
Line ProductDb.php
------ -------------------------------------------
22 Parameter #1 (string) is not safe-string.
------ -------------------------------------------
[ERROR] Found 1 error
更多控制,可以使用 safe-string
类型。
/** * @param safe-string $where * @return array<int,ProductDto> */ public function getProductList(string $where): array
如果我写下提示会发生什么?
6/6 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ ------------------------------------------------------
Line ProductPage.php
------ ------------------------------------------------------
13 Parameter #1 $where of method
App\ProductDb::getProductList() expects safe-string,
string given.
------ ------------------------------------------------------
[ERROR] Found 1 error
改为调用者错误。
如果字符串明显是“常量字符串(及其派生类型)”,则不会抛出错误。
class ProductPage { /** @return mixed */ public static function index(PDO $pdo, int $id) { $productModel = new ProductDb($pdo); $where = sprintf('where product_id > %d', $id); $products = $productModel->getProductList($where);
6/6 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
[OK] No errors
提示
添加返回类型规则
factory: Nish\PHPStan\Rules\SafeStringReturnTypeRule([
App\Db\Utils::getSafeConditionString,
])
tags: [phpstan.rules.rule]