From 57c0a82ca0e31af8c8f2981635b7490b0d233138 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 11:33:27 -0400 Subject: [PATCH 01/19] composer require phpstan/phpstan phpstan/extension-installer --dev -W --- composer.json | 9 ++++++--- phpstan.dist.neon | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 phpstan.dist.neon diff --git a/composer.json b/composer.json index 2e048d9..ca16beb 100644 --- a/composer.json +++ b/composer.json @@ -42,13 +42,16 @@ }, "config": { "allow-plugins": { - "pestphp/pest-plugin": true + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true } }, "require-dev": { "pestphp/pest": "^1.0 | ^2.0", "friendsofphp/php-cs-fixer": "^3.64", - "leafs/alchemy": "*" + "leafs/alchemy": "*", + "phpstan/phpstan": "^2.1", + "phpstan/extension-installer": "^1.4" }, "scripts": { "alchemy": "./vendor/bin/alchemy setup", @@ -56,4 +59,4 @@ "lint": "./vendor/bin/alchemy setup --lint", "actions": "./vendor/bin/alchemy setup --actions" } -} \ No newline at end of file +} diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..02619e8 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,8 @@ +includes: + - vendor/phpstan/phpstan/conf/bleedingEdge.neon + +parameters: + level: 0 + paths: + - src + - tests From 7e14e5f6a70b6924a542bd27a4b8da5ad4bc7cab Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 11:33:37 -0400 Subject: [PATCH 02/19] add phpstan badge to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 75f39bb..123258a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@

