From 4a7610376d8260e44955fe3ab6cc4b2d390b6f9e Mon Sep 17 00:00:00 2001 From: JonLee Date: Fri, 4 Sep 2026 00:29:51 +0800 Subject: [PATCH 1/2] fix: add PHPStan static analysis Add phpstan/phpstan as a dev dependency with a level 5 config (src/ only), bootstrap the ThinkPHP framework helper.php so app()/config()/config_path() are recognized, and inline-ignore the remaining false positives caused by think-orm's untyped fluent query builder methods. Run PHPStan as a step in the existing PHPUnit CI job (before the test suite) so a failing check blocks the job, and therefore also blocks semantic-release. --- .github/workflows/phpunit.yml | 3 +++ composer.json | 8 ++++++-- phpstan.neon.dist | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 59c63a0..95f5e23 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -53,6 +53,9 @@ jobs: composer require topthink/framework:${{ matrix.thinkphp }} --no-update --no-interaction composer install --prefer-dist --no-progress --no-suggest + - name: Run PHPStan + run: composer run phpstan -- --no-progress + - name: Run test suite run: ./vendor/bin/phpunit diff --git a/composer.json b/composer.json index 736d551..55914a2 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,11 @@ "require-dev": { "phpunit/phpunit": "~9.0", "php-coveralls/php-coveralls": "^2.7", - "topthink/think": "~8.0" + "topthink/think": "~8.0", + "phpstan/phpstan": "^2.0" + }, + "scripts": { + "phpstan": "phpstan analyse" }, "autoload": { "psr-4": { @@ -50,4 +54,4 @@ ] } } -} +} \ No newline at end of file diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..c2fece6 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,26 @@ +parameters: + level: 5 + paths: + - src + excludePaths: + - vendor + bootstrapFiles: + - vendor/topthink/framework/src/helper.php + ignoreErrors: + - + message: '#^Call to an undefined method iterable\&think\\Collection\:\:hidden\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/adapter/DatabaseAdapter.php + + - + message: '#^Call to an undefined method think\\db\\Query\:\:toArray\(\)\.$#' + identifier: method.notFound + count: 1 + path: src/adapter/DatabaseAdapter.php + + - + message: '#^Parameter \#1 \$model of method tauthz\\cache\\CacheHandlerContract\:\:cachePolicies\(\) expects tauthz\\model\\Rule, think\\db\\Query given\.$#' + identifier: argument.type + count: 2 + path: src/adapter/DatabaseAdapter.php From ce95a3b0d417ec9d4de0f4e3b4e499c6694959c7 Mon Sep 17 00:00:00 2001 From: JonLee Date: Fri, 4 Sep 2026 00:30:07 +0800 Subject: [PATCH 2/2] fix: resolve issues reported by phpstan - import Throwable/LogicException instead of resolving them in the local namespace - add missing return in Publish::execute() - correct Configurable::config() $default PHPDoc type - simplify redundant is_null() checks proven dead code by phpstan --- src/adapter/DatabaseAdapter.php | 7 ++++--- src/command/Publish.php | 26 ++++++++++++++------------ src/traits/Configurable.php | 2 +- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/adapter/DatabaseAdapter.php b/src/adapter/DatabaseAdapter.php index 48ee90f..f61bfa6 100644 --- a/src/adapter/DatabaseAdapter.php +++ b/src/adapter/DatabaseAdapter.php @@ -10,6 +10,7 @@ use Casbin\Exceptions\InvalidFilterTypeException; use tauthz\traits\Configurable; use think\facade\Db; +use Throwable; /** * DatabaseAdapter. @@ -64,7 +65,7 @@ public function filterRule(array $rule): array $i = count($rule) - 1; for (; $i >= 0; $i--) { - if ($rule[$i] != '' && !is_null($rule[$i])) { + if ($rule[$i] != '') { break; } } @@ -359,8 +360,8 @@ public function loadFilteredPolicy(Model $model, $filter): void } $rows = $instance->select()->hidden(['id'])->toArray(); foreach ($rows as $row) { - $row = array_filter($row, fn ($value) => !is_null($value) && $value !== ''); - $line = implode(', ', array_filter($row, fn ($val) => '' != $val && !is_null($val))); + $row = array_filter($row, fn($value) => $value !== ''); + $line = implode(', ', array_filter($row, fn($val) => '' != $val)); $this->loadPolicyLine(trim($line), $model); } diff --git a/src/command/Publish.php b/src/command/Publish.php index 796ef0a..beee32c 100644 --- a/src/command/Publish.php +++ b/src/command/Publish.php @@ -3,6 +3,7 @@ namespace tauthz\command; use think\console\{Command, Input, Output}; +use LogicException; /** * 发布配置文件、迁移文件指令 @@ -28,27 +29,28 @@ protected function configure() protected function execute(Input $input, Output $output) { $destination = $this->app->getRootPath() . '/database/migrations/'; - if(!is_dir($destination)){ + if (!is_dir($destination)) { mkdir($destination, 0755, true); } - $source = __DIR__.'/../../database/migrations/'; + $source = __DIR__ . '/../../database/migrations/'; $handle = dir($source); - - while($entry=$handle->read()) { - if(($entry!=".")&&($entry!="..")){ - if(is_file($source.$entry)){ - copy($source.$entry, $destination.$entry); + + while ($entry = $handle->read()) { + if (($entry != ".") && ($entry != "..")) { + if (is_file($source . $entry)) { + copy($source . $entry, $destination . $entry); } } } - if (!file_exists(config_path().'tauthz-rbac-model.conf')) { - copy(__DIR__.'/../../config/tauthz-rbac-model.conf', config_path().'tauthz-rbac-model.conf'); + if (!file_exists(config_path() . 'tauthz-rbac-model.conf')) { + copy(__DIR__ . '/../../config/tauthz-rbac-model.conf', config_path() . 'tauthz-rbac-model.conf'); } - if (!file_exists(config_path().'tauthz.php')) { - copy(__DIR__.'/../../config/tauthz.php', config_path().'tauthz.php'); + if (!file_exists(config_path() . 'tauthz.php')) { + copy(__DIR__ . '/../../config/tauthz.php', config_path() . 'tauthz.php'); } + + return 0; } } - diff --git a/src/traits/Configurable.php b/src/traits/Configurable.php index 0f50e7d..61f4b82 100644 --- a/src/traits/Configurable.php +++ b/src/traits/Configurable.php @@ -8,7 +8,7 @@ trait Configurable * Gets config value by key. * * @param string $key - * @param string $default + * @param mixed $default * * @return mixed */