diff --git a/src/api-client/src/PendingRequest.php b/src/api-client/src/PendingRequest.php index ff32055478..34fee56b9c 100644 --- a/src/api-client/src/PendingRequest.php +++ b/src/api-client/src/PendingRequest.php @@ -5,7 +5,9 @@ namespace Hypervel\ApiClient; use BadMethodCallException; +use Closure; use GuzzleHttp\ClientInterface; +use GuzzleHttp\Cookie\SetCookie; use GuzzleHttp\Promise\PromiseInterface; use Hypervel\ApiClient\Concerns\HasContext; use Hypervel\Container\Container; @@ -16,19 +18,22 @@ use Hypervel\Http\Client\Request as HttpRequest; use Hypervel\Http\Client\Response as HttpResponse; use Hypervel\Pipeline\Pipeline; +use Hypervel\Support\Collection; use Hypervel\Support\Facades\Http; +use Hypervel\Support\Stringable; use Hypervel\Support\Traits\Conditionable; use Hypervel\Support\Traits\ForwardsCalls; use InvalidArgumentException; use JsonSerializable; use LogicException; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\StreamInterface; use Throwable; /** * @template TResource of ApiResource = ApiResource * @method static baseUrl(string $url) - * @method static withBody(null|resource|\Psr\Http\Message\StreamInterface|string|\Hypervel\Support\Stringable $content, string $contentType = 'application/json') + * @method static withBody(null|resource|StreamInterface|string|Stringable $content, string $contentType = 'application/json') * @method static asJson() * @method static asForm() * @method static attach(array|string $name, resource|string $contents = '', ?string $filename = null, array $headers = []) @@ -46,15 +51,15 @@ * @method static withToken(string $token, string $type = 'Bearer') * @method static withUserAgent(bool|string $userAgent) * @method static withUrlParameters(array $parameters = []) - * @method static withCookie(\GuzzleHttp\Cookie\SetCookie $cookie) + * @method static withCookie(SetCookie $cookie) * @method static withCookies(array $cookies, string $domain) * @method static maxRedirects(int $max) * @method static withoutRedirecting() * @method static withoutVerifying() - * @method static sink(\Psr\Http\Message\StreamInterface|resource|string $to) + * @method static sink(resource|StreamInterface|string $to) * @method static timeout(float|int $seconds) * @method static connectTimeout(float|int $seconds) - * @method static retry(array|int $times, \Closure|int $sleepMilliseconds = 0, ?callable $when = null, bool $throw = true) + * @method static retry(array|int $times, Closure|int $sleepMilliseconds = 0, ?callable $when = null, bool $throw = true) * @method static withOptions(array $options) * @method static withMiddleware(callable $middleware) * @method static withRequestMiddleware(callable $middleware) @@ -69,7 +74,7 @@ * @method static throwUnless(bool|callable $condition, ?callable $callback = null) * @method static dump() * @method static dd() - * @method static stub(callable|\Hypervel\Support\Collection $callback) + * @method static stub(callable|Collection $callback) * @method static preventStrayRequests(bool $prevent = true) * @method static allowStrayRequests(array $only) * @method static truncateExceptionsAt(int $length) diff --git a/src/auth/src/Access/Gate.php b/src/auth/src/Access/Gate.php index 7de0f87bf3..ab7d0e3f0b 100644 --- a/src/auth/src/Access/Gate.php +++ b/src/auth/src/Access/Gate.php @@ -4,6 +4,7 @@ namespace Hypervel\Auth\Access; +use ArrayObject; use Closure; use Exception; use Hypervel\Auth\Access\Events\GateEvaluated; @@ -52,18 +53,18 @@ class Gate implements GateContract protected $guessPolicyNamesUsingCallback; /** - * Cached model class to policy class mappings. + * The policy classes resolved for this gate's policy configuration. * - * Stores the resolved policy class string (or false for "no policy found") - * per model class. Persists for the worker lifetime — model-to-policy - * mappings don't change at runtime. The policy *instance* is not cached - * here; resolvePolicy() goes through the container each time. + * Stores the resolved policy class (or false for "no policy found") per + * model class. Gates created by forUser() share it; policy() and + * guessPolicyNamesUsing() replace it because they change how policies + * resolve. The policy *instance* is not cached here; resolvePolicy() + * goes through the container each time. Created on first use so that + * forUser() does not allocate a cache it immediately replaces. * - * Explicit policies ($this->policies) bypass this cache entirely. - * - * @var array + * @var null|ArrayObject */ - protected static array $policyClassCache = []; + protected ?ArrayObject $policyClassCache = null; /** * Cached guest-access results for class methods. @@ -269,6 +270,10 @@ public function policy(string $class, string $policy): static { $this->policies[$class] = $policy; + // Registrations change subclass and inherited-attribute resolution, so + // detach from the cache shared with gates keeping the previous policies. + $this->policyClassCache = null; + return $this; } @@ -601,17 +606,13 @@ public function getPolicyFor(object|string $class): mixed $class = get_class($class); } - // Explicitly registered policies bypass the cache — they're a fast - // hash lookup, and the policies array can be modified at runtime. + // Explicitly registered policies are a fast hash lookup, so they bypass the cache. if (isset($this->policies[$class])) { return $this->resolvePolicy($this->policies[$class]); } - if (! array_key_exists($class, static::$policyClassCache)) { - static::$policyClassCache[$class] = $this->resolvePolicyClass($class); - } - - $policyClass = static::$policyClassCache[$class]; + $this->policyClassCache ??= new ArrayObject; + $policyClass = $this->policyClassCache[$class] ??= $this->resolvePolicyClass($class); return $policyClass !== false ? $this->resolvePolicy($policyClass) @@ -621,9 +622,9 @@ public function getPolicyFor(object|string $class): mixed /** * Resolve the policy class for the given model class. * - * Checks the UsePolicy attribute, convention-based guessing, and - * subclass fallback. Returns the policy class string or false if - * no policy is found. + * Checks the UsePolicy attribute, convention-based guessing, subclass + * fallback, and inherited UsePolicy attributes. Returns the policy class + * string or false if no policy is found. * * @return class-string|false */ @@ -647,7 +648,7 @@ private function resolvePolicyClass(string $class): string|false } } - return false; + return $this->getPolicyFromAttribute($class, includeParents: true) ?? false; } /** @@ -656,17 +657,23 @@ private function resolvePolicyClass(string $class): string|false * @param class-string $class * @return null|class-string */ - protected function getPolicyFromAttribute(string $class): ?string + protected function getPolicyFromAttribute(string $class, bool $includeParents = false): ?string { if (! class_exists($class)) { return null; } - $attributes = (new ReflectionClass($class))->getAttributes(UsePolicy::class); + $reflection = new ReflectionClass($class); - return $attributes !== [] - ? $attributes[0]->newInstance()->class - : null; + do { + $attributes = $reflection->getAttributes(UsePolicy::class); + + if ($attributes !== []) { + return $attributes[0]->newInstance()->class; + } + } while ($includeParents && $reflection = $reflection->getParentClass()); + + return null; } /** @@ -705,8 +712,8 @@ public function guessPolicyNamesUsing(callable $callback): static $this->guessPolicyNamesUsingCallback = $callback; // A custom guess callback changes how unregistered policies are resolved, - // so any cached results from the default guesser may be stale. - static::$policyClassCache = []; + // so detach from the cache shared with gates keeping the previous guesser. + $this->policyClassCache = null; return $this; } @@ -805,7 +812,7 @@ protected function formatAbilityToMethod(string $ability): string */ public function forUser(mixed $user): static { - return new static( + $gate = new static( $this->container, fn () => $user, $this->abilities, @@ -814,6 +821,11 @@ public function forUser(mixed $user): static $this->afterCallbacks, $this->guessPolicyNamesUsingCallback, ); + + // The new gate has the same policy configuration, so it shares resolved policies. + $gate->policyClassCache = $this->policyClassCache ??= new ArrayObject; + + return $gate; } /** @@ -1124,7 +1136,6 @@ protected function buildScopeSelection( */ public static function flushState(): void { - static::$policyClassCache = []; static::$guestMethodCache = []; static::$guestCallbackCache = null; static::$abilityMethodCache = []; diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index 12f3d01347..7f51ca4b1b 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -8,6 +8,7 @@ use Closure; use GuzzleHttp\Client as GuzzleClient; use Hypervel\Broadcasting\Broadcasters\AblyBroadcaster; +use Hypervel\Broadcasting\Broadcasters\Broadcaster as BaseBroadcaster; use Hypervel\Broadcasting\Broadcasters\LogBroadcaster; use Hypervel\Broadcasting\Broadcasters\NullBroadcaster; use Hypervel\Broadcasting\Broadcasters\PusherBroadcaster; @@ -48,7 +49,7 @@ use function Hypervel\Support\enum_value; /** - * @mixin \Hypervel\Broadcasting\Broadcasters\Broadcaster + * @mixin BaseBroadcaster */ class BroadcastManager implements BroadcastingFactoryContract { diff --git a/src/collections/src/Collection.php b/src/collections/src/Collection.php index 6afa883370..0190db29ab 100644 --- a/src/collections/src/Collection.php +++ b/src/collections/src/Collection.php @@ -27,12 +27,12 @@ * @template-covariant TValue * * @implements ArrayAccess - * @implements \Hypervel\Support\Enumerable + * @implements Enumerable */ class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable, Transient { /** - * @use \Hypervel\Support\Traits\EnumeratesValues + * @use EnumeratesValues */ use EnumeratesValues; @@ -93,7 +93,7 @@ public function all(): array /** * Get a lazy collection for the items in this collection. * - * @return \Hypervel\Support\LazyCollection + * @return LazyCollection */ public function lazy(): LazyCollection { @@ -856,7 +856,7 @@ public function merge(mixed $items): static * @template TMergeRecursiveValue * * @param Arrayable|iterable $items - * @return static + * @return static|TMergeRecursiveValue|TValue)> */ public function mergeRecursive(mixed $items): static { diff --git a/src/collections/src/Enumerable.php b/src/collections/src/Enumerable.php index 9ee50a001a..de8571733a 100644 --- a/src/collections/src/Enumerable.php +++ b/src/collections/src/Enumerable.php @@ -658,7 +658,7 @@ public function merge(mixed $items): static; * @template TMergeRecursiveValue * * @param Arrayable|iterable $items - * @return static + * @return static|TMergeRecursiveValue|TValue)> */ public function mergeRecursive(mixed $items): static; diff --git a/src/collections/src/helpers.php b/src/collections/src/helpers.php index 87b042de3d..6aef7a8c39 100644 --- a/src/collections/src/helpers.php +++ b/src/collections/src/helpers.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Hypervel\Contracts\Support\Arrayable; use Hypervel\Support\Arr; use Hypervel\Support\Collection; @@ -12,8 +13,8 @@ * @template TKey of array-key * @template TValue * - * @param null|\Hypervel\Contracts\Support\Arrayable|iterable $value - * @return \Hypervel\Support\Collection + * @param null|Arrayable|iterable $value + * @return Collection */ function collect($value = []): Collection { @@ -258,7 +259,7 @@ function last($array) * @template TValue * @template TArgs * - * @param (\Closure(TArgs): TValue)|TValue $value + * @param (Closure(TArgs): TValue)|TValue $value * @param TArgs ...$args * @return TValue */ @@ -278,9 +279,9 @@ function value($value, ...$args) * @template TDefaultArgs * * @param mixed $condition - * @param (\Closure(TArgs): TValue)|TValue $value - * @param (\Closure(TDefaultArgs): TDefault)|TDefault $default - * @return ($condition is \Closure ? TDefault|TValue : ($condition is empty ? TDefault : TValue)) + * @param (Closure(TArgs): TValue)|TValue $value + * @param (Closure(TDefaultArgs): TDefault)|TDefault $default + * @return ($condition is Closure ? TDefault|TValue : ($condition is empty ? TDefault : TValue)) */ function when($condition, $value, $default = null) { diff --git a/src/container/src/Attributes/BindWhen.php b/src/container/src/Attributes/BindWhen.php index 4b82aca2d8..f0b20ff609 100644 --- a/src/container/src/Attributes/BindWhen.php +++ b/src/container/src/Attributes/BindWhen.php @@ -6,6 +6,7 @@ use Attribute; use Closure; +use Hypervel\Contracts\Container\Container; /** * Define a binding selected by a boot-stable condition. @@ -25,7 +26,7 @@ class BindWhen /** * The condition that determines if the binding should apply. * - * @var Closure(\Hypervel\Contracts\Container\Container): bool + * @var Closure(Container): bool */ public Closure $condition; @@ -33,7 +34,7 @@ class BindWhen * Create a new attribute instance. * * @param class-string $concrete - * @param Closure(\Hypervel\Contracts\Container\Container): bool $condition + * @param Closure(Container): bool $condition */ public function __construct(string $concrete, Closure $condition) { diff --git a/src/contracts/src/Validation/InvokableRule.php b/src/contracts/src/Validation/InvokableRule.php deleted file mode 100644 index cff723468f..0000000000 --- a/src/contracts/src/Validation/InvokableRule.php +++ /dev/null @@ -1,21 +0,0 @@ - */ + /** @var array */ protected array $rules = []; /** * Create a custom rule attribute. */ - public function __construct(string|array|ValidationRule|RuleContract|InvokableRuleContract|ValidationRuleContract ...$rules) + public function __construct(string|array|ValidationRule|RuleContract|ValidationRuleContract ...$rules) { $this->rules = $rules; } diff --git a/src/data/src/Support/Validation/RuleDenormalizer.php b/src/data/src/Support/Validation/RuleDenormalizer.php index f2dd0a2e18..120dd3890c 100644 --- a/src/data/src/Support/Validation/RuleDenormalizer.php +++ b/src/data/src/Support/Validation/RuleDenormalizer.php @@ -6,8 +6,6 @@ use BackedEnum; use DateTimeInterface; -use Hypervel\Contracts\Validation\InvokableRule as InvokableRuleContract; -use Hypervel\Contracts\Validation\Rule as RuleContract; use Hypervel\Data\Attributes\Validation\CustomValidationAttribute; use Hypervel\Data\Attributes\Validation\ObjectValidationAttribute; use Hypervel\Data\Attributes\Validation\Rule; @@ -21,7 +19,7 @@ class RuleDenormalizer /** * Convert one declaration into Validator rules. * - * @return list + * @return list */ public function execute(mixed $rule, ValidationPath $path): array { @@ -57,10 +55,6 @@ public function execute(mixed $rule, ValidationPath $path): array return $this->execute($rule->get(), $path); } - if ($rule instanceof RuleContract || $rule instanceof InvokableRuleContract) { - return [$rule]; - } - return [$rule]; } diff --git a/src/di/src/Aop/ProxyCallVisitor.php b/src/di/src/Aop/ProxyCallVisitor.php index 6cdece0676..fb787637e3 100644 --- a/src/di/src/Aop/ProxyCallVisitor.php +++ b/src/di/src/Aop/ProxyCallVisitor.php @@ -33,6 +33,7 @@ use PhpParser\Node\Scalar\MagicConst\Method as MagicConstMethod; use PhpParser\Node\Scalar\MagicConst\Trait_ as MagicConstTrait; use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\ClassMethod; @@ -775,7 +776,7 @@ private function uniqueVariableName(string $base, array $usedVariables): string /** * Find every named class-like and its namespace. * - * @return array}> + * @return array}> */ private function findNamedClassLikes(array $nodes): array { @@ -809,7 +810,7 @@ private function findNamedClassLikes(array $nodes): array /** * Collect function import aliases for the target namespace. * - * @param array $statements + * @param array $statements * @return array */ private function collectFunctionImports(array $statements): array diff --git a/src/docs/authorization.md b/src/docs/authorization.md index 6235621270..66d9d9c1f0 100644 --- a/src/docs/authorization.md +++ b/src/docs/authorization.md @@ -410,6 +410,8 @@ class Order extends Model } ``` +Models that extend a class with the `UsePolicy` attribute use its policy as well, unless the child model declares its own `UsePolicy` attribute, a policy is discovered for it, or a policy is registered for it or one of its parent classes. + ## Writing Policies diff --git a/src/docs/database.md b/src/docs/database.md index 670662742e..7595546af2 100644 --- a/src/docs/database.md +++ b/src/docs/database.md @@ -138,7 +138,7 @@ These URLs typically follow a standard schema convention: driver://username:password@host:port/database?options ``` -For convenience, Hypervel supports these URLs as an alternative to configuring your database with multiple configuration options. If the `url` configuration option is present, it will be used to extract the database connection and credential information. In Hypervel's default database configuration file, SQLite uses the `DATABASE_URL` environment variable while MariaDB, MySQL, and PostgreSQL use the `DB_URL` environment variable. +For convenience, Hypervel supports these URLs as an alternative to configuring your database with multiple configuration options. If the `url` (or corresponding `DB_URL` environment variable) configuration option is present, it will be used to extract the database connection and credential information. ### Read and Write Connections diff --git a/src/docs/porting-from-laravel.md b/src/docs/porting-from-laravel.md index c4b00ab354..4508bfa50e 100644 --- a/src/docs/porting-from-laravel.md +++ b/src/docs/porting-from-laravel.md @@ -530,6 +530,8 @@ Handwritten validation parameters use standard CSV quoting. Replace backslash-es `FailOnUnknownFields` accepts the contents of `array` fields without child rules. Add child rules or allowed keys (`array:name,email`) when those contents must be restricted. See [unknown fields](/docs/{{version}}/validation#request-failing-on-unknown-fields). +Laravel's deprecated `InvokableRule` contract is not available. Change rules that implement it to implement `Hypervel\Contracts\Validation\ValidationRule` and rename their `__invoke` method to `validate`; code that calls such a rule object directly must call `validate` or keep its own `__invoke` method. See [rule objects](/docs/{{version}}/validation#using-rule-objects). + ### Request and Input Data diff --git a/src/docs/queues.md b/src/docs/queues.md index b3ad0a73bd..746a1de318 100644 --- a/src/docs/queues.md +++ b/src/docs/queues.md @@ -2990,6 +2990,10 @@ After resuming a queue, workers will begin processing new jobs from that queue i Queue workers report paused and resumed queues in their console output. +Pausing or resuming a queue dispatches the `Hypervel\Queue\Events\QueuePaused` or `Hypervel\Queue\Events\QueueResumed` event in the process that made the change. Their `connection` and `queue` properties identify the queue, and the `QueuePaused` event's `ttl` property is `null` unless the queue was paused for a limited time. Pausing or resuming every queue with the `--all` option dispatches the `QueuesPaused` or `QueuesResumed` event instead. Running workers dispatch the `WorkerQueuePaused` and `WorkerQueueResumed` events, with `connectionName` and `queue` properties, when they detect that one of their queues has been paused or resumed. + +A worker process may also be paused as a whole by sending it the `SIGUSR2` signal and resumed with `SIGCONT`. The worker dispatches the `WorkerPausing` and `WorkerResuming` events when it receives these signals. Both events provide the worker's `connectionName`, `queue`, and `workerOptions`. + #### Worker Restart and Pause Signals diff --git a/src/docs/socialite.md b/src/docs/socialite.md index 42545f2c48..a998ae5133 100644 --- a/src/docs/socialite.md +++ b/src/docs/socialite.md @@ -103,6 +103,8 @@ Route::get('/auth/callback', function () { The `redirect` method provided by the `Socialite` facade takes care of redirecting the user to the OAuth provider, while the `user` method will examine the incoming request and retrieve the user's information from the provider after they have approved the authentication request. +For OAuth 2.0 providers, after validating the state, the `user` method will throw a `Hypervel\Socialite\Two\Exceptions\InvalidCodeException` if the authorization code is missing, empty, or not a string. This typically happens when the user declines the authentication request. + ### Authentication and Storage diff --git a/src/encryption/src/Encrypter.php b/src/encryption/src/Encrypter.php index 3f0bd3bc9e..f8b5e81a77 100644 --- a/src/encryption/src/Encrypter.php +++ b/src/encryption/src/Encrypter.php @@ -78,7 +78,7 @@ public static function generateKey(string $cipher): string /** * Encrypt the given value. * - * @throws \Hypervel\Contracts\Encryption\EncryptException + * @throws EncryptException */ public function encrypt(#[SensitiveParameter] mixed $value, bool $serialize = true): string { @@ -116,7 +116,7 @@ public function encrypt(#[SensitiveParameter] mixed $value, bool $serialize = tr /** * Encrypt a string without serialization. * - * @throws \Hypervel\Contracts\Encryption\EncryptException + * @throws EncryptException */ public function encryptString(#[SensitiveParameter] string $value): string { @@ -126,7 +126,7 @@ public function encryptString(#[SensitiveParameter] string $value): string /** * Decrypt the given value. * - * @throws \Hypervel\Contracts\Encryption\DecryptException + * @throws DecryptException */ public function decrypt(string $payload, bool $unserialize = true): mixed { @@ -193,7 +193,7 @@ public function decrypt(string $payload, bool $unserialize = true): mixed /** * Decrypt the given string without unserialization. * - * @throws \Hypervel\Contracts\Encryption\DecryptException + * @throws DecryptException */ public function decryptString(string $payload): string { @@ -211,7 +211,7 @@ protected function hash(#[SensitiveParameter] string $iv, #[SensitiveParameter] /** * Get the JSON array from the given payload. * - * @throws \Hypervel\Contracts\Encryption\DecryptException + * @throws DecryptException */ protected function getJsonPayload(string $payload): array { diff --git a/src/encryption/src/EncryptionServiceProvider.php b/src/encryption/src/EncryptionServiceProvider.php index 1e25877c10..1410c4ff3c 100644 --- a/src/encryption/src/EncryptionServiceProvider.php +++ b/src/encryption/src/EncryptionServiceProvider.php @@ -73,7 +73,7 @@ protected function parseKey(#[SensitiveParameter] array $config): string /** * Extract the encryption key from the given configuration. * - * @throws \Hypervel\Encryption\MissingAppKeyException + * @throws MissingAppKeyException */ protected function key(#[SensitiveParameter] array $config): string { diff --git a/src/facade-documenter/facade.php b/src/facade-documenter/facade.php index c0c359062c..a5e110b719 100755 --- a/src/facade-documenter/facade.php +++ b/src/facade-documenter/facade.php @@ -30,6 +30,7 @@ use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode; use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode; @@ -255,8 +256,8 @@ function debug($message) /** * Resolve the proxies for the Facade. * - * @param \ReflectionClass $class - * @return \Hypervel\Support\Collection + * @param ReflectionClass $class + * @return Collection */ function resolveProxies($class) { @@ -268,7 +269,7 @@ function resolveProxies($class) * Determine the fully qualified class name. * * @param string $class - * @param \ReflectionClass $source + * @param ReflectionClass $source * @return class-string */ function determineFqcn($class, $source) @@ -319,7 +320,7 @@ function resolveCanonicalClassName(string $name): ?string /** * Resolve a class name relative to the method that declares it. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method */ function resolveRelativeClassName($method, string $name): ?string { @@ -336,8 +337,8 @@ function resolveRelativeClassName($method, string $name): ?string /** * Resolve the classes referenced in the @see docblocks. * - * @param \ReflectionClass $class - * @return \Hypervel\Support\Collection + * @param ReflectionClass $class + * @return Collection */ function resolveDocSees($class) { @@ -348,8 +349,8 @@ function resolveDocSees($class) /** * Resolve the classes referenced methods in the @methods docblocks. * - * @param \ReflectionClass $class - * @return \Hypervel\Support\Collection<\PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode> + * @param ReflectionClass $class + * @return Collection */ function resolveDocMethods($class) { @@ -385,8 +386,8 @@ function resolveDocMethods($class) /** * Resolve the parameters type from the @param docblocks. * - * @param \ReflectionMethodDecorator $method - * @param \ReflectionParameter $parameter + * @param ReflectionMethodDecorator $method + * @param ReflectionParameter $parameter * @return null|string */ function resolveDocParamType($method, $parameter) @@ -420,7 +421,7 @@ function resolveDocParamType($method, $parameter) /** * Resolve the return type from the @return docblock. * - * @param \ReflectionMethodDecorator $method + * @param ReflectionMethodDecorator $method * @return null|string */ function resolveReturnDocType($method) @@ -442,7 +443,7 @@ function resolveReturnDocType($method) * Parse the given docblock. * * @param string $docblock - * @return \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode + * @return PhpDocNode */ function parseDocblock($docblock) { @@ -475,8 +476,8 @@ function parseDocblock($docblock) /** * Resolve the types from the docblock. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method - * @param \PHPStan\PhpDocParser\Ast\Type\TypeNode $typeNode + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method + * @param TypeNode $typeNode * @return null|string */ function resolveDocblockTypes($method, $typeNode, int $depth = 1) @@ -725,8 +726,8 @@ function resolveDocblockTypes($method, $typeNode, int $depth = 1) /** * Resolve and flatten the members of a PHPDoc union. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method - * @param \PHPStan\PhpDocParser\Ast\Type\TypeNode $typeNode + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method + * @param TypeNode $typeNode * @return list */ function resolveDocblockUnionMembers($method, $typeNode, int $depth): array @@ -772,7 +773,7 @@ function resolveDocblockUnionMembers($method, $typeNode, int $depth): array /** * Resolve a method template type by name. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method */ function resolveTemplateType($method, string $name, int $depth): ?string { @@ -823,8 +824,8 @@ function isArrayRefinementType(string $name): bool * matching constants and unioning their inferred value types. Returns 'mixed' * when the constant or class cannot be resolved. * - * @param \PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode $node - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method + * @param ConstFetchNode $node + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method * @return string */ function resolveConstFetchType($node, $method) @@ -859,8 +860,8 @@ function resolveConstFetchType($node, $method) /** * Resolve key-of<...> / value-of<...> when the inner type is a ConstFetchNode. * - * @param \PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode $node - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method + * @param ConstFetchNode $node + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method * @param bool $keyType true to resolve the key type, false to resolve the value type * @return string */ @@ -900,7 +901,7 @@ function resolveKeyOrValueOf($node, $method, $keyType) * null when the class cannot be found. * * @param string $className - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method * @return null|string */ function resolveClassConstantClass($className, $method) @@ -943,8 +944,8 @@ function inferValueType($value) * Unknown node types must return false so new PHPDoc syntax falls back to a * conservative union instead of producing a misleading conditional. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method - * @param \PHPStan\PhpDocParser\Ast\Type\TypeNode $typeNode + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method + * @param TypeNode $typeNode * @return bool */ function canPreserveConditionalTarget($method, $typeNode) @@ -1140,8 +1141,8 @@ function isKnownOptionalDependency($type) /** * Resolve the declared type. * - * @param \ReflectionMethodDecorator $method - * @param null|\ReflectionType $type + * @param ReflectionMethodDecorator $method + * @param null|ReflectionType $type * @return null|string */ function resolveType($method, $type) @@ -1193,7 +1194,7 @@ function resolveType($method, $type) * * @param string $docblock * @param string $tag - * @return \Hypervel\Support\Collection + * @return Collection */ function resolveDocTags($docblock, $tag) { @@ -1210,8 +1211,8 @@ function resolveDocTags($docblock, $tag) /** * Resolve method names that should be excluded from a facade docblock. * - * @param \ReflectionClass $facade - * @return \Hypervel\Support\Collection + * @param ReflectionClass $facade + * @return Collection */ function resolveIgnoredMethods($facade) { @@ -1229,9 +1230,9 @@ function resolveIgnoredMethods($facade) /** * Recursively resolve docblock mixins. * - * @param \ReflectionClass $class - * @param \Hypervel\Support\Collection $encountered - * @return \Hypervel\Support\Collection<\ReflectionClass> + * @param ReflectionClass $class + * @param Collection $encountered + * @return Collection */ function resolveDocMixins($class, $encountered = new Collection) { @@ -1253,8 +1254,8 @@ function resolveDocMixins($class, $encountered = new Collection) /** * Resolve the classes referenced methods in the @methods docblocks. * - * @param \ReflectionMethodDecorator $method - * @return \Hypervel\Support\Collection + * @param ReflectionMethodDecorator $method + * @return Collection */ function resolveDocParameters($method) { @@ -1265,7 +1266,7 @@ function resolveDocParameters($method) /** * Determine if the method is magic. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return bool */ function isMagic($method) @@ -1276,7 +1277,7 @@ function isMagic($method) /** * Determine if the method is marked as @internal. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return bool */ function isInternal($method) @@ -1291,7 +1292,7 @@ function isInternal($method) /** * Determine if the method is deprecated. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return bool */ function isDeprecated($method) @@ -1306,7 +1307,7 @@ function isDeprecated($method) /** * Determine if the method is for a builtin contract. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return bool */ function fulfillsBuiltinInterface($method) @@ -1325,7 +1326,7 @@ function fulfillsBuiltinInterface($method) /** * Resolve the methods name. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return string */ function resolveName($method) @@ -1338,8 +1339,8 @@ function resolveName($method) /** * Resolve the classes methods. * - * @param \ReflectionClass $class - * @return \Hypervel\Support\Collection<\PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator> + * @param ReflectionClass $class + * @return Collection */ function resolveMethods($class) { @@ -1354,8 +1355,8 @@ function resolveMethods($class) /** * Determine if the given method conflicts with a Facade method. * - * @param \ReflectionClass $facade - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method + * @param ReflectionClass $facade + * @param MethodTagValueNode|ReflectionMethodDecorator $method * @return bool */ function conflictsWithFacade($facade, $method) @@ -1374,8 +1375,8 @@ function conflictsWithFacade($facade, $method) /** * Normalise the method details into a easier format to work with. * - * @param \PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode|\ReflectionMethodDecorator $method - * @return array|\PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode + * @param MethodTagValueNode|ReflectionMethodDecorator $method + * @return array|MethodTagValueNode */ function normaliseDetails($method) { @@ -1405,8 +1406,8 @@ function normaliseDetails($method) /** * Resolve the parameters for the method. * - * @param \ReflectionMethodDecorator $method - * @return \Hypervel\Support\Collection + * @param ReflectionMethodDecorator $method + * @return Collection */ function resolveParameters($method) { @@ -1423,8 +1424,8 @@ function resolveParameters($method) * the exact direct or nested trait source range holds the relevant `use` * statements. * - * @param \ReflectionClassDocblockContext|\ReflectionMethodDecorator $method - * @return \ReflectionClass + * @param ReflectionClassDocblockContext|ReflectionMethodDecorator $method + * @return ReflectionClass */ function resolveImportSource($method) { @@ -1458,7 +1459,7 @@ function resolveImportSource($method) * Resolve every direct and nested trait used by a class. * * @param array $resolved - * @return list<\ReflectionClass> + * @return list */ function resolveNestedTraits(ReflectionClass $class, array &$resolved = []): array { @@ -1480,8 +1481,8 @@ function resolveNestedTraits(ReflectionClass $class, array &$resolved = []): arr /** * Resolve the classes imports. * - * @param \ReflectionClass $class - * @return \Hypervel\Support\Collection + * @param ReflectionClass $class + * @return Collection */ function resolveClassImports($class) { @@ -1579,7 +1580,7 @@ function isIgnorablePhpToken(PhpToken $token): bool /** * Return the next significant PHP token index. * - * @param list<\PhpToken> $tokens + * @param list $tokens */ function nextSignificantPhpTokenIndex(array $tokens, int $offset): ?int { @@ -1595,7 +1596,7 @@ function nextSignificantPhpTokenIndex(array $tokens, int $offset): ?int /** * Parse the class imports from one namespace-level use statement. * - * @param list<\PhpToken> $tokens + * @param list $tokens * @return array */ function parseClassUseStatement(array $tokens): array @@ -1642,8 +1643,8 @@ function parseClassUseStatement(array $tokens): array /** * Split use-statement tokens into comma-separated imports. * - * @param list<\PhpToken> $tokens - * @return list> + * @param list $tokens + * @return list> */ function splitClassUseSegments(array $tokens): array { @@ -1671,7 +1672,7 @@ function splitClassUseSegments(array $tokens): array /** * Convert parsed use segments into an alias map. * - * @param list> $segments + * @param list> $segments * @return array */ function parseClassUseSegments(array $segments, string $prefix = ''): array @@ -1721,7 +1722,7 @@ function parseClassUseSegments(array $segments, string $prefix = ''): array * matching its representation keeps generated metadata stable after formatting. * * @param string $method - * @param \Hypervel\Support\Collection $imports + * @param Collection $imports * @return string */ function shortenImportedGlobalTypes($method, $imports) @@ -1863,7 +1864,7 @@ public function sourceClass(): ReflectionClass } /** - * @mixin \ReflectionMethod + * @mixin ReflectionMethod */ class ReflectionMethodDecorator { diff --git a/src/filesystem/src/FilesystemAdapter.php b/src/filesystem/src/FilesystemAdapter.php index ea7236e100..a2245d247b 100644 --- a/src/filesystem/src/FilesystemAdapter.php +++ b/src/filesystem/src/FilesystemAdapter.php @@ -56,7 +56,7 @@ use Throwable; /** - * @mixin \League\Flysystem\FilesystemOperator + * @mixin FilesystemOperator */ class FilesystemAdapter implements CloudFilesystemContract { diff --git a/src/filesystem/src/FilesystemManager.php b/src/filesystem/src/FilesystemManager.php index e756daed07..3ae222df0b 100644 --- a/src/filesystem/src/FilesystemManager.php +++ b/src/filesystem/src/FilesystemManager.php @@ -41,12 +41,12 @@ use function Hypervel\Support\enum_value; /** - * @method \Hypervel\Contracts\Filesystem\Filesystem|mixed when(null|\Closure|mixed $value = null, null|callable $callback = null, null|callable $default = null) - * @method \Hypervel\Contracts\Filesystem\Filesystem|mixed unless(null|\Closure|mixed $value = null, null|callable $callback = null, null|callable $default = null) + * @method Filesystem|mixed when(null|Closure|mixed $value = null, null|callable $callback = null, null|callable $default = null) + * @method Filesystem|mixed unless(null|Closure|mixed $value = null, null|callable $callback = null, null|callable $default = null) * - * @mixin \Hypervel\Contracts\Filesystem\Filesystem - * @mixin \Hypervel\Filesystem\FilesystemAdapter - * @mixin \Hypervel\Filesystem\ClientPooledFilesystem + * @mixin Filesystem + * @mixin FilesystemAdapter + * @mixin ClientPooledFilesystem */ class FilesystemManager implements FactoryContract { diff --git a/src/fortify/src/InteractsWithTwoFactorState.php b/src/fortify/src/InteractsWithTwoFactorState.php index 203e74533c..6d8c3c1662 100644 --- a/src/fortify/src/InteractsWithTwoFactorState.php +++ b/src/fortify/src/InteractsWithTwoFactorState.php @@ -6,9 +6,10 @@ use Hypervel\Container\Container; use Hypervel\Fortify\Actions\DisableTwoFactorAuthentication; +use Hypervel\Foundation\Http\FormRequest; /** - * @mixin \Hypervel\Foundation\Http\FormRequest + * @mixin FormRequest */ trait InteractsWithTwoFactorState { diff --git a/src/fortify/src/PasskeyAuthenticatable.php b/src/fortify/src/PasskeyAuthenticatable.php index df44c94bfa..f3792ee425 100644 --- a/src/fortify/src/PasskeyAuthenticatable.php +++ b/src/fortify/src/PasskeyAuthenticatable.php @@ -4,10 +4,11 @@ namespace Hypervel\Fortify; +use Hypervel\Fortify\Contracts\PasskeyUser; use Hypervel\Passkeys\PasskeyAuthenticatable as BasePasskeyAuthenticatable; /** - * @phpstan-require-implements \Hypervel\Fortify\Contracts\PasskeyUser + * @phpstan-require-implements PasskeyUser */ trait PasskeyAuthenticatable { diff --git a/src/foundation/config/database.php b/src/foundation/config/database.php index 7336b3f9cc..40a6df06a9 100644 --- a/src/foundation/config/database.php +++ b/src/foundation/config/database.php @@ -47,7 +47,7 @@ 'connections' => [ 'sqlite' => [ 'driver' => 'sqlite', - 'url' => env('DATABASE_URL'), + 'url' => env('DB_URL'), 'database' => env('DB_DATABASE', database_path('database.sqlite')), 'prefix' => '', 'prefix_indexes' => null, diff --git a/src/foundation/src/Configuration/Exceptions.php b/src/foundation/src/Configuration/Exceptions.php index f98c38de86..52b59540c5 100644 --- a/src/foundation/src/Configuration/Exceptions.php +++ b/src/foundation/src/Configuration/Exceptions.php @@ -24,6 +24,9 @@ public function __construct(public Handler $handler) /** * Register a reportable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. */ public function report(callable $using): ReportableHandler { @@ -32,6 +35,9 @@ public function report(callable $using): ReportableHandler /** * Register a reportable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. */ public function reportable(callable $reportUsing): ReportableHandler { @@ -40,6 +46,9 @@ public function reportable(callable $reportUsing): ReportableHandler /** * Register a renderable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * rendering for every subsequent request in the worker. */ public function render(callable $using): static { @@ -50,6 +59,9 @@ public function render(callable $using): static /** * Register a renderable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * rendering for every subsequent request in the worker. */ public function renderable(callable $renderUsing): static { @@ -60,6 +72,9 @@ public function renderable(callable $renderUsing): static /** * Register a callback to prepare the final, rendered exception response. + * + * Boot-only. The callback replaces the shared handler's response callback for + * every subsequent request in the worker. */ public function respond(callable $using): static { @@ -70,6 +85,9 @@ public function respond(callable $using): static /** * Specify the callback that should be used to throttle reportable exceptions. + * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. */ public function throttle(callable $throttleUsing): static { @@ -81,6 +99,9 @@ public function throttle(callable $throttleUsing): static /** * Register a new exception mapping. * + * Boot-only. The mapping persists on the shared handler and affects exception + * reporting and rendering for every subsequent request and job in the worker. + * * @throws InvalidArgumentException */ public function map(Closure|string $from, Closure|string|null $to = null): static @@ -93,6 +114,9 @@ public function map(Closure|string $from, Closure|string|null $to = null): stati /** * Set the log level for the given exception type. * + * Boot-only. The level persists on the shared handler and affects how later + * exceptions of that type are logged in the worker. + * * @param class-string $type * @param LogLevel::* $level */ @@ -105,6 +129,9 @@ public function level(string $type, string $level): static /** * Register a closure that should be used to build exception context data. + * + * Boot-only. The closure persists on the shared handler and runs for every + * subsequently logged exception in the worker. */ public function context(Closure $contextCallback): static { @@ -115,6 +142,9 @@ public function context(Closure $contextCallback): static /** * Indicate that the given exception type should not be reported. + * + * Boot-only. The exception types persist on the shared handler and affect + * exception reporting for every subsequent request and job in the worker. */ public function dontReport(array|string $class): static { @@ -143,6 +173,9 @@ public function dontRetry(array|string $class): static /** * Register a callback to determine if an exception should not be reported. * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. + * * @param (Closure(Throwable): bool) $dontReportWhen */ public function dontReportWhen(Closure $dontReportWhen): static @@ -155,8 +188,8 @@ public function dontReportWhen(Closure $dontReportWhen): static /** * Register a callback to determine if an exception should stop job retries. * - * Boot-only. The callback persists on the shared handler and is considered for - * every subsequently failed job in the worker. + * Boot-only. The callback persists on the shared handler and affects job retry + * decisions for every subsequent job in the worker. * * @template TException of Throwable * @@ -171,6 +204,9 @@ public function dontRetryWhen(Closure $dontRetryWhen): static /** * Do not report duplicate exceptions. + * + * Boot-only. The setting persists on the shared handler and applies to every + * subsequent request and job in the worker. */ public function dontReportDuplicates(): static { @@ -181,6 +217,9 @@ public function dontReportDuplicates(): static /** * Indicate that the given attributes should never be flashed to the session on validation errors. + * + * Boot-only. The attributes persist on the shared handler and are omitted + * from every subsequent validation redirect in the worker. */ public function dontFlash(array|string $attributes): static { @@ -191,6 +230,9 @@ public function dontFlash(array|string $attributes): static /** * Register the callable that determines if the exception handler response should be JSON. + * + * Boot-only. The callable replaces the shared handler's JSON check for every + * subsequent request in the worker. */ public function shouldRenderJsonWhen(callable $callback): static { @@ -216,6 +258,9 @@ public function stopIgnoring(array|string $class): static /** * Set the truncation length for request exception messages. + * + * Boot-only. The global default persists for the worker lifetime across all + * coroutines; per-request truncation settings take precedence. */ public function truncateRequestExceptionsAt(int $length): static { @@ -226,6 +271,9 @@ public function truncateRequestExceptionsAt(int $length): static /** * Disable truncation of request exception messages. + * + * Boot-only. The global default persists for the worker lifetime across all + * coroutines; per-request truncation settings take precedence. */ public function dontTruncateRequestExceptions(): static { diff --git a/src/foundation/src/Exceptions/Handler.php b/src/foundation/src/Exceptions/Handler.php index 516b294b95..dc955a59cb 100644 --- a/src/foundation/src/Exceptions/Handler.php +++ b/src/foundation/src/Exceptions/Handler.php @@ -222,6 +222,9 @@ public function register(): void /** * Register a reportable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. */ public function reportable(callable $reportUsing): ReportableHandler { @@ -236,6 +239,9 @@ public function reportable(callable $reportUsing): ReportableHandler /** * Register a renderable callback. + * + * Boot-only. The callback persists on the shared handler and affects exception + * rendering for every subsequent request in the worker. */ public function renderable(callable $renderUsing): static { @@ -251,6 +257,9 @@ public function renderable(callable $renderUsing): static /** * Register a new exception mapping. * + * Boot-only. The mapping persists on the shared handler and affects exception + * reporting and rendering for every subsequent request and job in the worker. + * * @throws InvalidArgumentException */ public function map(callable|string $from, Closure|string|null $to = null): static @@ -276,6 +285,9 @@ public function map(callable|string $from, Closure|string|null $to = null): stat * Indicate that the given exception type should not be reported. * * Alias of "ignore". + * + * Boot-only. The exception types persist on the shared handler and affect + * exception reporting for every subsequent request and job in the worker. */ public function dontReport(array|string $exceptions): static { @@ -285,6 +297,9 @@ public function dontReport(array|string $exceptions): static /** * Register a callback to determine if an exception should not be reported. * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. + * * @param (callable(Throwable): bool) $dontReportWhen */ public function dontReportWhen(callable $dontReportWhen): static @@ -300,6 +315,9 @@ public function dontReportWhen(callable $dontReportWhen): static /** * Indicate that the given exception type should not be reported. + * + * Boot-only. The exception types persist on the shared handler and affect + * exception reporting for every subsequent request and job in the worker. */ public function ignore(array|string $exceptions): static { @@ -328,8 +346,8 @@ public function dontRetry(array|string $exceptions): static /** * Register a callback to determine if jobs should stop retrying for an exception. * - * Boot-only. The callback persists on the shared handler and is considered for - * every subsequently failed job in the worker. + * Boot-only. The callback persists on the shared handler and affects job retry + * decisions for every subsequent job in the worker. * * @template TException of Throwable * @@ -405,6 +423,9 @@ public function shouldStopRetries(Throwable $e): bool /** * Indicate that the given attributes should never be flashed to the session on validation errors. + * + * Boot-only. The attributes persist on the shared handler and are omitted + * from every subsequent validation redirect in the worker. */ public function dontFlash(array|string $attributes): static { @@ -418,6 +439,9 @@ public function dontFlash(array|string $attributes): static /** * Set the log level for the given exception type. * + * Boot-only. The level persists on the shared handler and affects how later + * exceptions of that type are logged in the worker. + * * @param class-string $type * @param LogLevel::* $level */ @@ -625,6 +649,9 @@ protected function throttle(Throwable $e): Lottery|AdmissionPolicy|null /** * Specify the callback that should be used to throttle reportable exceptions. + * + * Boot-only. The callback persists on the shared handler and affects exception + * reporting for every subsequent request and job in the worker. */ public function throttleUsing(callable $throttleUsing): static { @@ -718,6 +745,9 @@ protected function context(): array /** * Register a closure that should be used to build exception context data. + * + * Boot-only. The closure persists on the shared handler and runs for every + * subsequently logged exception in the worker. */ public function buildContextUsing(Closure $contextCallback): static { @@ -806,6 +836,9 @@ protected function afterResponseCallbacks(): array /** * Prepare the final, rendered response for an exception using the given callback. + * + * Boot-only. The callback replaces the shared handler's response callback for + * every subsequent request in the worker. */ public function respondUsing(callable $callback): static { @@ -956,6 +989,9 @@ protected function shouldReturnJson(Request $request, Throwable $e): bool /** * Register the callable that determines if the exception handler response should be JSON. * + * Boot-only. The callable replaces the shared handler's JSON check for every + * subsequent request in the worker. + * * @param callable(Request $request, Throwable): bool $callback */ public function shouldRenderJsonWhen(callable $callback): static @@ -1179,6 +1215,9 @@ public function renderForConsole(OutputInterface $output, Throwable $e): void /** * Do not report duplicate exceptions. + * + * Boot-only. The setting persists on the shared handler and applies to every + * subsequent request and job in the worker. */ public function dontReportDuplicates(): static { diff --git a/src/foundation/src/Exceptions/ReportableHandler.php b/src/foundation/src/Exceptions/ReportableHandler.php index af98f22e5d..8b452f4d6b 100644 --- a/src/foundation/src/Exceptions/ReportableHandler.php +++ b/src/foundation/src/Exceptions/ReportableHandler.php @@ -62,6 +62,9 @@ public function handles(Throwable $e): bool /** * Indicate that report handling should stop after invoking this callback. * + * Boot-only. The flag persists on the registered callback and stops report + * handling for every exception it subsequently handles in the worker. + * * @return $this */ public function stop(): static diff --git a/src/hashing/src/HashManager.php b/src/hashing/src/HashManager.php index 05f2d36ffd..30cd418716 100644 --- a/src/hashing/src/HashManager.php +++ b/src/hashing/src/HashManager.php @@ -9,7 +9,7 @@ use SensitiveParameter; /** - * @mixin \Hypervel\Contracts\Hashing\Hasher + * @mixin Hasher */ class HashManager extends Manager implements Hasher { diff --git a/src/http/src/Client/RequestException.php b/src/http/src/Client/RequestException.php index b7c990bd42..5aabca0d1e 100644 --- a/src/http/src/Client/RequestException.php +++ b/src/http/src/Client/RequestException.php @@ -43,8 +43,8 @@ public function __construct( /** * Enable truncation of request exception messages. * - * Boot-only. The flag persists in a static property for the worker lifetime - * and applies to every HTTP client exception across all coroutines. + * Boot-only. The global default persists for the worker lifetime across all + * coroutines; per-request truncation settings take precedence. */ public static function truncate(): void { @@ -54,8 +54,8 @@ public static function truncate(): void /** * Set the truncation length for request exception messages. * - * Boot-only. The length persists in a static property for the worker - * lifetime and applies to every HTTP client exception across all coroutines. + * Boot-only. The global default persists for the worker lifetime across all + * coroutines; per-request truncation settings take precedence. */ public static function truncateAt(int $length): void { @@ -65,8 +65,8 @@ public static function truncateAt(int $length): void /** * Disable truncation of request exception messages. * - * Boot-only. The flag persists in a static property for the worker lifetime - * and applies to every HTTP client exception across all coroutines. + * Boot-only. The global default persists for the worker lifetime across all + * coroutines; per-request truncation settings take precedence. */ public static function dontTruncate(): void { diff --git a/src/json-schema/src/Deserializer.php b/src/json-schema/src/Deserializer.php index 1b579ac83d..056879a5ad 100644 --- a/src/json-schema/src/Deserializer.php +++ b/src/json-schema/src/Deserializer.php @@ -4,6 +4,10 @@ namespace Hypervel\JsonSchema; +use Hypervel\JsonSchema\Types\ArrayType; +use Hypervel\JsonSchema\Types\IntegerType; +use Hypervel\JsonSchema\Types\NumberType; +use Hypervel\JsonSchema\Types\StringType; use InvalidArgumentException; use stdClass; @@ -323,9 +327,9 @@ protected function buildObject(array $schema, array $refs = []): Types\ObjectTyp * * @throws InvalidArgumentException */ - protected function buildArray(array $schema, array $refs = []): Types\ArrayType + protected function buildArray(array $schema, array $refs = []): ArrayType { - $type = new Types\ArrayType; + $type = new ArrayType; if (array_key_exists('items', $schema) && $schema['items'] !== true && $schema['items'] !== []) { $items = $this->ensureSchemaFragmentIsArray($schema['items'], 'the [items] keyword'); @@ -357,9 +361,9 @@ protected function buildArray(array $schema, array $refs = []): Types\ArrayType * * @param array $schema */ - protected function buildString(array $schema): Types\StringType + protected function buildString(array $schema): StringType { - $type = new Types\StringType; + $type = new StringType; $type = $this->applyIntegerBounds($type, $schema, 'minLength', 'maxLength'); @@ -387,9 +391,9 @@ protected function buildString(array $schema): Types\StringType * * @param array $schema */ - protected function buildInteger(array $schema): Types\IntegerType + protected function buildInteger(array $schema): IntegerType { - return $this->applyNumericBounds(new Types\IntegerType, $schema, $this->toInteger(...)); + return $this->applyNumericBounds(new IntegerType, $schema, $this->toInteger(...)); } /** @@ -397,15 +401,15 @@ protected function buildInteger(array $schema): Types\IntegerType * * @param array $schema */ - protected function buildNumber(array $schema): Types\NumberType + protected function buildNumber(array $schema): NumberType { - return $this->applyNumericBounds(new Types\NumberType, $schema); + return $this->applyNumericBounds(new NumberType, $schema); } /** * Apply the numeric bound keywords to the given integer or number type. * - * @template TType of Types\IntegerType|Types\NumberType + * @template TType of IntegerType|NumberType * * @param TType $type * @param array $schema @@ -414,7 +418,7 @@ protected function buildNumber(array $schema): Types\NumberType * * @throws InvalidArgumentException */ - protected function applyNumericBounds(Types\IntegerType|Types\NumberType $type, array $schema, ?callable $cast = null): Types\IntegerType|Types\NumberType + protected function applyNumericBounds(IntegerType|NumberType $type, array $schema, ?callable $cast = null): IntegerType|NumberType { $cast ??= static fn (int|float $value) => $value; @@ -436,7 +440,7 @@ protected function applyNumericBounds(Types\IntegerType|Types\NumberType $type, /** * Apply integer-valued minimum and maximum keywords to an array or string type. * - * @template TType of Types\ArrayType|Types\StringType + * @template TType of ArrayType|StringType * * @param TType $type * @param array $schema @@ -444,7 +448,7 @@ protected function applyNumericBounds(Types\IntegerType|Types\NumberType $type, * * @throws InvalidArgumentException */ - protected function applyIntegerBounds(Types\ArrayType|Types\StringType $type, array $schema, string $minimumKeyword, string $maximumKeyword): Types\ArrayType|Types\StringType + protected function applyIntegerBounds(ArrayType|StringType $type, array $schema, string $minimumKeyword, string $maximumKeyword): ArrayType|StringType { foreach ([$minimumKeyword => 'min', $maximumKeyword => 'max'] as $keyword => $method) { if (! array_key_exists($keyword, $schema)) { diff --git a/src/json-schema/src/JsonSchema.php b/src/json-schema/src/JsonSchema.php index d2dded713f..30775c568a 100644 --- a/src/json-schema/src/JsonSchema.php +++ b/src/json-schema/src/JsonSchema.php @@ -5,18 +5,26 @@ namespace Hypervel\JsonSchema; use Closure; +use Hypervel\JsonSchema\Types\AnyOfType; +use Hypervel\JsonSchema\Types\ArrayType; +use Hypervel\JsonSchema\Types\BooleanType; +use Hypervel\JsonSchema\Types\IntegerType; +use Hypervel\JsonSchema\Types\NumberType; +use Hypervel\JsonSchema\Types\ObjectType; +use Hypervel\JsonSchema\Types\StringType; use Hypervel\JsonSchema\Types\Type; +use Hypervel\JsonSchema\Types\UnionType; use InvalidArgumentException; /** - * @method static Types\ObjectType object(Closure|array $properties = []) - * @method static Types\AnyOfType anyOf(Closure|array $schemas) - * @method static Types\IntegerType integer() - * @method static Types\NumberType number() - * @method static Types\StringType string() - * @method static Types\BooleanType boolean() - * @method static Types\ArrayType array() - * @method static Types\UnionType union(array $types) + * @method static ObjectType object(Closure|array $properties = []) + * @method static AnyOfType anyOf(Closure|array $schemas) + * @method static IntegerType integer() + * @method static NumberType number() + * @method static StringType string() + * @method static BooleanType boolean() + * @method static ArrayType array() + * @method static UnionType union(array $types) */ class JsonSchema { diff --git a/src/json-schema/src/JsonSchemaTypeFactory.php b/src/json-schema/src/JsonSchemaTypeFactory.php index 4d7ce94563..1894b0397d 100644 --- a/src/json-schema/src/JsonSchemaTypeFactory.php +++ b/src/json-schema/src/JsonSchemaTypeFactory.php @@ -6,13 +6,14 @@ use Closure; use Hypervel\Contracts\JsonSchema\JsonSchema as JsonSchemaContract; +use Hypervel\JsonSchema\Types\Type; class JsonSchemaTypeFactory extends JsonSchema implements JsonSchemaContract { /** * Create a new object schema instance. * - * @param array|(Closure(JsonSchemaTypeFactory): array) $properties + * @param array|(Closure(JsonSchemaTypeFactory): array) $properties */ public function object(Closure|array $properties = []): Types\ObjectType { @@ -76,7 +77,7 @@ public function union(array $types): Types\UnionType /** * Create a new anyOf schema instance. * - * @param array|(Closure(JsonSchemaTypeFactory): array) $schemas + * @param array|(Closure(JsonSchemaTypeFactory): array) $schemas */ public function anyOf(Closure|array $schemas): Types\AnyOfType { diff --git a/src/jwt/src/Providers/Lcobucci.php b/src/jwt/src/Providers/Lcobucci.php index 4d0f139a8e..c8b19c6c54 100644 --- a/src/jwt/src/Providers/Lcobucci.php +++ b/src/jwt/src/Providers/Lcobucci.php @@ -19,6 +19,7 @@ use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa; +use Lcobucci\JWT\Token\Plain; use Lcobucci\JWT\Token\RegisteredClaims; use Lcobucci\JWT\Validation\Constraint\SignedWith; use Throwable; @@ -64,7 +65,7 @@ public function __construct(string $secret, string $algo, array $keys, ?Configur /** * Create a JSON Web Token. * - * @throws \Hypervel\Jwt\Exceptions\JwtException + * @throws JwtException */ public function encode(array $payload): string { @@ -82,12 +83,12 @@ public function encode(array $payload): string /** * Decode a JSON Web Token. * - * @throws \Hypervel\Jwt\Exceptions\JwtException + * @throws JwtException */ public function decode(string $token): array { try { - /** @var \Lcobucci\JWT\Token\Plain */ + /** @var Plain */ $token = $this->config->parser()->parse($token); } catch (Throwable $exception) { throw new TokenInvalidException( @@ -177,7 +178,7 @@ protected function buildConfig(): Configuration * * Signer is rebuilt before config because buildConfig() reads $this->signer. * - * @throws \Hypervel\Jwt\Exceptions\JwtException + * @throws JwtException */ protected function onConfigurationChanged(): void { @@ -188,7 +189,7 @@ protected function onConfigurationChanged(): void /** * Get the signer instance. * - * @throws \Hypervel\Jwt\Exceptions\JwtException + * @throws JwtException */ protected function getSigner(): Signer { @@ -208,9 +209,11 @@ protected function isAsymmetric(): bool } /** - * @throws \Hypervel\Jwt\Exceptions\JwtException + * Get the key used to sign the tokens. + * + * @throws JwtException */ - protected function getSigningKey(): mixed + protected function getSigningKey(): Key { if ($this->isAsymmetric()) { if (! $privateKey = $this->getPrivateKey()) { @@ -228,9 +231,11 @@ protected function getSigningKey(): mixed } /** - * @throws \Hypervel\Jwt\Exceptions\JwtException + * Get the key used to verify the tokens. + * + * @throws JwtException */ - protected function getVerificationKey(): mixed + protected function getVerificationKey(): Key { if ($this->isAsymmetric()) { if (! $public = $this->getPublicKey()) { diff --git a/src/log/src/functions.php b/src/log/src/functions.php index 9de785a4e9..da3b33ee28 100644 --- a/src/log/src/functions.php +++ b/src/log/src/functions.php @@ -12,7 +12,7 @@ /** * Log a debug message to the logs. * - * @return ($message is null ? \Psr\Log\LoggerInterface : null) + * @return ($message is null ? LoggerInterface : null) */ function log(Arrayable|Jsonable|Stringable|array|string|null $message = null, array $context = []): ?LoggerInterface { diff --git a/src/notifications/src/DatabaseNotificationCollection.php b/src/notifications/src/DatabaseNotificationCollection.php index da61c96412..a342c75801 100644 --- a/src/notifications/src/DatabaseNotificationCollection.php +++ b/src/notifications/src/DatabaseNotificationCollection.php @@ -10,7 +10,7 @@ * @template TKey of array-key * @template TModel of DatabaseNotification * - * @extends \Hypervel\Database\Eloquent\Collection + * @extends Collection */ class DatabaseNotificationCollection extends Collection { diff --git a/src/permission/src/Contracts/Permission.php b/src/permission/src/Contracts/Permission.php index 1127c595b2..ebfd08d7db 100644 --- a/src/permission/src/Contracts/Permission.php +++ b/src/permission/src/Contracts/Permission.php @@ -6,6 +6,7 @@ use Hypervel\Database\Eloquent\Relations\BelongsToMany; use Hypervel\Permission\Exceptions\PermissionDoesNotExist; +use Hypervel\Permission\Models\Permission as PermissionModel; use UnitEnum; /** @@ -13,9 +14,9 @@ * @property string $name * @property null|string $guard_name * - * @mixin \Hypervel\Permission\Models\Permission + * @mixin PermissionModel * - * @phpstan-require-extends \Hypervel\Permission\Models\Permission + * @phpstan-require-extends PermissionModel */ interface Permission { diff --git a/src/permission/src/Contracts/Role.php b/src/permission/src/Contracts/Role.php index 4712af5828..b01b260e59 100644 --- a/src/permission/src/Contracts/Role.php +++ b/src/permission/src/Contracts/Role.php @@ -6,6 +6,7 @@ use Hypervel\Database\Eloquent\Relations\BelongsToMany; use Hypervel\Permission\Exceptions\RoleDoesNotExist; +use Hypervel\Permission\Models\Role as RoleModel; use UnitEnum; /** @@ -13,9 +14,9 @@ * @property string $name * @property null|string $guard_name * - * @mixin \Hypervel\Permission\Models\Role + * @mixin RoleModel * - * @phpstan-require-extends \Hypervel\Permission\Models\Role + * @phpstan-require-extends RoleModel */ interface Role { diff --git a/src/redis/src/RedisManager.php b/src/redis/src/RedisManager.php index 673d401c08..703429cab9 100644 --- a/src/redis/src/RedisManager.php +++ b/src/redis/src/RedisManager.php @@ -21,7 +21,7 @@ use function Hypervel\Support\enum_value; /** - * @mixin \Hypervel\Redis\RedisProxy + * @mixin RedisProxy */ class RedisManager implements FactoryContract, ConnectionContract { diff --git a/src/redis/src/RedisProxy.php b/src/redis/src/RedisProxy.php index 3d17b12a5c..ea14eb0cfa 100644 --- a/src/redis/src/RedisProxy.php +++ b/src/redis/src/RedisProxy.php @@ -34,7 +34,7 @@ * the same connection within a coroutine. * * @method bool|Redis discard() - * @mixin \Hypervel\Redis\RedisConnection + * @mixin RedisConnection */ class RedisProxy implements ConnectionContract { diff --git a/src/reflection/src/helpers.php b/src/reflection/src/helpers.php index c49e23ff90..4a8b6bb75c 100644 --- a/src/reflection/src/helpers.php +++ b/src/reflection/src/helpers.php @@ -16,7 +16,7 @@ * @param array $eager * @return TValue * - * @throws \ReflectionException + * @throws ReflectionException */ function lazy(string|Closure $class, Closure|int $callback = 0, int $options = 0, array $eager = []): object { @@ -64,7 +64,7 @@ public function typeFromParameter(Closure $callback): string * @param array $eager * @return TValue * - * @throws \ReflectionException + * @throws ReflectionException */ function proxy(string|Closure $class, Closure|int $callback = 0, int $options = 0, array $eager = []): object { diff --git a/src/sanctum/src/HasApiTokens.php b/src/sanctum/src/HasApiTokens.php index 399ca94a49..5d495aaa6c 100644 --- a/src/sanctum/src/HasApiTokens.php +++ b/src/sanctum/src/HasApiTokens.php @@ -13,7 +13,7 @@ use function Hypervel\Support\enum_value; /** - * @template TToken of \Hypervel\Sanctum\Contracts\HasAbilities = \Hypervel\Sanctum\PersonalAccessToken + * @template TToken of HasAbilities = PersonalAccessToken */ trait HasApiTokens { diff --git a/src/sanctum/src/PersonalAccessToken.php b/src/sanctum/src/PersonalAccessToken.php index fad74e3e5c..425e1e6d6d 100644 --- a/src/sanctum/src/PersonalAccessToken.php +++ b/src/sanctum/src/PersonalAccessToken.php @@ -9,6 +9,7 @@ use Hypervel\Container\Container; use Hypervel\Contracts\Auth\Authenticatable; use Hypervel\Contracts\Cache\Repository as CacheRepository; +use Hypervel\Database\Eloquent\Builder; use Hypervel\Database\Eloquent\Model; use Hypervel\Database\Eloquent\Relations\MorphTo; use Hypervel\Sanctum\Contracts\HasAbilities; @@ -22,10 +23,10 @@ * @property array $abilities * @property string $token * @property string $name - * @property \Hypervel\Database\Eloquent\Model $tokenable + * @property Model $tokenable * @property ?CarbonInterface $last_used_at * @property ?CarbonInterface $expires_at - * @method static \Hypervel\Database\Eloquent\Builder where(string $column, mixed $operator = null, mixed $value = null, string $boolean = 'and') + * @method static Builder where(string $column, mixed $operator = null, mixed $value = null, string $boolean = 'and') * @method static static|null find(mixed $id, array $columns = ['*']) */ class PersonalAccessToken extends Model implements HasAbilities diff --git a/src/sanctum/src/SanctumGuard.php b/src/sanctum/src/SanctumGuard.php index 98077a1308..f84468ebc0 100644 --- a/src/sanctum/src/SanctumGuard.php +++ b/src/sanctum/src/SanctumGuard.php @@ -14,6 +14,7 @@ use Hypervel\Contracts\Container\Container; use Hypervel\Contracts\Events\Dispatcher; use Hypervel\Http\Request; +use Hypervel\Sanctum\Contracts\HasApiTokens as HasApiTokensContract; use Hypervel\Sanctum\Events\TokenAuthenticated; use Hypervel\Support\Traits\Macroable; use InvalidArgumentException; @@ -111,7 +112,7 @@ public function user(): ?Authenticatable } if ($this->supportsTokens($user)) { - /** @var Authenticatable&\Hypervel\Sanctum\Contracts\HasApiTokens $tokenUser */ + /** @var Authenticatable&HasApiTokensContract $tokenUser */ $tokenUser = $user; $user = $tokenUser->withAccessToken(new TransientToken); } @@ -130,7 +131,7 @@ public function user(): ?Authenticatable $tokenable = $model::findTokenable($accessToken); if ($this->supportsTokens($tokenable)) { - /** @var Authenticatable&\Hypervel\Sanctum\Contracts\HasApiTokens $tokenable */ + /** @var Authenticatable&HasApiTokensContract $tokenable */ $user = $tokenable->withAccessToken($accessToken); if ($this->events?->hasListeners(TokenAuthenticated::class)) { diff --git a/src/scout/src/Searchable.php b/src/scout/src/Searchable.php index 918ba4fe82..d2d2c659a2 100644 --- a/src/scout/src/Searchable.php +++ b/src/scout/src/Searchable.php @@ -27,7 +27,7 @@ /** * Provides full-text search capabilities to Eloquent models. * - * @mixin \Hypervel\Database\Eloquent\Model + * @mixin Model */ trait Searchable { diff --git a/src/sentry/src/SentryServiceProvider.php b/src/sentry/src/SentryServiceProvider.php index 16aa16a47f..4673a9ad6d 100644 --- a/src/sentry/src/SentryServiceProvider.php +++ b/src/sentry/src/SentryServiceProvider.php @@ -52,6 +52,7 @@ use RuntimeException; use Sentry\ClientBuilder; use Sentry\Integration as SdkIntegration; +use Sentry\Integration\IntegrationInterface; use Sentry\Logger\DebugFileLogger; use Sentry\SentrySdk; use Sentry\Serializer\RepresentationSerializer; @@ -248,7 +249,7 @@ protected function configureAndRegisterClient(): void if ($options->hasDefaultIntegrations()) { // Remove the default error and fatal exception listeners to let the framework handle those // through the exception handler and log channel integration - $integrations = array_filter($integrations, static function (SdkIntegration\IntegrationInterface $integration): bool { + $integrations = array_filter($integrations, static function (IntegrationInterface $integration): bool { if ($integration instanceof SdkIntegration\ErrorListenerIntegration) { return false; } @@ -362,7 +363,7 @@ protected function bindEvents(): void $handler = new EventHandler($this->app, $userConfig); try { - /** @var \Hypervel\Contracts\Events\Dispatcher $dispatcher */ + /** @var Dispatcher $dispatcher */ $dispatcher = $this->app->make('events'); $handler->subscribe($dispatcher); @@ -459,7 +460,7 @@ private function bindTracingEvents(array $tracingConfig): void $handler = new TracingEventHandler($tracingConfig); try { - /** @var \Hypervel\Contracts\Events\Dispatcher $dispatcher */ + /** @var Dispatcher $dispatcher */ $dispatcher = $this->app->make('events'); $handler->subscribe($dispatcher); @@ -707,7 +708,7 @@ protected function registerAboutCommandIntegration(): void /** * Resolve the integrations from the user configuration with the container. * - * @return SdkIntegration\IntegrationInterface[] + * @return IntegrationInterface[] */ private function resolveIntegrationsFromUserConfig(array $userIntegrations): array { @@ -719,16 +720,16 @@ private function resolveIntegrationsFromUserConfig(array $userIntegrations): arr $integrations = []; foreach ($integrationsToResolve as $userIntegration) { - if ($userIntegration instanceof SdkIntegration\IntegrationInterface) { + if ($userIntegration instanceof IntegrationInterface) { $integrations[] = $userIntegration; } elseif (is_string($userIntegration)) { $resolvedIntegration = $this->app->make($userIntegration); - if (! $resolvedIntegration instanceof SdkIntegration\IntegrationInterface) { + if (! $resolvedIntegration instanceof IntegrationInterface) { throw new RuntimeException( sprintf( 'Sentry integrations must be an instance of `%s` got `%s`.', - SdkIntegration\IntegrationInterface::class, + IntegrationInterface::class, $resolvedIntegration::class ) ); @@ -739,7 +740,7 @@ private function resolveIntegrationsFromUserConfig(array $userIntegrations): arr throw new RuntimeException( sprintf( 'Sentry integrations must either be a valid container reference or an instance of `%s`.', - SdkIntegration\IntegrationInterface::class + IntegrationInterface::class ) ); } diff --git a/src/session/src/Middleware/StartSession.php b/src/session/src/Middleware/StartSession.php index 5d3172a13c..92efffa082 100644 --- a/src/session/src/Middleware/StartSession.php +++ b/src/session/src/Middleware/StartSession.php @@ -9,6 +9,7 @@ use Hypervel\Context\CoroutineContext; use Hypervel\Contracts\Cache\Factory as CacheFactoryContract; use Hypervel\Contracts\Cache\LockProvider; +use Hypervel\Contracts\Cache\Repository; use Hypervel\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract; use Hypervel\Contracts\Session\Session; use Hypervel\Http\Request; @@ -70,7 +71,7 @@ protected function handleRequestWhileBlocking(Request $request, Session $session $lockFor = $request->route()->locksFor() ?: $this->manager->defaultRouteBlockLockSeconds(); - /** @var \Hypervel\Contracts\Cache\Repository&LockProvider $store */ + /** @var LockProvider&Repository $store */ $store = $this->cache->store($this->manager->blockDriver()); $lock = $store ->lock('session:' . $session->getId(), (int) $lockFor) diff --git a/src/session/src/SessionManager.php b/src/session/src/SessionManager.php index 45c07357e3..ba89b5efeb 100644 --- a/src/session/src/SessionManager.php +++ b/src/session/src/SessionManager.php @@ -18,7 +18,7 @@ use function Hypervel\Support\enum_value; /** - * @mixin \Hypervel\Session\Store + * @mixin Store */ class SessionManager extends Manager { diff --git a/src/socialite/src/Two/AbstractProvider.php b/src/socialite/src/Two/AbstractProvider.php index 29e25b91e5..434cc26504 100644 --- a/src/socialite/src/Two/AbstractProvider.php +++ b/src/socialite/src/Two/AbstractProvider.php @@ -11,6 +11,7 @@ use Hypervel\Socialite\AbstractProvider as BaseProvider; use Hypervel\Socialite\Contracts\Provider as ProviderContract; use Hypervel\Socialite\Two\Exceptions\InvalidAudienceException; +use Hypervel\Socialite\Two\Exceptions\InvalidCodeException; use Hypervel\Support\Arr; use Hypervel\Support\Str; use SensitiveParameter; @@ -146,6 +147,9 @@ protected function formatScopes(array $scopes, string $scopeSeparator): string /** * Get the User instance for the authenticated user. + * + * @throws InvalidStateException + * @throws InvalidCodeException */ public function user(): User { @@ -352,10 +356,19 @@ protected function parseApprovedScopes(#[SensitiveParameter] array $response): a /** * Get the code from the request. + * + * @throws InvalidCodeException */ protected function getCode(): string { - return $this->getRequest()->input('code'); + $code = $this->getRequest()->input('code'); + + // Declined authorization can omit the code; throw an Exception rather than letting the return type raise a TypeError. + if (! is_string($code) || $code === '') { + throw new InvalidCodeException('The authorization code is missing or invalid.'); + } + + return $code; } /** diff --git a/src/socialite/src/Two/Concerns/InteractsWithJwks.php b/src/socialite/src/Two/Concerns/InteractsWithJwks.php index da46e479fb..49eccb4059 100644 --- a/src/socialite/src/Two/Concerns/InteractsWithJwks.php +++ b/src/socialite/src/Two/Concerns/InteractsWithJwks.php @@ -7,12 +7,13 @@ use Firebase\JWT\JWK; use Firebase\JWT\JWT; use Firebase\JWT\SignatureInvalidException; +use Hypervel\Socialite\Two\AbstractProvider; use Psr\Http\Message\ResponseInterface; use SensitiveParameter; use UnexpectedValueException; /** - * @phpstan-require-extends \Hypervel\Socialite\Two\AbstractProvider + * @phpstan-require-extends AbstractProvider */ trait InteractsWithJwks { diff --git a/src/socialite/src/Two/Exceptions/InvalidCodeException.php b/src/socialite/src/Two/Exceptions/InvalidCodeException.php new file mode 100644 index 0000000000..30e16d2af1 --- /dev/null +++ b/src/socialite/src/Two/Exceptions/InvalidCodeException.php @@ -0,0 +1,11 @@ + $units + * * @phpstan-return non-empty-string|false */ protected static function summarize(float|int $number, int $precision = 0, ?int $maxPrecision = null, array $units = []): false|string diff --git a/src/telescope/src/Jobs/ProcessPendingUpdates.php b/src/telescope/src/Jobs/ProcessPendingUpdates.php index 8a11e54f3d..edad1a5aed 100644 --- a/src/telescope/src/Jobs/ProcessPendingUpdates.php +++ b/src/telescope/src/Jobs/ProcessPendingUpdates.php @@ -11,6 +11,7 @@ use Hypervel\Queue\SerializesModels; use Hypervel\Support\Collection; use Hypervel\Telescope\Contracts\EntriesRepository; +use Hypervel\Telescope\EntryUpdate; class ProcessPendingUpdates implements ShouldQueue { @@ -22,7 +23,7 @@ class ProcessPendingUpdates implements ShouldQueue /** * Create a new job instance. * - * @param Collection $pendingUpdates the pending entry updates + * @param Collection $pendingUpdates the pending entry updates * @param int $attempt the number of times the job has been attempted */ public function __construct( diff --git a/src/telescope/src/Watchers/ScheduleWatcher.php b/src/telescope/src/Watchers/ScheduleWatcher.php index c2705922f2..54f7d1c0ba 100644 --- a/src/telescope/src/Watchers/ScheduleWatcher.php +++ b/src/telescope/src/Watchers/ScheduleWatcher.php @@ -4,7 +4,8 @@ namespace Hypervel\Telescope\Watchers; -use Hypervel\Console\Events; +use Hypervel\Console\Events\ScheduledTaskFailed; +use Hypervel\Console\Events\ScheduledTaskFinished; use Hypervel\Console\Scheduling\CallbackEvent; use Hypervel\Console\Scheduling\Event; use Hypervel\Context\CoroutineContext; @@ -31,21 +32,21 @@ public function register(Application $app): void $app->make(Dispatcher::class) ->listen([ - Events\ScheduledTaskFinished::class, - Events\ScheduledTaskFailed::class, + ScheduledTaskFinished::class, + ScheduledTaskFailed::class, ], [$this, 'recordCommand']); } /** * Record a scheduled command that was executed. */ - public function recordCommand(Events\ScheduledTaskFailed|Events\ScheduledTaskFinished $event): void + public function recordCommand(ScheduledTaskFailed|ScheduledTaskFinished $event): void { if (! Telescope::isRecording()) { return; } - if ($event instanceof Events\ScheduledTaskFinished) { + if ($event instanceof ScheduledTaskFinished) { $this->recordFinishedCommand($event); return; @@ -57,7 +58,7 @@ public function recordCommand(Events\ScheduledTaskFailed|Events\ScheduledTaskFin /** * Record a successfully finished scheduled command. */ - protected function recordFinishedCommand(Events\ScheduledTaskFinished $event): void + protected function recordFinishedCommand(ScheduledTaskFinished $event): void { $task = $event->task; $exitCode = $task->exitCode(); @@ -88,7 +89,7 @@ protected function recordFinishedCommand(Events\ScheduledTaskFinished $event): v /** * Record a failed scheduled command. */ - protected function recordFailedCommand(Events\ScheduledTaskFailed $event): void + protected function recordFailedCommand(ScheduledTaskFailed $event): void { $outcome = [ 'status' => 'failed', diff --git a/src/validation/README.md b/src/validation/README.md index b24e7c4a66..648448759a 100644 --- a/src/validation/README.md +++ b/src/validation/README.md @@ -11,5 +11,6 @@ Documentation: https://hypervel.org/docs/validation - Scalar `in` and `not_in` rules compare the submitted value with the rule's literal values as strings. Numeric strings are not loosely coerced. - Date comparison rules allow a referenced field to be missing or `null` unless it is also required. Unparseable date strings and invalid referenced values fail validation instead of being compared with `null`. - Rule keys may escape a literal asterisk as `\*`, matching the existing `\.` literal-dot syntax. +- Laravel's deprecated `InvokableRule` contract is not provided. Implement `ValidationRule` and its `validate()` method instead. Ported from: https://github.com/laravel/framework diff --git a/src/validation/src/ConditionalRules.php b/src/validation/src/ConditionalRules.php index a028e81f30..fe3cdb4724 100644 --- a/src/validation/src/ConditionalRules.php +++ b/src/validation/src/ConditionalRules.php @@ -5,25 +5,30 @@ namespace Hypervel\Validation; use Closure; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule; use Hypervel\Contracts\Validation\ValidationRule; use Hypervel\Support\Fluent; class ConditionalRules { + /** + * The boolean condition indicating if the rules should be added to the attribute. + */ + protected bool|Closure $condition; + /** * Create a new conditional rules instance. * - * @param bool|Closure $condition the boolean condition indicating if the rules should be added to the attribute - * @param array|Closure|InvokableRule|Rule|string|ValidationRule $rules the rules to be added to the attribute - * @param array|Closure|InvokableRule|Rule|string|ValidationRule $defaultRules the rules to be added to the attribute if the condition fails + * @param bool|callable $condition the boolean condition indicating if the rules should be added to the attribute + * @param array|Closure|Rule|string|ValidationRule $rules the rules to be added to the attribute + * @param array|Closure|Rule|string|ValidationRule $defaultRules the rules to be added to the attribute if the condition fails */ public function __construct( - protected bool|Closure $condition, - protected array|Closure|InvokableRule|Rule|string|ValidationRule $rules, - protected array|Closure|InvokableRule|Rule|string|ValidationRule $defaultRules = [] + bool|callable $condition, + protected array|Closure|Rule|string|ValidationRule $rules, + protected array|Closure|Rule|string|ValidationRule $defaultRules = [] ) { + $this->condition = is_bool($condition) ? $condition : $condition(...); } /** diff --git a/src/validation/src/InvokableValidationRule.php b/src/validation/src/InvokableValidationRule.php index d9fcb13b0a..af574b6a65 100644 --- a/src/validation/src/InvokableValidationRule.php +++ b/src/validation/src/InvokableValidationRule.php @@ -6,7 +6,6 @@ use Hypervel\Contracts\Validation\DataAwareRule; use Hypervel\Contracts\Validation\ImplicitRule; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule; use Hypervel\Contracts\Validation\ValidationRule; use Hypervel\Contracts\Validation\ValidatorAwareRule; @@ -39,17 +38,17 @@ class InvokableValidationRule implements Rule, ValidatorAwareRule /** * Create a new explicit Invokable validation rule. * - * @param InvokableRule|ValidationRule $invokable the invokable that validates the attribute + * @param ValidationRule $invokable the invokable that validates the attribute */ protected function __construct( - protected InvokableRule|ValidationRule $invokable + protected ValidationRule $invokable ) { } /** * Create a new implicit or explicit Invokable validation rule. */ - public static function make(InvokableRule|ValidationRule $invokable): InvokableValidationRule + public static function make(ValidationRule $invokable): InvokableValidationRule { if ($invokable->implicit ?? false) { return new class($invokable) extends InvokableValidationRule implements ImplicitRule {}; @@ -73,11 +72,7 @@ public function passes(string $attribute, mixed $value): bool $this->invokable->setValidator($this->validator); } - $method = $this->invokable instanceof ValidationRule - ? 'validate' - : '__invoke'; - - $this->invokable->{$method}($attribute, $value, function ($attribute, $message = null) { + $this->invokable->validate($attribute, $value, function ($attribute, $message = null) { $this->failed = true; return $this->pendingPotentiallyTranslatedString($attribute, $message); @@ -89,7 +84,7 @@ public function passes(string $attribute, mixed $value): bool /** * Get the underlying invokable rule. */ - public function invokable(): InvokableRule|ValidationRule + public function invokable(): ValidationRule { return $this->invokable; } diff --git a/src/validation/src/Rule.php b/src/validation/src/Rule.php index 299303b5fa..5b0bf6de0d 100644 --- a/src/validation/src/Rule.php +++ b/src/validation/src/Rule.php @@ -7,7 +7,6 @@ use Closure; use Hypervel\Context\CoroutineContext; use Hypervel\Contracts\Support\Arrayable; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule as RuleContract; use Hypervel\Contracts\Validation\ValidationRule; use Hypervel\Support\Arr; @@ -64,8 +63,8 @@ public static function can(string $ability, mixed ...$arguments): Can */ public static function when( bool|callable $condition, - array|Closure|InvokableRule|RuleContract|string|ValidationRule $rules, - array|Closure|InvokableRule|RuleContract|string|ValidationRule $defaultRules = [] + array|Closure|RuleContract|string|ValidationRule $rules, + array|Closure|RuleContract|string|ValidationRule $defaultRules = [] ): ConditionalRules { return new ConditionalRules($condition, $rules, $defaultRules); } @@ -75,8 +74,8 @@ public static function when( */ public static function unless( bool|callable $condition, - array|Closure|InvokableRule|RuleContract|string|ValidationRule $rules, - array|Closure|InvokableRule|RuleContract|string|ValidationRule $defaultRules = [] + array|Closure|RuleContract|string|ValidationRule $rules, + array|Closure|RuleContract|string|ValidationRule $defaultRules = [] ): ConditionalRules { return new ConditionalRules($condition, $defaultRules, $rules); } diff --git a/src/validation/src/Rules/Concerns/ClonesCustomRules.php b/src/validation/src/Rules/Concerns/ClonesCustomRules.php index 84f50cf4c5..f31a090841 100644 --- a/src/validation/src/Rules/Concerns/ClonesCustomRules.php +++ b/src/validation/src/Rules/Concerns/ClonesCustomRules.php @@ -4,7 +4,6 @@ namespace Hypervel\Validation\Rules\Concerns; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule; use Hypervel\Contracts\Validation\ValidationRule; @@ -16,9 +15,7 @@ trait ClonesCustomRules public function __clone(): void { foreach ($this->customRules as $key => $rule) { - if ($rule instanceof Rule - || $rule instanceof InvokableRule - || $rule instanceof ValidationRule) { + if ($rule instanceof Rule || $rule instanceof ValidationRule) { $this->customRules[$key] = clone $rule; } } diff --git a/src/validation/src/ValidationRuleParser.php b/src/validation/src/ValidationRuleParser.php index 670c7c55b2..11b8e39340 100644 --- a/src/validation/src/ValidationRuleParser.php +++ b/src/validation/src/ValidationRuleParser.php @@ -7,7 +7,6 @@ use Closure; use Hypervel\Context\CoroutineContext; use Hypervel\Contracts\Validation\CompilableRules; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule as RuleContract; use Hypervel\Contracts\Validation\ValidationRule; use Hypervel\Support\Arr; @@ -118,7 +117,9 @@ protected function prepareRule(mixed $rule, string $attribute): mixed $rule = new ClosureValidationRule($rule); } - if ($rule instanceof InvokableRule || $rule instanceof ValidationRule) { + // REMOVED: Laravel's deprecated InvokableRule contract is omitted; + // implement ValidationRule::validate() instead. + if ($rule instanceof ValidationRule) { $rule = InvokableValidationRule::make($rule); } @@ -145,7 +146,6 @@ public static function ruleReducesToString(mixed $rule): bool { return is_object($rule) && ! $rule instanceof Closure - && ! $rule instanceof InvokableRule && ! $rule instanceof ValidationRule && ! $rule instanceof RuleContract && ! $rule instanceof CompilableRules diff --git a/tests/Auth/AuthAccessGateTest.php b/tests/Auth/AuthAccessGateTest.php index 1a4ffae83d..f3826c9a08 100644 --- a/tests/Auth/AuthAccessGateTest.php +++ b/tests/Auth/AuthAccessGateTest.php @@ -14,6 +14,7 @@ use Hypervel\Events\Dispatcher as EventDispatcher; use Hypervel\Support\Testing\Fakes\EventFake; use Hypervel\Tests\Auth\Fixtures\AbilitiesEnum; +use Hypervel\Tests\Auth\Fixtures\ChildOfDummyWithUsePolicy; use Hypervel\Tests\Auth\Fixtures\DummyWithoutUsePolicy; use Hypervel\Tests\Auth\Fixtures\DummyWithUsePolicy; use Hypervel\Tests\Auth\Fixtures\DummyWithUsePolicyPolicy; @@ -1580,13 +1581,23 @@ public function testGateEvaluationDispatchesToAnEventFakeWithoutRealListeners(): public function testPolicyCacheReturnsSameResultOnSecondCall() { $gate = $this->getBasicGate(); - $gate->policy(AccessGateTestDummy::class, AccessGateTestPolicy::class); - $first = $gate->getPolicyFor(new AccessGateTestDummy); - $second = $gate->getPolicyFor(new AccessGateTestDummy); + $guesses = 0; + $gate->guessPolicyNamesUsing(function () use (&$guesses) { + ++$guesses; + + return [AccessGateTestPolicy::class]; + }); + + // Resolve through gates created by forUser() first, so the result must reach the shared cache. + $first = $gate->forUser((object) ['id' => 2])->getPolicyFor(new AccessGateTestDummy); + $second = $gate->forUser((object) ['id' => 3])->getPolicyFor(new AccessGateTestDummy); + $third = $gate->getPolicyFor(new AccessGateTestDummy); $this->assertInstanceOf(AccessGateTestPolicy::class, $first); $this->assertInstanceOf(AccessGateTestPolicy::class, $second); + $this->assertInstanceOf(AccessGateTestPolicy::class, $third); + $this->assertSame(1, $guesses); } public function testPolicyCacheDoesNotInterfereWithExplicitPolicies() @@ -1603,26 +1614,53 @@ public function testPolicyCacheDoesNotInterfereWithExplicitPolicies() $this->assertInstanceOf(AccessGateTestPolicy::class, $result); } - public function testPolicyCacheIsClearedByFlushState() + public function testPolicyCacheIsClearedWhenGuessCallbackChanges() { $gate = $this->getBasicGate(); $counter = 0; - $gate->guessPolicyNamesUsing(function () use (&$counter) { + $guesser = function () use (&$counter) { ++$counter; return []; - }); + }; + $gate->guessPolicyNamesUsing($guesser); + $gate->getPolicyFor('SomeClassA'); $gate->getPolicyFor('SomeClassA'); $this->assertSame(1, $counter); - // After flush, the guesser should be called again for the same class - Gate::flushState(); + // After the guesser changes, the same class should be guessed again + $gate->guessPolicyNamesUsing($guesser); $gate->getPolicyFor('SomeClassA'); $this->assertSame(2, $counter); } + public function testPolicyCacheIsClearedWhenAPolicyIsRegistered() + { + $gate = $this->getBasicGate(); + $userGate = $gate->forUser((object) ['id' => 2]); + + $this->assertInstanceOf(DummyWithUsePolicyPolicy::class, $gate->getPolicyFor(ChildOfDummyWithUsePolicy::class)); + + $gate->policy(DummyWithUsePolicy::class, AccessGateTestPolicy::class); + + // The registered parent policy precedes the inherited attribute, while the + // earlier gate keeps its previous policies and cached resolution. + $this->assertInstanceOf(AccessGateTestPolicy::class, $gate->getPolicyFor(ChildOfDummyWithUsePolicy::class)); + $this->assertInstanceOf(DummyWithUsePolicyPolicy::class, $userGate->getPolicyFor(ChildOfDummyWithUsePolicy::class)); + } + + public function testPolicyCacheIsNotSharedBetweenGatesWithDifferentPolicies() + { + $registered = $this->getBasicGate(); + $registered->policy(DummyWithUsePolicy::class, AccessGateTestPolicy::class); + $unregistered = $this->getBasicGate(); + + $this->assertInstanceOf(AccessGateTestPolicy::class, $registered->getPolicyFor(ChildOfDummyWithUsePolicy::class)); + $this->assertInstanceOf(DummyWithUsePolicyPolicy::class, $unregistered->getPolicyFor(ChildOfDummyWithUsePolicy::class)); + } + public function testPolicyCacheStoresNullForClassWithNoPolicy() { $gate = $this->getBasicGate(); @@ -1675,10 +1713,7 @@ public function testFlushStateRestoresLazyGuestCallbackCacheSentinel(): void public function testFlushStateClearsAllCaches() { $gate = $this->getBasicGate(); - - // Populate policy cache $gate->policy(AccessGateTestDummy::class, AccessGateTestPolicy::class); - $gate->getPolicyFor(new AccessGateTestDummy); // Populate ability method cache $gate->check('update-dash', new AccessGateTestDummy); @@ -1687,7 +1722,6 @@ public function testFlushStateClearsAllCaches() Gate::flushState(); // Verify everything still works after flush - $gate->policy(AccessGateTestDummy::class, AccessGateTestPolicy::class); $result = $gate->getPolicyFor(new AccessGateTestDummy); $this->assertInstanceOf(AccessGateTestPolicy::class, $result); } diff --git a/tests/Auth/AuthPasswordResetServiceProviderTest.php b/tests/Auth/AuthPasswordResetServiceProviderTest.php index c3c0e35c17..62dd68e176 100644 --- a/tests/Auth/AuthPasswordResetServiceProviderTest.php +++ b/tests/Auth/AuthPasswordResetServiceProviderTest.php @@ -11,6 +11,7 @@ use Hypervel\Contracts\Foundation\Application; use Hypervel\Tests\TestCase; use Mockery as m; +use Mockery\MockInterface; class AuthPasswordResetServiceProviderTest extends TestCase { @@ -39,7 +40,7 @@ public function testEventRebindRefreshesAnAlreadyResolvedPasswordManager(): void /** * Register the provider and return its event-rebind callback. * - * @return array{Application&m\MockInterface, Closure} + * @return array{Application&MockInterface, Closure} */ private function registerProvider(): array { diff --git a/tests/Auth/Fixtures/ChildOfDummyWithUsePolicy.php b/tests/Auth/Fixtures/ChildOfDummyWithUsePolicy.php new file mode 100644 index 0000000000..4a566c5b74 --- /dev/null +++ b/tests/Auth/Fixtures/ChildOfDummyWithUsePolicy.php @@ -0,0 +1,12 @@ +assertInstanceOf( + AuthTestUserPolicy::class, + Gate::getPolicyFor(AuthTestUser::class) + ); + + $this->assertInstanceOf( + AuthTestUserPolicy::class, + Gate::getPolicyFor(ModelAuthTestUser::class) + ); + + $this->assertNull( + Gate::getPolicyFor(static::class) + ); + } + + public function testPolicyCanBeGuessedForParallelClassHierarchies(): void + { + $this->assertInstanceOf( + TopTestUserPolicy::class, + Gate::getPolicyFor(TopTestUser::class) + ); + + $this->assertInstanceOf( + SubTestUserPolicy::class, + Gate::getPolicyFor(SubTestUser::class) + ); + } + + public function testPolicyCanBeGuessedUsingCallback(): void + { + Gate::guessPolicyNamesUsing(function () { + return AuthTestUserPolicy::class; + }); + + $this->assertInstanceOf( + AuthTestUserPolicy::class, + Gate::getPolicyFor(AuthTestUser::class) + ); + } + + public function testPolicyCanBeGuessedMultipleTimes(): void + { + Gate::guessPolicyNamesUsing(function () { + return [ + 'App\Policies\TestUserPolicy', + AuthTestUserPolicy::class, + ]; + }); + + $this->assertInstanceOf( + AuthTestUserPolicy::class, + Gate::getPolicyFor(AuthTestUser::class) + ); + } + + public function testPolicyCanBeGivenByAttribute(): void + { + Gate::guessPolicyNamesUsing(fn () => [AuthTestUserPolicy::class]); + + $this->assertInstanceOf(PostPolicy::class, Gate::getPolicyFor(Post::class)); + } + + public function testPolicyGivenByAttributeIsInheritedByChildClasses(): void + { + $this->assertInstanceOf(PostPolicy::class, Gate::getPolicyFor(ChildPost::class)); + $this->assertInstanceOf(PostPolicy::class, Gate::getPolicyFor(GrandchildPost::class)); + } + + public function testPolicyGivenByAttributeOnChildClassOverridesParentAttribute(): void + { + $this->assertInstanceOf(AudioPostPolicy::class, Gate::getPolicyFor(AudioPost::class)); + } + + public function testRegisteredPolicyTakesPrecedenceOverInheritedAttribute(): void + { + Gate::policy(ChildPost::class, AudioPostPolicy::class); + + $this->assertInstanceOf(AudioPostPolicy::class, Gate::getPolicyFor(ChildPost::class)); + } + + public function testGuessedPolicyTakesPrecedenceOverInheritedAttribute(): void + { + Gate::guessPolicyNamesUsing(fn () => [AuthTestUserPolicy::class]); + + $this->assertInstanceOf(AuthTestUserPolicy::class, Gate::getPolicyFor(ChildPost::class)); + } +} + +#[UsePolicy(PostPolicy::class)] +class Post extends Model +{ +} + +class ChildPost extends Post +{ +} + +class GrandchildPost extends ChildPost +{ +} + +#[UsePolicy(AudioPostPolicy::class)] +class AudioPost extends Post +{ +} + +class PostPolicy +{ +} + +class AudioPostPolicy +{ +} diff --git a/tests/Cache/Redis/RedisCacheTestCase.php b/tests/Cache/Redis/RedisCacheTestCase.php index d636b7263f..d4f88b45ef 100644 --- a/tests/Cache/Redis/RedisCacheTestCase.php +++ b/tests/Cache/Redis/RedisCacheTestCase.php @@ -15,6 +15,7 @@ use Hypervel\Tests\Redis\Fixtures\FakeRedisClient; use Hypervel\Tests\Redis\Fixtures\PhpRedisConnectionStub; use Mockery as m; +use Mockery\MockInterface; use Redis; use RedisCluster; @@ -67,9 +68,9 @@ protected function setUp(): void * unexpected fallthrough to real Redis connections when expectations * don't match. * - * @return m\MockInterface|PhpRedisConnection connection with _mockClient property for setting expectations + * @return MockInterface|PhpRedisConnection connection with _mockClient property for setting expectations */ - protected function mockConnection(): m\MockInterface|PhpRedisConnection + protected function mockConnection(): MockInterface|PhpRedisConnection { // Anonymous mock - not bound to Redis extension class // This prevents fallthrough to real Redis when expectations don't match @@ -121,9 +122,9 @@ protected function mockConnection(): m\MockInterface|PhpRedisConnection * by its class definition, triggering cluster mode (sequential commands * instead of pipelines). * - * @return m\MockInterface|PhpRedisClusterConnection connection with _mockClient property for setting expectations + * @return MockInterface|PhpRedisClusterConnection connection with _mockClient property for setting expectations */ - protected function mockClusterConnection(): m\MockInterface|PhpRedisClusterConnection + protected function mockClusterConnection(): MockInterface|PhpRedisClusterConnection { // Mock that identifies as RedisCluster for instanceof checks $client = m::mock(RedisCluster::class); @@ -165,7 +166,7 @@ protected function mockClusterConnection(): m\MockInterface|PhpRedisClusterConne protected function createRedisFactory( RedisConnection $connection, string $connectionName = 'default' - ): m\MockInterface|RedisFactory { + ): MockInterface|RedisFactory { $redisProxy = m::mock(RedisProxy::class); $redisProxy->shouldReceive('withConnection') ->andReturnUsing(fn (callable $callback) => $callback($connection)); @@ -184,13 +185,13 @@ protected function createRedisFactory( /** * Create a RedisStore with a mocked connection. * - * @param m\MockInterface|RedisConnection $connection the mocked connection (from mockConnection()) + * @param MockInterface|RedisConnection $connection the mocked connection (from mockConnection()) * @param string $prefix cache key prefix * @param string $connectionName Redis connection name * @param null|string $tagMode optional tag mode ('any' or 'all'). If provided, setTagMode() is called. */ protected function createStore( - m\MockInterface|RedisConnection $connection, + MockInterface|RedisConnection $connection, string $prefix = 'prefix:', string $connectionName = 'default', ?string $tagMode = null, @@ -225,7 +226,7 @@ protected function createStore( * @param string $prefix cache key prefix * @param string $connectionName Redis connection name * @param null|string $tagMode optional tag mode ('any' or 'all') - * @return array{0: RedisStore, 1: m\MockInterface, 2: m\MockInterface} [store, clusterClient, connection] + * @return array{0: RedisStore, 1: MockInterface, 2: MockInterface} [store, clusterClient, connection] */ protected function createClusterStore( string $prefix = 'prefix:', diff --git a/tests/Console/ArtisanCommandTest.php b/tests/Console/ArtisanCommandTest.php index ed88d1b038..88b554a19d 100644 --- a/tests/Console/ArtisanCommandTest.php +++ b/tests/Console/ArtisanCommandTest.php @@ -526,6 +526,10 @@ protected function verifyMockeryExpectationsNow(): void m::close(); } + /** + * Don't allow Mockery's InvalidCountException to be reported. Mocks setup + * in PendingCommand cause PHPUnit tearDown() to later throw the exception. + */ protected function ignoringMockOnceExceptions(callable $callback): void { try { diff --git a/tests/Container/ContainerResolveNonInstantiableTest.php b/tests/Container/ContainerResolveNonInstantiableTest.php index b5afe99443..83d9cb5eaa 100644 --- a/tests/Container/ContainerResolveNonInstantiableTest.php +++ b/tests/Container/ContainerResolveNonInstantiableTest.php @@ -66,7 +66,7 @@ public function __construct(?TestInterface $testObject = null, int $i = 0) class VariadicParentClass { /** - * @var \Hypervel\Tests\Container\ChildClass + * @var ChildClass */ public $child; diff --git a/tests/Foundation/Testing/Concerns/InteractsWithDatabaseTest.php b/tests/Foundation/Testing/Concerns/InteractsWithDatabaseTest.php index 8806db5f34..329bd9b244 100644 --- a/tests/Foundation/Testing/Concerns/InteractsWithDatabaseTest.php +++ b/tests/Foundation/Testing/Concerns/InteractsWithDatabaseTest.php @@ -4,6 +4,7 @@ namespace Hypervel\Tests\Foundation\Testing\Concerns; +use Faker\Generator; use Hypervel\Database\Connection; use Hypervel\Database\Query\Expression; use Hypervel\Database\Query\Grammars\SQLiteGrammar; @@ -254,7 +255,7 @@ public function testFactoryUsesConfiguredFakerLocale() // Trigger faker initialization by calling make() $factory->make(); - /** @var \Faker\Generator $faker */ + /** @var Generator $faker */ $faker = $fakerProperty->getValue($factory); $providerClasses = array_map(fn ($provider) => get_class($provider), $faker->getProviders()); diff --git a/tests/Integration/Auth/EloquentUserProviderCacheTest.php b/tests/Integration/Auth/EloquentUserProviderCacheTest.php index 4fdfa26388..3faad48b07 100644 --- a/tests/Integration/Auth/EloquentUserProviderCacheTest.php +++ b/tests/Integration/Auth/EloquentUserProviderCacheTest.php @@ -11,6 +11,7 @@ use Hypervel\Cache\CacheManager; use Hypervel\Cache\FileStore; use Hypervel\Cache\ModelCacheCoordinator; +use Hypervel\Contracts\Auth\Authenticatable; use Hypervel\Contracts\Cache\Repository as CacheRepository; use Hypervel\Contracts\Foundation\Application as ApplicationContract; use Hypervel\Database\Eloquent\Builder; @@ -726,7 +727,7 @@ protected function fireUserEvent(string $event, User $user): void /** * Create a provider backed by the real serializing cache manager. * - * @param class-string<\Hypervel\Contracts\Auth\Authenticatable&Model> $model + * @param class-string $model */ protected function makeRealCachedProvider(string $model = User::class): EloquentUserProvider { diff --git a/tests/Integration/Auth/Fixtures/Models/AuthTestUser.php b/tests/Integration/Auth/Fixtures/Models/AuthTestUser.php new file mode 100644 index 0000000000..7065e465d2 --- /dev/null +++ b/tests/Integration/Auth/Fixtures/Models/AuthTestUser.php @@ -0,0 +1,30 @@ +lock('foo')->forceRelease(); diff --git a/tests/Integration/Database/Queue/BatchableTransactionTest.php b/tests/Integration/Database/Queue/BatchableTransactionTest.php index 1ad8381210..919d446a20 100644 --- a/tests/Integration/Database/Queue/BatchableTransactionTest.php +++ b/tests/Integration/Database/Queue/BatchableTransactionTest.php @@ -47,7 +47,6 @@ public function testItCanHandleTimeoutJob(): void 'DB_CONNECTION' => config('database.default'), 'DB_DATABASE' => config('database.connections.' . config('database.default') . '.database'), 'DB_URL' => false, - 'DATABASE_URL' => false, 'DB_POOLED_URL' => false, 'QUEUE_CONNECTION' => config('queue.default'), ])->run(); diff --git a/tests/Integration/Database/Queue/QueueTransactionTest.php b/tests/Integration/Database/Queue/QueueTransactionTest.php index 09add7c8a1..6b540c88d1 100644 --- a/tests/Integration/Database/Queue/QueueTransactionTest.php +++ b/tests/Integration/Database/Queue/QueueTransactionTest.php @@ -46,7 +46,6 @@ public function testItCanHandleTimeoutJob(ShouldQueue $job): void 'DB_CONNECTION' => config('database.default'), 'DB_DATABASE' => config('database.connections.' . config('database.default') . '.database'), 'DB_URL' => false, - 'DATABASE_URL' => false, 'DB_POOLED_URL' => false, 'QUEUE_CONNECTION' => config('queue.default'), ])->run(); diff --git a/tests/Integration/Generators/EnumMakeCommandTest.php b/tests/Integration/Generators/EnumMakeCommandTest.php index 09ce4def2f..059f9b4cc9 100644 --- a/tests/Integration/Generators/EnumMakeCommandTest.php +++ b/tests/Integration/Generators/EnumMakeCommandTest.php @@ -4,6 +4,8 @@ namespace Hypervel\Tests\Integration\Generators; +use Hypervel\Filesystem\Filesystem; + class EnumMakeCommandTest extends TestCase { protected array $files = [ @@ -50,7 +52,7 @@ public function testItCanGenerateEnumFileInEnumsFolder(): void { $enumsFolderPath = app_path('Enums'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($enumsFolderPath); @@ -70,7 +72,7 @@ public function testItCanGenerateEnumFileInEnumerationsFolder(): void { $enumerationsFolderPath = app_path('Enumerations'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($enumerationsFolderPath); diff --git a/tests/Integration/Generators/InterfaceMakeCommandTest.php b/tests/Integration/Generators/InterfaceMakeCommandTest.php index 085f0c939f..69ada9f58b 100644 --- a/tests/Integration/Generators/InterfaceMakeCommandTest.php +++ b/tests/Integration/Generators/InterfaceMakeCommandTest.php @@ -4,6 +4,8 @@ namespace Hypervel\Tests\Integration\Generators; +use Hypervel\Filesystem\Filesystem; + class InterfaceMakeCommandTest extends TestCase { protected array $files = [ @@ -27,7 +29,7 @@ public function testItCanGenerateInterfaceFileWhenContractsFolderExists(): void { $interfacesFolderPath = app_path('Contracts'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($interfacesFolderPath); @@ -47,7 +49,7 @@ public function testItCanGenerateInterfaceFileWhenInterfacesFolderExists(): void { $interfacesFolderPath = app_path('Interfaces'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($interfacesFolderPath); diff --git a/tests/Integration/Generators/TraitMakeCommandTest.php b/tests/Integration/Generators/TraitMakeCommandTest.php index 1494ee9f73..3c80f87332 100644 --- a/tests/Integration/Generators/TraitMakeCommandTest.php +++ b/tests/Integration/Generators/TraitMakeCommandTest.php @@ -4,6 +4,8 @@ namespace Hypervel\Tests\Integration\Generators; +use Hypervel\Filesystem\Filesystem; + class TraitMakeCommandTest extends TestCase { protected array $files = [ @@ -27,7 +29,7 @@ public function testItCanGenerateTraitFileWhenTraitsFolderExists(): void { $traitsFolderPath = app_path('Traits'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($traitsFolderPath); @@ -47,7 +49,7 @@ public function testItCanGenerateTraitFileWhenConcernsFolderExists(): void { $traitsFolderPath = app_path('Concerns'); - /** @var \Hypervel\Filesystem\Filesystem $files */ + /** @var Filesystem $files */ $files = $this->app->make('files'); $files->ensureDirectoryExists($traitsFolderPath); diff --git a/tests/Integration/Http/Middleware/PreventRequestForgeryServerRuntimeTest.php b/tests/Integration/Http/Middleware/PreventRequestForgeryServerRuntimeTest.php index 380e63ee55..31407df0d8 100644 --- a/tests/Integration/Http/Middleware/PreventRequestForgeryServerRuntimeTest.php +++ b/tests/Integration/Http/Middleware/PreventRequestForgeryServerRuntimeTest.php @@ -10,6 +10,7 @@ use Hypervel\Support\Collection; use Hypervel\Support\Facades\Route; use Hypervel\Testbench\TestCase; +use Symfony\Component\HttpFoundation\Cookie; class PreventRequestForgeryServerRuntimeTest extends TestCase { @@ -77,11 +78,13 @@ public function testServerRuntimeAcceptsMatchingCsrfToken(): void } /** - * @param array $cookies + * Get the named cookie from the given response cookies. + * + * @param array $cookies */ - protected function cookieFromResponse(array $cookies, string $name): \Symfony\Component\HttpFoundation\Cookie + protected function cookieFromResponse(array $cookies, string $name): Cookie { return Collection::make($cookies) - ->first(fn (\Symfony\Component\HttpFoundation\Cookie $cookie): bool => $cookie->getName() === $name); + ->first(fn (Cookie $cookie): bool => $cookie->getName() === $name); } } diff --git a/tests/Integration/Reverb/Fixtures/server.php b/tests/Integration/Reverb/Fixtures/server.php index 207d856dee..fb0a88530b 100644 --- a/tests/Integration/Reverb/Fixtures/server.php +++ b/tests/Integration/Reverb/Fixtures/server.php @@ -28,6 +28,7 @@ use Hypervel\Reverb\Webhooks\Jobs\WebhookDeliveryJob; use Hypervel\Server\ServerFactory; use Hypervel\Support\Facades\Queue; +use Hypervel\Support\Testing\Fakes\QueueFake; use Hypervel\Testbench\Bootstrapper; use Hypervel\Testbench\Foundation\Application as TestbenchApplication; use Hypervel\Tests\Integration\Reverb\Fixtures\ParallelTestApplicationProvider; @@ -214,7 +215,7 @@ }); $app->make(ReverbRouter::class)->get('/_test/queued-jobs', function () { - /** @var \Hypervel\Support\Testing\Fakes\QueueFake $fake */ + /** @var QueueFake $fake */ $fake = Queue::getFacadeRoot(); $jobs = $fake->pushed(WebhookDeliveryJob::class)->map(function (WebhookDeliveryJob $job) { diff --git a/tests/Redis/Operations/FlushByPatternTest.php b/tests/Redis/Operations/FlushByPatternTest.php index d110eee12a..4f107fafe5 100644 --- a/tests/Redis/Operations/FlushByPatternTest.php +++ b/tests/Redis/Operations/FlushByPatternTest.php @@ -11,6 +11,7 @@ use Hypervel\Tests\Redis\Fixtures\PhpRedisConnectionStub; use Hypervel\Tests\TestCase; use Mockery as m; +use Mockery\MockInterface; use RedisException; /** @@ -24,7 +25,7 @@ class FlushByPatternTest extends TestCase * Returns both the connection (as a partial mock) and the client so tests * can set up unlink expectations while using FakeRedisClient for scan behavior. * - * @return array{m\MockInterface&PhpRedisConnectionStub, FakeRedisClient} + * @return array{MockInterface&PhpRedisConnectionStub, FakeRedisClient} */ private function createConnectionWithClient(FakeRedisClient $client): array { diff --git a/tests/Routing/RouteRegistrarTest.php b/tests/Routing/RouteRegistrarTest.php index 75682a2f3c..81c4e0f81b 100644 --- a/tests/Routing/RouteRegistrarTest.php +++ b/tests/Routing/RouteRegistrarTest.php @@ -12,6 +12,7 @@ use Hypervel\Routing\Contracts\CallableDispatcher as CallableDispatcherContract; use Hypervel\Routing\Contracts\ControllerDispatcher as ControllerDispatcherContract; use Hypervel\Routing\ControllerDispatcher; +use Hypervel\Routing\Route; use Hypervel\Routing\Router; use Hypervel\Routing\RouteRegistrar; use Hypervel\Tests\Routing\Fixtures\CategoryBackedEnum; @@ -173,7 +174,7 @@ public function testResourceWithTrashed() 'destroy', ]); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertTrue($route->allowsTrashedBindings()); } @@ -550,8 +551,8 @@ public function testRouteGroupChaining() $routeCollection = $this->router->getRoutes(); - $this->assertInstanceOf(\Hypervel\Routing\Route::class, $routeCollection->match(Request::create('foo', 'GET'))); - $this->assertInstanceOf(\Hypervel\Routing\Route::class, $routeCollection->match(Request::create('bar', 'GET'))); + $this->assertInstanceOf(Route::class, $routeCollection->match(Request::create('foo', 'GET'))); + $this->assertInstanceOf(Route::class, $routeCollection->match(Request::create('bar', 'GET'))); } public function testRegisteringNonApprovedAttributesThrows(): void @@ -1268,7 +1269,7 @@ public function testResourceWheres() $this->router->resource('users', RouteRegistrarControllerStub::class) ->where($wheres); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1281,7 +1282,7 @@ public function testWhereNumberRegistration() $this->router->get('/{foo}/{bar}')->whereNumber(['foo', 'bar']); $this->router->get('/api/{bar}/{foo}')->whereNumber(['bar', 'foo']); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1294,7 +1295,7 @@ public function testWhereAlphaRegistration() $this->router->get('/{foo}/{bar}')->whereAlpha(['foo', 'bar']); $this->router->get('/api/{bar}/{foo}')->whereAlpha(['bar', 'foo']); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1306,7 +1307,7 @@ public function testWhereAlphaNumericRegistration() $this->router->get('/{foo}')->whereAlphaNumeric(['1a2b3c']); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1339,7 +1340,7 @@ public function testWhereInRegistration() $this->router->get('/{foo}/{bar}')->whereIn(['foo', 'bar'], ['one', 'two']); $this->router->get('/api/{bar}/{foo}')->whereIn(['bar', 'foo'], ['one', 'two']); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1370,7 +1371,7 @@ public function testGroupWhereNumberRegistrationOnRouteRegistrar() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1388,7 +1389,7 @@ public function testGroupWhereAlphaRegistrationOnRouteRegistrar() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1402,7 +1403,7 @@ public function testGroupWhereAlphaNumericRegistrationOnRouteRegistrar() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1420,7 +1421,7 @@ public function testGroupWhereInRegistrationOnRouteRegistrar() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1438,7 +1439,7 @@ public function testGroupWhereNumberRegistrationOnRouter() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1456,7 +1457,7 @@ public function testGroupWhereAlphaRegistrationOnRouter() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1470,7 +1471,7 @@ public function testGroupWhereAlphaNumericRegistrationOnRouter() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1488,7 +1489,7 @@ public function testGroupWhereInRegistrationOnRouter() $router->get('/'); }); - /** @var \Hypervel\Routing\Route $route */ + /** @var Route $route */ foreach ($this->router->getRoutes() as $route) { $this->assertEquals($wheres, $route->wheres); } @@ -1825,7 +1826,7 @@ public function testCanSetExcludedMiddlewareForSpecifiedMethodsOnRegisteredSingl /** * Get the last route registered with the router. * - * @return \Hypervel\Routing\Route + * @return Route */ protected function getRoute() { diff --git a/tests/Sentry/Features/LogLogsIntegrationTest.php b/tests/Sentry/Features/LogLogsIntegrationTest.php index f06d292df7..a29f141695 100644 --- a/tests/Sentry/Features/LogLogsIntegrationTest.php +++ b/tests/Sentry/Features/LogLogsIntegrationTest.php @@ -11,6 +11,7 @@ use Hypervel\Support\Facades\Log; use Hypervel\Tests\Sentry\SentryTestCase; use Sentry\EventType; +use Sentry\Logs\Log as SentryLog; use Sentry\Logs\LogLevel; use function Sentry\logger; @@ -178,7 +179,11 @@ public function testLogChannelAddsContextAsAttributes(): void $this->assertEquals('bar', $log->attributes()->get('foo')->getValue()); } - /** @return \Sentry\Logs\Log[] */ + /** + * Get and flush the captured logs. + * + * @return SentryLog[] + */ private function getAndFlushCapturedLogs(): array { $logs = logger()->aggregator()->all(); diff --git a/tests/Sentry/Features/ViewEngineDecoratorTest.php b/tests/Sentry/Features/ViewEngineDecoratorTest.php index cade70cb34..defae52b0a 100644 --- a/tests/Sentry/Features/ViewEngineDecoratorTest.php +++ b/tests/Sentry/Features/ViewEngineDecoratorTest.php @@ -8,6 +8,7 @@ use Hypervel\Sentry\Tracing\ViewEngineDecorator; use Hypervel\Tests\Sentry\SentryTestCase; use Hypervel\View\Engines\EngineResolver; +use Hypervel\View\Factory; use ReflectionProperty; use Sentry\Tracing\Transaction; @@ -57,7 +58,7 @@ public function testViewNameIsAvailableDuringRender(): void file_put_contents($viewDir . '/hello.blade.php', 'Hello'); try { - /** @var \Hypervel\View\Factory $viewFactory */ + /** @var Factory $viewFactory */ $viewFactory = $this->app->make('view'); $viewFactory->addNamespace('sentrytest', $viewDir); @@ -83,7 +84,7 @@ public function testViewNamesAreIsolatedBetweenConcurrentRenders(): void file_put_contents($viewDir . '/second.blade.php', 'Second'); try { - /** @var \Hypervel\View\Factory $viewFactory */ + /** @var Factory $viewFactory */ $viewFactory = $this->app->make('view'); $viewFactory->addNamespace('sentryrace', $viewDir); diff --git a/tests/Socialite/Fixtures/FacebookTestProviderStub.php b/tests/Socialite/Fixtures/FacebookTestProviderStub.php index 41d53c011e..39ada9a2fb 100644 --- a/tests/Socialite/Fixtures/FacebookTestProviderStub.php +++ b/tests/Socialite/Fixtures/FacebookTestProviderStub.php @@ -7,11 +7,12 @@ use GuzzleHttp\Client; use Hypervel\Socialite\Two\FacebookProvider; use Mockery as m; +use Mockery\MockInterface; class FacebookTestProviderStub extends FacebookProvider { /** - * @var \GuzzleHttp\Client|\Mockery\MockInterface + * @var Client|MockInterface */ public $http; diff --git a/tests/Socialite/Fixtures/GoogleTestProviderStub.php b/tests/Socialite/Fixtures/GoogleTestProviderStub.php index ecc9d7347a..f538e42a2b 100644 --- a/tests/Socialite/Fixtures/GoogleTestProviderStub.php +++ b/tests/Socialite/Fixtures/GoogleTestProviderStub.php @@ -8,11 +8,12 @@ use Hypervel\Socialite\Two\GoogleProvider; use Hypervel\Socialite\Two\User; use Mockery as m; +use Mockery\MockInterface; class GoogleTestProviderStub extends GoogleProvider { /** - * @var \GuzzleHttp\Client|\Mockery\MockInterface + * @var Client|MockInterface */ public $http; diff --git a/tests/Socialite/OAuthTwoTest.php b/tests/Socialite/OAuthTwoTest.php index 151039d1b9..31a1f95670 100644 --- a/tests/Socialite/OAuthTwoTest.php +++ b/tests/Socialite/OAuthTwoTest.php @@ -9,6 +9,7 @@ use Hypervel\Coroutine\Coroutine; use Hypervel\Http\RedirectResponse; use Hypervel\Http\Request; +use Hypervel\Socialite\Two\Exceptions\InvalidCodeException; use Hypervel\Socialite\Two\InvalidStateException; use Hypervel\Socialite\Two\Token; use Hypervel\Socialite\Two\User; @@ -379,6 +380,46 @@ public function testExceptionIsThrownIfStateIsNotSet(): void $provider->user(); } + #[DataProvider('invalidAuthorizationCodeProvider')] + public function testExceptionIsThrownIfAuthorizationCodeIsInvalid(mixed $code): void + { + $request = m::mock(Request::class); + $request->shouldReceive('session') + ->andReturn($session = m::mock(SessionContract::class)); + $request->shouldReceive('input') + ->with('state') + ->once() + ->andReturn(str_repeat('A', 40)); + $request->shouldReceive('input') + ->with('code') + ->once() + ->andReturn($code); + + $session->expects('pull')->with('state')->andReturns(str_repeat('A', 40)); + $provider = new OAuthTwoTestProviderStub( + $request, + 'client_id', + 'client_secret', + 'redirect' + ); + $provider->http = m::mock(Client::class); + $provider->http->shouldNotReceive('post'); + + $this->expectException(InvalidCodeException::class); + $this->expectExceptionMessage('The authorization code is missing or invalid.'); + + $provider->user(); + } + + public static function invalidAuthorizationCodeProvider(): array + { + return [ + 'declined authorization' => [null], + 'array' => [['code']], + 'empty string' => [''], + ]; + } + public function testUserRefreshesToken(): void { $request = m::mock(Request::class); diff --git a/tests/Support/SupportStringableTest.php b/tests/Support/SupportStringableTest.php index 91a40403a4..9472ccf85d 100644 --- a/tests/Support/SupportStringableTest.php +++ b/tests/Support/SupportStringableTest.php @@ -25,10 +25,9 @@ class SupportStringableTest extends TestCase protected Container $container; /** - * @param string $string - * @return \Hypervel\Support\Stringable + * Create a new stringable instance. */ - protected function stringable($string = '') + protected function stringable(mixed $string = ''): Stringable { return new Stringable($string); } diff --git a/tests/Validation/ValidationDefaultRuleIsolationTest.php b/tests/Validation/ValidationDefaultRuleIsolationTest.php index f593b2e7e8..f969a7ea91 100644 --- a/tests/Validation/ValidationDefaultRuleIsolationTest.php +++ b/tests/Validation/ValidationDefaultRuleIsolationTest.php @@ -5,7 +5,6 @@ namespace Hypervel\Tests\Validation; use Closure; -use Hypervel\Contracts\Validation\InvokableRule; use Hypervel\Contracts\Validation\Rule; use Hypervel\Contracts\Validation\ValidationRule; use Hypervel\Engine\Channel; @@ -46,20 +45,17 @@ public function testCallableResultsAreClonedBeforeMutation(): void public function testNestedExecutableRulesAreClonedWithThePrototype(): void { $legacy = new DefaultLegacyRule; - $invokable = new DefaultInvokableRule; $validation = new DefaultValidationRule; - Password::defaults(Password::min(8)->rules([$legacy, $invokable, $validation])); + Password::defaults(Password::min(8)->rules([$legacy, $validation])); $first = Password::default()->appliedRules()['customRules']; $second = Password::default()->appliedRules()['customRules']; $this->assertNotSame($legacy, $first[0]); - $this->assertNotSame($invokable, $first[1]); - $this->assertNotSame($validation, $first[2]); + $this->assertNotSame($validation, $first[1]); $this->assertNotSame($first[0], $second[0]); $this->assertNotSame($first[1], $second[1]); - $this->assertNotSame($first[2], $second[2]); } public function testConfiguredDefaultsAreIsolatedBetweenCoroutines(): void @@ -125,13 +121,6 @@ public function message(): string } } -class DefaultInvokableRule implements InvokableRule -{ - public function __invoke(string $attribute, mixed $value, Closure $fail): void - { - } -} - class DefaultValidationRule implements ValidationRule { public function validate(string $attribute, mixed $value, Closure $fail): void diff --git a/tests/Validation/ValidationRuleParserTest.php b/tests/Validation/ValidationRuleParserTest.php index 5976186e7e..e9953c1e42 100644 --- a/tests/Validation/ValidationRuleParserTest.php +++ b/tests/Validation/ValidationRuleParserTest.php @@ -102,6 +102,27 @@ public function testConditionalRulesWithDefault(): void ], $rules); } + public function testConditionalRulesAcceptNonClosureCallableConditions(): void + { + $rules = ValidationRuleParser::filterConditionalRules([ + 'name' => Rule::when([$this, 'conditionIsMet'], ['required'], ['nullable']), + 'email' => Rule::unless([$this, 'conditionIsMet'], ['required'], ['nullable']), + ]); + + $this->assertSame([ + 'name' => ['required'], + 'email' => ['nullable'], + ], $rules); + } + + /** + * Determine if the conditional test rules apply. + */ + public function conditionIsMet(Fluent $input): bool + { + return true; + } + public function testEmptyConditionalRulesArePreserved(): void { $isAdmin = true; diff --git a/types/Collections/Collection.php b/types/Collections/Collection.php index 2ab8cbec00..b0620de55c 100644 --- a/types/Collections/Collection.php +++ b/types/Collections/Collection.php @@ -673,6 +673,8 @@ function ($collection, $count) { assertType('Hypervel\Support\Collection', $collection::make([1])->mergeRecursive([2 => 'string'])); assertType('Hypervel\Support\Collection', $collection::make(['string'])->mergeRecursive(['string'])); +assertType('Hypervel\Support\Collection|int>', $collection::make(['a' => 1])->mergeRecursive(['a' => 2])); +assertType('Hypervel\Support\Collection|int>', $collection::make([1, 'a' => 1])->mergeRecursive(['a' => 2])); assertType('Hypervel\Support\Collection', $collection::make(['string' => 'string'])->combine([2])); assertType('Hypervel\Support\Collection', $collection::make([1])->combine([1])); diff --git a/types/Collections/LazyCollection.php b/types/Collections/LazyCollection.php index f11377a788..6f4ac83174 100644 --- a/types/Collections/LazyCollection.php +++ b/types/Collections/LazyCollection.php @@ -552,6 +552,8 @@ public function toArray(): array assertType('Hypervel\Support\LazyCollection', $collection::make([1])->mergeRecursive([2])); assertType('Hypervel\Support\LazyCollection', $collection::make(['string'])->mergeRecursive(['string'])); +assertType('Hypervel\Support\LazyCollection|int>', $collection::make(['a' => 1])->mergeRecursive(['a' => 2])); +assertType('Hypervel\Support\LazyCollection|int>', $collection::make([1, 'a' => 1])->mergeRecursive(['a' => 2])); assertType('Hypervel\Support\LazyCollection', $collection::make(['string' => 'string'])->combine([2])); assertType('Hypervel\Support\LazyCollection', $collection::make([1])->combine([1]));