diff --git a/README.md b/README.md index 75f39bb..c5bf234 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@

+![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) diff --git a/composer.json b/composer.json index e6caea1..0f34c87 100644 --- a/composer.json +++ b/composer.json @@ -41,13 +41,19 @@ }, "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": "*", + "phpstan/phpstan": "^2.1", + "phpstan/extension-installer": "^1.4", + "leafs/leaf": "^4.4", + "league/oauth2-google": "^4.0", + "leafs/billing": "^0.2.0" "rector/rector": "^2.2" }, "scripts": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..b7260f7 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,475 @@ +parameters: + ignoreErrors: + - + rawMessage: Access to an undefined property Leaf\Auth\User::$email. + identifier: property.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: 'Function createTableForUsers() has parameter $table with no type specified.' + identifier: missingType.parameter + count: 1 + path: tests/Pest.php + + - + rawMessage: 'Function deleteUser() has no return type specified.' + identifier: missingType.return + count: 1 + path: tests/Pest.php + + - + 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: tests/Pest.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 + 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: Cannot access property $accessToken on object|null. + identifier: property.nonObject + count: 1 + path: tests/login.test.php + + - + 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/login.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 + 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: 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: '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 + 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: 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().' + 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 + 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: 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 + count: 1 + path: tests/table.test.php + + - + rawMessage: 'Undefined variable: $this' + identifier: variable.undefined + count: 5 + path: tests/table.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 + 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: 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: '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 + 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: 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 + count: 2 + path: tests/user.test.php diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..c275599 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,10 @@ +includes: + - vendor/phpstan/phpstan/conf/bleedingEdge.neon + - phpstan-baseline.neon + +parameters: + level: 8 + paths: + - src + - tests + treatPhpDocTypesAsCertain: false diff --git a/src/Auth.php b/src/Auth.php index bd7cb65..2d4b3ee 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -27,9 +27,9 @@ class Auth { /** * The currently authenticated user - * @var User + * @var ?User */ - protected $user; + protected $user = null; /** * Internal instance of Leaf DB @@ -39,13 +39,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() @@ -118,7 +116,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 = []) @@ -132,7 +137,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 = []) @@ -163,7 +168,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( @@ -215,8 +223,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) { @@ -234,7 +243,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) @@ -249,7 +258,7 @@ public function createRoles(array $roles) /** * Return all roles and their permissions * - * @return array + * @return array */ public function roles() { @@ -261,7 +270,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 @@ -315,7 +324,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 @@ -376,7 +385,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 @@ -500,7 +509,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 */ @@ -558,7 +567,9 @@ 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 + * @return User|false|never + * @throws \Exception If database connection is not established */ public function createUserFor($userData) { @@ -613,6 +624,7 @@ public function createUserFor($userData) /** * Get saved OAuth token + * @return mixed */ public function oauthToken() { @@ -624,7 +636,7 @@ public function oauthToken() * --- * Sign out the currently authenticated user * - * @param string|array|callable|null $redirectUrl 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 @@ -721,7 +733,10 @@ public function data() /** * Get generated access tokens - * @return array|null + * @return array{ + * access?: string, + * refresh?: string, + * }|null */ public function tokens() { @@ -738,6 +753,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) { @@ -746,16 +763,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; @@ -763,60 +782,74 @@ public function middleware(string $middleware, callable $callback) auth()->clearErrors(); }); + + return; } if ($middleware === 'is') { - return app()->registerMiddleware('is', function ($role) use ($callback) { - if (!$this->user() || ($this->user() && $this->user()->isNot($role))) { + 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) { - if (!$this->user() || ($this->user() && $this->user()->is($role))) { + 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) { - if (!$this->user() || ($this->user() && $this->user()->cannot($role))) { + 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) { - if (!$this->user() || ($this->user() && $this->user()->can($role))) { + 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); @@ -824,6 +857,7 @@ public function middleware(string $middleware, callable $callback) /** * Parse the current user's token + * @return array|null */ public function parseToken() { @@ -909,6 +943,10 @@ protected function checkDbConnection(): void } } + /** + * @param string|array $value + * @return ?mixed + */ protected function getFromSession($value) { if ($this->checkAndExpireSession()) { @@ -918,6 +956,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')) { @@ -942,6 +984,7 @@ protected function checkAndExpireSession(): bool return $isSessionExpired; } + /** @return ?string */ protected function getTokenFromRequest() { $headers = null; @@ -971,6 +1014,7 @@ protected function getTokenFromRequest() return null; } + /** @return ?mixed */ protected function getTokenFromSession() { return Session::get('auth.token'); @@ -978,6 +1022,7 @@ protected function getTokenFromSession() /** * Clear all errors caught + * @return void */ public function clearErrors() { @@ -986,6 +1031,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 5e3ab5d..17b920d 100644 --- a/src/Auth/Model.php +++ b/src/Auth/Model.php @@ -31,11 +31,16 @@ class Model */ protected 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']; @@ -46,7 +51,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 */ @@ -66,7 +71,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 Db */ @@ -117,11 +122,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 be62f19..df6f3ae 100644 --- a/src/Auth/User.php +++ b/src/Auth/User.php @@ -22,7 +22,7 @@ class User /** * Internal instance of Leaf database - * @var \Leaf\DB + * @var \Leaf\Db */ protected $db; @@ -32,13 +32,14 @@ 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 = []; @@ -48,6 +49,10 @@ class User */ protected $errorsArray = []; + /** + * @param array $data + * @param bool $session + */ public function __construct($data, $session = true) { $this->data = $data; @@ -89,7 +94,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); @@ -297,7 +302,10 @@ public function getAuthInfo(): object /** * Return generated tokens - * @return array + * @return array{ + * access?: string, + * refresh?: string, + * } */ public function tokens(): array { @@ -306,6 +314,7 @@ public function tokens(): array /** * Generate a new JWT for the user + * @param int $tokenLifetime * @return string */ public function generateToken($tokenLifetime): string @@ -386,6 +395,7 @@ public function verifyEmail(): bool } } + /** @return array */ public function get() { $userData = $this->data; @@ -436,9 +446,13 @@ public function errors() public function __toString() { - return json_encode($this->get()); + return json_encode($this->get()) ?: ''; } + /** + * @param string $name + * @return ?mixed + */ public function __get($name) { // using data instead of get() here because @@ -447,16 +461,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 25370ba..efb4fb8 100644 --- a/src/Auth/UsesRoles.php +++ b/src/Auth/UsesRoles.php @@ -14,20 +14,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 @@ -57,7 +53,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 @@ -71,6 +67,7 @@ public function can($permission): bool /** * Check if a user does not have a permission + * @param string[] $permission */ public function cannot($permission): bool { @@ -79,7 +76,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 @@ -93,6 +90,7 @@ public function is($role): bool /** * Check if user does not have a role + * @param string[] $role */ public function isNot($role): bool { @@ -101,7 +99,7 @@ public function isNot($role): bool /** * Return the user's roles - * @return array + * @return string[] */ public function roles(): array { @@ -110,7 +108,7 @@ public function roles(): array /** * Return the user's permissions - * @return array + * @return string[] */ public function permissions(): array { @@ -119,7 +117,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 { @@ -142,7 +140,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 { @@ -168,8 +166,8 @@ protected function setRolesAndPermissions($roles): void /** * Get the permissions for a role * - * @param string|array $role - * @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 b3cc78a..7107bcc 100644 --- a/src/Auth/UsesSubscriptions.php +++ b/src/Auth/UsesSubscriptions.php @@ -16,15 +16,14 @@ trait UsesSubscriptions { /** * User Subscription - * @var array|null + * @var array|null */ protected $subscription = null; /** * Get current subscription * - * @param string|array $subscription The subscription to assign - * @return array|null + * @return mixed[]|null */ public function subscription(): ?array { @@ -76,6 +75,6 @@ public function cancelSubscription(): bool return true; } - return billing()->cancelSubcription($subscription['subscription_id']); + return billing()->cancelSubscription($subscription['subscription_id']); } } 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',