From f7e2fb82cd3b54a37f12bcdaa9d0a1054b2bc087 Mon Sep 17 00:00:00 2001 From: bota Date: Wed, 22 Apr 2026 10:04:24 +0300 Subject: [PATCH 1/4] Issue #509: Bump phpunit to v12.5.23 Signed-off-by: bota --- .github/workflows/codecov.yml | 1 - .github/workflows/qodana_code_quality.yml | 2 +- .github/workflows/static-analysis.yml | 1 - .../workflows/validate-database-schema.yml | 1 - CHANGELOG.md | 23 +++++++ README.md | 8 +-- SECURITY.md | 2 +- composer.json | 4 +- phpunit.xml | 5 +- src/Core/src/App/src/Service/IpService.php | 2 +- test/Functional/AdminTest.php | 2 +- test/Functional/UserTest.php | 24 +++---- test/Unit/Admin/Service/AdminServiceTest.php | 10 +-- .../AuthenticationMiddlewareTest.php | 9 +-- .../AuthorizationMiddlewareTest.php | 30 +++++---- .../ContentNegotiationMiddlewareTest.php | 4 +- .../Middleware/DeprecationMiddlewareTest.php | 62 +++++++++---------- test/Unit/App/Template/ParserTest.php | 10 +-- test/Unit/App/Template/RendererTest.php | 10 +-- test/Unit/CliTest.php | 4 +- .../User/Service/UserAvatarServiceTest.php | 43 ++++++++++--- test/Unit/User/Service/UserServiceTest.php | 18 +++--- 22 files changed, 164 insertions(+), 111 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 9393a6d7..37efd3b1 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -15,7 +15,6 @@ jobs: - ubuntu-latest php: - - "8.2" - "8.3" - "8.4" - "8.5" diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml index 7ec009b8..b93bbc0a 100644 --- a/.github/workflows/qodana_code_quality.yml +++ b/.github/workflows/qodana_code_quality.yml @@ -16,7 +16,7 @@ jobs: checks: write strategy: matrix: - php-versions: [ '8.2', '8.3', '8.4', '8.5' ] + php-versions: ['8.3', '8.4', '8.5' ] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 9976515a..63e06de2 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -15,7 +15,6 @@ jobs: - ubuntu-latest php: - - "8.2" - "8.3" - "8.4" - "8.5" diff --git a/.github/workflows/validate-database-schema.yml b/.github/workflows/validate-database-schema.yml index 9acb8708..a3580284 100644 --- a/.github/workflows/validate-database-schema.yml +++ b/.github/workflows/validate-database-schema.yml @@ -22,7 +22,6 @@ jobs: - ubuntu-latest php: - - "8.2" - "8.3" - "8.4" - "8.5" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b074213..38cb7eba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 7.1.0 - 2026-04-22 + +### Changed + +* Issue [#509](https://github.com/dotkernel/api/issues/509): Bump `PHPUnit` to version `12.5.23` by [@SergiuBota1](https://github.com/SergiuBota1) in [#510](https://github.com/dotkernel/admin/pull/510) + +### Added + +* Nothing + +### Deprecated + +* Nothing + +### Removed + +* Removed PHP `8.2` support + +### Fixed + +* Fixed PHPUnit notices caused by mock objects without expectations configured +* Fixed PHPUnit deprecation warnings caused by using `with()` on test stubs + ## 7.0.0 - 2025-11-26 ### Changed diff --git a/README.md b/README.md index 59f40584..52e4ec8a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/ | Branch | Release | PSR-11 | OSS Lifecycle | PHP Version | |--------|----------|--------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| 7.0 | `>= 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | +| 7.0 | `>= 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | | 6.0 | `< 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F6.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/6.1.0) | | 5.0 | `< 6.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F5.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.3.0) | | 4.0 | `< 5.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F4.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.5.0) | @@ -30,10 +30,10 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/ [![GitHub stars](https://img.shields.io/github/stars/dotkernel/api)](https://github.com/dotkernel/api/stargazers) [![GitHub license](https://img.shields.io/github/license/dotkernel/api)](https://github.com/dotkernel/api/blob/6.0/LICENSE.md) -[![Build Static](https://github.com/dotkernel/api/actions/workflows/continuous-integration.yml/badge.svg?branch=6.0)](https://github.com/dotkernel/api/actions/workflows/continuous-integration.yml) +[![Build Static](https://github.com/dotkernel/api/actions/workflows/continuous-integration.yml/badge.svg?branch=7.0)](https://github.com/dotkernel/api/actions/workflows/continuous-integration.yml) [![codecov](https://codecov.io/gh/dotkernel/api/graph/badge.svg?token=53FN78G5CK)](https://codecov.io/gh/dotkernel/api) -[![Qodana](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml/badge.svg?branch=6.0)](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml) -[![PHPStan](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml/badge.svg?branch=6.0)](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml) +[![Qodana](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml/badge.svg?branch=7.0)](https://github.com/dotkernel/api/actions/workflows/qodana_code_quality.yml) +[![PHPStan](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml/badge.svg?branch=7.0)](https://github.com/dotkernel/api/actions/workflows/static-analysis.yml) ## Getting Started diff --git a/SECURITY.md b/SECURITY.md index c331e61e..5b2b15a8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ | Version | Supported | PHP Version | |---------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| 7.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | +| 7.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | | 6.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F6.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/6.1.0) | | 5.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F5.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.3.0) | | 4.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F4.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.5.0) | diff --git a/composer.json b/composer.json index edbeba76..18d135bc 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ } }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "ext-gd": "*", "ext-json": "*", "dotkernel/dot-cache": "^4.3", @@ -89,7 +89,7 @@ "phpstan/phpstan": "^2.1.11", "phpstan/phpstan-doctrine": "^2.0.2", "phpstan/phpstan-phpunit": "^2.0.6", - "phpunit/phpunit": "^10.5.45", + "phpunit/phpunit": "^12.5.23", "roave/security-advisories": "dev-latest", "symfony/var-dumper": "^7.2.3" }, diff --git a/phpunit.xml b/phpunit.xml index 4921775a..46f795a1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -4,7 +4,10 @@ bootstrap="vendor/autoload.php" stopOnError="true" stopOnFailure="true" - colors="true"> + colors="true" + displayDetailsOnPhpunitNotices="true" + displayDetailsOnTestsThatTriggerDeprecations="true" + displayDetailsOnPhpunitDeprecations="true"> ./test/Unit diff --git a/src/Core/src/App/src/Service/IpService.php b/src/Core/src/App/src/Service/IpService.php index bb46cab1..840acb9e 100644 --- a/src/Core/src/App/src/Service/IpService.php +++ b/src/Core/src/App/src/Service/IpService.php @@ -19,7 +19,7 @@ class IpService * @phpstan-param array{ * HTTP_X_FORWARDED_FOR?: string, * HTTP_CLIENT_IP?: string, - * REMOTE_ADDR?: string, + * REMOTE_ADDR: string, * } $server */ public static function getUserIp(array $server): mixed diff --git a/test/Functional/AdminTest.php b/test/Functional/AdminTest.php index 266df273..ad0d6e4e 100644 --- a/test/Functional/AdminTest.php +++ b/test/Functional/AdminTest.php @@ -406,7 +406,7 @@ public function testAdminCanCreateUserAccount(): void $userRole = $userRoleRepository->findOneBy(['name' => UserRoleEnum::User]); $this->assertInstanceOf(UserRole::class, $userRole); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $userData = [ diff --git a/test/Functional/UserTest.php b/test/Functional/UserTest.php index 1eae7fab..d92d11d3 100644 --- a/test/Functional/UserTest.php +++ b/test/Functional/UserTest.php @@ -47,7 +47,7 @@ public function testRegisterAccountDuplicateIdentity(): void 'status' => UserStatusEnum::Pending, ]); - $userAvatarService = $this->createMock(UserAvatarServiceInterface::class); + $userAvatarService = $this->createStub(UserAvatarServiceInterface::class); $this->replaceService(UserAvatarServiceInterface::class, $userAvatarService); $response = $this->post('/user/account', $this->getValidUserData(['status' => UserStatusEnum::Pending->value])); @@ -71,7 +71,7 @@ public function testRegisterAccountDuplicateEmail(): void 'status' => UserStatusEnum::Pending, ]); - $userAvatarService = $this->createMock(UserAvatarServiceInterface::class); + $userAvatarService = $this->createStub(UserAvatarServiceInterface::class); $this->replaceService(UserAvatarServiceInterface::class, $userAvatarService); $response = $this->post('/user/account', $this->getValidUserData(['status' => UserStatusEnum::Pending->value])); @@ -90,10 +90,10 @@ public function testRegisterAccountDuplicateEmail(): void */ public function testRegisterAccount(): void { - $userAvatarService = $this->createMock(UserAvatarServiceInterface::class); + $userAvatarService = $this->createStub(UserAvatarServiceInterface::class); $this->replaceService(UserAvatarServiceInterface::class, $userAvatarService); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $user = $this->getValidUserData([ @@ -121,7 +121,7 @@ public function testRegisterAccount(): void public function testCreateMyAvatar(): void { $userAvatarRepository = $this->getEntityManager()->getRepository(UserAvatar::class); - $userAvatarService = $this->getMockBuilder(UserAvatarService::class) + $userAvatarService = $this->getStubBuilder(UserAvatarService::class) ->setConstructorArgs([ $userAvatarRepository, [], @@ -133,7 +133,7 @@ public function testCreateMyAvatar(): void 'createFileName', 'saveAvatarImage', ]) - ->getMock(); + ->getStub(); $this->replaceService(UserAvatarServiceInterface::class, $userAvatarService); /** @var non-empty-string $identity */ @@ -274,7 +274,7 @@ public function testActivateAccountByEmail(): void ]); $this->assertInstanceOf(UserDetail::class, $user->getDetail()); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->post('/user/account/activate', [ @@ -342,7 +342,7 @@ public function testRequestResetPasswordExpired(): void $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->patch('/user/account/reset-password/' . $resetPassword->getHash(), [ @@ -376,7 +376,7 @@ public function testRequestResetPasswordAlreadyUsed(): void $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->patch('/user/account/reset-password/' . $resetPassword->getHash(), [ @@ -410,7 +410,7 @@ public function testResetPassword(): void $this->getEntityManager()->persist($user); $this->getEntityManager()->flush(); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->patch('/user/account/reset-password/' . $resetPassword->getHash(), [ @@ -433,7 +433,7 @@ public function testResetPassword(): void */ public function testResetPasswordByEmail(): void { - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $user = $this->createUser(); @@ -495,7 +495,7 @@ public function testRecoverAccountByIdentity(): void $user = $this->createUser(); $this->assertInstanceOf(UserDetail::class, $user->getDetail()); - $mailService = $this->createMock(MailService::class); + $mailService = $this->createStub(MailService::class); $this->replaceService(MailService::class, $mailService); $response = $this->post('/user/account/recover', [ diff --git a/test/Unit/Admin/Service/AdminServiceTest.php b/test/Unit/Admin/Service/AdminServiceTest.php index b69fdd4e..a0ed734c 100644 --- a/test/Unit/Admin/Service/AdminServiceTest.php +++ b/test/Unit/Admin/Service/AdminServiceTest.php @@ -14,7 +14,7 @@ use Core\Admin\Repository\AdminRepository; use Core\Admin\Repository\AdminRoleRepository; use Exception; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use function array_merge; @@ -23,16 +23,16 @@ class AdminServiceTest extends TestCase { private AdminServiceInterface $adminService; - private AdminRepository&MockObject $adminRepository; - private AdminRoleRepository&MockObject $adminRoleRepository; + private AdminRepository&Stub $adminRepository; + private AdminRoleRepository&Stub $adminRoleRepository; /** * @throws \PHPUnit\Framework\MockObject\Exception */ public function setUp(): void { - $this->adminRepository = $this->createMock(AdminRepository::class); - $this->adminRoleRepository = $this->createMock(AdminRoleRepository::class); + $this->adminRepository = $this->createStub(AdminRepository::class); + $this->adminRoleRepository = $this->createStub(AdminRoleRepository::class); $this->adminService = new AdminService($this->adminRepository, $this->adminRoleRepository); } diff --git a/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php b/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php index 620ccbdd..a8793031 100644 --- a/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php +++ b/test/Unit/App/Middleware/AuthenticationMiddlewareTest.php @@ -11,6 +11,7 @@ use Mezzio\Authentication\UserInterface; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -19,19 +20,19 @@ class AuthenticationMiddlewareTest extends TestCase { private AuthenticationMiddleware $authenticationMiddleware; - private AuthenticationInterface&MockObject $auth; + private AuthenticationInterface&Stub $auth; private ServerRequestInterface $request; private RequestHandlerInterface&MockObject $handler; - private ResponseInterface&MockObject $response; + private ResponseInterface&Stub $response; /** * @throws Exception */ public function setUp(): void { - $this->auth = $this->createMock(AuthenticationInterface::class); + $this->auth = $this->createStub(AuthenticationInterface::class); $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->response = $this->createMock(ResponseInterface::class); + $this->response = $this->createStub(ResponseInterface::class); $this->request = new ServerRequest(); $this->authenticationMiddleware = new AuthenticationMiddleware($this->auth); diff --git a/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php b/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php index 40f8896c..28e6d9c4 100644 --- a/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php +++ b/test/Unit/App/Middleware/AuthorizationMiddlewareTest.php @@ -22,7 +22,7 @@ use Mezzio\Authentication\UserInterface; use Mezzio\Authorization\AuthorizationInterface; use PHPUnit\Framework\MockObject\Exception; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -33,23 +33,23 @@ class AuthorizationMiddlewareTest extends TestCase { private AuthorizationMiddleware $authorizationMiddleware; - private UserRepository&MockObject $userRepository; - private AdminRepository&MockObject $adminRepository; - private AuthorizationInterface&MockObject $authorization; + private UserRepository&Stub $userRepository; + private AdminRepository&Stub $adminRepository; + private AuthorizationInterface&Stub $authorization; private ServerRequestInterface $request; - private RequestHandlerInterface&MockObject $handler; - private ResponseInterface $response; + private RequestHandlerInterface&Stub $handler; + private ResponseInterface&Stub $response; /** * @throws Exception */ public function setUp(): void { - $this->userRepository = $this->createMock(UserRepository::class); - $this->adminRepository = $this->createMock(AdminRepository::class); - $this->authorization = $this->createMock(AuthorizationInterface::class); - $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->response = $this->createMock(ResponseInterface::class); + $this->userRepository = $this->createStub(UserRepository::class); + $this->adminRepository = $this->createStub(AdminRepository::class); + $this->authorization = $this->createStub(AuthorizationInterface::class); + $this->handler = $this->createStub(RequestHandlerInterface::class); + $this->response = $this->createStub(ResponseInterface::class); $this->request = new ServerRequest(); $this->authorizationMiddleware = new AuthorizationMiddleware( @@ -150,6 +150,9 @@ public function testAuthorizationNotGranted(): void ); } + /** + * @throws Exception + */ public function testAuthorizationAccessGranted(): void { $user = (new User()) @@ -162,7 +165,8 @@ public function testAuthorizationAccessGranted(): void $identity = new UserIdentity('test@dotkernel.com', ['user'], ['oauth_client_id' => 'frontend']); $this->request = $this->request->withAttribute(UserInterface::class, $identity); - $this->handler + $handler = $this->createMock(RequestHandlerInterface::class); + $handler ->expects($this->once()) ->method('handle') ->willReturnCallback(function (ServerRequestInterface $request) use ($identity) { @@ -173,6 +177,6 @@ public function testAuthorizationAccessGranted(): void return $this->response; }); - $this->authorizationMiddleware->process($this->request, $this->handler); + $this->authorizationMiddleware->process($this->request, $handler); } } diff --git a/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php b/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php index 08f7e05b..6ef089ff 100644 --- a/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php +++ b/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php @@ -42,12 +42,12 @@ class ContentNegotiationMiddlewareTest extends TestCase */ protected function setUp(): void { - $this->handler = $this->createMock(RequestHandlerInterface::class); + $this->handler = $this->createStub(RequestHandlerInterface::class); $this->routeResult = RouteResult::fromRoute( new Route( '/test-route', - $this->createMock(MiddlewareInterface::class), + $this->createStub(MiddlewareInterface::class), name: 'test.route' ) ); diff --git a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php index 859949e9..4a30c878 100644 --- a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php +++ b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php @@ -15,7 +15,7 @@ use Mezzio\Router\Route; use Mezzio\Router\RouteResult; use PHPUnit\Framework\MockObject\Exception; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -29,8 +29,8 @@ class DeprecationMiddlewareTest extends TestCase { private DeprecationMiddleware $deprecationMiddleware; - private ServerRequestInterface&MockObject $request; - private RequestHandlerInterface&MockObject $handler; + private ServerRequestInterface&Stub $request; + private RequestHandlerInterface&Stub $handler; private ResponseInterface $response; private const VERSIONING_CONFIG = [ @@ -42,8 +42,8 @@ class DeprecationMiddlewareTest extends TestCase */ protected function setUp(): void { - $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->request = $this->createMock(ServerRequestInterface::class); + $this->handler = $this->createStub(RequestHandlerInterface::class); + $this->request = $this->createStub(ServerRequestInterface::class); $this->response = new EmptyResponse(); $this->deprecationMiddleware = new DeprecationMiddleware(self::VERSIONING_CONFIG); @@ -69,19 +69,19 @@ public function handle(ServerRequestInterface $request): ResponseInterface } }; - $routeResult = $this->createMock(RouteResult::class); - $route = $this->createMock(Route::class); + $routeResult = $this->createStub(RouteResult::class); + $route = $this->createStub(Route::class); $lazyLoadingMiddleware = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $handler::class, ); $route->method('getMiddleware')->willReturn($lazyLoadingMiddleware); $routeResult->method('isFailure')->willReturn(false); $routeResult->method('getMatchedRoute')->willReturn($route); - $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); + $this->request->method('getAttribute')->willReturn($routeResult); $this->request->method('getMethod')->willReturn(RequestMethodInterface::METHOD_GET); - $this->handler->method('handle')->with($this->request)->willReturn($this->response); + $this->handler->method('handle')->willReturn($this->response); $response = $this->deprecationMiddleware->process($this->request, $this->handler); @@ -123,16 +123,16 @@ public function process( } }; - $routeResult = $this->createMock(RouteResult::class); - $route = $this->createMock(Route::class); + $routeResult = $this->createStub(RouteResult::class); + $route = $this->createStub(Route::class); $lazyLoadingMiddleware = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $middleware::class ); $lazyLoadingMiddlewareHandler = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $handler::class, ); @@ -143,9 +143,9 @@ public function process( $route->method('getMiddleware')->willReturn($middlewarePipeline); $routeResult->method('isFailure')->willReturn(false); $routeResult->method('getMatchedRoute')->willReturn($route); - $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); + $this->request->method('getAttribute')->willReturn($routeResult); $this->request->method('getMethod')->willReturn(RequestMethodInterface::METHOD_GET); - $this->handler->method('handle')->with($this->request)->willReturn($this->response); + $this->handler->method('handle')->willReturn($this->response); $response = $this->deprecationMiddleware->process($this->request, $this->handler); @@ -172,19 +172,19 @@ public function handle(ServerRequestInterface $request): ResponseInterface } }; - $routeResult = $this->createMock(RouteResult::class); - $route = $this->createMock(Route::class); + $routeResult = $this->createStub(RouteResult::class); + $route = $this->createStub(Route::class); $lazyLoadingMiddleware = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $handler::class, ); $route->method('getMiddleware')->willReturn($lazyLoadingMiddleware); $routeResult->method('isFailure')->willReturn(false); $routeResult->method('getMatchedRoute')->willReturn($route); - $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); + $this->request->method('getAttribute')->willReturn($routeResult); $this->request->method('getMethod')->willReturn(RequestMethodInterface::METHOD_GET); - $this->handler->method('handle')->with($this->request)->willReturn($this->response); + $this->handler->method('handle')->willReturn($this->response); $response = $this->deprecationMiddleware->process($this->request, $this->handler); @@ -211,19 +211,19 @@ public function handle(ServerRequestInterface $request): ResponseInterface } }; - $routeResult = $this->createMock(RouteResult::class); - $route = $this->createMock(Route::class); + $routeResult = $this->createStub(RouteResult::class); + $route = $this->createStub(Route::class); $lazyLoadingMiddleware = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $handler::class, ); $route->method('getMiddleware')->willReturn($lazyLoadingMiddleware); $routeResult->method('isFailure')->willReturn(false); $routeResult->method('getMatchedRoute')->willReturn($route); - $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); + $this->request->method('getAttribute')->willReturn($routeResult); $this->request->method('getMethod')->willReturn(RequestMethodInterface::METHOD_GET); - $this->handler->method('handle')->with($this->request)->willReturn($this->response); + $this->handler->method('handle')->willReturn($this->response); $response = $this->deprecationMiddleware->process($this->request, $this->handler); @@ -250,19 +250,19 @@ public function handle(ServerRequestInterface $request): ResponseInterface } }; - $routeResult = $this->createMock(RouteResult::class); - $route = $this->createMock(Route::class); + $routeResult = $this->createStub(RouteResult::class); + $route = $this->createStub(Route::class); $lazyLoadingMiddleware = new LazyLoadingMiddleware( - $this->createMock(MiddlewareContainer::class), + $this->createStub(MiddlewareContainer::class), $handler::class, ); $route->method('getMiddleware')->willReturn($lazyLoadingMiddleware); $routeResult->method('isFailure')->willReturn(false); $routeResult->method('getMatchedRoute')->willReturn($route); - $this->request->method('getAttribute')->with(RouteResult::class)->willReturn($routeResult); + $this->request->method('getAttribute')->willReturn($routeResult); $this->request->method('getMethod')->willReturn(RequestMethodInterface::METHOD_GET); - $this->handler->method('handle')->with($this->request)->willReturn($this->response); + $this->handler->method('handle')->willReturn($this->response); $response = (new DeprecationMiddleware([]))->process($this->request, $this->handler); diff --git a/test/Unit/App/Template/ParserTest.php b/test/Unit/App/Template/ParserTest.php index 19f8d247..53e2036c 100644 --- a/test/Unit/App/Template/ParserTest.php +++ b/test/Unit/App/Template/ParserTest.php @@ -23,7 +23,7 @@ class ParserTest extends TestCase public function testWillInitiate(): void { $parser = new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), [] ); @@ -36,14 +36,14 @@ public function testWillInitiate(): void public function testWillSetGlobals(): void { $parser = new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), [] ); $this->assertSame([], $parser->getGlobals()); $parser = new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), [ RendererInterface::class => [ 'globals' => [ @@ -56,7 +56,7 @@ public function testWillSetGlobals(): void $this->assertSame(['test' => 'test'], $parser->getGlobals()); $parser = new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), [ RendererInterface::class => [ 'globals' => [ @@ -83,7 +83,7 @@ public function testWillSetGlobals(): void public function testWillParseVariables(): void { $parser = new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), [ 'application' => [ 'foo' => 'Foo', diff --git a/test/Unit/App/Template/RendererTest.php b/test/Unit/App/Template/RendererTest.php index f6d5d71d..e2122f79 100644 --- a/test/Unit/App/Template/RendererTest.php +++ b/test/Unit/App/Template/RendererTest.php @@ -23,7 +23,7 @@ class RendererTest extends TestCase public function testWillInitiate(): void { $renderer = new Renderer( - $this->createMock(ParserInterface::class), + $this->createStub(ParserInterface::class), [] ); @@ -40,7 +40,7 @@ public function testWillPopulatePropertiesFromConfig(): void ]; $renderer = new Renderer( - $this->createMock(ParserInterface::class), + $this->createStub(ParserInterface::class), [ 'templates' => $templates, RendererInterface::class => [ @@ -62,7 +62,7 @@ public function testWillThrowErrorOnUnregisteredTemplateNamespace(): void ]; $renderer = new Renderer( - $this->createMock(ParserInterface::class), + $this->createStub(ParserInterface::class), [ 'templates' => $templates, RendererInterface::class => [ @@ -86,7 +86,7 @@ public function testWillThrowErrorWhenTemplateNotFound(): void ]; $renderer = new Renderer( - $this->createMock(ParserInterface::class), + $this->createStub(ParserInterface::class), [ 'templates' => $templates, RendererInterface::class => [ @@ -116,7 +116,7 @@ public function testWillRenderTemplate(): void $renderer = new Renderer( new Parser( - $this->createMock(UrlHelperInterface::class), + $this->createStub(UrlHelperInterface::class), $config ), $config, diff --git a/test/Unit/CliTest.php b/test/Unit/CliTest.php index 2ca725a9..8bc65834 100644 --- a/test/Unit/CliTest.php +++ b/test/Unit/CliTest.php @@ -20,7 +20,7 @@ class CliTest extends TestCase public function testWillListCommandsWhenNoCommandSpecified(): void { $application = new Application( - $this->createMock(FileLockerInterface::class), + $this->createStub(FileLockerInterface::class), [] ); $application->setAutoExit(false); @@ -41,7 +41,7 @@ public function testWillListCommandsWhenNoCommandSpecified(): void public function testWillListCommands(): void { $application = new Application( - $this->createMock(FileLockerInterface::class), + $this->createStub(FileLockerInterface::class), [] ); diff --git a/test/Unit/User/Service/UserAvatarServiceTest.php b/test/Unit/User/Service/UserAvatarServiceTest.php index a6f03635..e32775a9 100644 --- a/test/Unit/User/Service/UserAvatarServiceTest.php +++ b/test/Unit/User/Service/UserAvatarServiceTest.php @@ -5,27 +5,27 @@ namespace ApiTest\Unit\User\Service; use Api\User\Service\UserAvatarService; -use Api\User\Service\UserAvatarServiceInterface; use Core\User\Entity\User; use Core\User\Entity\UserAvatar; use Core\User\Repository\UserAvatarRepository; use Laminas\Diactoros\UploadedFile; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; class UserAvatarServiceTest extends TestCase { - private UserAvatarServiceInterface&MockObject $subject; - private UploadedFile $uploadedFile; + private UserAvatarService&MockObject $subject; + private UploadedFile&Stub $uploadedFile; /** * @throws Exception */ public function setUp(): void { - $userAvatarRepository = $this->createMock(UserAvatarRepository::class); - $this->uploadedFile = $this->createMock(UploadedFile::class); + $userAvatarRepository = $this->createStub(UserAvatarRepository::class); + $this->uploadedFile = $this->createStub(UploadedFile::class); $this->subject = $this->getMockBuilder(UserAvatarService::class) ->setConstructorArgs([ $userAvatarRepository, @@ -46,8 +46,21 @@ public function testCreateAvatarOverwrite(): void { $fileName = 'file_name'; - $this->subject->method('getUserAvatarDirectoryPath')->willReturn('/test'); - $this->subject->method('createFileName')->willReturn($fileName); + $this->subject->expects($this->once()) + ->method('getUserAvatarDirectoryPath') + ->willReturn('/test/'); + + $this->subject->expects($this->once()) + ->method('ensureDirectoryExists') + ->with('/test/'); + + $this->subject->expects($this->once()) + ->method('deleteAvatarFile') + ->with('/test/test'); + + $this->subject->expects($this->once()) + ->method('createFileName') + ->willReturn($fileName); $user = $this->getUser(); $avatar = $this->subject->saveAvatar($user, $this->uploadedFile); @@ -59,8 +72,20 @@ public function testCreateAvatarDefault(): void { $fileName = 'file_name'; - $this->subject->method('getUserAvatarDirectoryPath')->willReturn('/test'); - $this->subject->method('createFileName')->willReturn($fileName); + $this->subject->expects($this->once()) + ->method('getUserAvatarDirectoryPath') + ->willReturn('/test/'); + + $this->subject->expects($this->once()) + ->method('ensureDirectoryExists') + ->with('/test/'); + + $this->subject->expects($this->never()) + ->method('deleteAvatarFile'); + + $this->subject->expects($this->once()) + ->method('createFileName') + ->willReturn($fileName); $user = new User(); $avatar = $this->subject->saveAvatar($user, $this->uploadedFile); diff --git a/test/Unit/User/Service/UserServiceTest.php b/test/Unit/User/Service/UserServiceTest.php index 5504500f..4c8c0a62 100644 --- a/test/Unit/User/Service/UserServiceTest.php +++ b/test/Unit/User/Service/UserServiceTest.php @@ -20,7 +20,7 @@ use Core\User\Repository\UserRepository; use Core\User\Repository\UserRoleRepository; use Exception; -use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use function array_merge; @@ -29,20 +29,20 @@ class UserServiceTest extends TestCase { private UserServiceInterface $subject; - private UserRepository&MockObject $userRepository; - private UserDetailRepository&MockObject $userDetailRepository; - private UserRoleRepository&MockObject $userRoleRepository; + private UserRepository&Stub $userRepository; + private UserDetailRepository&Stub $userDetailRepository; + private UserRoleRepository&Stub $userRoleRepository; /** * @throws \PHPUnit\Framework\MockObject\Exception */ public function setUp(): void { - $oAuthAccessTokenRepository = $this->createMock(OAuthAccessTokenRepository::class); - $oAuthRefreshTokenRepository = $this->createMock(OAuthRefreshTokenRepository::class); - $this->userRepository = $this->createMock(UserRepository::class); - $this->userDetailRepository = $this->createMock(UserDetailRepository::class); - $this->userRoleRepository = $this->createMock(UserRoleRepository::class); + $oAuthAccessTokenRepository = $this->createStub(OAuthAccessTokenRepository::class); + $oAuthRefreshTokenRepository = $this->createStub(OAuthRefreshTokenRepository::class); + $this->userRepository = $this->createStub(UserRepository::class); + $this->userDetailRepository = $this->createStub(UserDetailRepository::class); + $this->userRoleRepository = $this->createStub(UserRoleRepository::class); $this->subject = new UserService( $oAuthAccessTokenRepository, $oAuthRefreshTokenRepository, From 75800ad2a8739fe289dbdb5f36f5743f391e2372 Mon Sep 17 00:00:00 2001 From: bota Date: Wed, 22 Apr 2026 10:27:17 +0300 Subject: [PATCH 2/4] fixed Qodana notices Signed-off-by: bota --- test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php | 2 +- test/Unit/App/Middleware/DeprecationMiddlewareTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php b/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php index 6ef089ff..0a7a4210 100644 --- a/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php +++ b/test/Unit/App/Middleware/ContentNegotiationMiddlewareTest.php @@ -23,7 +23,7 @@ class ContentNegotiationMiddlewareTest extends TestCase private RequestHandlerInterface $handler; private RouteResult $routeResult; - private const CONFIG + private const array CONFIG = [ 'test.route' => [ 'Accept' => [ diff --git a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php index 4a30c878..bcc47255 100644 --- a/test/Unit/App/Middleware/DeprecationMiddlewareTest.php +++ b/test/Unit/App/Middleware/DeprecationMiddlewareTest.php @@ -33,7 +33,7 @@ class DeprecationMiddlewareTest extends TestCase private RequestHandlerInterface&Stub $handler; private ResponseInterface $response; - private const VERSIONING_CONFIG = [ + private const array VERSIONING_CONFIG = [ 'documentation_url' => 'www.example.com', ]; From 3aafba78ed5237dec1d8bbc96ed89d57fce3134b Mon Sep 17 00:00:00 2001 From: bota Date: Wed, 22 Apr 2026 13:07:35 +0300 Subject: [PATCH 3/4] update readme and security Signed-off-by: bota --- README.md | 3 ++- SECURITY.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52e4ec8a..91be7c9f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/ | Branch | Release | PSR-11 | OSS Lifecycle | PHP Version | |--------|----------|--------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| 7.0 | `>= 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | +| 7.0 | `>= 7.1` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | +| 7.0 | `= 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | | 6.0 | `< 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F6.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/6.1.0) | | 5.0 | `< 6.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F5.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.3.0) | | 4.0 | `< 5.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F4.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.5.0) | diff --git a/SECURITY.md b/SECURITY.md index 5b2b15a8..43ee0b66 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,8 @@ | Version | Supported | PHP Version | |---------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| 7.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | +| 7.1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | +| 7.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | | 6.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F6.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/6.1.0) | | 5.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F5.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.3.0) | | 4.0 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F4.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.5.0) | From 33e42671dc620d9cdd8179cb7df709b95306d639 Mon Sep 17 00:00:00 2001 From: bota Date: Wed, 22 Apr 2026 13:17:06 +0300 Subject: [PATCH 4/4] fixed readme Signed-off-by: bota --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91be7c9f..64f417f8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Documentation is available at: https://docs.dotkernel.org/api-documentation/ | Branch | Release | PSR-11 | OSS Lifecycle | PHP Version | |--------|----------|--------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| | 7.0 | `>= 7.1` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.1.0) | -| 7.0 | `= 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | +| 7.0 | `< 7.1` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F7.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/7.0.0) | | 6.0 | `< 7.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F6.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/6.1.0) | | 5.0 | `< 6.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F5.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/5.3.0) | | 4.0 | `< 5.0` | 1 | ![OSS Lifecycle](https://img.shields.io/osslifecycle?file_url=https%3A%2F%2Fgithub.com%2Fdotkernel%2Fapi%2Fblob%2F4.0%2FOSSMETADATA) | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/api/4.5.0) |