+![PHPStan: Level 0](https://img.shields.io/badge/PHPStan-level%200-brightgreen.svg?style=flat) [![Latest Stable Version](https://poser.pugx.org/leafs/auth/v/stable)](https://packagist.org/packages/leafs/auth) [![Total Downloads](https://poser.pugx.org/leafs/auth/downloads)](https://packagist.org/packages/leafs/auth) [![License](https://poser.pugx.org/leafs/auth/license)](https://packagist.org/packages/leafs/auth) From 059f2c78422c499182d690236401d04050e35506 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 18:55:26 -0400 Subject: [PATCH 03/19] composer exec phpstan -- --generate-baseline --- phpstan-baseline.neon | 97 +++++++++++++++++++++++++++++++++++++++++++ phpstan.dist.neon | 1 + 2 files changed, 98 insertions(+) create mode 100644 phpstan-baseline.neon diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..f207b19 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,97 @@ +parameters: + ignoreErrors: + - + rawMessage: 'Call to static method error() on an unknown class Leaf\Exception\General.' + identifier: class.notFound + count: 4 + path: src/Auth.php + + - + rawMessage: Function _env not found. + identifier: function.notFound + count: 7 + path: src/Auth.php + + - + rawMessage: Function app not found. + identifier: function.notFound + count: 9 + path: src/Auth.php + + - + rawMessage: Instantiated class League\OAuth2\Client\Provider\Google not found. + identifier: class.notFound + count: 1 + path: src/Auth.php + + - + rawMessage: Access to constant STATUS_ACTIVE on an unknown class Leaf\Billing\Subscription. + identifier: class.notFound + count: 1 + path: src/Auth/User.php + + - + rawMessage: Access to constant STATUS_CANCELLED on an unknown class Leaf\Billing\Subscription. + identifier: class.notFound + count: 1 + path: src/Auth/User.php + + - + rawMessage: Access to constant STATUS_TRIAL on an unknown class Leaf\Billing\Subscription. + identifier: class.notFound + count: 1 + path: src/Auth/User.php + + - + rawMessage: Function billing not found. + identifier: function.notFound + count: 3 + path: src/Auth/User.php + + - + rawMessage: 'Call to static method get() on an unknown class Leaf\Config.' + identifier: class.notFound + count: 1 + path: src/functions.php + + - + rawMessage: 'Call to static method getStatic() on an unknown class Leaf\Config.' + identifier: class.notFound + count: 1 + path: src/functions.php + + - + rawMessage: 'Call to static method singleton() on an unknown class Leaf\Config.' + identifier: class.notFound + count: 1 + path: src/functions.php + + - + rawMessage: 'Undefined variable: $this' + identifier: variable.undefined + count: 1 + path: tests/register.test.php + + - + rawMessage: Function createUsersTable not found. + identifier: function.notFound + count: 1 + path: tests/table.test.php + + - + rawMessage: 'Undefined variable: $this' + identifier: variable.undefined + count: 5 + path: tests/table.test.php + + - + rawMessage: 'Undefined variable: $this' + identifier: variable.undefined + count: 7 + path: tests/update.test.php + + - + rawMessage: 'Undefined variable: $this' + identifier: variable.undefined + count: 2 + path: tests/user.test.php diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 02619e8..258c749 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -1,5 +1,6 @@ includes: - vendor/phpstan/phpstan/conf/bleedingEdge.neon + - phpstan-baseline.neon parameters: level: 0 From 403f60fbf9485f47478964741f8747cb0a4d6c17 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:03:46 -0400 Subject: [PATCH 04/19] remove unused $testUser in user.test.php --- tests/user.test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/user.test.php b/tests/user.test.php index 4086518..c100b2e 100644 --- a/tests/user.test.php +++ b/tests/user.test.php @@ -63,7 +63,7 @@ expect($auth->user())->toBeInstanceOf(\Leaf\Auth\User::class); expect($auth->user()->username)->toBe($testUser['username']); - $auth->logout(function ($auth) use ($testUser) { + $auth->logout(function ($auth) { expect($auth)->toBeInstanceOf(\Leaf\Auth::class); }); From 82930a9c4b28ec7c4a2c4ef2b1c2f2bc7fabe21a Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:05:00 -0400 Subject: [PATCH 05/19] fix Auth @param and @returns following phpstan l2 --- phpstan-baseline.neon | 252 ++++++++++++++++++++++++++++++++++++++++++ phpstan.dist.neon | 2 +- src/Auth.php | 6 +- 3 files changed, 256 insertions(+), 4 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f207b19..e348cf9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,11 @@ parameters: ignoreErrors: + - + rawMessage: Access to an undefined property Leaf\Auth\User::$email. + identifier: property.notFound + count: 1 + path: src/Auth.php + - rawMessage: 'Call to static method error() on an unknown class Leaf\Exception\General.' identifier: class.notFound @@ -24,6 +30,24 @@ parameters: count: 1 path: src/Auth.php + - + rawMessage: 'Method Leaf\Auth::client() has invalid return type League\OAuth2\Client\Provider\AbstractProvider.' + identifier: class.notFound + count: 1 + path: src/Auth.php + + - + rawMessage: 'Parameter $client of method Leaf\Auth::withProvider() has invalid type League\OAuth2\Client\Provider\AbstractProvider.' + identifier: class.notFound + count: 1 + path: src/Auth.php + + - + rawMessage: 'Variable $credentials in isset() is never defined.' + identifier: isset.variable + count: 2 + path: src/Auth.php + - rawMessage: Access to constant STATUS_ACTIVE on an unknown class Leaf\Billing\Subscription. identifier: class.notFound @@ -66,12 +90,174 @@ parameters: count: 1 path: src/functions.php + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 4 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeFalse() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeInstanceOf() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeNull() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeNull() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeString() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to method toBeTrue() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 3 + path: tests/login.test.php + + - + rawMessage: Access to an undefined property Leaf\Auth\User::$password. + identifier: property.notFound + count: 2 + path: tests/register.test.php + + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 2 + path: tests/register.test.php + + - + rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/register.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 3 + path: tests/register.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/register.test.php + + - + rawMessage: 'Call to method toBeFalse() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/register.test.php + + - + rawMessage: 'Call to method toBeInstanceOf() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/register.test.php + + - + rawMessage: 'Call to method toBeTrue() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 6 + path: tests/register.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined count: 1 path: tests/register.test.php + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 6 + path: tests/session.test.php + + - + rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 20 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBeGreaterThan() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBeInstanceOf() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 8 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBeNull() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 6 + path: tests/session.test.php + + - + rawMessage: 'Call to method toBeTrue() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 8 + path: tests/session.test.php + + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 2 + path: tests/table.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 5 + path: tests/table.test.php + - rawMessage: Function createUsersTable not found. identifier: function.notFound @@ -84,12 +270,78 @@ parameters: count: 5 path: tests/table.test.php + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 2 + path: tests/update.test.php + + - + rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/update.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 3 + path: tests/update.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/update.test.php + + - + rawMessage: 'Call to method toBeFalse() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/update.test.php + + - + rawMessage: 'Call to method toBeTrue() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/update.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined count: 7 path: tests/update.test.php + - + rawMessage: Access to an undefined property Leaf\Auth\User::$username. + identifier: property.notFound + count: 2 + path: tests/user.test.php + + - + rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/user.test.php + + - + rawMessage: 'Call to method toBeInstanceOf() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 3 + path: tests/user.test.php + + - + rawMessage: 'Call to method toBeNull() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 1 + path: tests/user.test.php + + - + rawMessage: 'Call to method toBeTrue() of internal class Pest\Expectation from outside its root namespace Pest.' + identifier: method.internalClass + count: 2 + path: tests/user.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 258c749..dacb77e 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,7 @@ includes: - phpstan-baseline.neon parameters: - level: 0 + level: 2 paths: - src - tests diff --git a/src/Auth.php b/src/Auth.php index 7806538..12d64db 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -142,7 +142,7 @@ public function autoConnect(array $pdoOptions = []) * Pass in db connection instance directly * * @param \PDO $connection A connection instance of your db - * @return $this; + * @return $this */ public function dbConnection(\PDO $connection) { @@ -553,7 +553,7 @@ public function find($id) * --- * Create an account for another user * - * @param array The user details to save + * @param array $userData The user details to save */ public function createUserFor($userData) { @@ -619,7 +619,7 @@ public function oauthToken() * --- * Sign out the currently authenticated user * - * @param string|array|callable|null $redirectUrl Redirect to this url after logout + * @param string|array|callable|null $action Redirect to this url after logout * @return bool */ public function logout($action = null): bool From da96e8a68da391dc7ab2c12ffc1107450b4b61f3 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:05:41 -0400 Subject: [PATCH 06/19] change Leaf\DB to Leaf\Db --- src/Auth/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/User.php b/src/Auth/User.php index c397b18..47d111e 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -20,7 +20,7 @@ class User /** * Internal instance of Leaf database - * @var \Leaf\DB + * @var \Leaf\Db */ protected $db; From 2abe7455ddaa0b32c9a6bddbd3cdc27dc4a28409 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:07:17 -0400 Subject: [PATCH 07/19] ensure $sessionLifetime will be integer --- src/Auth/User.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/User.php b/src/Auth/User.php index 47d111e..b7d5edc 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -81,7 +81,7 @@ public function __construct($data, $session = true) $sessionLifetime = $sessionLifetime && !is_numeric($sessionLifetime) ? strtotime($sessionLifetime) - : (time() + $sessionLifetime); + : (time() + intval($sessionLifetime)); $this->tokens['access'] = $this->generateToken($sessionLifetime); $this->tokens['refresh'] = $this->generateToken($sessionLifetime + 259200); From 5ce34eecc6c7e382d2cda22628bdd79a15d5ee4b Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:07:46 -0400 Subject: [PATCH 08/19] fix $roles param in UsesRoles::getRolePermissions --- src/Auth/UsesRoles.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/UsesRoles.php b/src/Auth/UsesRoles.php index ba82196..86b434d 100644 --- a/src/Auth/UsesRoles.php +++ b/src/Auth/UsesRoles.php @@ -166,7 +166,7 @@ protected function setRolesAndPermissions($roles): void /** * Get the permissions for a role * - * @param string|array $role + * @param string|array $roles * @return array */ protected function getRolePermissions($roles): array From 8e3333b5013e3bf8a7ca6d68d905b0164b93b1ef Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:08:17 -0400 Subject: [PATCH 09/19] remove undefined parameter $subcription in UsesSubscriptions::subscriptions --- src/Auth/UsesSubscriptions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Auth/UsesSubscriptions.php b/src/Auth/UsesSubscriptions.php index 2dabad7..cce488d 100644 --- a/src/Auth/UsesSubscriptions.php +++ b/src/Auth/UsesSubscriptions.php @@ -21,7 +21,6 @@ trait UsesSubscriptions /** * Get current subscription * - * @param string|array $subscription The subscription to assign * @return array|null */ public function subscription(): ?array From c6df37b1eaeef2dafb6231b8bed3fa6c9fed2e05 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:21:03 -0400 Subject: [PATCH 10/19] allow null in Auth::$user --- phpstan-baseline.neon | 6 ++++++ phpstan.dist.neon | 2 +- src/Auth.php | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e348cf9..14071bd 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -258,6 +258,12 @@ parameters: count: 5 path: tests/table.test.php + - + rawMessage: Cannot access offset 'user' on bool. + identifier: offsetAccess.nonOffsetAccessible + count: 3 + path: tests/table.test.php + - rawMessage: Function createUsersTable not found. identifier: function.notFound diff --git a/phpstan.dist.neon b/phpstan.dist.neon index dacb77e..f909955 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,7 @@ includes: - phpstan-baseline.neon parameters: - level: 2 + level: 3 paths: - src - tests diff --git a/src/Auth.php b/src/Auth.php index 12d64db..d0519c6 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -22,9 +22,9 @@ class Auth { /** * The currently authenticated user - * @var User + * @var ?User */ - protected $user; + protected $user = null; /** * Internal instance of Leaf DB From fdec2b9146506ac6eac687caf9309d691e2c27d8 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:21:39 -0400 Subject: [PATCH 11/19] todo: rewrite the test, Auth::update doesn't returns an array --- tests/table.test.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/table.test.php b/tests/table.test.php index ff235cd..f33216b 100644 --- a/tests/table.test.php +++ b/tests/table.test.php @@ -59,6 +59,7 @@ $this->fail(json_encode($auth->errors())); } + // TODO: $response is a bool, not a bool|array $response = $auth->update([ 'username' => 'test-user55', 'email' => 'test-user55@example.com', From 3e8e73347335db765ca4962a497365553d32da57 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 20:31:23 -0400 Subject: [PATCH 12/19] if !$this->user() is true, user is not authenticated, else php checks the right side of ||, for then $this->user() will be Leaf\Auth\User --- phpstan.dist.neon | 3 ++- src/Auth.php | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index f909955..95e4a99 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,8 @@ includes: - phpstan-baseline.neon parameters: - level: 3 + level: 4 paths: - src - tests + treatPhpDocTypesAsCertain: false diff --git a/src/Auth.php b/src/Auth.php index d0519c6..98d6cba 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -762,7 +762,7 @@ public function middleware(string $middleware, callable $callback) if ($middleware === 'is') { return app()->registerMiddleware('is', function ($role) use ($callback) { - if (!$this->user() || ($this->user() && $this->user()->isNot($role))) { + if (!$this->user() || $this->user()->isNot($role)) { $callback($role); exit; } @@ -771,7 +771,7 @@ public function middleware(string $middleware, callable $callback) if ($middleware === 'isNot') { return app()->registerMiddleware('isNot', function ($role) use ($callback) { - if (!$this->user() || ($this->user() && $this->user()->is($role))) { + if (!$this->user() || $this->user()->is($role)) { $callback($role); exit; } @@ -780,7 +780,7 @@ public function middleware(string $middleware, callable $callback) if ($middleware === 'can') { return app()->registerMiddleware('can', function ($role) use ($callback) { - if (!$this->user() || ($this->user() && $this->user()->cannot($role))) { + if (!$this->user() || $this->user()->cannot($role)) { $callback($role); exit; } @@ -789,7 +789,7 @@ public function middleware(string $middleware, callable $callback) if ($middleware === 'cannot') { return app()->registerMiddleware('cannot', function ($role) use ($callback) { - if (!$this->user() || ($this->user() && $this->user()->can($role))) { + if (!$this->user() || $this->user()->can($role)) { $callback($role); exit; } From 63a6568b9b1c3ca9cf600580e63770efd64df344 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:35:49 -0400 Subject: [PATCH 13/19] composer require leafs/leaf league/oauth2-google --dev -W --- composer.json | 4 ++- phpstan-baseline.neon | 60 +++++++++++-------------------------------- 2 files changed, 18 insertions(+), 46 deletions(-) diff --git a/composer.json b/composer.json index ca16beb..9c8bc6b 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,9 @@ "friendsofphp/php-cs-fixer": "^3.64", "leafs/alchemy": "*", "phpstan/phpstan": "^2.1", - "phpstan/extension-installer": "^1.4" + "phpstan/extension-installer": "^1.4", + "leafs/leaf": "^4.4", + "league/oauth2-google": "^4.0" }, "scripts": { "alchemy": "./vendor/bin/alchemy setup", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 14071bd..7d16ea1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6,42 +6,6 @@ parameters: count: 1 path: src/Auth.php - - - rawMessage: 'Call to static method error() on an unknown class Leaf\Exception\General.' - identifier: class.notFound - count: 4 - path: src/Auth.php - - - - rawMessage: Function _env not found. - identifier: function.notFound - count: 7 - path: src/Auth.php - - - - rawMessage: Function app not found. - identifier: function.notFound - count: 9 - path: src/Auth.php - - - - rawMessage: Instantiated class League\OAuth2\Client\Provider\Google not found. - identifier: class.notFound - count: 1 - path: src/Auth.php - - - - rawMessage: 'Method Leaf\Auth::client() has invalid return type League\OAuth2\Client\Provider\AbstractProvider.' - identifier: class.notFound - count: 1 - path: src/Auth.php - - - - rawMessage: 'Parameter $client of method Leaf\Auth::withProvider() has invalid type League\OAuth2\Client\Provider\AbstractProvider.' - identifier: class.notFound - count: 1 - path: src/Auth.php - - rawMessage: 'Variable $credentials in isset() is never defined.' identifier: isset.variable @@ -73,22 +37,28 @@ parameters: path: src/Auth/User.php - - rawMessage: 'Call to static method get() on an unknown class Leaf\Config.' - identifier: class.notFound + rawMessage: 'Function createTableForUsers() has parameter $table with no type specified.' + identifier: missingType.parameter count: 1 - path: src/functions.php + path: tests/Pest.php - - rawMessage: 'Call to static method getStatic() on an unknown class Leaf\Config.' - identifier: class.notFound + rawMessage: 'Function deleteUser() has no return type specified.' + identifier: missingType.return count: 1 - path: src/functions.php + path: tests/Pest.php - - rawMessage: 'Call to static method singleton() on an unknown class Leaf\Config.' - identifier: class.notFound + rawMessage: 'Function deleteUser() has parameter $table with no type specified.' + identifier: missingType.parameter + count: 1 + path: tests/Pest.php + + - + rawMessage: 'Function getDatabaseConnection() return type has no value type specified in iterable type array.' + identifier: missingType.iterableValue count: 1 - path: src/functions.php + path: tests/Pest.php - rawMessage: Access to an undefined property Leaf\Auth\User::$username. From c080d0f5f5416a0aa0ce91e049847f242c94e0d7 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:36:20 -0400 Subject: [PATCH 14/19] improve docblocks typehinting with phpstan l6 --- phpstan.dist.neon | 2 +- src/Auth.php | 96 +++++++++++++++++++++++++--------- src/Auth/Config.php | 17 +++--- src/Auth/Model.php | 24 +++++++-- src/Auth/User.php | 37 +++++++++++-- src/Auth/UsesRoles.php | 28 +++++----- src/Auth/UsesSubscriptions.php | 4 +- 7 files changed, 148 insertions(+), 60 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 95e4a99..88e63f8 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,7 @@ includes: - phpstan-baseline.neon parameters: - level: 4 + level: 6 paths: - src - tests diff --git a/src/Auth.php b/src/Auth.php index 98d6cba..4eb13d8 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -34,13 +34,11 @@ class Auth /** * All errors caught - * @var array + * @var array */ protected $errorsArray = []; - /** - * Configured oauth clients - */ + /** @var array Configured oauth clients */ protected $oauthClients = []; public function __construct() @@ -113,7 +111,14 @@ class_exists('League\OAuth2\Client\Provider\Google') && /** * Connect leaf auth to the database - * @param array $dbConfig Configuration for leaf db connection + * @param array{ + * host?: string, + * dbname?: string, + * username?: string, + * password?: string, + * dbtype?: string, + * pdoOptions?: mixed[], + * } $dbConfig Configuration for leaf db connection * @return $this */ public function connect($dbConfig = []) @@ -127,7 +132,7 @@ public function connect($dbConfig = []) /** * Connect to database using environment variables * - * @param array $pdoOptions Options for PDO connection + * @param mixed[] $pdoOptions Options for PDO connection * @return $this */ public function autoConnect(array $pdoOptions = []) @@ -158,7 +163,10 @@ public function dbConnection(\PDO $connection) * Register a Google OAuth client to use with Leaf Auth, should be a league/oauth2-client compatible client. * @param string $clientId * @param string $clientSecret - * @param array $options + * @param array{ + * name?: string, + * redirectUri?: string, + * } $options * @return static */ public function withGoogle( @@ -210,8 +218,9 @@ public function client(string $clientName) /** * Get/Set Leaf Auth config * - * @param string|array $config The auth config key or array of config + * @param string|array $config The auth config key or array of config * @param mixed $value The value if $config is a string + * @return mixed|void */ public function config($config, $value = null) { @@ -229,7 +238,7 @@ public function config($config, $value = null) /** * Create roles and permissions * - * @param array $roles Array of roles and their permissions + * @param array $roles Array of roles and their permissions * @return Auth */ public function createRoles(array $roles) @@ -244,7 +253,7 @@ public function createRoles(array $roles) /** * Return all roles and their permissions * - * @return array + * @return array */ public function roles() { @@ -256,7 +265,7 @@ public function roles() * --- * Verify user credentials and sign them in with token or session * - * @param array $credentials User credentials + * @param array $credentials User credentials * @return bool */ public function login(array $credentials): bool @@ -310,7 +319,7 @@ public function login(array $credentials): bool * --- * Save a new user to the database * - * @param array $userData User data + * @param array $userData User data * @return bool */ public function register(array $userData): bool @@ -371,7 +380,7 @@ public function register(array $userData): bool * --- * Update user data in the database * - * @param array $userData User data + * @param array $userData User data * @return bool */ public function update(array $userData): bool @@ -495,7 +504,7 @@ public function updatePassword(string $oldPassword, string $newPassword): bool /** * Create a new user from OAuth * - * @param array $userData User data + * @param array $userData User data * * @return bool */ @@ -553,7 +562,9 @@ public function find($id) * --- * Create an account for another user * - * @param array $userData The user details to save + * @param array $userData The user details to save + * @return User|false|never + * @throws \Exception If database connection is not established */ public function createUserFor($userData) { @@ -608,6 +619,7 @@ public function createUserFor($userData) /** * Get saved OAuth token + * @return mixed */ public function oauthToken() { @@ -619,7 +631,7 @@ public function oauthToken() * --- * Sign out the currently authenticated user * - * @param string|array|callable|null $action Redirect to this url after logout + * @param string|mixed[]|callable|null $action Redirect to this url after logout * @return bool */ public function logout($action = null): bool @@ -716,7 +728,10 @@ public function data() /** * Get generated access tokens - * @return array|null + * @return array{ + * access?: string, + * refresh?: string, + * }|null */ public function tokens() { @@ -733,6 +748,8 @@ public function tokens() * Register auth middleware for your Leaf apps * @param string $middleware The middleware to register * @param callable $callback The callback to run if middleware fails + * @return void|never + * @throws \Exception If not used with leafs/leaf installed */ public function middleware(string $middleware, callable $callback) { @@ -741,16 +758,18 @@ public function middleware(string $middleware, callable $callback) } if ($middleware === 'auth.required') { - return app()->registerMiddleware('auth.required', function () use ($callback) { + app()->registerMiddleware('auth.required', function () use ($callback) { if (!$this->user()) { $callback(); exit; } }); + + return; } if ($middleware === 'auth.guest') { - return app()->registerMiddleware('auth.guest', function () use ($callback) { + app()->registerMiddleware('auth.guest', function () use ($callback) { if ($this->user()) { $callback(); exit; @@ -758,60 +777,74 @@ public function middleware(string $middleware, callable $callback) auth()->clearErrors(); }); + + return; } if ($middleware === 'is') { - return app()->registerMiddleware('is', function ($role) use ($callback) { + app()->registerMiddleware('is', function ($role) use ($callback) { if (!$this->user() || $this->user()->isNot($role)) { $callback($role); exit; } }); + + return; } if ($middleware === 'isNot') { - return app()->registerMiddleware('isNot', function ($role) use ($callback) { + app()->registerMiddleware('isNot', function ($role) use ($callback) { if (!$this->user() || $this->user()->is($role)) { $callback($role); exit; } }); + + return; } if ($middleware === 'can') { - return app()->registerMiddleware('can', function ($role) use ($callback) { + app()->registerMiddleware('can', function ($role) use ($callback) { if (!$this->user() || $this->user()->cannot($role)) { $callback($role); exit; } }); + + return; } if ($middleware === 'cannot') { - return app()->registerMiddleware('cannot', function ($role) use ($callback) { + app()->registerMiddleware('cannot', function ($role) use ($callback) { if (!$this->user() || $this->user()->can($role)) { $callback($role); exit; } }); + + return; } if ($middleware === 'auth.verified') { - return app()->registerMiddleware('auth.verified', function () use ($callback) { + app()->registerMiddleware('auth.verified', function () use ($callback) { if (!$this->user() || !$this->user()->isVerified()) { $callback(); exit; } }); + + return; } if ($middleware === 'auth.unverified') { - return app()->registerMiddleware('auth.unverified', function () use ($callback) { + app()->registerMiddleware('auth.unverified', function () use ($callback) { if (!$this->user() || $this->user()->isVerified()) { $callback(); exit; } }); + + return; } app()->registerMiddleware($middleware, $callback); @@ -819,6 +852,7 @@ public function middleware(string $middleware, callable $callback) /** * Parse the current user's token + * @return array|null */ public function parseToken() { @@ -904,6 +938,10 @@ protected function checkDbConnection(): void } } + /** + * @param string|array $value + * @return ?mixed + */ protected function getFromSession($value) { if ($this->checkAndExpireSession()) { @@ -913,6 +951,10 @@ protected function getFromSession($value) return Session::get($value); } + /** + * @return void|never + * @throws \Exception If sessions are not enabled + */ protected function sessionCheck() { if (!Config::get('session')) { @@ -937,6 +979,7 @@ protected function checkAndExpireSession(): bool return $isSessionExpired; } + /** @return ?string */ protected function getTokenFromRequest() { $headers = null; @@ -966,6 +1009,7 @@ protected function getTokenFromRequest() return null; } + /** @return ?mixed */ protected function getTokenFromSession() { return Session::get('auth.token'); @@ -973,6 +1017,7 @@ protected function getTokenFromSession() /** * Clear all errors caught + * @return void */ public function clearErrors() { @@ -981,6 +1026,7 @@ public function clearErrors() /** * Return all errors caught + * @return array */ public function errors(): array { diff --git a/src/Auth/Config.php b/src/Auth/Config.php index adec30e..865e192 100644 --- a/src/Auth/Config.php +++ b/src/Auth/Config.php @@ -12,10 +12,7 @@ */ class Config { - /** - * configuration for Leaf Auth - * @var array - */ + /** @var array Configuration for Leaf Auth */ protected static array $config = [ 'id.key' => 'id', 'db.table' => 'users', @@ -42,13 +39,12 @@ class Config 'messages.loginPasswordError' => 'Password is incorrect!', ]; - /** - * Additional user information for cache - */ + /** @var array Additional user information for cache */ protected static array $userCache = []; /** * Set Leaf Auth config + * @param array $config */ public static function set($config): void { @@ -57,6 +53,7 @@ public static function set($config): void /** * Overwrite Leaf Auth config + * @param array $config */ public static function overwrite($config): void { @@ -65,6 +62,8 @@ public static function overwrite($config): void /** * Get Leaf Auth config + * @param ?string $key + * @return ?mixed */ public static function get($key = null) { @@ -77,6 +76,8 @@ public static function get($key = null) /** * Set user cache + * @param string $key + * @param mixed $value */ public static function setUserCache($key, $value): void { @@ -89,6 +90,8 @@ public static function setUserCache($key, $value): void /** * Get user cache + * @param ?string $key + * @return mixed|array */ public static function getUserCache($key = null) { diff --git a/src/Auth/Model.php b/src/Auth/Model.php index f14ecb0..23b761e 100644 --- a/src/Auth/Model.php +++ b/src/Auth/Model.php @@ -29,11 +29,16 @@ class Model */ protected \Leaf\Db $db; - /** - * User data to save - */ + /** @var array User data to save */ protected array $dataToSave = []; + /** + * @param array{ + * db: \Leaf\Db, + * user: User, + * table: string, + * } $data + */ public function __construct($data) { $this->db = $data['db']; @@ -44,7 +49,7 @@ public function __construct($data) /** * Create a new model resource * - * @param array $data Data to be inserted + * @param array $data Data to be inserted * * @return PDOStatement|null */ @@ -64,7 +69,7 @@ public function create(array $data): ?PDOStatement /** * Update a model resource * - * @param array $data Data to be updated + * @param array $data Data to be updated * * @return \Leaf\Db */ @@ -115,11 +120,20 @@ public function save(): ?PDOStatement return $success; } + /** + * @param string $name + * @param mixed $value + */ public function __set($name, $value) { $this->dataToSave[$name] = $value; } + /** + * @param string $name + * @param mixed[] $arguments + * @return mixed + */ public function __call($name, $arguments) { return $this->table()->$name(...$arguments); diff --git a/src/Auth/User.php b/src/Auth/User.php index b7d5edc..c551233 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -30,16 +30,21 @@ class User */ protected $session; - /** - * User Information - */ + /** @var array User Information */ protected array $data = []; /** - * User Tokens + * @var array{ + * access?: string, + * refresh?: string, + * } User Tokens */ protected array $tokens = []; + /** + * @param array $data + * @param bool $session + */ public function __construct($data, $session = true) { $this->data = $data; @@ -125,7 +130,10 @@ public function getAuthInfo(): object /** * Return generated tokens - * @return array + * @return array{ + * access?: string, + * refresh?: string, + * } */ public function tokens(): array { @@ -134,6 +142,7 @@ public function tokens(): array /** * Generate a new JWT for the user + * @param int $tokenLifetime * @return string */ public function generateToken($tokenLifetime): string @@ -214,6 +223,7 @@ public function verifyEmail(): bool } } + /** @return array */ public function get() { $userData = $this->data; @@ -258,6 +268,10 @@ public function __toString() return json_encode($this->get()); } + /** + * @param string $name + * @return ?mixed + */ public function __get($name) { // using data instead of get() here because @@ -266,16 +280,29 @@ public function __get($name) return $this->data[$name] ?? null; } + /** + * @param string $name + * @param mixed $value + * @return void + */ public function __set($name, $value) { $this->data[$name] = $value; } + /** + * @param string $name + * @return bool + */ public function __isset($name) { return isset($this->data[$name]); } + /** + * @param string $name + * @return void + */ public function __unset($name) { unset($this->data[$name]); diff --git a/src/Auth/UsesRoles.php b/src/Auth/UsesRoles.php index 86b434d..c4b3690 100644 --- a/src/Auth/UsesRoles.php +++ b/src/Auth/UsesRoles.php @@ -12,20 +12,16 @@ */ trait UsesRoles { - /** - * User Permissions - */ + /** @var string[] User Permissions */ protected array $permissions = []; - /** - * User Roles - */ + /** @var string[] User Roles */ protected array $roles = []; /** * Assign new role to user * - * @param string|array $role The role to assign + * @param string|string[] $role The role to assign * @return bool */ public function assign($role): bool @@ -55,7 +51,7 @@ public function assign($role): bool /** * Check if user has a permission - * @param string|array $permission The permission(s) to check + * @param string|string[] $permission The permission(s) to check * @return bool */ public function can($permission): bool @@ -69,6 +65,7 @@ public function can($permission): bool /** * Check if a user does not have a permission + * @param string[] $permission */ public function cannot($permission): bool { @@ -77,7 +74,7 @@ public function cannot($permission): bool /** * Check if user has a role - * @param string|array $role The role(s) to check + * @param string|string[] $role The role(s) to check * @return bool */ public function is($role): bool @@ -91,6 +88,7 @@ public function is($role): bool /** * Check if user does not have a role + * @param string[] $role */ public function isNot($role): bool { @@ -99,7 +97,7 @@ public function isNot($role): bool /** * Return the user's roles - * @return array + * @return string[] */ public function roles(): array { @@ -108,7 +106,7 @@ public function roles(): array /** * Return the user's permissions - * @return array + * @return string[] */ public function permissions(): array { @@ -117,7 +115,7 @@ public function permissions(): array /** * Remove a role from a user - * @param string|array $role The role(s) to revoke + * @param string|string[] $role The role(s) to revoke */ public function unassign($role): void { @@ -140,7 +138,7 @@ public function unassign($role): void /** * Set the roles and permissions for a user * - * @param string|array $roles The role(s) to set + * @param string|string[] $roles The role(s) to set */ protected function setRolesAndPermissions($roles): void { @@ -166,8 +164,8 @@ protected function setRolesAndPermissions($roles): void /** * Get the permissions for a role * - * @param string|array $roles - * @return array + * @param string|string[] $roles + * @return string[] */ protected function getRolePermissions($roles): array { diff --git a/src/Auth/UsesSubscriptions.php b/src/Auth/UsesSubscriptions.php index cce488d..8f4fc62 100644 --- a/src/Auth/UsesSubscriptions.php +++ b/src/Auth/UsesSubscriptions.php @@ -14,14 +14,14 @@ trait UsesSubscriptions { /** * User Subscription - * @var array|null + * @var mixed[]|null */ protected $subscription = null; /** * Get current subscription * - * @return array|null + * @return mixed[]|null */ public function subscription(): ?array { From b94245dd12077e266bedc3126951667f06e2be36 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:39:12 -0400 Subject: [PATCH 15/19] remove implicit false to string conversion in User::__toString --- phpstan-baseline.neon | 174 ++++++++++++++++++++++++++++++++++++++++++ phpstan.dist.neon | 2 +- src/Auth/User.php | 2 +- 3 files changed, 176 insertions(+), 2 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7d16ea1..90effce 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -66,6 +66,60 @@ parameters: count: 2 path: tests/login.test.php + - + rawMessage: Access to an undefined property object::$accessToken. + identifier: property.notFound + count: 1 + path: tests/login.test.php + + - + rawMessage: Access to an undefined property object::$refreshToken. + identifier: property.notFound + count: 1 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' + identifier: method.notFound + count: 1 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 4 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeFalse().' + identifier: method.notFound + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeInstanceOf().' + identifier: method.notFound + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeNull().' + identifier: method.notFound + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeString().' + identifier: method.notFound + count: 2 + path: tests/login.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeTrue().' + identifier: method.notFound + count: 3 + path: tests/login.test.php + - rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass @@ -126,6 +180,36 @@ parameters: count: 2 path: tests/register.test.php + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' + identifier: method.notFound + count: 1 + path: tests/register.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 3 + path: tests/register.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeFalse().' + identifier: method.notFound + count: 1 + path: tests/register.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeInstanceOf().' + identifier: method.notFound + count: 2 + path: tests/register.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeTrue().' + identifier: method.notFound + count: 6 + path: tests/register.test.php + - rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass @@ -174,6 +258,42 @@ parameters: count: 6 path: tests/session.test.php + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' + identifier: method.notFound + count: 1 + path: tests/session.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 20 + path: tests/session.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeGreaterThan().' + identifier: method.notFound + count: 1 + path: tests/session.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeInstanceOf().' + identifier: method.notFound + count: 8 + path: tests/session.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeNull().' + identifier: method.notFound + count: 6 + path: tests/session.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeTrue().' + identifier: method.notFound + count: 8 + path: tests/session.test.php + - rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass @@ -222,6 +342,12 @@ parameters: count: 2 path: tests/table.test.php + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 5 + path: tests/table.test.php + - rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass @@ -252,6 +378,30 @@ parameters: count: 2 path: tests/update.test.php + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' + identifier: method.notFound + count: 2 + path: tests/update.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 3 + path: tests/update.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeFalse().' + identifier: method.notFound + count: 1 + path: tests/update.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeTrue().' + identifier: method.notFound + count: 1 + path: tests/update.test.php + - rawMessage: 'Call to method not() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass @@ -294,6 +444,30 @@ parameters: count: 2 path: tests/user.test.php + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' + identifier: method.notFound + count: 2 + path: tests/user.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeInstanceOf().' + identifier: method.notFound + count: 3 + path: tests/user.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeNull().' + identifier: method.notFound + count: 1 + path: tests/user.test.php + + - + rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBeTrue().' + identifier: method.notFound + count: 2 + path: tests/user.test.php + - rawMessage: 'Call to method toBe() of internal class Pest\Expectation from outside its root namespace Pest.' identifier: method.internalClass diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 88e63f8..0d0cbba 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,7 @@ includes: - phpstan-baseline.neon parameters: - level: 6 + level: 7 paths: - src - tests diff --git a/src/Auth/User.php b/src/Auth/User.php index c551233..65539c4 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -265,7 +265,7 @@ public function setDb($db) public function __toString() { - return json_encode($this->get()); + return json_encode($this->get()) ?: ''; } /** From 857e2330d02799598b304c84be409f550e967eea Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:46:13 -0400 Subject: [PATCH 16/19] composer require leafs/billing --dev -W --- composer.json | 3 +- phpstan-baseline.neon | 118 +++++++++++++++++------------------------- 2 files changed, 49 insertions(+), 72 deletions(-) diff --git a/composer.json b/composer.json index 9c8bc6b..3dbe6a7 100644 --- a/composer.json +++ b/composer.json @@ -53,7 +53,8 @@ "phpstan/phpstan": "^2.1", "phpstan/extension-installer": "^1.4", "leafs/leaf": "^4.4", - "league/oauth2-google": "^4.0" + "league/oauth2-google": "^4.0", + "leafs/billing": "^0.2.0" }, "scripts": { "alchemy": "./vendor/bin/alchemy setup", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 90effce..b7260f7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -12,30 +12,6 @@ parameters: count: 2 path: src/Auth.php - - - rawMessage: Access to constant STATUS_ACTIVE on an unknown class Leaf\Billing\Subscription. - identifier: class.notFound - count: 1 - path: src/Auth/User.php - - - - rawMessage: Access to constant STATUS_CANCELLED on an unknown class Leaf\Billing\Subscription. - identifier: class.notFound - count: 1 - path: src/Auth/User.php - - - - rawMessage: Access to constant STATUS_TRIAL on an unknown class Leaf\Billing\Subscription. - identifier: class.notFound - count: 1 - path: src/Auth/User.php - - - - rawMessage: Function billing not found. - identifier: function.notFound - count: 3 - path: src/Auth/User.php - - rawMessage: 'Function createTableForUsers() has parameter $table with no type specified.' identifier: missingType.parameter @@ -60,24 +36,6 @@ parameters: count: 1 path: tests/Pest.php - - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound - count: 2 - path: tests/login.test.php - - - - rawMessage: Access to an undefined property object::$accessToken. - identifier: property.notFound - count: 1 - path: tests/login.test.php - - - - rawMessage: Access to an undefined property object::$refreshToken. - identifier: property.notFound - count: 1 - path: tests/login.test.php - - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' identifier: method.notFound @@ -169,16 +127,22 @@ parameters: path: tests/login.test.php - - rawMessage: Access to an undefined property Leaf\Auth\User::$password. - identifier: property.notFound - count: 2 - path: tests/register.test.php + rawMessage: Cannot access property $accessToken on object|null. + identifier: property.nonObject + count: 1 + path: tests/login.test.php - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound + rawMessage: Cannot access property $refreshToken on object|null. + identifier: property.nonObject + count: 1 + path: tests/login.test.php + + - + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject count: 2 - path: tests/register.test.php + path: tests/login.test.php - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' @@ -246,18 +210,24 @@ parameters: count: 6 path: tests/register.test.php + - + rawMessage: Cannot access property $password on Leaf\Auth\User|null. + identifier: property.nonObject + count: 2 + path: tests/register.test.php + + - + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject + count: 2 + path: tests/register.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined count: 1 path: tests/register.test.php - - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound - count: 6 - path: tests/session.test.php - - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' identifier: method.notFound @@ -337,10 +307,10 @@ parameters: path: tests/session.test.php - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound - count: 2 - path: tests/table.test.php + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject + count: 6 + path: tests/session.test.php - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' @@ -360,6 +330,12 @@ parameters: count: 3 path: tests/table.test.php + - + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject + count: 2 + path: tests/table.test.php + - rawMessage: Function createUsersTable not found. identifier: function.notFound @@ -372,12 +348,6 @@ parameters: count: 5 path: tests/table.test.php - - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound - count: 2 - path: tests/update.test.php - - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::not().' identifier: method.notFound @@ -432,18 +402,18 @@ parameters: count: 1 path: tests/update.test.php + - + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject + count: 2 + path: tests/update.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined count: 7 path: tests/update.test.php - - - rawMessage: Access to an undefined property Leaf\Auth\User::$username. - identifier: property.notFound - count: 2 - path: tests/user.test.php - - rawMessage: 'Call to an undefined method Pest\Expectation|Pest\Support\Extendable::toBe().' identifier: method.notFound @@ -492,6 +462,12 @@ parameters: count: 2 path: tests/user.test.php + - + rawMessage: Cannot access property $username on Leaf\Auth\User|null. + identifier: property.nonObject + count: 2 + path: tests/user.test.php + - rawMessage: 'Undefined variable: $this' identifier: variable.undefined From 3c51d4b53af08dc79d18840ad0f04870a113bf82 Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:47:40 -0400 Subject: [PATCH 17/19] fix phpstan l8 warnings for possibly unset keys of UsesSubscriptions::$subscriptions --- phpstan.dist.neon | 2 +- src/Auth/UsesSubscriptions.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 0d0cbba..c275599 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -3,7 +3,7 @@ includes: - phpstan-baseline.neon parameters: - level: 7 + level: 8 paths: - src - tests diff --git a/src/Auth/UsesSubscriptions.php b/src/Auth/UsesSubscriptions.php index 8f4fc62..02b19d8 100644 --- a/src/Auth/UsesSubscriptions.php +++ b/src/Auth/UsesSubscriptions.php @@ -14,7 +14,7 @@ trait UsesSubscriptions { /** * User Subscription - * @var mixed[]|null + * @var array|null */ protected $subscription = null; @@ -49,7 +49,7 @@ public function subscription(): ?array */ public function hasSubscription(): bool { - return $this->subscription() && $this->subscription['status'] !== \Leaf\Billing\Subscription::STATUS_CANCELLED; + return $this->subscription() && ($this->subscription['status'] ?? null) !== \Leaf\Billing\Subscription::STATUS_CANCELLED; } /** @@ -58,7 +58,7 @@ public function hasSubscription(): bool */ public function hasActiveSubscription(): bool { - return $this->subscription() && ($this->subscription['status'] === \Leaf\Billing\Subscription::STATUS_ACTIVE || $this->subscription['status'] === \Leaf\Billing\Subscription::STATUS_TRIAL); + return $this->subscription() && (($this->subscription['status'] ?? null) === \Leaf\Billing\Subscription::STATUS_ACTIVE || ($this->subscription['status'] ?? null) === \Leaf\Billing\Subscription::STATUS_TRIAL); } /** From 9e2367dcc45fac220f5351fa5e99119f44ae3ebe Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:48:04 -0400 Subject: [PATCH 18/19] fix typo on UsesSubscriptions::cancelSubscription --- src/Auth/UsesSubscriptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Auth/UsesSubscriptions.php b/src/Auth/UsesSubscriptions.php index 02b19d8..2c77004 100644 --- a/src/Auth/UsesSubscriptions.php +++ b/src/Auth/UsesSubscriptions.php @@ -73,6 +73,6 @@ public function cancelSubscription(): bool return true; } - return billing()->cancelSubcription($subscription['subscription_id']); + return billing()->cancelSubscription($subscription['subscription_id']); } } From 1fc0c86e807cadebe164af515b9d973034c1987d Mon Sep 17 00:00:00 2001 From: fadrian06 Date: Wed, 5 Nov 2025 21:49:21 -0400 Subject: [PATCH 19/19] fix many errors of phpstan l8 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 123258a..c5bf234 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

-![PHPStan: Level 0](https://img.shields.io/badge/PHPStan-level%200-brightgreen.svg?style=flat) +![PHPStan: Level 8](https://img.shields.io/badge/PHPStan-level%208-brightgreen.svg?style=flat) [![Latest Stable Version](https://poser.pugx.org/leafs/auth/v/stable)](https://packagist.org/packages/leafs/auth) [![Total Downloads](https://poser.pugx.org/leafs/auth/downloads)](https://packagist.org/packages/leafs/auth) [![License](https://poser.pugx.org/leafs/auth/license)](https://packagist.org/packages/leafs/auth)