From 3d1100cbdca6e31efa24a5df0a735cbf61cbaead Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 8 Sep 2025 09:35:32 +0100 Subject: [PATCH 01/43] Upgrade to php 8.4 --- .github/workflows/ci.yml | 83 +++++++++---------- composer.json | 43 ++++++---- rector.php | 16 ++++ src/Application.php | 16 ++-- .../RegisterExceptionHandler.php | 6 +- src/Config.php | 2 +- src/Contracts/QueryBuilder.php | 2 +- src/Encrypter.php | 5 +- src/Exceptions/Handler.php | 2 +- src/Facades/Config.php | 1 + src/Facades/Log.php | 1 + src/Facades/MiddlewareAliases.php | 1 + src/Facades/Router.php | 1 + src/Facades/Session.php | 1 + src/Http/Lumberjack.php | 5 +- src/Http/Middleware/PasswordProtected.php | 2 +- src/Http/MiddlewareAliasStore.php | 6 +- src/Http/Router.php | 5 +- src/Page.php | 1 + src/Post.php | 9 +- src/Providers/SessionServiceProvider.php | 8 +- .../WordPressControllersServiceProvider.php | 16 ++-- src/QueryBuilder.php | 4 +- src/ScopedQueryBuilder.php | 16 ++-- src/Session/EncryptedStore.php | 4 +- src/Session/FileSessionHandler.php | 9 +- src/Session/Store.php | 2 +- src/ViewModel.php | 32 +++---- src/functions.php | 20 ++--- tests/Unit/ApplicationTest.php | 20 +---- .../RegisterExceptionHandlerTest.php | 16 +--- tests/Unit/GlobalFunctionsTest.php | 4 +- tests/Unit/HelpersTest.php | 15 +--- .../Http/Middleware/PasswordProtectTest.php | 8 +- tests/Unit/Http/MiddlewareAliasStoreTest.php | 11 +-- tests/Unit/Http/RouterTest.php | 10 +-- tests/Unit/PostQueryBuilderTest.php | 3 +- tests/Unit/PostTest.php | 16 ++-- .../CustomPostTypesServiceProviderTest.php | 2 + .../Providers/RouterServiceProviderTest.php | 11 +-- ...ordPressControllersServiceProviderTest.php | 27 +++--- tests/Unit/QueryBuilderTest.php | 1 + tests/Unit/ScopedQueryBuilderTest.php | 3 +- tests/Unit/Session/FileSessionHandlerTest.php | 2 +- tests/Unit/Session/SessionManagerTest.php | 4 +- 45 files changed, 202 insertions(+), 270 deletions(-) create mode 100644 rector.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f54dc58..2dd86fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,45 +3,44 @@ name: CI on: [push, pull_request] jobs: - build-test: - runs-on: ubuntu-latest - strategy: - matrix: - php_version: [8.1, 8.2, 8.3] - composer_flags: ['', '--prefer-lowest'] - - steps: - - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php_version }} - extensions: xdebug - - - name: Install dependencies - uses: php-actions/composer@v5 - with: - php_version: ${{ matrix.php_version }} - args: ${{ matrix.composer_flags }} - command: update - - - name: Run tests - run: ./vendor/bin/phpunit --coverage-clover ./tests/logs/clover.xml - env: - XDEBUG_MODE: coverage - - - name: Run Codesniffer - run: vendor/bin/phpcs --standard=PSR2 ./src - - # - name: Submit coverage to Coveralls - # # We use php-coveralls library for this, as the official Coveralls GitHub Action lacks support for clover reports: - # # https://github.com/coverallsapp/github-action/issues/15 - # env: - # COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # COVERALLS_PARALLEL: true - # COVERALLS_FLAG_NAME: ${{ github.job }}-PHP-${{ matrix.php_version }} ${{ matrix.composer_flags }} - # run: | - # composer global require php-coveralls/php-coveralls - # ~/.composer/vendor/bin/php-coveralls -v - + build-test: + runs-on: ubuntu-latest + strategy: + matrix: + php_version: [8.4] + composer_flags: ["", "--prefer-lowest"] + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + extensions: xdebug + + - name: Install dependencies + uses: php-actions/composer@v5 + with: + php_version: ${{ matrix.php_version }} + args: ${{ matrix.composer_flags }} + command: update + + - name: Run tests + run: ./vendor/bin/phpunit --coverage-clover ./tests/logs/clover.xml + env: + XDEBUG_MODE: coverage + + - name: Run Codesniffer + run: vendor/bin/phpcs --standard=PSR2 ./src + + # - name: Submit coverage to Coveralls + # # We use php-coveralls library for this, as the official Coveralls GitHub Action lacks support for clover reports: + # # https://github.com/coverallsapp/github-action/issues/15 + # env: + # COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # COVERALLS_PARALLEL: true + # COVERALLS_FLAG_NAME: ${{ github.job }}-PHP-${{ matrix.php_version }} ${{ matrix.composer_flags }} + # run: | + # composer global require php-coveralls/php-coveralls + # ~/.composer/vendor/bin/php-coveralls -v diff --git a/composer.json b/composer.json index 0cf2c1c..6f118cb 100644 --- a/composer.json +++ b/composer.json @@ -3,26 +3,24 @@ "description": "A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code", "license": "MIT", "require": { - "php": ">=8.1", - "php-di/php-di": "^6.4.0", - "rareloop/router": "^6.0.2", - "psr/container": "^1.1.2", - "psr/http-message": "^1.1", + "php": ">=8.4", + "php-di/php-di": "^7", + "rareloop/router": "dev-feature/upgrade-dependencies as 6.0.2", + "psr/container": "^2", + "psr/http-message": "^2", "psr/http-server-middleware": "^1.0.2", "blast/facades": "^1.0", "timber/timber": "^2.3.0", - "monolog/monolog": "^2.9.1", - "http-interop/response-sender": "^1.0", + "monolog/monolog": "^3.9", "symfony/debug": "^4.4.44", - "illuminate/collections": "^8.53.1||^9.52.16", + "illuminate/collections": "^12", "statamic/stringy": "^3.1.3", - "laminas/laminas-diactoros": "^2.25.2", - "rareloop/psr7-server-request-extension": "^2.1.0", + "laminas/laminas-diactoros": "^3.6.0", + "rareloop/psr7-server-request-extension": "dev-master as 3.0", "mmeyer2k/dcrypt": "^8.3.1", "spatie/macroable": "^1.0.1", - "mindplay/middleman": "^3.1.0", - "psr/log": "^1.1.4", - "laminas/laminas-zendframework-bridge": "^1.7", + "mindplay/middleman": "^4.0.4", + "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6" }, "require-dev": { @@ -31,9 +29,10 @@ "mockery/mockery": "^1.6.6", "brain/monkey": "^2.6.1", "squizlabs/php_codesniffer": "^3.7.2", - "php-mock/php-mock": "^2.4.1", + "php-mock/php-mock": "^2.6.2", "mikey179/vfsstream": "1.6.11", - "dms/phpunit-arraysubset-asserts": "^0.3.1" + "dms/phpunit-arraysubset-asserts": "^0.3.1", + "rector/rector": "^2.1" }, "autoload": { "psr-4": { @@ -49,5 +48,15 @@ "allow-plugins": { "composer/installers": true } - } -} \ No newline at end of file + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/rareloop/router" + }, + { + "type": "vcs", + "url": "https://github.com/tommitchelmore/psr7-server-request-extension" + } + ] +} diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..91bcf48 --- /dev/null +++ b/rector.php @@ -0,0 +1,16 @@ +withPaths([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + // uncomment to reach your current PHP version + ->withPhpSets(php84: true); + // ->withTypeCoverageLevel(0) + // ->withDeadCodeLevel(0) + // ->withCodeQualityLevel(0); diff --git a/src/Application.php b/src/Application.php index 22f7443..61f2666 100644 --- a/src/Application.php +++ b/src/Application.php @@ -182,11 +182,9 @@ public function register($provider) public function getProvider($provider) { - $providerClass = is_string($provider) ? $provider : get_class($provider); + $providerClass = is_string($provider) ? $provider : $provider::class; - return (new Collection($this->loadedProviders))->first(function ($provider) use ($providerClass) { - return get_class($provider) === $providerClass; - }); + return new Collection($this->loadedProviders)->first(fn($provider) => $provider::class === $providerClass); } public function getLoadedProviders() @@ -275,7 +273,7 @@ public function detectWhenRequestHasNotBeenHandled() }); } - public function shutdown(ResponseInterface $response = null) + public function shutdown(?ResponseInterface $response = null) { if ($response) { global $wp; @@ -300,16 +298,12 @@ protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $respo header_remove($parts[0]); return $parts; - })->filter(function ($header) { - return !in_array(strtolower($header[0]), ['content-type']); - }); + })->filter(fn($header) => !in_array(strtolower((string) $header[0]), ['content-type'])); // Add the previously sent headers into the response // Note: You can't mutate a response so we need to use the reduce to end up with a response // object with all the correct headers - $responseToSend = collect($headersToAdd)->reduce(function ($newResponse, $header) { - return $newResponse->withAddedHeader($header[0], $header[1]); - }, $response); + $responseToSend = collect($headersToAdd)->reduce(fn($newResponse, $header) => $newResponse->withAddedHeader($header[0], $header[1]), $response); return $responseToSend; } diff --git a/src/Bootstrappers/RegisterExceptionHandler.php b/src/Bootstrappers/RegisterExceptionHandler.php index b4ff838..14de197 100644 --- a/src/Bootstrappers/RegisterExceptionHandler.php +++ b/src/Bootstrappers/RegisterExceptionHandler.php @@ -34,8 +34,8 @@ public function bootstrap(Application $app) } error_reporting(-1); - set_error_handler([$this, 'handleError']); - set_exception_handler([$this, 'handleException']); + set_error_handler($this->handleError(...)); + set_exception_handler($this->handleException(...)); register_shutdown_function([$this, 'handleShutdown']); } @@ -50,7 +50,7 @@ public function handleException($e) try { $request = $this->app->get('request'); - } catch (NotFoundException $notFoundException) { + } catch (NotFoundException) { $request = ServerRequestFactory::fromGlobals( $_SERVER, $_GET, diff --git a/src/Config.php b/src/Config.php index 86b0ea3..9ea2e60 100644 --- a/src/Config.php +++ b/src/Config.php @@ -9,7 +9,7 @@ class Config { private $data = []; - public function __construct(string $path = null) + public function __construct(?string $path = null) { if ($path) { $this->load($path); diff --git a/src/Contracts/QueryBuilder.php b/src/Contracts/QueryBuilder.php index 848720d..8797b6f 100644 --- a/src/Contracts/QueryBuilder.php +++ b/src/Contracts/QueryBuilder.php @@ -16,7 +16,7 @@ public function offset($offset): QueryBuilder; public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuilder; - public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilder; + public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilder; public function whereIdIn(array $ids): QueryBuilder; diff --git a/src/Encrypter.php b/src/Encrypter.php index 12e3049..cc912d8 100644 --- a/src/Encrypter.php +++ b/src/Encrypter.php @@ -7,11 +7,8 @@ class Encrypter implements EncrypterContract { - protected $key; - - public function __construct($key) + public function __construct(protected $key) { - $this->key = $key; } public function encrypt($data) diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 045b6f1..7d59ef8 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -45,6 +45,6 @@ public function render(ServerRequestInterface $request, Exception $e) : Response protected function shouldNotReport(Exception $e) { - return in_array(get_class($e), $this->dontReport); + return in_array($e::class, $this->dontReport); } } diff --git a/src/Facades/Config.php b/src/Facades/Config.php index 174a692..41b7be7 100644 --- a/src/Facades/Config.php +++ b/src/Facades/Config.php @@ -6,6 +6,7 @@ class Config extends AbstractFacade { + #[\Override] protected static function accessor() { return 'config'; diff --git a/src/Facades/Log.php b/src/Facades/Log.php index 324c66b..fe0a8c3 100644 --- a/src/Facades/Log.php +++ b/src/Facades/Log.php @@ -6,6 +6,7 @@ class Log extends AbstractFacade { + #[\Override] protected static function accessor() { return 'logger'; diff --git a/src/Facades/MiddlewareAliases.php b/src/Facades/MiddlewareAliases.php index cf8bf7d..340128b 100644 --- a/src/Facades/MiddlewareAliases.php +++ b/src/Facades/MiddlewareAliases.php @@ -6,6 +6,7 @@ class MiddlewareAliases extends AbstractFacade { + #[\Override] protected static function accessor() { return 'middleware-alias-store'; diff --git a/src/Facades/Router.php b/src/Facades/Router.php index 6859dec..5db20d4 100644 --- a/src/Facades/Router.php +++ b/src/Facades/Router.php @@ -6,6 +6,7 @@ class Router extends AbstractFacade { + #[\Override] protected static function accessor() { return 'router'; diff --git a/src/Facades/Session.php b/src/Facades/Session.php index cf9b053..036aad0 100644 --- a/src/Facades/Session.php +++ b/src/Facades/Session.php @@ -6,6 +6,7 @@ class Session extends AbstractFacade { + #[\Override] protected static function accessor() { return 'session'; diff --git a/src/Http/Lumberjack.php b/src/Http/Lumberjack.php index df31f57..8225b1b 100644 --- a/src/Http/Lumberjack.php +++ b/src/Http/Lumberjack.php @@ -13,8 +13,6 @@ class Lumberjack { - private $app; - protected $bootstrappers = [ LoadConfiguration::class, RegisterExceptionHandler::class, @@ -25,9 +23,8 @@ class Lumberjack RegisterRequestHandler::class, ]; - public function __construct(Application $app) + public function __construct(private readonly Application $app) { - $this->app = $app; } public function bootstrap() diff --git a/src/Http/Middleware/PasswordProtected.php b/src/Http/Middleware/PasswordProtected.php index 23e886d..ef1bd96 100644 --- a/src/Http/Middleware/PasswordProtected.php +++ b/src/Http/Middleware/PasswordProtected.php @@ -37,7 +37,7 @@ protected function handlePasswordProtected(): ?ResponseInterface try { return new TimberResponse($template, $context); - } catch (TwigTemplateNotFoundException $e) { + } catch (TwigTemplateNotFoundException) { return null; } } diff --git a/src/Http/MiddlewareAliasStore.php b/src/Http/MiddlewareAliasStore.php index c9c2682..b86eb5b 100644 --- a/src/Http/MiddlewareAliasStore.php +++ b/src/Http/MiddlewareAliasStore.php @@ -17,7 +17,7 @@ public function set(string $name, $middleware) public function get(string $name) { - list($name, $params) = $this->parseName($name); + [$name, $params] = $this->parseName($name); $middleware = $this->aliases[$name]; @@ -34,7 +34,7 @@ public function get(string $name) protected function parseName($name) : array { - list($name, $params) = array_pad(explode(':', $name), 2, ''); + [$name, $params] = array_pad(explode(':', (string) $name), 2, ''); $params = explode(',', $params); @@ -43,7 +43,7 @@ protected function parseName($name) : array public function has(string $name) : bool { - list($name, $params) = $this->parseName($name); + [$name, $params] = $this->parseName($name); return isset($this->aliases[$name]); } diff --git a/src/Http/Router.php b/src/Http/Router.php index 7753b07..3eb1d25 100644 --- a/src/Http/Router.php +++ b/src/Http/Router.php @@ -17,6 +17,7 @@ class Router extends RareRouter * @param callable|string $callback * @return \Rareloop\Router\Route */ + #[\Override] public function map(array $verbs, string $uri, $callback): Route { if ($this->isControllerString($callback)) { @@ -34,7 +35,7 @@ public function map(array $verbs, string $uri, $callback): Route */ private function isControllerString($callback) : bool { - return is_string($callback) && strpos($callback, '@') !== false; + return is_string($callback) && str_contains($callback, '@'); } /** @@ -45,7 +46,7 @@ private function isControllerString($callback) : bool */ private function normaliseCallbackString(string $callback) : string { - @list($controller, $method) = explode('@', $callback); + @[$controller, $method] = explode('@', $callback); if (class_exists($this->defaultControllerNamespace . $controller)) { return $this->defaultControllerNamespace . $callback; diff --git a/src/Page.php b/src/Page.php index 8398eea..edaee2b 100644 --- a/src/Page.php +++ b/src/Page.php @@ -11,6 +11,7 @@ class Page extends Post * * @return string */ + #[\Override] public static function getPostType() { return 'page'; diff --git a/src/Post.php b/src/Post.php index 38f4258..62fc2d3 100644 --- a/src/Post.php +++ b/src/Post.php @@ -26,6 +26,7 @@ public function __construct(mixed $wpPost = null, $preventTimberInit = false) } } + #[\Override] public function __call($name, $arguments) { if (static::hasMacro($name)) { @@ -46,7 +47,7 @@ public static function __callStatic($name, $arguments) return call_user_func_array([$builder, $name], $arguments); } - trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR); + trigger_error('Call to undefined method ' . self::class . '::' . $name . '()', E_USER_ERROR); } /** @@ -101,7 +102,7 @@ public static function register() throw new PostTypeRegistrationException('Config not set'); } - add_filter('timber/post/classmap', [static::class, 'filterTimberPostClassMap']); + add_filter('timber/post/classmap', static::filterTimberPostClassMap(...)); register_post_type($postType, $config); } @@ -119,7 +120,7 @@ public static function filterTimberPostClassMap(array $classMap): array */ public static function all($perPage = -1, $orderby = 'menu_order', $order = 'ASC') { - $order = strtoupper($order); + $order = strtoupper((string) $order); $args = [ 'posts_per_page' => $perPage, @@ -161,6 +162,6 @@ public static function query($args = null) */ private static function posts($args = null) { - return collect(Timber::get_posts($args, get_called_class())); + return collect(Timber::get_posts($args, static::class)); } } diff --git a/src/Providers/SessionServiceProvider.php b/src/Providers/SessionServiceProvider.php index 8ca5715..d7dca2d 100644 --- a/src/Providers/SessionServiceProvider.php +++ b/src/Providers/SessionServiceProvider.php @@ -43,12 +43,8 @@ public function boot() setcookie( $this->session->getName(), - $this->session->getId(), - time() + ($cookieOptions['lifetime'] * 60), - $cookieOptions['path'], - $cookieOptions['domain'], - $cookieOptions['secure'], - $cookieOptions['httpOnly'] + (string) $this->session->getId(), + ['expires' => time() + ($cookieOptions['lifetime'] * 60), 'path' => $cookieOptions['path'], 'domain' => $cookieOptions['domain'], 'secure' => $cookieOptions['secure'], 'httponly' => $cookieOptions['httpOnly']] ); $cookieSet = true; diff --git a/src/Providers/WordPressControllersServiceProvider.php b/src/Providers/WordPressControllersServiceProvider.php index 29170f5..94a8716 100644 --- a/src/Providers/WordPressControllersServiceProvider.php +++ b/src/Providers/WordPressControllersServiceProvider.php @@ -16,7 +16,7 @@ class WordPressControllersServiceProvider extends ServiceProvider { public function boot() { - add_filter('template_include', [$this, 'handleTemplateInclude']); + add_filter('template_include', $this->handleTemplateInclude(...)); } public function handleTemplateInclude($template) @@ -38,14 +38,14 @@ public function handleTemplateInclude($template) if ($response) { $this->app->shutdown($response); } else { - $this->app->bind('__wp-controller-miss-template', basename($template)); + $this->app->bind('__wp-controller-miss-template', basename((string) $template)); $this->app->bind('__wp-controller-miss-controller', $controller); } } public function getControllerClassFromTemplate($template) { - $controllerName = Stringy::create(basename($template, '.php'))->upperCamelize() . 'Controller'; + $controllerName = Stringy::create(basename((string) $template, '.php'))->upperCamelize() . 'Controller'; // Classes can't start with a number so we have to special case the behaviour here if ($controllerName === '404Controller') { @@ -76,11 +76,7 @@ public function handleRequest(RequestInterface $request, $controllerName, $metho if ($controller instanceof ProvidesControllerMiddleware) { $controllerMiddleware = new Collection($controller->getControllerMiddleware()); - $middlewares = $controllerMiddleware->reject(function ($cm) use ($methodName) { - return $cm->excludedForMethod($methodName); - })->map(function ($cm) { - return $cm->middleware(); - })->all(); + $middlewares = $controllerMiddleware->reject(fn($cm) => $cm->excludedForMethod($methodName))->map(fn($cm) => $cm->middleware())->all(); } $middlewares = [ @@ -102,9 +98,7 @@ private function createDispatcher(array $middlewares): Dispatcher $resolver = null; if ($this->app->has('middleware-resolver')) { - $resolver = function ($name) { - return $this->app->get('middleware-resolver')->resolve($name); - }; + $resolver = (fn($name) => $this->app->get('middleware-resolver')->resolve($name)); } return new Dispatcher($middlewares, $resolver); diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 384fee6..004f019 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -82,7 +82,7 @@ public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuild return $this; } - public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilderContract + public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilderContract { $order = strtoupper($order); @@ -122,7 +122,7 @@ public function whereStatus(): QueryBuilderContract protected function initialiseMetaQuery() { - $this->params['meta_query'] = $this->params['meta_query'] ?? []; + $this->params['meta_query'] ??= []; } public function whereMeta($key, $value, $compare = '=', $type = null): QueryBuilderContract diff --git a/src/ScopedQueryBuilder.php b/src/ScopedQueryBuilder.php index f6089c1..70ba909 100644 --- a/src/ScopedQueryBuilder.php +++ b/src/ScopedQueryBuilder.php @@ -12,14 +12,10 @@ class ScopedQueryBuilder { - protected $postClass; - protected $queryBuilder; - public function __construct($postClass) + public function __construct(protected $postClass) { - $this->postClass = $postClass; - $this->queryBuilder = Helpers::app(QueryBuilderContract::class); $this->queryBuilder @@ -38,12 +34,10 @@ public function __call($name, $arguments) } // See if this is a scope function that needs calling - $scopeFunctionName = 'scope' . ucfirst($name); + $scopeFunctionName = 'scope' . ucfirst((string) $name); $reflection = new ReflectionClass($this->postClass); - $publicMethods = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))->map(function ($method) { - return $method->getName(); - })->toArray(); + $publicMethods = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))->map(fn($method) => $method->getName())->toArray(); if (!in_array($scopeFunctionName, $publicMethods)) { trigger_error( @@ -54,7 +48,7 @@ public function __call($name, $arguments) array_unshift($arguments, $this); - return (new $this->postClass(false, true))->{$scopeFunctionName}(...$arguments); + return new $this->postClass(false, true)->{$scopeFunctionName}(...$arguments); } /** @@ -77,7 +71,7 @@ protected function hasQueryBuilderMethod(string $name): bool return false; } - public function wherePostType($postType) + public function wherePostType($postType): never { throw new CannotRedeclarePostTypeOnQueryException; } diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 856b7c1..3992cf4 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -17,7 +17,7 @@ public function __construct( SessionHandlerInterface $handler, Encrypter $encrypter, $id = null, - HandlerInterface $exceptionHandler = null + ?HandlerInterface $exceptionHandler = null ) { $this->encrypter = $encrypter; $this->exceptionHandler = $exceptionHandler; @@ -25,11 +25,13 @@ public function __construct( parent::__construct($name, $handler, $id); } + #[\Override] protected function prepareForStorage($data) { return $this->encrypter->encrypt($data); } + #[\Override] protected function prepareForUnserialize($data) { try { diff --git a/src/Session/FileSessionHandler.php b/src/Session/FileSessionHandler.php index 80d5a48..9efa24b 100644 --- a/src/Session/FileSessionHandler.php +++ b/src/Session/FileSessionHandler.php @@ -8,13 +8,8 @@ class FileSessionHandler implements SessionHandlerInterface { - protected $path; - protected $prefix; - - public function __construct($path, $prefix = 'lumberjack_session_') + public function __construct(protected $path, protected $prefix = 'lumberjack_session_') { - $this->path = $path; - $this->prefix = $prefix; } #[\ReturnTypeWillChange] @@ -46,7 +41,7 @@ public function write($sessionId, $data) { try { file_put_contents($this->getFilepath($sessionId), $data); - } catch (Exception $e) { + } catch (Exception) { Log::error('Failed to create session on disk'); } diff --git a/src/Session/Store.php b/src/Session/Store.php index 9c58b64..62cbbbc 100644 --- a/src/Session/Store.php +++ b/src/Session/Store.php @@ -194,7 +194,7 @@ public function getId() public function setId($id = null) { - $id = $id ?? static::random(40); + $id ??= static::random(40); $this->id = $id; } diff --git a/src/ViewModel.php b/src/ViewModel.php index a7a9a31..724cede 100644 --- a/src/ViewModel.php +++ b/src/ViewModel.php @@ -12,20 +12,16 @@ abstract class ViewModel implements Arrayable public function toArray() : array { $propertyKeyValues = collect($this->validPropertyNames()) - ->mapWithKeys(function ($method) { - return [ - $method => $this->{$method}, - ]; - }) + ->mapWithKeys(fn($method) => [ + $method => $this->{$method}, + ]) ->toArray(); $methodKeyValues = collect($this->validMethodNames()) ->whereNotIn(null, $this->ignoredMethods()) - ->mapWithKeys(function ($method) { - return [ - $method => call_user_func([$this, $method]), - ]; - }) + ->mapWithKeys(fn($method) => [ + $method => call_user_func([$this, $method]), + ]) ->toArray(); return array_merge($propertyKeyValues, $methodKeyValues); @@ -35,12 +31,8 @@ protected function validMethodNames() : array { $class = new ReflectionClass(static::class); return collect($class->getMethods(ReflectionMethod::IS_PUBLIC)) - ->reject(function ($method) { - return $method->isStatic() || $method->getNumberOfParameters() > 0; - }) - ->map(function ($method) { - return $method->getName(); - }) + ->reject(fn($method) => $method->isStatic() || $method->getNumberOfParameters() > 0) + ->map(fn($method) => $method->getName()) ->all(); } @@ -49,12 +41,8 @@ protected function validPropertyNames() : array $class = new ReflectionClass(static::class); return collect($class->getProperties(ReflectionProperty::IS_PUBLIC)) - ->reject(function ($property) { - return $property->isStatic(); - }) - ->map(function ($property) { - return $property->getName(); - }) + ->reject(fn($property) => $property->isStatic()) + ->map(fn($property) => $property->getName()) ->all(); } diff --git a/src/functions.php b/src/functions.php index 92043dd..20acd20 100644 --- a/src/functions.php +++ b/src/functions.php @@ -5,69 +5,69 @@ if (!function_exists('app')) { function app() { - return call_user_func_array([Helpers::class, 'app'], func_get_args()); + return call_user_func_array(Helpers::app(...), func_get_args()); } } if (!function_exists('config')) { function config() { - return call_user_func_array([Helpers::class, 'config'], func_get_args()); + return call_user_func_array(Helpers::config(...), func_get_args()); } } if (!function_exists('view')) { function view() { - return call_user_func_array([Helpers::class, 'view'], func_get_args()); + return call_user_func_array(Helpers::view(...), func_get_args()); } } if (!function_exists('route')) { function route() { - return call_user_func_array([Helpers::class, 'route'], func_get_args()); + return call_user_func_array(Helpers::route(...), func_get_args()); } } if (!function_exists('redirect')) { function redirect() { - return call_user_func_array([Helpers::class, 'redirect'], func_get_args()); + return call_user_func_array(Helpers::redirect(...), func_get_args()); } } if (!function_exists('report')) { function report() { - return call_user_func_array([Helpers::class, 'report'], func_get_args()); + return call_user_func_array(Helpers::report(...), func_get_args()); } } if (!function_exists('session')) { function session() { - return call_user_func_array([Helpers::class, 'session'], func_get_args()); + return call_user_func_array(Helpers::session(...), func_get_args()); } } if (!function_exists('back')) { function back() { - return call_user_func_array([Helpers::class, 'back'], func_get_args()); + return call_user_func_array(Helpers::back(...), func_get_args()); } } if (!function_exists('request')) { function request() { - return call_user_func_array([Helpers::class, 'request'], func_get_args()); + return call_user_func_array(Helpers::request(...), func_get_args()); } } if (!function_exists('logger')) { function logger() { - return call_user_func_array([Helpers::class, 'logger'], func_get_args()); + return call_user_func_array(Helpers::logger(...), func_get_args()); } } diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index cc5f13c..2e6bee6 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -498,9 +498,7 @@ private function createPhpSapiNameMock($value, $namespace) $builder->setNamespace($namespace) ->setName('php_sapi_name') ->setFunction( - function () use ($value) { - return $value; - } + fn() => $value ); return $builder->build(); @@ -552,21 +550,15 @@ public function calling_detectWhenRequestHasNotBeenHandled_adds_actions() class BootstrapperBootstrapTester { - public $callback; - - public function __construct($callback) + public function __construct(public $callback) { - $this->callback = $callback; } } abstract class TestBootstrapperBase { - private BootstrapperBootstrapTester $tester; - - public function __construct(BootstrapperBootstrapTester $tester) + public function __construct(private readonly BootstrapperBootstrapTester $tester) { - $this->tester = $tester; } public function bootstrap(Application $app) @@ -654,13 +646,9 @@ public function __construct(TestInterface $test) class RequiresAdditionalConstructorParams { public $param; - public $param1; - public $param2; - public function __construct(TestInterface $test, $param1, $param2) + public function __construct(TestInterface $test, public $param1, public $param2) { $this->param = $test; - $this->param1 = $param1; - $this->param2 = $param2; } } diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php index c19f006..403b4ec 100644 --- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php +++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php @@ -57,9 +57,7 @@ public function E_USER_NOTICE_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { - return $e->getSeverity() === E_USER_NOTICE && $e->getMessage() === 'Test Error'; - })); + $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_NOTICE && $e->getMessage() === 'Test Error')); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -80,9 +78,7 @@ public function E_USER_DEPRECATED_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { - return $e->getSeverity() === E_USER_DEPRECATED && $e->getMessage() === 'Test Error'; - })); + $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_DEPRECATED && $e->getMessage() === 'Test Error')); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -103,9 +99,7 @@ public function E_DEPRECATED_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { - return $e->getSeverity() === E_DEPRECATED && $e->getMessage() === 'Test Error'; - })); + $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_DEPRECATED && $e->getMessage() === 'Test Error')); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -128,9 +122,7 @@ public function custom_error_level_can_be_set_for_report_only() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { - return $e->getSeverity() === E_USER_ERROR && $e->getMessage() === 'Test Error'; - })); + $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_ERROR && $e->getMessage() === 'Test Error')); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); diff --git a/tests/Unit/GlobalFunctionsTest.php b/tests/Unit/GlobalFunctionsTest.php index bc0d1d6..a0e7a5d 100644 --- a/tests/Unit/GlobalFunctionsTest.php +++ b/tests/Unit/GlobalFunctionsTest.php @@ -46,8 +46,6 @@ public static function globalHelperFunctions() { $reflection = new \ReflectionClass(Helpers::class); - return collect($reflection->getMethods(\ReflectionMethod::IS_STATIC))->map(function ($function) { - return [$function->name]; - })->toArray(); + return collect($reflection->getMethods(\ReflectionMethod::IS_STATIC))->map(fn($function) => [$function->name])->toArray(); } } diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index 11505cd..a4d1c7c 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -147,7 +147,7 @@ public function can_get_a_url_for_a_named_route() $url = Helpers::route('test.route'); - $this->assertSame('test/route', trim($url, '/')); + $this->assertSame('test/route', trim((string) $url, '/')); } /** @test */ @@ -164,7 +164,7 @@ public function can_get_a_url_for_a_named_route_with_params() 'name' => 'route', ]); - $this->assertSame('test/route', trim($url, '/')); + $this->assertSame('test/route', trim((string) $url, '/')); } /** @test */ @@ -207,9 +207,7 @@ public function can_report_an_exception() $handler = \Mockery::mock(TestExceptionHandler::class . '[report]', [$app]); $handler->shouldReceive('report')->with($exception)->once(); - $app->bind(HandlerInterface::class, function () use ($handler) { - return $handler; - }); + $app->bind(HandlerInterface::class, fn() => $handler); Helpers::report($exception); } @@ -375,12 +373,7 @@ class TestExceptionHandler extends Handler class RequiresConstructorParams { - public $param1; - public $param2; - - public function __construct($param1, $param2) + public function __construct(public $param1, public $param2) { - $this->param1 = $param1; - $this->param2 = $param2; } } diff --git a/tests/Unit/Http/Middleware/PasswordProtectTest.php b/tests/Unit/Http/Middleware/PasswordProtectTest.php index a21fc20..523e1b4 100644 --- a/tests/Unit/Http/Middleware/PasswordProtectTest.php +++ b/tests/Unit/Http/Middleware/PasswordProtectTest.php @@ -52,9 +52,7 @@ public function it_does_nothing_when_the_password_twig_file_is_not_found() $timber = \Mockery::mock('alias:' . Timber::class); $timber->shouldReceive('get_post')->once(); $timber->shouldReceive('compile') - ->withArgs(function ($template) { - return $template === 'single-password.twig'; - }) + ->withArgs(fn($template) => $template === 'single-password.twig') ->once() ->andReturn(false); @@ -84,9 +82,7 @@ public function it_renders_the_password_template_when_needed() $timber = \Mockery::mock('alias:' . Timber::class); $timber->shouldReceive('get_post')->once(); $timber->shouldReceive('compile') - ->withArgs(function ($template) { - return $template === 'single-password.twig'; - }) + ->withArgs(fn($template) => $template === 'single-password.twig') ->once() ->andReturn('testing123'); diff --git a/tests/Unit/Http/MiddlewareAliasStoreTest.php b/tests/Unit/Http/MiddlewareAliasStoreTest.php index 6d97f28..6818104 100644 --- a/tests/Unit/Http/MiddlewareAliasStoreTest.php +++ b/tests/Unit/Http/MiddlewareAliasStoreTest.php @@ -29,9 +29,7 @@ public function can_register_an_alias_for_a_middleware_closure_factory() $store = new MiddlewareAliasStore; $middleware = Mockery::mock(MiddlewareInterface::class); - $store->set('middlewarekey', function () use ($middleware) { - return $middleware; - }); + $store->set('middlewarekey', fn() => $middleware); $this->assertSame($middleware, $store->get('middlewarekey')); } @@ -108,12 +106,7 @@ class MASTestClass class MASTestClassWithConstructorParams { - public $param1; - public $param2; - - public function __construct($param1, $param2) + public function __construct(public $param1, public $param2) { - $this->param1 = $param1; - $this->param2 = $param2; } } diff --git a/tests/Unit/Http/RouterTest.php b/tests/Unit/Http/RouterTest.php index 9447e00..1422145 100644 --- a/tests/Unit/Http/RouterTest.php +++ b/tests/Unit/Http/RouterTest.php @@ -42,7 +42,7 @@ public function controller_does_not_have_namespace_added_when_it_is_callable() $router = new Router; $controller = new RouterTestController; - $route = $router->get('/test/123', [$controller, 'test']); + $route = $router->get('/test/123', $controller->test(...)); $this->assertSame(RouterTestController::class . '@test', $route->getActionName()); } @@ -63,9 +63,7 @@ public function controller_does_not_have_namespace_added_when_it_is_closure() */ public function can_extend_post_behaviour_with_macros() { - Router::macro('testFunctionAddedByMacro', function () { - return 'abc123'; - }); + Router::macro('testFunctionAddedByMacro', fn() => 'abc123'); $queryBuilder = new Router(); @@ -90,9 +88,7 @@ class RouterMixin { function testFunctionAddedByMixin() { - return function() { - return 'abc123'; - }; + return fn() => 'abc123'; } } diff --git a/tests/Unit/PostQueryBuilderTest.php b/tests/Unit/PostQueryBuilderTest.php index 8cb501d..0ad0fea 100644 --- a/tests/Unit/PostQueryBuilderTest.php +++ b/tests/Unit/PostQueryBuilderTest.php @@ -55,7 +55,7 @@ public function throw_error_on_missing_static_function() try { Post::missingStaticFunction(); - } catch (Throwable $e) { + } catch (Throwable) { $errorThrown = true; } @@ -84,6 +84,7 @@ public static function setCreateBuilderResponse($builder) static::$injectedBuilder = $builder; } + #[\Override] public static function builder(): ScopedQueryBuilder { return static::$injectedBuilder; diff --git a/tests/Unit/PostTest.php b/tests/Unit/PostTest.php index 4aabdc7..e57e3d9 100644 --- a/tests/Unit/PostTest.php +++ b/tests/Unit/PostTest.php @@ -30,7 +30,7 @@ public function register_function_calls_register_post_type_when_post_type_and_co RegisterablePostType::register(); - $this->assertNotFalse(has_filter('timber/post/classmap', [RegisterablePostType::class, 'filterTimberPostClassMap'])); + $this->assertNotFalse(has_filter('timber/post/classmap', RegisterablePostType::filterTimberPostClassMap(...))); } /** @test */ @@ -213,9 +213,7 @@ public function all_can_have_order_set() */ public function can_extend_post_behaviour_with_macros() { - Post::macro('testFunctionAddedByMacro', function () { - return 'abc123'; - }); + Post::macro('testFunctionAddedByMacro', fn() => 'abc123'); $post = new Post(null, true); @@ -228,9 +226,7 @@ public function can_extend_post_behaviour_with_macros() */ public function macros_set_correct_this_context_on_instances() { - PostWithPrivateData::macro('testFunctionAddedByMacro', function () { - return $this->dummyData; - }); + PostWithPrivateData::macro('testFunctionAddedByMacro', fn() => $this->dummyData); $post = new PostWithPrivateData(null, true); @@ -254,9 +250,7 @@ class PostMixin { function testFunctionAddedByMixin() { - return function () { - return 'abc123'; - }; + return fn() => 'abc123'; } } @@ -267,6 +261,7 @@ class PostWithPrivateData extends Post class RegisterablePostType extends Post { + #[\Override] public static function getPostType(): string { return 'registerable_post_type'; @@ -317,6 +312,7 @@ protected static function getPostTypeConfig(): array class UnregisterablePostTypeWithoutConfig extends Post { + #[\Override] public static function getPostType(): string { return 'post_type'; diff --git a/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php b/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php index ae58e05..ac2aa05 100644 --- a/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php +++ b/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php @@ -37,6 +37,7 @@ public function should_call_register_post_type_for_each_configured_post_type() class CustomPost1 extends Post { + #[\Override] public static function getPostType() { return 'custom_post_1'; @@ -52,6 +53,7 @@ protected static function getPostTypeConfig() class CustomPost2 extends Post { + #[\Override] public static function getPostType() { return 'custom_post_1'; diff --git a/tests/Unit/Providers/RouterServiceProviderTest.php b/tests/Unit/Providers/RouterServiceProviderTest.php index 780f363..b8f0ca2 100644 --- a/tests/Unit/Providers/RouterServiceProviderTest.php +++ b/tests/Unit/Providers/RouterServiceProviderTest.php @@ -102,9 +102,7 @@ public function basedir_is_set_from_wordpress_config() $lumberjack->bootstrap(); $router = $app->get('router'); - $router->get('/test/123', function () { - return 'abc123'; - }); + $router->get('/test/123', fn() => 'abc123'); $response = $router->match($request); $this->assertSame(200, $response->getStatusCode()); @@ -287,13 +285,8 @@ private function setSiteUrl($url) class RSPAddHeaderMiddleware implements MiddlewareInterface { - private $key; - private $value; - - public function __construct($key, $value) + public function __construct(private $key, private $value) { - $this->key = $key; - $this->value = $value; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php index f588181..90fc6f2 100644 --- a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php +++ b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php @@ -40,7 +40,7 @@ public function template_include_filter_is_applied_on_boot() $app->register($provider); $app->boot(); - $this->assertNotFalse(has_filter('template_include', [$provider, 'handleTemplateInclude'])); + $this->assertNotFalse(has_filter('template_include', $provider->handleTemplateInclude(...))); } /** @test */ @@ -195,7 +195,7 @@ public function handle_request_returns_response_when_controller_does_exist() $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestController::class, 'handle'); @@ -212,7 +212,7 @@ public function handle_request_returns_response_when_controller_returns_a_respon $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerReturningAResponsable::class, 'handle'); @@ -230,7 +230,7 @@ public function handle_request_resolves_constructor_params_from_container() $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithConstructorParams::class, 'handle'); @@ -247,7 +247,7 @@ public function handle_request_resolves_controller_method_params_from_container( $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithHandleParams::class, 'handle'); @@ -267,7 +267,7 @@ public function handle_request_supports_middleware() $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -288,7 +288,7 @@ public function handle_request_adds_password_protect_middleware() $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -308,7 +308,7 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_ $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -328,7 +328,7 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_ $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot($app); + $provider->boot(); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -358,7 +358,7 @@ public function handle_request_supports_middleware_aliases() $provider = new WordPressControllersServiceProvider($app); $routerProvider->register(); $routerProvider->boot(); - $provider->boot($app); + $provider->boot(); $store = $app->get(MiddlewareAliases::class); $store->set('middleware-key', new AddHeaderMiddleware('X-Header', 'testing123')); @@ -447,13 +447,8 @@ public function handle() class AddHeaderMiddleware implements MiddlewareInterface { - private $key; - private $value; - - public function __construct($key, $value) + public function __construct(private $key, private $value) { - $this->key = $key; - $this->value = $value; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/tests/Unit/QueryBuilderTest.php b/tests/Unit/QueryBuilderTest.php index 20a1426..a059f5c 100644 --- a/tests/Unit/QueryBuilderTest.php +++ b/tests/Unit/QueryBuilderTest.php @@ -526,6 +526,7 @@ function testFunctionAddedByMixin() class PostWithCustomPostType extends Post { + #[\Override] public static function getPostType() { return 'post_with_query_scope'; diff --git a/tests/Unit/ScopedQueryBuilderTest.php b/tests/Unit/ScopedQueryBuilderTest.php index 33483b1..9f3ae13 100644 --- a/tests/Unit/ScopedQueryBuilderTest.php +++ b/tests/Unit/ScopedQueryBuilderTest.php @@ -114,7 +114,7 @@ public function missing_query_scope_throws_an_error() try { $builder = new ScopedQueryBuilder(PostWithQueryScope::class); $builder->nonExistentScope(); - } catch (Throwable $e) { + } catch (Throwable) { $errorThrown = true; } @@ -161,6 +161,7 @@ public function nonStandardMethod() class PostWithQueryScope extends Post { + #[\Override] public static function getPostType() { return 'post_with_query_scope'; diff --git a/tests/Unit/Session/FileSessionHandlerTest.php b/tests/Unit/Session/FileSessionHandlerTest.php index c2801e9..2a6ac05 100644 --- a/tests/Unit/Session/FileSessionHandlerTest.php +++ b/tests/Unit/Session/FileSessionHandlerTest.php @@ -32,7 +32,7 @@ public function close_returns_true() { $handler = new FileSessionHandler(vfsStream::url('exampleDir')); - $this->assertTrue($handler->close('save-path', 'session-name')); + $this->assertTrue($handler->close()); } /** @test */ diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index c46a2c9..f11a012 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -74,9 +74,7 @@ public function can_extend_list_of_drivers() $app = new Application; $manager = new SessionManager($app); - $manager->extend('test', function () { - return new Store('name', new TestSessionHandler); - }); + $manager->extend('test', fn() => new Store('name', new TestSessionHandler)); $this->assertInstanceOf(TestSessionHandler::class, $manager->driver('test')->getHandler()); } From bbaae010c3ddfc3097306bb9ea66105522bdfddc Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 8 Sep 2025 09:38:00 +0100 Subject: [PATCH 02/43] Upgrade phpdi --- src/Application.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Application.php b/src/Application.php index 61f2666..81fa4a4 100644 --- a/src/Application.php +++ b/src/Application.php @@ -3,12 +3,11 @@ namespace Rareloop\Lumberjack; use Closure; -use DI\ContainerBuilder; +use DI\Container; use Illuminate\Support\Collection; use Interop\Container\ContainerInterface as InteropContainerInterface; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; -use Rareloop\Router\Invoker; use function Http\Response\send; class Application implements ContainerInterface, InteropContainerInterface @@ -24,7 +23,7 @@ class Application implements ContainerInterface, InteropContainerInterface public function __construct($basePath = false) { - $this->container = ContainerBuilder::buildDevContainer(); + $this->container = new Container(); $this->bind(Application::class, $this); @@ -232,7 +231,7 @@ public function bootstrapWith(array $bootstrappers) * * @return boolean */ - public function hasRequestBeenHandled() : bool + public function hasRequestBeenHandled(): bool { return $this->requestHandled; } @@ -263,10 +262,10 @@ public function detectWhenRequestHasNotBeenHandled() if ($this->has('__wp-controller-miss-template') && $this->has('__wp-controller-miss-controller')) { wp_die( 'Loaded template ' . - $this->get('__wp-controller-miss-template') . - ' but couldn\'t find class ' . - $this->get('__wp-controller-miss-controller') . - '' + $this->get('__wp-controller-miss-template') . + ' but couldn\'t find class ' . + $this->get('__wp-controller-miss-controller') . + '' ); } } @@ -288,7 +287,7 @@ public function shutdown(?ResponseInterface $response = null) die(); } - protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $response) : ResponseInterface + protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $response): ResponseInterface { // 1. Format the previously sent headers into an array of [key, value] // 2. Remove all headers from the output that we find From 338629f918054a52c66ae6efa453ab16cbe8e6fe Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 8 Sep 2025 09:41:41 +0100 Subject: [PATCH 03/43] Upgrade container interface methods --- src/Application.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Application.php b/src/Application.php index 81fa4a4..fe368b4 100644 --- a/src/Application.php +++ b/src/Application.php @@ -5,12 +5,11 @@ use Closure; use DI\Container; use Illuminate\Support\Collection; -use Interop\Container\ContainerInterface as InteropContainerInterface; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use function Http\Response\send; -class Application implements ContainerInterface, InteropContainerInterface +class Application implements ContainerInterface { private $container; private $loadedProviders = []; @@ -151,7 +150,7 @@ private function isSingletonClassBind($id) * * @return bool */ - public function has($id) + public function has(string $id): bool { return $this->container->has($id); } From 55e922c4b37621a67ea8092d96e2975954bde483 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 8 Sep 2025 09:43:47 +0100 Subject: [PATCH 04/43] Replace `\Zend\Diactoros` imports with `\Laminas\Diactoros` --- .../RegisterExceptionHandler.php | 2 +- src/Exceptions/Handler.php | 4 +-- src/Http/Responses/RedirectResponse.php | 2 +- src/Http/Responses/TimberResponse.php | 4 +-- src/Http/ServerRequest.php | 6 ++-- src/Providers/RouterServiceProvider.php | 2 +- .../WordPressControllersServiceProvider.php | 2 +- .../RegisterExceptionHandlerTest.php | 6 ++-- tests/Unit/Exceptions/HandlerTest.php | 8 ++---- tests/Unit/Http/ServerRequestTest.php | 2 +- .../Providers/RouterServiceProviderTest.php | 15 ++++------ ...ordPressControllersServiceProviderTest.php | 28 ++++++------------- 12 files changed, 32 insertions(+), 49 deletions(-) diff --git a/src/Bootstrappers/RegisterExceptionHandler.php b/src/Bootstrappers/RegisterExceptionHandler.php index 14de197..79424c0 100644 --- a/src/Bootstrappers/RegisterExceptionHandler.php +++ b/src/Bootstrappers/RegisterExceptionHandler.php @@ -10,7 +10,7 @@ use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Router\Responsable; use Symfony\Component\Debug\Exception\FatalErrorException; -use Zend\Diactoros\ServerRequestFactory; +use Laminas\Diactoros\ServerRequestFactory; use function Http\Response\send; /** diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 7d59ef8..e6b4067 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -9,7 +9,7 @@ use Rareloop\Lumberjack\Facades\Config; use Symfony\Component\Debug\ExceptionHandler as SymfonyExceptionHandler; use Symfony\Component\Debug\Exception\FlattenException; -use Zend\Diactoros\Response\HtmlResponse; +use Laminas\Diactoros\Response\HtmlResponse; class Handler implements HandlerInterface { @@ -34,7 +34,7 @@ public function report(Exception $e) } } - public function render(ServerRequestInterface $request, Exception $e) : ResponseInterface + public function render(ServerRequestInterface $request, Exception $e): ResponseInterface { $e = FlattenException::create($e); diff --git a/src/Http/Responses/RedirectResponse.php b/src/Http/Responses/RedirectResponse.php index d51db77..a6d992d 100644 --- a/src/Http/Responses/RedirectResponse.php +++ b/src/Http/Responses/RedirectResponse.php @@ -3,7 +3,7 @@ namespace Rareloop\Lumberjack\Http\Responses; use Rareloop\Lumberjack\Helpers; -use Zend\Diactoros\Response\RedirectResponse as DiactorosRedirectResponse; +use Laminas\Diactoros\Response\RedirectResponse as DiactorosRedirectResponse; class RedirectResponse extends DiactorosRedirectResponse { diff --git a/src/Http/Responses/TimberResponse.php b/src/Http/Responses/TimberResponse.php index 06a31cb..8ac161e 100644 --- a/src/Http/Responses/TimberResponse.php +++ b/src/Http/Responses/TimberResponse.php @@ -6,7 +6,7 @@ use Rareloop\Lumberjack\Contracts\Arrayable; use Rareloop\Lumberjack\Exceptions\TwigTemplateNotFoundException; use Timber\Timber; -use Zend\Diactoros\Response\HtmlResponse; +use Laminas\Diactoros\Response\HtmlResponse; class TimberResponse extends HtmlResponse { @@ -21,7 +21,7 @@ public function __construct($twigTemplate, $context, $status = 200, array $heade parent::__construct($template, $status, $headers); } - private function flattenContextToArrays(array $context) : array + private function flattenContextToArrays(array $context): array { // Recursively walk the array, when we find something that implements the Arrayable interface // flatten it to an array. Because we're passing by reference by updating what the value of diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php index 5135e39..3274e66 100644 --- a/src/Http/ServerRequest.php +++ b/src/Http/ServerRequest.php @@ -5,7 +5,7 @@ use Psr\Http\Message\ServerRequestInterface; use Rareloop\Psr7ServerRequestExtension\InteractsWithInput; use Rareloop\Psr7ServerRequestExtension\InteractsWithUri; -use Zend\Diactoros\ServerRequest as DiactorosServerRequest; +use Laminas\Diactoros\ServerRequest as DiactorosServerRequest; class ServerRequest extends DiactorosServerRequest { @@ -34,7 +34,7 @@ public static function fromRequest(ServerRequestInterface $request) ); } - public function ajax() : bool + public function ajax(): bool { if (!$this->hasHeader('X-Requested-With')) { return false; @@ -43,7 +43,7 @@ public function ajax() : bool return 'XMLHttpRequest' === $this->getHeader('X-Requested-With')[0]; } - public function getMethod() : string + public function getMethod(): string { return strtoupper(parent::getMethod()); } diff --git a/src/Providers/RouterServiceProvider.php b/src/Providers/RouterServiceProvider.php index 5a61ee5..81f82e1 100644 --- a/src/Providers/RouterServiceProvider.php +++ b/src/Providers/RouterServiceProvider.php @@ -9,7 +9,7 @@ use Rareloop\Lumberjack\Http\Router; use Rareloop\Lumberjack\Http\ServerRequest; use Rareloop\Router\MiddlewareResolver as MiddlewareResolverInterface; -use Zend\Diactoros\ServerRequestFactory; +use Laminas\Diactoros\ServerRequestFactory; class RouterServiceProvider extends ServiceProvider { diff --git a/src/Providers/WordPressControllersServiceProvider.php b/src/Providers/WordPressControllersServiceProvider.php index 94a8716..07766b1 100644 --- a/src/Providers/WordPressControllersServiceProvider.php +++ b/src/Providers/WordPressControllersServiceProvider.php @@ -9,7 +9,7 @@ use Rareloop\Router\ResponseFactory; use Psr\Http\Message\RequestInterface; use Rareloop\Lumberjack\Http\Middleware\PasswordProtected; -use Zend\Diactoros\ServerRequestFactory; +use Laminas\Diactoros\ServerRequestFactory; use Rareloop\Router\ProvidesControllerMiddleware; class WordPressControllersServiceProvider extends ServiceProvider diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php index 403b4ec..873c272 100644 --- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php +++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php @@ -15,9 +15,9 @@ use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\Responsable; use Symfony\Component\Debug\Exception\FatalErrorException; -use Zend\Diactoros\Response; -use Zend\Diactoros\Response\TextResponse; -use Zend\Diactoros\ServerRequest; +use Laminas\Diactoros\Response; +use Laminas\Diactoros\Response\TextResponse; +use Laminas\Diactoros\ServerRequest; /** * @runTestsInSeparateProcesses diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index dda8be0..21889d7 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -9,8 +9,8 @@ use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; use Rareloop\Lumberjack\Exceptions\Handler; -use Zend\Diactoros\Response\HtmlResponse; -use Zend\Diactoros\ServerRequest; +use Laminas\Diactoros\Response\HtmlResponse; +use Laminas\Diactoros\ServerRequest; class HandlerTest extends TestCase { @@ -124,6 +124,4 @@ class HandlerWithBlacklist extends Handler ]; } -class BlacklistedException extends \Exception -{ -} +class BlacklistedException extends \Exception {} diff --git a/tests/Unit/Http/ServerRequestTest.php b/tests/Unit/Http/ServerRequestTest.php index 2fe274c..2aa4667 100644 --- a/tests/Unit/Http/ServerRequestTest.php +++ b/tests/Unit/Http/ServerRequestTest.php @@ -8,7 +8,7 @@ use Rareloop\Lumberjack\Http\ServerRequest; use Rareloop\Psr7ServerRequestExtension\InteractsWithInput; use Rareloop\Psr7ServerRequestExtension\InteractsWithUri; -use Zend\Diactoros\ServerRequest as DiactorosServerRequest; +use Laminas\Diactoros\ServerRequest as DiactorosServerRequest; class ServerRequestTest extends TestCase { diff --git a/tests/Unit/Providers/RouterServiceProviderTest.php b/tests/Unit/Providers/RouterServiceProviderTest.php index b8f0ca2..7926a8c 100644 --- a/tests/Unit/Providers/RouterServiceProviderTest.php +++ b/tests/Unit/Providers/RouterServiceProviderTest.php @@ -18,10 +18,10 @@ use Rareloop\Lumberjack\Providers\RouterServiceProvider; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\MiddlewareResolver; -use Zend\Diactoros\Request; -use Zend\Diactoros\Response\HtmlResponse; -use Zend\Diactoros\Response\TextResponse; -use Zend\Diactoros\ServerRequest; +use Laminas\Diactoros\Request; +use Laminas\Diactoros\Response\HtmlResponse; +use Laminas\Diactoros\Response\TextResponse; +use Laminas\Diactoros\ServerRequest; class RouterServiceProviderTest extends TestCase { @@ -79,8 +79,7 @@ public function configured_router_can_resolve_middleware_aliases() $store->set('middleware-key', new RSPAddHeaderMiddleware('X-Key', 'abc')); $request = new ServerRequest([], [], '/test/123', 'GET'); - $router->get('/test/123', function () { - })->middleware('middleware-key'); + $router->get('/test/123', function () {})->middleware('middleware-key'); $response = $router->match($request); $this->assertTrue($response->hasHeader('X-Key')); @@ -285,9 +284,7 @@ private function setSiteUrl($url) class RSPAddHeaderMiddleware implements MiddlewareInterface { - public function __construct(private $key, private $value) - { - } + public function __construct(private $key, private $value) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php index 90fc6f2..bee3e8b 100644 --- a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php +++ b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php @@ -22,8 +22,8 @@ use Rareloop\Lumberjack\Providers\WordPressControllersServiceProvider; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\Responsable; -use Zend\Diactoros\Response\TextResponse; -use Zend\Diactoros\ServerRequest; +use Laminas\Diactoros\Response\TextResponse; +use Laminas\Diactoros\ServerRequest; use \Mockery; use Rareloop\Lumberjack\Http\Middleware\PasswordProtected; @@ -399,27 +399,19 @@ public function handle_template_include_will_not_call_app_shutdown_when_it_has_n class TestController { - public function handle() - { - } + public function handle() {} } class TestControllerWithConstructorParams { - public function __construct(Application $app) - { - } + public function __construct(Application $app) {} - public function handle() - { - } + public function handle() {} } class TestControllerWithHandleParams { - public function handle(Application $app) - { - } + public function handle(Application $app) {} } class MyResponsable implements Responsable @@ -440,16 +432,12 @@ public function handle() class TestControllerWithMiddleware extends Controller { - public function handle() - { - } + public function handle() {} } class AddHeaderMiddleware implements MiddlewareInterface { - public function __construct(private $key, private $value) - { - } + public function __construct(private $key, private $value) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { From c7790dce4d07b717be96fa5b1a01f1af5ad60685 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 8 Sep 2025 14:12:43 +0100 Subject: [PATCH 05/43] Replace Blast Facades with own implementation --- composer.json | 1 - src/Bootstrappers/RegisterFacades.php | 4 +- src/FacadeManager.php | 25 ++++++++ src/Facades/Config.php | 5 +- src/Facades/Facade.php | 21 +++++++ src/Facades/Log.php | 5 +- src/Facades/MiddlewareAliases.php | 4 +- src/Facades/Router.php | 5 +- src/Facades/Session.php | 3 +- tests/Unit/Facades/FacadeTest.php | 69 +++++++++++++++++++++++ tests/Unit/Facades/Stubs/Foo.php | 11 ++++ tests/Unit/Facades/Stubs/FooFacade.php | 13 +++++ tests/Unit/Facades/Stubs/FooInterface.php | 8 +++ 13 files changed, 154 insertions(+), 20 deletions(-) create mode 100644 src/FacadeManager.php create mode 100644 src/Facades/Facade.php create mode 100644 tests/Unit/Facades/FacadeTest.php create mode 100644 tests/Unit/Facades/Stubs/Foo.php create mode 100644 tests/Unit/Facades/Stubs/FooFacade.php create mode 100644 tests/Unit/Facades/Stubs/FooInterface.php diff --git a/composer.json b/composer.json index 6f118cb..5604969 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,6 @@ "psr/container": "^2", "psr/http-message": "^2", "psr/http-server-middleware": "^1.0.2", - "blast/facades": "^1.0", "timber/timber": "^2.3.0", "monolog/monolog": "^3.9", "symfony/debug": "^4.4.44", diff --git a/src/Bootstrappers/RegisterFacades.php b/src/Bootstrappers/RegisterFacades.php index d5b34a2..11e8b60 100644 --- a/src/Bootstrappers/RegisterFacades.php +++ b/src/Bootstrappers/RegisterFacades.php @@ -2,13 +2,13 @@ namespace Rareloop\Lumberjack\Bootstrappers; -use Blast\Facades\FacadeFactory; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeManager; class RegisterFacades { public function bootstrap(Application $app) { - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); } } diff --git a/src/FacadeManager.php b/src/FacadeManager.php new file mode 100644 index 0000000..97730a0 --- /dev/null +++ b/src/FacadeManager.php @@ -0,0 +1,25 @@ +get($accessor), $name], $arguments); + } +} diff --git a/src/Facades/Config.php b/src/Facades/Config.php index 41b7be7..9c4f604 100644 --- a/src/Facades/Config.php +++ b/src/Facades/Config.php @@ -2,11 +2,8 @@ namespace Rareloop\Lumberjack\Facades; -use Blast\Facades\AbstractFacade; - -class Config extends AbstractFacade +class Config extends Facade { - #[\Override] protected static function accessor() { return 'config'; diff --git a/src/Facades/Facade.php b/src/Facades/Facade.php new file mode 100644 index 0000000..c585d93 --- /dev/null +++ b/src/Facades/Facade.php @@ -0,0 +1,21 @@ +get(static::accessor()); + } + + abstract public static function accessor(): string; +} diff --git a/src/Facades/Log.php b/src/Facades/Log.php index fe0a8c3..164227c 100644 --- a/src/Facades/Log.php +++ b/src/Facades/Log.php @@ -2,11 +2,8 @@ namespace Rareloop\Lumberjack\Facades; -use Blast\Facades\AbstractFacade; - -class Log extends AbstractFacade +class Log extends Facade { - #[\Override] protected static function accessor() { return 'logger'; diff --git a/src/Facades/MiddlewareAliases.php b/src/Facades/MiddlewareAliases.php index 340128b..3b4f703 100644 --- a/src/Facades/MiddlewareAliases.php +++ b/src/Facades/MiddlewareAliases.php @@ -2,11 +2,9 @@ namespace Rareloop\Lumberjack\Facades; -use Blast\Facades\AbstractFacade; -class MiddlewareAliases extends AbstractFacade +class MiddlewareAliases extends Facade { - #[\Override] protected static function accessor() { return 'middleware-alias-store'; diff --git a/src/Facades/Router.php b/src/Facades/Router.php index 5db20d4..0b9e12c 100644 --- a/src/Facades/Router.php +++ b/src/Facades/Router.php @@ -2,11 +2,8 @@ namespace Rareloop\Lumberjack\Facades; -use Blast\Facades\AbstractFacade; - -class Router extends AbstractFacade +class Router extends Facade { - #[\Override] protected static function accessor() { return 'router'; diff --git a/src/Facades/Session.php b/src/Facades/Session.php index 036aad0..b4b0526 100644 --- a/src/Facades/Session.php +++ b/src/Facades/Session.php @@ -4,9 +4,8 @@ use Blast\Facades\AbstractFacade; -class Session extends AbstractFacade +class Session extends Facade { - #[\Override] protected static function accessor() { return 'session'; diff --git a/tests/Unit/Facades/FacadeTest.php b/tests/Unit/Facades/FacadeTest.php new file mode 100644 index 0000000..3c3075c --- /dev/null +++ b/tests/Unit/Facades/FacadeTest.php @@ -0,0 +1,69 @@ +container = new Container(); + $this->container->set(FooFacade::accessor(), new Foo()); + } + + /** @test */ + public function can_initiate_facades() + { + FacadeManager::setContainer($this->container); + $this->assertInstanceOf(ContainerInterface::class, FacadeManager::getContainer()); + } + + /** @test */ + public function can_get_facade_instance() + { + FacadeManager::setContainer($this->container); + + $instance = FooFacade::__instance(); + + $this->assertInstanceOf(FooInterface::class, $instance); + $this->assertInstanceOf(Foo::class, $instance); + } + + /** @test */ + public function can_swap_instances() + { + FacadeManager::setContainer($this->container); + + $instance = FooFacade::__instance(); + + $this->assertInstanceOf(FooInterface::class, $instance); + $this->assertInstanceOf(Foo::class, $instance); + + $this->container->set(FooFacade::accessor(), 'bar'); + + $instance = FooFacade::__instance(); + + $this->assertEquals('bar', $instance); + } + + public function can_call_functions() + { + FacadeManager::setContainer($this->container); + + $this->assertEquals('bar', forward_static_call([FooFacade::class, 'foo'])); + $this->assertEquals('bar', call_user_func('FooFacade::class::foo')); + $this->assertEquals('bar', FooFacade::foo()); + } +} diff --git a/tests/Unit/Facades/Stubs/Foo.php b/tests/Unit/Facades/Stubs/Foo.php new file mode 100644 index 0000000..d5ba726 --- /dev/null +++ b/tests/Unit/Facades/Stubs/Foo.php @@ -0,0 +1,11 @@ + Date: Mon, 8 Sep 2025 14:21:30 +0100 Subject: [PATCH 06/43] Fix facade tests --- src/FacadeManager.php | 2 +- src/Facades/Facade.php | 2 +- src/Facades/Session.php | 2 - .../Bootstrappers/RegisterFacadesTest.php | 5 +-- tests/Unit/Exceptions/HandlerTest.php | 10 ++--- tests/Unit/Facades/ConfigTest.php | 4 +- tests/Unit/Facades/LogTest.php | 4 +- tests/Unit/Facades/MiddlewareAliasesTest.php | 4 +- tests/Unit/Facades/RouterTest.php | 4 +- tests/Unit/Facades/SessionTest.php | 4 +- tests/Unit/HelpersTest.php | 42 +++++++++---------- 11 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/FacadeManager.php b/src/FacadeManager.php index 97730a0..5892e7c 100644 --- a/src/FacadeManager.php +++ b/src/FacadeManager.php @@ -20,6 +20,6 @@ public static function setContainer(ContainerInterface $container) public static function create(string $accessor, $name, array $arguments = []) { - return call_user_func([static::$container->get($accessor), $name], $arguments); + return call_user_func([static::$container->get($accessor), $name], ...$arguments); } } diff --git a/src/Facades/Facade.php b/src/Facades/Facade.php index c585d93..913d8f6 100644 --- a/src/Facades/Facade.php +++ b/src/Facades/Facade.php @@ -17,5 +17,5 @@ public static function __instance() return FacadeManager::getContainer()->get(static::accessor()); } - abstract public static function accessor(): string; + abstract protected static function accessor(); } diff --git a/src/Facades/Session.php b/src/Facades/Session.php index b4b0526..4dfbfc1 100644 --- a/src/Facades/Session.php +++ b/src/Facades/Session.php @@ -2,8 +2,6 @@ namespace Rareloop\Lumberjack\Facades; -use Blast\Facades\AbstractFacade; - class Session extends Facade { protected static function accessor() diff --git a/tests/Unit/Bootstrappers/RegisterFacadesTest.php b/tests/Unit/Bootstrappers/RegisterFacadesTest.php index 71653c9..871442f 100644 --- a/tests/Unit/Bootstrappers/RegisterFacadesTest.php +++ b/tests/Unit/Bootstrappers/RegisterFacadesTest.php @@ -2,11 +2,10 @@ namespace Rareloop\Lumberjack\Test\Bootstrappers; -use Blast\Facades\FacadeFactory; -use Mockery; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Bootstrappers\RegisterFacades; +use Rareloop\Lumberjack\FacadeManager; class RegisterFacadesTest extends TestCase { @@ -18,6 +17,6 @@ public function boots_all_registered_providers() $registerFacadesBootstrapper = new RegisterFacades; $registerFacadesBootstrapper->bootstrap($app); - $this->assertSame($app, FacadeFactory::getContainer()); + $this->assertSame($app, FacadeManager::getContainer()); } } diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index 21889d7..113f5e0 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -2,7 +2,6 @@ namespace Rareloop\Lumberjack\Test\Exceptions; -use Blast\Facades\FacadeFactory; use Mockery; use Monolog\Logger; use PHPUnit\Framework\TestCase; @@ -11,6 +10,7 @@ use Rareloop\Lumberjack\Exceptions\Handler; use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\ServerRequest; +use Rareloop\Lumberjack\FacadeManager; class HandlerTest extends TestCase { @@ -52,7 +52,7 @@ public function blacklisted_exception_types_will_not_be_logged() public function render_should_return_an_html_response_when_debug_is_enabled() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config; $config->set('app.debug', true); $app->bind('config', $config); @@ -69,7 +69,7 @@ public function render_should_return_an_html_response_when_debug_is_enabled() public function render_should_return_an_html_response_when_debug_is_disabled() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config; $config->set('app.debug', false); $app->bind('config', $config); @@ -86,7 +86,7 @@ public function render_should_return_an_html_response_when_debug_is_disabled() public function render_should_include_stack_trace_when_debug_is_enabled() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config; $config->set('app.debug', true); $app->bind('config', $config); @@ -103,7 +103,7 @@ public function render_should_include_stack_trace_when_debug_is_enabled() public function render_should_not_include_stack_trace_when_debug_is_disabled() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config; $config->set('app.debug', false); $app->bind('config', $config); diff --git a/tests/Unit/Facades/ConfigTest.php b/tests/Unit/Facades/ConfigTest.php index fef46bd..bbcab55 100644 --- a/tests/Unit/Facades/ConfigTest.php +++ b/tests/Unit/Facades/ConfigTest.php @@ -2,10 +2,10 @@ namespace Rareloop\Lumberjack\Test\Facades; -use Blast\Facades\FacadeFactory; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; +use Rareloop\Lumberjack\FacadeManager; use Rareloop\Lumberjack\Facades\Config as ConfigFacade; class ConfigTest extends TestCase @@ -14,7 +14,7 @@ class ConfigTest extends TestCase public function test_facade() { $app = new Application(); - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config(); $config->set('app.environment', 'production'); diff --git a/tests/Unit/Facades/LogTest.php b/tests/Unit/Facades/LogTest.php index 4ae6019..4c42400 100644 --- a/tests/Unit/Facades/LogTest.php +++ b/tests/Unit/Facades/LogTest.php @@ -2,10 +2,10 @@ namespace Rareloop\Lumberjack\Test\Facades; -use Blast\Facades\FacadeFactory; use Monolog\Logger; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeManager; use Rareloop\Lumberjack\Facades\Log as LogFacade; class LogTest extends TestCase @@ -14,7 +14,7 @@ class LogTest extends TestCase public function test_facade() { $app = new Application(); - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $logger = new Logger('app'); $app->bind('logger', $logger); diff --git a/tests/Unit/Facades/MiddlewareAliasesTest.php b/tests/Unit/Facades/MiddlewareAliasesTest.php index 5cf4da6..04fdf64 100644 --- a/tests/Unit/Facades/MiddlewareAliasesTest.php +++ b/tests/Unit/Facades/MiddlewareAliasesTest.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Test\Facades; -use Blast\Facades\FacadeFactory; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeManager; use Rareloop\Lumberjack\Facades\MiddlewareAliases; use Rareloop\Lumberjack\Http\MiddlewareAliasStore; @@ -14,7 +14,7 @@ class MiddlewareAliasesTest extends TestCase public function test_facade() { $app = new Application(); - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new MiddlewareAliasStore(); $app->bind('middleware-alias-store', $store); diff --git a/tests/Unit/Facades/RouterTest.php b/tests/Unit/Facades/RouterTest.php index 71ee82a..ec92e5c 100644 --- a/tests/Unit/Facades/RouterTest.php +++ b/tests/Unit/Facades/RouterTest.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Test\Facades; -use Blast\Facades\FacadeFactory; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeManager; use Rareloop\Lumberjack\Facades\Router as RouterFacade; use Rareloop\Lumberjack\Http\Router; @@ -14,7 +14,7 @@ class RouterTest extends TestCase public function test_facade() { $app = new Application(); - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $router = new Router(); $app->bind('router', $router); diff --git a/tests/Unit/Facades/SessionTest.php b/tests/Unit/Facades/SessionTest.php index af7103e..1b59eb1 100644 --- a/tests/Unit/Facades/SessionTest.php +++ b/tests/Unit/Facades/SessionTest.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Test\Facades; -use Blast\Facades\FacadeFactory; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeManager; use Rareloop\Lumberjack\Facades\Session; use Rareloop\Lumberjack\Session\SessionManager; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; @@ -15,7 +15,7 @@ class SessionTest extends TestCase public function test_facade() { $app = new Application(); - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index a4d1c7c..129c43d 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -6,7 +6,6 @@ use Rareloop\Router\Router; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Config; -use Blast\Facades\FacadeFactory; use Rareloop\Lumberjack\Helpers; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Facades\Session; @@ -18,6 +17,7 @@ use Rareloop\Lumberjack\Http\Responses\TimberResponse; use Rareloop\Lumberjack\Http\Responses\RedirectResponse; use Monolog\Logger; +use Rareloop\Lumberjack\FacadeManager; /** * @runTestsInSeparateProcesses @@ -48,7 +48,7 @@ public function can_resolve_something_from_the_container() public function can_retrieve_a_config_value() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config(); $config->set('app.environment', 'production'); @@ -61,7 +61,7 @@ public function can_retrieve_a_config_value() public function can_retrieve_a_default_when_no_config_value_is_set() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config(); $app->bind('config', $config); @@ -73,7 +73,7 @@ public function can_retrieve_a_default_when_no_config_value_is_set() public function can_set_a_config_value_when_array_passed_to_config_helper() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $config = new Config(); $app->bind('config', $config); @@ -139,10 +139,9 @@ public function can_get_a_timber_response_with_specific_headers() public function can_get_a_url_for_a_named_route() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $router = new Router; - $router->get('test/route', function () { - })->name('test.route'); + $router->get('test/route', function () {})->name('test.route'); $app->bind('router', $router); $url = Helpers::route('test.route'); @@ -154,10 +153,9 @@ public function can_get_a_url_for_a_named_route() public function can_get_a_url_for_a_named_route_with_params() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $router = new Router; - $router->get('test/{name}', function ($name) { - })->name('test.route'); + $router->get('test/{name}', function ($name) {})->name('test.route'); $app->bind('router', $router); $url = Helpers::route('test.route', [ @@ -216,7 +214,7 @@ public function can_report_an_exception() public function can_access_an_item_in_the_session_by_key() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -230,7 +228,7 @@ public function can_access_an_item_in_the_session_by_key() public function can_access_an_item_in_the_session_by_key_with_default() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -242,7 +240,7 @@ public function can_access_an_item_in_the_session_by_key_with_default() public function can_add_an_item_in_the_session() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -256,7 +254,7 @@ public function can_add_an_item_in_the_session() public function can_add_multiple_items_to_the_session() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -271,7 +269,7 @@ public function can_add_multiple_items_to_the_session() public function can_resolve_the_session_manager() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -283,7 +281,7 @@ public function can_resolve_the_session_manager() public function can_redirect_back() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); $store->setPreviousUrl('http://domain.com/previous/url'); @@ -298,7 +296,7 @@ public function can_redirect_back() public function can_get_server_request() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $request = new ServerRequest([], [], '/test/123', 'GET'); $app->bind('request', $request); @@ -310,7 +308,7 @@ public function can_get_server_request() public function can_get_logger() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $logger = new Logger('app'); $app->bind('logger', $logger); @@ -325,7 +323,7 @@ public function can_get_logger() public function can_write_debug_log() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $logger = \Mockery::mock(Logger::class)->makePartial(); $logger->shouldReceive('debug')->with('Example message', [])->once(); @@ -339,7 +337,7 @@ public function can_write_debug_log() public function can_write_debug_log_with_context() { $app = new Application; - FacadeFactory::setContainer($app); + FacadeManager::setContainer($app); $logger = \Mockery::mock(Logger::class)->makePartial(); $logger->shouldReceive('debug')->with('Example message', [ @@ -373,7 +371,5 @@ class TestExceptionHandler extends Handler class RequiresConstructorParams { - public function __construct(public $param1, public $param2) - { - } + public function __construct(public $param1, public $param2) {} } From 98b9d289d7fab5037fe665830e25a89f503044a7 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:19:43 +0100 Subject: [PATCH 07/43] Replace `Logger::[level]` calls with `Level::[level]` --- src/Providers/LogServiceProvider.php | 5 +++-- tests/Unit/Providers/LogServiceProviderTest.php | 17 +++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Providers/LogServiceProvider.php b/src/Providers/LogServiceProvider.php index b04a0b3..a6cf812 100644 --- a/src/Providers/LogServiceProvider.php +++ b/src/Providers/LogServiceProvider.php @@ -7,6 +7,7 @@ use Monolog\Handler\StreamHandler; use Monolog\Formatter\LineFormatter; use Monolog\Handler\ErrorLogHandler; +use Monolog\Level; class LogServiceProvider extends ServiceProvider { @@ -44,9 +45,9 @@ private function shouldUseErrorLogHandler() return $config && $config->get('app.logs.path') === false && $config->get('app.logs.enabled') === true; } - private function getLogLevel() + private function getLogLevel(): Level { - $logLevel = Logger::DEBUG; + $logLevel = Level::Debug; if ($this->app->has('config')) { $logLevel = $this->app->get('config')->get('app.logs.level', $logLevel); diff --git a/tests/Unit/Providers/LogServiceProviderTest.php b/tests/Unit/Providers/LogServiceProviderTest.php index 1b16036..27b60f2 100644 --- a/tests/Unit/Providers/LogServiceProviderTest.php +++ b/tests/Unit/Providers/LogServiceProviderTest.php @@ -4,6 +4,7 @@ use Brain\Monkey\Functions; use Monolog\Handler\ErrorLogHandler; +use Monolog\Level; use Monolog\Logger; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -22,7 +23,7 @@ public function log_object_is_always_registered() { Functions\expect('is_admin')->once()->andReturn(false); - $app = new Application(__DIR__.'/../'); + $app = new Application(__DIR__ . '/../'); $lumberjack = new Lumberjack($app); $lumberjack->bootstrap(); @@ -37,7 +38,7 @@ public function log_object_is_always_registered() * @codingStandardsIgnoreLine */ function default_handler_is_in_memory_stream() { - $app = new Application(__DIR__.'/../'); + $app = new Application(__DIR__ . '/../'); $config = new Config; $app->bind('config', $config); @@ -52,7 +53,7 @@ function default_handler_is_in_memory_stream() /** @test */ public function default_log_warning_level_is_debug() { - $app = new Application(__DIR__.'/../'); + $app = new Application(__DIR__ . '/../'); $config = new Config; $app->bind('config', $config); @@ -61,13 +62,13 @@ public function default_log_warning_level_is_debug() RegisterProviders::class, ]); - $this->assertSame(Logger::DEBUG, $app->get('logger')->getHandlers()[0]->getLevel()); + $this->assertSame(Level::Debug, $app->get('logger')->getHandlers()[0]->getLevel()); } /** @test */ public function stream_is_used_when_path_is_set_but_logging_is_disabled() { - $app = new Application(__DIR__.'/../'); + $app = new Application(__DIR__ . '/../'); $config = new Config; $config->set('app.logs.enabled', false); @@ -84,17 +85,17 @@ public function stream_is_used_when_path_is_set_but_logging_is_disabled() /** @test */ public function log_warning_level_can_be_set_in_config() { - $app = new Application(__DIR__.'/../'); + $app = new Application(__DIR__ . '/../'); $config = new Config; - $config->set('app.logs.level', Logger::ERROR); + $config->set('app.logs.level', Level::Error); $app->bind('config', $config); $app->bootstrapWith([ RegisterProviders::class, ]); - $this->assertSame(Logger::ERROR, $app->get('logger')->getHandlers()[0]->getLevel()); + $this->assertSame(Level::Error, $app->get('logger')->getHandlers()[0]->getLevel()); } /** @test */ From 4d43027a6594e156363ebbb3a271df0938f834fb Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:22:50 +0100 Subject: [PATCH 08/43] Update mindplay/middleman ->dispatch to ->handle --- src/Providers/WordPressControllersServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Providers/WordPressControllersServiceProvider.php b/src/Providers/WordPressControllersServiceProvider.php index 07766b1..3b3dd2e 100644 --- a/src/Providers/WordPressControllersServiceProvider.php +++ b/src/Providers/WordPressControllersServiceProvider.php @@ -90,7 +90,7 @@ function ($request) use ($controller, $methodName) { ]; $dispatcher = $this->createDispatcher($middlewares); - return $dispatcher->dispatch($request); + return $dispatcher->handle($request); } private function createDispatcher(array $middlewares): Dispatcher From 106232fd95e545cf8e3db954bcf9a6390288c5d3 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:25:28 +0100 Subject: [PATCH 09/43] Remove support for callable route destinations (removed in Rareloop router upgrade) --- tests/Unit/Http/RouterTest.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/Unit/Http/RouterTest.php b/tests/Unit/Http/RouterTest.php index 1422145..dd00b06 100644 --- a/tests/Unit/Http/RouterTest.php +++ b/tests/Unit/Http/RouterTest.php @@ -36,17 +36,6 @@ public function controller_does_not_have_namespace_added_when_it_already_exists( $this->assertSame(RouterTestController::class . '@test', $route->getActionName()); } - /** @test */ - public function controller_does_not_have_namespace_added_when_it_is_callable() - { - $router = new Router; - $controller = new RouterTestController; - - $route = $router->get('/test/123', $controller->test(...)); - - $this->assertSame(RouterTestController::class . '@test', $route->getActionName()); - } - /** @test */ public function controller_does_not_have_namespace_added_when_it_is_closure() { From 37224e14303dafcc3d75916c33fdc3641bbdf950 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:34:31 +0100 Subject: [PATCH 10/43] Resolve `trigger_error` deprecations --- src/Post.php | 3 ++- src/ScopedQueryBuilder.php | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Post.php b/src/Post.php index 62fc2d3..2dada06 100644 --- a/src/Post.php +++ b/src/Post.php @@ -2,6 +2,7 @@ namespace Rareloop\Lumberjack; +use Error; use Rareloop\Lumberjack\Exceptions\PostTypeRegistrationException; use Rareloop\Lumberjack\ScopedQueryBuilder; use Spatie\Macroable\Macroable; @@ -47,7 +48,7 @@ public static function __callStatic($name, $arguments) return call_user_func_array([$builder, $name], $arguments); } - trigger_error('Call to undefined method ' . self::class . '::' . $name . '()', E_USER_ERROR); + throw new Error('Call to undefined method ' . self::class . '::' . $name . '()'); } /** diff --git a/src/ScopedQueryBuilder.php b/src/ScopedQueryBuilder.php index 70ba909..b28a777 100644 --- a/src/ScopedQueryBuilder.php +++ b/src/ScopedQueryBuilder.php @@ -2,6 +2,7 @@ namespace Rareloop\Lumberjack; +use Error; use Rareloop\Lumberjack\Contracts\QueryBuilder as QueryBuilderContract; use Rareloop\Lumberjack\Exceptions\CannotRedeclarePostClassOnQueryException; use Rareloop\Lumberjack\Exceptions\CannotRedeclarePostTypeOnQueryException; @@ -40,10 +41,7 @@ public function __call($name, $arguments) $publicMethods = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))->map(fn($method) => $method->getName())->toArray(); if (!in_array($scopeFunctionName, $publicMethods)) { - trigger_error( - 'Call to undefined method ' . $this->postClass . '::' . $scopeFunctionName . '()', - E_USER_ERROR - ); + throw new Error('Call to undefined method ' . $this->postClass . '::' . $scopeFunctionName . '()'); } array_unshift($arguments, $this); From dbc3874f657eef8afbd0d7f6b3b08656b01f1e19 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:38:43 +0100 Subject: [PATCH 11/43] Bump mmeyer2k/dcrypt --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5604969..f8a4429 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", "rareloop/psr7-server-request-extension": "dev-master as 3.0", - "mmeyer2k/dcrypt": "^8.3.1", + "mmeyer2k/dcrypt": "^9", "spatie/macroable": "^1.0.1", "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", From cc2aa78062d2d85ea554168a1136428ac9629d0c Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 14:56:06 +0100 Subject: [PATCH 12/43] Bump bugfix release of mikey179/vfsstream - fixes test failure --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f8a4429..c2ec483 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "brain/monkey": "^2.6.1", "squizlabs/php_codesniffer": "^3.7.2", "php-mock/php-mock": "^2.6.2", - "mikey179/vfsstream": "1.6.11", + "mikey179/vfsstream": "^1.6.12", "dms/phpunit-arraysubset-asserts": "^0.3.1", "rector/rector": "^2.1" }, From 5f0c4c6d6b6f389f162c810dea36d8c8fd39760b Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Wed, 1 Oct 2025 16:56:26 +0100 Subject: [PATCH 13/43] Rework encryption --- composer.json | 4 +- src/Contracts/Encrypter.php | 10 --- src/Encrypter.php | 23 ------ src/Providers/EncryptionServiceProvider.php | 7 +- src/Session/EncryptedStore.php | 2 +- src/Session/SessionManager.php | 4 +- src/Session/Store.php | 4 +- tests/Unit/EncrypterTest.php | 81 ------------------- .../EncryptionServiceProviderTest.php | 6 +- tests/Unit/Session/EncryptedStoreTest.php | 18 ++--- tests/Unit/Session/SessionManagerTest.php | 14 ++-- tests/Unit/Session/StoreTest.php | 6 +- 12 files changed, 28 insertions(+), 151 deletions(-) delete mode 100644 src/Contracts/Encrypter.php delete mode 100644 src/Encrypter.php delete mode 100644 tests/Unit/EncrypterTest.php diff --git a/composer.json b/composer.json index c2ec483..8a2f668 100644 --- a/composer.json +++ b/composer.json @@ -16,11 +16,11 @@ "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", "rareloop/psr7-server-request-extension": "dev-master as 3.0", - "mmeyer2k/dcrypt": "^9", "spatie/macroable": "^1.0.1", "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", - "symfony/var-dumper": "^5.0||^6.3.6" + "symfony/var-dumper": "^5.0||^6.3.6", + "illuminate/encryption": "^12.32" }, "require-dev": { "phpunit/phpunit": "^9.6.13", diff --git a/src/Contracts/Encrypter.php b/src/Contracts/Encrypter.php deleted file mode 100644 index b6a00c1..0000000 --- a/src/Contracts/Encrypter.php +++ /dev/null @@ -1,10 +0,0 @@ -key); - } - - public function decrypt($data) - { - return @unserialize(AesCbc::decrypt($data, $this->key)); - } -} diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php index ec762cc..1f462e1 100644 --- a/src/Providers/EncryptionServiceProvider.php +++ b/src/Providers/EncryptionServiceProvider.php @@ -2,11 +2,8 @@ namespace Rareloop\Lumberjack\Providers; -use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; -use Rareloop\Lumberjack\Encrypter; -use Rareloop\Lumberjack\Facades\Config; -use Rareloop\Lumberjack\Session\SessionManager; +use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; +use Illuminate\Encryption\Encrypter; class EncryptionServiceProvider extends ServiceProvider { diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 3992cf4..6fccda0 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -3,8 +3,8 @@ namespace Rareloop\Lumberjack\Session; use Exception; +use Illuminate\Contracts\Encryption\Encrypter; use SessionHandlerInterface; -use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; class EncryptedStore extends Store diff --git a/src/Session/SessionManager.php b/src/Session/SessionManager.php index 0926a03..31bad1a 100644 --- a/src/Session/SessionManager.php +++ b/src/Session/SessionManager.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Session; +use Illuminate\Contracts\Encryption\Encrypter; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; -use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; use Rareloop\Lumberjack\Manager; class SessionManager extends Manager @@ -53,7 +53,7 @@ protected function buildSession($handler) $sessionId = ($_COOKIE[$this->name] ?? null); if ($this->config->get('session.encrypt')) { - $encrypter = $this->app->get(EncrypterContract::class); + $encrypter = $this->app->get(Encrypter::class); return new EncryptedStore($this->name, $handler, $encrypter, $sessionId); } diff --git a/src/Session/Store.php b/src/Session/Store.php index 62cbbbc..9e73bf9 100644 --- a/src/Session/Store.php +++ b/src/Session/Store.php @@ -36,7 +36,7 @@ protected function loadSession() protected function readFromHandler() { $data = $this->handler->read($this->id); - $data = @unserialize($this->prepareForUnserialize($data)); + $data = $this->prepareForUnserialize($data); if ($data !== false && !is_null($data) && is_array($data)) { return $data; @@ -49,7 +49,7 @@ public function save() { $this->ageFlashData(); - $this->handler->write($this->id, $this->prepareForStorage(@serialize($this->attributes))); + $this->handler->write($this->id, $this->prepareForStorage($this->attributes)); return $this; } diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php deleted file mode 100644 index df52253..0000000 --- a/tests/Unit/EncrypterTest.php +++ /dev/null @@ -1,81 +0,0 @@ -shouldReceive('encrypt')->withArgs(function ($data, $key) { - if ($key !== 'secret-key') { - return false; - } - - if ($data !== @serialize('test-string')) { - return false; - } - - return true; - })->once(); - - $encrypter = new Encrypter($key); - $encrypter->encrypt('test-string'); - } - - /** @test */ - public function can_decrypt_data() - { - $key = 'secret-key'; - $dcrypt = Mockery::mock('alias:' . AesCbc::class); - $dcrypt->shouldReceive('decrypt')->with('test-string', $key)->once(); - - $encrypter = new Encrypter($key); - $encrypter->decrypt('test-string'); - } - - /** @test */ - public function can_process_strings() - { - $payload = 'test-string'; - $encrypter = new Encrypter('secret-key'); - - $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); - } - - /** @test */ - public function can_process_arrays() - { - $payload = ['foo' => 'bar']; - $encrypter = new Encrypter('secret-key'); - - $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); - } - - /** @test */ - public function can_process_objects() - { - $payload = new \stdClass; - $payload->foo = 'bar'; - $encrypter = new Encrypter('secret-key'); - - $output = $encrypter->decrypt($encrypter->encrypt($payload)); - - $this->assertInstanceOf(\stdClass::class, $output); - $this->assertSame('bar', $output->foo); - } -} diff --git a/tests/Unit/Providers/EncryptionServiceProviderTest.php b/tests/Unit/Providers/EncryptionServiceProviderTest.php index 89ccfdb..001b072 100644 --- a/tests/Unit/Providers/EncryptionServiceProviderTest.php +++ b/tests/Unit/Providers/EncryptionServiceProviderTest.php @@ -2,11 +2,11 @@ namespace Rareloop\Lumberjack\Test\Providers; +use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; +use Illuminate\Encryption\Encrypter; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; -use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; -use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Providers\EncryptionServiceProvider; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; @@ -18,7 +18,7 @@ class EncryptionServiceProviderTest extends TestCase public function encryptor_is_registered_in_container_when_a_config_key_is_present() { $config = new Config; - $config->set('app.key', 'encryption-key'); + $config->set('app.key', Encrypter::generateKey('aes-128-cbc')); $app = new Application(); $app->bind('config', $config); diff --git a/tests/Unit/Session/EncryptedStoreTest.php b/tests/Unit/Session/EncryptedStoreTest.php index 8cd64c4..d9ccb65 100644 --- a/tests/Unit/Session/EncryptedStoreTest.php +++ b/tests/Unit/Session/EncryptedStoreTest.php @@ -3,12 +3,11 @@ namespace Rareloop\Lumberjack\Test; use Dcrypt\AesCbc; +use Illuminate\Encryption\Encrypter; use Mockery; use PHPUnit\Framework\TestCase; -use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Session\EncryptedStore; -use Rareloop\Lumberjack\Session\Store; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; class EncryptedStoreTest extends TestCase @@ -19,10 +18,8 @@ class EncryptedStoreTest extends TestCase public function data_is_encrypted_before_it_is_saved() { $serializedString = @serialize(['foo' => 'bar']); - $encrypter = Mockery::mock(Encrypter::class . '[encrypt]', ['encryption-key']); - $encrypter->shouldReceive('encrypt')->withArgs(function ($string) { - $array = @unserialize($string); - + $encrypter = Mockery::mock(Encrypter::class); + $encrypter->shouldReceive('encrypt')->withArgs(function ($array) { return $array['foo'] === 'bar'; })->once(); @@ -36,17 +33,16 @@ public function data_is_encrypted_before_it_is_saved() /** @test */ public function data_is_decrypted_before_it_is_loaded() { - $encryptionKey = 'encryption-key'; - + $encrypter = new Encrypter(Encrypter::generateKey('aes-128-cbc')); // Create the string that would have been stored by an encrypted store // Serialize once for the Encrypter and once for the Encrypted store - $encryptedString = AesCbc::encrypt(@serialize(@serialize(['foo' => 'bar'])), $encryptionKey); + $encryptedString = $encrypter->encrypt(['foo' => 'bar']); // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); $handler->shouldReceive('read')->andReturn($encryptedString); - $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id'); + $store = new EncryptedStore('session-name', $handler, $encrypter, 'session-id'); $store->start(); $this->assertSame('bar', $store->get('foo')); @@ -58,7 +54,7 @@ public function data_is_decrypted_before_it_is_loaded() */ public function unexpected_session_data_is_handled_gracefully($previousSessionValue) { - $encryptionKey = 'encryption-key'; + $encryptionKey = Encrypter::generateKey('aes-128-cbc'); // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index f11a012..c5ba3e0 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -3,17 +3,17 @@ namespace Rareloop\Lumberjack\Test; use Mockery; +use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; -use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; -use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; -use Rareloop\Lumberjack\Encrypter; +use Illuminate\Encryption\Encrypter; +use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\Session\Store; use Rareloop\Lumberjack\Session\EncryptedStore; -use Rareloop\Lumberjack\Session\FileSessionHandler; use Rareloop\Lumberjack\Session\SessionManager; -use Rareloop\Lumberjack\Session\Store; +use Rareloop\Lumberjack\Session\FileSessionHandler; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; -use org\bovigo\vfs\vfsStream; +use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; class SessionManagerTest extends TestCase { @@ -92,7 +92,7 @@ public function can_create_an_unencrypted_store() public function can_create_an_encrypted_store() { $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); - $app->bind(EncrypterContract::class, new Encrypter('encryption-key')); + $app->bind(EncrypterContract::class, new Encrypter(Encrypter::generateKey('aes-128-cbc'))); $manager = new SessionManager($app); diff --git a/tests/Unit/Session/StoreTest.php b/tests/Unit/Session/StoreTest.php index b167f8a..e399091 100644 --- a/tests/Unit/Session/StoreTest.php +++ b/tests/Unit/Session/StoreTest.php @@ -205,7 +205,7 @@ public function can_flush_all_values() public function starting_a_session_loads_data_from_handler() { $handler = Mockery::mock(NullSessionHandler::class . '[read]'); - $handler->shouldReceive('read')->once()->with('session-id')->andReturn(serialize(['foo' => 'bar'])); + $handler->shouldReceive('read')->once()->with('session-id')->andReturn(['foo' => 'bar']); $store = new Store('session-name', $handler, 'session-id'); @@ -219,9 +219,7 @@ public function saving_a_session_writes_data_to_handler() { $handler = Mockery::mock(NullSessionHandler::class . '[write]'); $handler->shouldReceive('write')->once()->with('session-id', Mockery::on(function ($argument) { - $array = @unserialize($argument); - - return isset($array['foo']) && $array['foo'] === 'bar'; + return isset($argument['foo']) && $argument['foo'] === 'bar'; })); $store = new Store('session-name', $handler, 'session-id'); From 9f304688749ed59c95cf1f513646734603597cf0 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 6 Oct 2025 14:30:47 +0100 Subject: [PATCH 14/43] Re-support php >= 8.1 --- .github/workflows/ci.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dd86fc..b4598b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php_version: [8.4] + php_version: [8.1, 8.2, 8.3, 8.4] composer_flags: ["", "--prefer-lowest"] steps: diff --git a/composer.json b/composer.json index 8a2f668..80ec94b 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code", "license": "MIT", "require": { - "php": ">=8.4", + "php": ">=8.1", "php-di/php-di": "^7", "rareloop/router": "dev-feature/upgrade-dependencies as 6.0.2", "psr/container": "^2", From 300e934dd5625925952ab8a455c96d4bac95befd Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 6 Oct 2025 14:35:48 +0100 Subject: [PATCH 15/43] Refactor Facade -> AbstractFacade --- src/Facades/{Facade.php => AbstractFacade.php} | 3 +-- src/Facades/Config.php | 2 +- src/Facades/Log.php | 2 +- src/Facades/MiddlewareAliases.php | 2 +- src/Facades/Router.php | 2 +- src/Facades/Session.php | 2 +- tests/Unit/Facades/Stubs/FooFacade.php | 4 ++-- 7 files changed, 8 insertions(+), 9 deletions(-) rename src/Facades/{Facade.php => AbstractFacade.php} (92%) diff --git a/src/Facades/Facade.php b/src/Facades/AbstractFacade.php similarity index 92% rename from src/Facades/Facade.php rename to src/Facades/AbstractFacade.php index 913d8f6..22697fa 100644 --- a/src/Facades/Facade.php +++ b/src/Facades/AbstractFacade.php @@ -2,10 +2,9 @@ namespace Rareloop\Lumberjack\Facades; -use Mockery; use Rareloop\Lumberjack\FacadeManager; -abstract class Facade +abstract class AbstractFacade { public static function __callStatic($name, array $arguments = []) { diff --git a/src/Facades/Config.php b/src/Facades/Config.php index 9c4f604..bfd4a26 100644 --- a/src/Facades/Config.php +++ b/src/Facades/Config.php @@ -2,7 +2,7 @@ namespace Rareloop\Lumberjack\Facades; -class Config extends Facade +class Config extends AbstractFacade { protected static function accessor() { diff --git a/src/Facades/Log.php b/src/Facades/Log.php index 164227c..d7bbf8b 100644 --- a/src/Facades/Log.php +++ b/src/Facades/Log.php @@ -2,7 +2,7 @@ namespace Rareloop\Lumberjack\Facades; -class Log extends Facade +class Log extends AbstractFacade { protected static function accessor() { diff --git a/src/Facades/MiddlewareAliases.php b/src/Facades/MiddlewareAliases.php index 3b4f703..dece0e4 100644 --- a/src/Facades/MiddlewareAliases.php +++ b/src/Facades/MiddlewareAliases.php @@ -3,7 +3,7 @@ namespace Rareloop\Lumberjack\Facades; -class MiddlewareAliases extends Facade +class MiddlewareAliases extends AbstractFacade { protected static function accessor() { diff --git a/src/Facades/Router.php b/src/Facades/Router.php index 0b9e12c..85fa666 100644 --- a/src/Facades/Router.php +++ b/src/Facades/Router.php @@ -2,7 +2,7 @@ namespace Rareloop\Lumberjack\Facades; -class Router extends Facade +class Router extends AbstractFacade { protected static function accessor() { diff --git a/src/Facades/Session.php b/src/Facades/Session.php index 4dfbfc1..2b28d9f 100644 --- a/src/Facades/Session.php +++ b/src/Facades/Session.php @@ -2,7 +2,7 @@ namespace Rareloop\Lumberjack\Facades; -class Session extends Facade +class Session extends AbstractFacade { protected static function accessor() { diff --git a/tests/Unit/Facades/Stubs/FooFacade.php b/tests/Unit/Facades/Stubs/FooFacade.php index c9081ec..6f460c4 100644 --- a/tests/Unit/Facades/Stubs/FooFacade.php +++ b/tests/Unit/Facades/Stubs/FooFacade.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Test\Unit\Facades\Stubs; -use Rareloop\Lumberjack\Facades\Facade; +use Rareloop\Lumberjack\Facades\AbstractFacade; -class FooFacade extends Facade +class FooFacade extends AbstractFacade { public static function accessor(): string { From ea7735023aaee38c4c1f11e5205bc83bf0844dde Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 6 Oct 2025 15:12:59 +0100 Subject: [PATCH 16/43] Switch to Spatie/ignition --- .gitignore | 1 + composer.json | 4 ++-- src/Exceptions/Handler.php | 22 +++++++++++++------ .../RegisterExceptionHandlerTest.php | 1 - tests/Unit/PostQueryBuilderTest.php | 11 ++-------- tests/Unit/ScopedQueryBuilderTest.php | 13 +++-------- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 188e4c4..3882089 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ vendor composer.lock /tests/Unit/logs +.phpunit.result.cache diff --git a/composer.json b/composer.json index 80ec94b..b690964 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "psr/http-server-middleware": "^1.0.2", "timber/timber": "^2.3.0", "monolog/monolog": "^3.9", - "symfony/debug": "^4.4.44", "illuminate/collections": "^12", "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", @@ -20,7 +19,8 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "illuminate/encryption": "^12.32" + "illuminate/encryption": "^12.32", + "spatie/ignition": "^1.15" }, "require-dev": { "phpunit/phpunit": "^9.6.13", diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index e6b4067..99c794e 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -3,13 +3,12 @@ namespace Rareloop\Lumberjack\Exceptions; use Exception; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; +use Spatie\Ignition\Ignition; use Rareloop\Lumberjack\Application; +use Psr\Http\Message\ResponseInterface; use Rareloop\Lumberjack\Facades\Config; -use Symfony\Component\Debug\ExceptionHandler as SymfonyExceptionHandler; -use Symfony\Component\Debug\Exception\FlattenException; use Laminas\Diactoros\Response\HtmlResponse; +use Psr\Http\Message\ServerRequestInterface; class Handler implements HandlerInterface { @@ -36,11 +35,20 @@ public function report(Exception $e) public function render(ServerRequestInterface $request, Exception $e): ResponseInterface { - $e = FlattenException::create($e); + $isDebug = Config::get('app.debug', false) === true; + + $ignition = Ignition::make() + ->shouldDisplayException($isDebug) + ->runningInProductionEnvironment(!$isDebug) + ->register(); + + ob_start(); + + $ignition->handleException($e); - $handler = new SymfonyExceptionHandler(Config::get('app.debug', false)); + $html = ob_get_clean(); - return new HtmlResponse($handler->getHtml($e), $e->getStatusCode(), $e->getHeaders()); + return new HtmlResponse($html); } protected function shouldNotReport(Exception $e) diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php index 873c272..cf1c230 100644 --- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php +++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php @@ -14,7 +14,6 @@ use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\Responsable; -use Symfony\Component\Debug\Exception\FatalErrorException; use Laminas\Diactoros\Response; use Laminas\Diactoros\Response\TextResponse; use Laminas\Diactoros\ServerRequest; diff --git a/tests/Unit/PostQueryBuilderTest.php b/tests/Unit/PostQueryBuilderTest.php index 0ad0fea..a8af46b 100644 --- a/tests/Unit/PostQueryBuilderTest.php +++ b/tests/Unit/PostQueryBuilderTest.php @@ -47,19 +47,12 @@ public function can_create_a_builder_from_static_functions() /** * @test - * @runInSeparateProcess */ public function throw_error_on_missing_static_function() { - $errorThrown = false; + $this->expectException(Throwable::class); - try { - Post::missingStaticFunction(); - } catch (Throwable) { - $errorThrown = true; - } - - $this->assertTrue($errorThrown); + Post::missingStaticFunction(); } private function assertQueryBuilder($function, $params, $postType) diff --git a/tests/Unit/ScopedQueryBuilderTest.php b/tests/Unit/ScopedQueryBuilderTest.php index 9f3ae13..30afa52 100644 --- a/tests/Unit/ScopedQueryBuilderTest.php +++ b/tests/Unit/ScopedQueryBuilderTest.php @@ -105,20 +105,13 @@ public function can_pass_params_into_a_query_scope_on_post_object() /** * @test - * @runInSeparateProcess */ public function missing_query_scope_throws_an_error() { - $errorThrown = false; - - try { - $builder = new ScopedQueryBuilder(PostWithQueryScope::class); - $builder->nonExistentScope(); - } catch (Throwable) { - $errorThrown = true; - } + $this->expectException(Throwable::class); - $this->assertTrue($errorThrown); + $builder = new ScopedQueryBuilder(PostWithQueryScope::class); + $builder->nonExistentScope(); } /** @test */ From 9cf7e13283ff6bd13421fbd9682558eb6a4b4ca7 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Mon, 6 Oct 2025 15:14:01 +0100 Subject: [PATCH 17/43] Upgrade phpunit config --- phpunit.xml | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 62992c9..7157380 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,22 +1,13 @@ - - - - tests - - - - - src/ - - + + + + src/ + + + + + tests + + From 7bbeb1b66010adfa0d37413064e3904b7137e512 Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Tue, 7 Oct 2025 10:25:41 +0100 Subject: [PATCH 18/43] Remove php84 syntax from Application --- src/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application.php b/src/Application.php index fe368b4..19ed156 100644 --- a/src/Application.php +++ b/src/Application.php @@ -182,7 +182,7 @@ public function getProvider($provider) { $providerClass = is_string($provider) ? $provider : $provider::class; - return new Collection($this->loadedProviders)->first(fn($provider) => $provider::class === $providerClass); + return (new Collection($this->loadedProviders))->first(fn($provider) => $provider::class === $providerClass); } public function getLoadedProviders() From 27a4e6d03498decf317547387cad9dd2f59e4b5e Mon Sep 17 00:00:00 2001 From: Tom Mitchelmore Date: Tue, 7 Oct 2025 10:37:52 +0100 Subject: [PATCH 19/43] Implement SAPIEmitter and resolve old symfony error usage --- composer.json | 4 +++- src/Application.php | 3 ++- .../RegisterExceptionHandler.php | 22 +++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index b690964..c5efefc 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,9 @@ "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", "illuminate/encryption": "^12.32", - "spatie/ignition": "^1.15" + "spatie/ignition": "^1.15", + "laminas/laminas-httphandlerrunner": "^2.12", + "symfony/error-handler": "^7.3" }, "require-dev": { "phpunit/phpunit": "^9.6.13", diff --git a/src/Application.php b/src/Application.php index 19ed156..37a1b40 100644 --- a/src/Application.php +++ b/src/Application.php @@ -5,6 +5,7 @@ use Closure; use DI\Container; use Illuminate\Support\Collection; +use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use function Http\Response\send; @@ -280,7 +281,7 @@ public function shutdown(?ResponseInterface $response = null) // If we're handling a WordPressController response at this point then WordPress will already have // sent headers as it happens earlier in the lifecycle. For this scenario we need to do a bit more // work to make sure that duplicate headers are not sent back. - send($this->removeSentHeadersAndMoveIntoResponse($response)); + (new SapiEmitter())->emit($this->removeSentHeadersAndMoveIntoResponse($response)); } die(); diff --git a/src/Bootstrappers/RegisterExceptionHandler.php b/src/Bootstrappers/RegisterExceptionHandler.php index 79424c0..5ca7ba3 100644 --- a/src/Bootstrappers/RegisterExceptionHandler.php +++ b/src/Bootstrappers/RegisterExceptionHandler.php @@ -2,16 +2,18 @@ namespace Rareloop\Lumberjack\Bootstrappers; -use DI\NotFoundException; use Error; use ErrorException; -use Psr\Http\Message\ResponseInterface; +use DI\NotFoundException; +use function Http\Response\send; +use Rareloop\Router\Responsable; use Rareloop\Lumberjack\Application; +use Psr\Http\Message\ResponseInterface; +use Laminas\Diactoros\ServerRequestFactory; +use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; -use Rareloop\Router\Responsable; use Symfony\Component\Debug\Exception\FatalErrorException; -use Laminas\Diactoros\ServerRequestFactory; -use function Http\Response\send; +use Symfony\Component\ErrorHandler\Error\FatalError; /** * Determine whether or not we should be in debug mode or not @@ -70,7 +72,7 @@ public function handleException($e) public function send(ResponseInterface $response) { - @send($response); + @(new SapiEmitter())->emit($response); } protected function getExceptionHandler(): HandlerInterface @@ -122,16 +124,14 @@ public function handleShutdown() * * @param array $error * @param int|null $traceOffset - * @return \Symfony\Component\Debug\Exception\FatalErrorException + * @return \Symfony\Component\ErrorHandler\Error\FatalError */ protected function fatalExceptionFromError(array $error, $traceOffset = null) { - return new FatalErrorException( + return new FatalError( $error['message'], - $error['type'], 0, - $error['file'], - $error['line'], + $error, $traceOffset ); } From e0d6f5598fc4122252474002689030ba3210710e Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Wed, 8 Oct 2025 15:57:15 +0100 Subject: [PATCH 20/43] Roll collections version back to 10 to support PHP 8.1 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index c5efefc..7ad9506 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "psr/http-server-middleware": "^1.0.2", "timber/timber": "^2.3.0", "monolog/monolog": "^3.9", - "illuminate/collections": "^12", + "illuminate/collections": "^10", "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", "rareloop/psr7-server-request-extension": "dev-master as 3.0", @@ -60,4 +60,4 @@ "url": "https://github.com/tommitchelmore/psr7-server-request-extension" } ] -} +} \ No newline at end of file From 9fabb30cc55f22bde6929e94e29fee8834626b39 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Wed, 8 Oct 2025 16:01:39 +0100 Subject: [PATCH 21/43] Ensure illuminate/encryption is also on v10 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7ad9506..b194152 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "illuminate/encryption": "^12.32", + "illuminate/encryption": "^10", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^7.3" From 9181dedccdd847e904d9a1e18dcc2243a565fd6c Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 16:12:37 +0100 Subject: [PATCH 22/43] Revert Rector changes --- composer.json | 7 +- phpunit.xml | 31 ++++--- rector.php | 16 ---- src/Application.php | 16 ++-- .../RegisterExceptionHandler.php | 6 +- src/Bootstrappers/RegisterFacades.php | 4 +- src/Contracts/Encrypter.php | 10 +++ src/Encrypter.php | 26 ++++++ src/Exceptions/Handler.php | 2 +- src/{FacadeManager.php => FacadeFactory.php} | 2 +- src/Facades/AbstractFacade.php | 6 +- src/Http/Lumberjack.php | 5 +- src/Http/Middleware/PasswordProtected.php | 2 +- src/Http/MiddlewareAliasStore.php | 10 +-- src/Http/Router.php | 5 +- src/Page.php | 1 - src/Post.php | 10 +-- src/Providers/EncryptionServiceProvider.php | 7 +- src/Providers/SessionServiceProvider.php | 8 +- .../WordPressControllersServiceProvider.php | 18 +++-- src/QueryBuilder.php | 4 +- src/ScopedQueryBuilder.php | 22 +++-- src/Session/EncryptedStore.php | 6 +- src/Session/FileSessionHandler.php | 9 ++- src/Session/SessionManager.php | 4 +- src/Session/Store.php | 6 +- src/ViewModel.php | 40 +++++---- src/functions.php | 20 ++--- tests/Unit/ApplicationTest.php | 64 +++++++-------- .../RegisterExceptionHandlerTest.php | 23 ++++-- .../Bootstrappers/RegisterFacadesTest.php | 4 +- tests/Unit/EncrypterTest.php | 81 +++++++++++++++++++ tests/Unit/Exceptions/HandlerTest.php | 10 +-- tests/Unit/Facades/ConfigTest.php | 4 +- tests/Unit/Facades/FacadeTest.php | 12 +-- tests/Unit/Facades/LogTest.php | 4 +- tests/Unit/Facades/MiddlewareAliasesTest.php | 4 +- tests/Unit/Facades/RouterTest.php | 4 +- tests/Unit/Facades/SessionTest.php | 4 +- tests/Unit/GlobalFunctionsTest.php | 4 +- tests/Unit/HelpersTest.php | 45 ++++++----- .../Http/Middleware/PasswordProtectTest.php | 8 +- tests/Unit/Http/MiddlewareAliasStoreTest.php | 11 ++- tests/Unit/Http/RouterTest.php | 19 ++++- tests/Unit/PostQueryBuilderTest.php | 11 ++- tests/Unit/PostTest.php | 16 ++-- .../CustomPostTypesServiceProviderTest.php | 2 - .../EncryptionServiceProviderTest.php | 6 +- .../Providers/RouterServiceProviderTest.php | 13 ++- ...ordPressControllersServiceProviderTest.php | 49 +++++++---- tests/Unit/QueryBuilderTest.php | 1 - tests/Unit/ScopedQueryBuilderTest.php | 13 ++- tests/Unit/Session/EncryptedStoreTest.php | 18 +++-- tests/Unit/Session/FileSessionHandlerTest.php | 2 +- tests/Unit/Session/SessionManagerTest.php | 18 +++-- tests/Unit/Session/StoreTest.php | 6 +- 56 files changed, 495 insertions(+), 264 deletions(-) delete mode 100644 rector.php create mode 100644 src/Contracts/Encrypter.php create mode 100644 src/Encrypter.php rename src/{FacadeManager.php => FacadeFactory.php} (96%) create mode 100644 tests/Unit/EncrypterTest.php diff --git a/composer.json b/composer.json index b194152..a1fd5a6 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": ">=8.1", "php-di/php-di": "^7", - "rareloop/router": "dev-feature/upgrade-dependencies as 6.0.2", + "rareloop/router": "dev-feature/upgrade-dependencies as 6.0.3", "psr/container": "^2", "psr/http-message": "^2", "psr/http-server-middleware": "^1.0.2", @@ -22,7 +22,7 @@ "illuminate/encryption": "^10", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", - "symfony/error-handler": "^7.3" + "symfony/error-handler": "^6.0" }, "require-dev": { "phpunit/phpunit": "^9.6.13", @@ -32,8 +32,7 @@ "squizlabs/php_codesniffer": "^3.7.2", "php-mock/php-mock": "^2.6.2", "mikey179/vfsstream": "^1.6.12", - "dms/phpunit-arraysubset-asserts": "^0.3.1", - "rector/rector": "^2.1" + "dms/phpunit-arraysubset-asserts": "^0.3.1" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml index 7157380..62992c9 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,22 @@ - - - - src/ - - - - - tests - - + + + + tests + + + + + src/ + + diff --git a/rector.php b/rector.php deleted file mode 100644 index 91bcf48..0000000 --- a/rector.php +++ /dev/null @@ -1,16 +0,0 @@ -withPaths([ - __DIR__ . '/src', - __DIR__ . '/tests', - ]) - // uncomment to reach your current PHP version - ->withPhpSets(php84: true); - // ->withTypeCoverageLevel(0) - // ->withDeadCodeLevel(0) - // ->withCodeQualityLevel(0); diff --git a/src/Application.php b/src/Application.php index 37a1b40..9714ab2 100644 --- a/src/Application.php +++ b/src/Application.php @@ -151,7 +151,7 @@ private function isSingletonClassBind($id) * * @return bool */ - public function has(string $id): bool + public function has($id) { return $this->container->has($id); } @@ -181,9 +181,11 @@ public function register($provider) public function getProvider($provider) { - $providerClass = is_string($provider) ? $provider : $provider::class; + $providerClass = is_string($provider) ? $provider : get_class($provider); - return (new Collection($this->loadedProviders))->first(fn($provider) => $provider::class === $providerClass); + return (new Collection($this->loadedProviders))->first(function ($provider) use ($providerClass) { + return get_class($provider) === $providerClass; + }); } public function getLoadedProviders() @@ -297,12 +299,16 @@ protected function removeSentHeadersAndMoveIntoResponse(ResponseInterface $respo header_remove($parts[0]); return $parts; - })->filter(fn($header) => !in_array(strtolower((string) $header[0]), ['content-type'])); + })->filter(function ($header) { + return !in_array(strtolower($header[0]), ['content-type']); + }); // Add the previously sent headers into the response // Note: You can't mutate a response so we need to use the reduce to end up with a response // object with all the correct headers - $responseToSend = collect($headersToAdd)->reduce(fn($newResponse, $header) => $newResponse->withAddedHeader($header[0], $header[1]), $response); + $responseToSend = collect($headersToAdd)->reduce(function ($newResponse, $header) { + return $newResponse->withAddedHeader($header[0], $header[1]); + }, $response); return $responseToSend; } diff --git a/src/Bootstrappers/RegisterExceptionHandler.php b/src/Bootstrappers/RegisterExceptionHandler.php index 5ca7ba3..64e946d 100644 --- a/src/Bootstrappers/RegisterExceptionHandler.php +++ b/src/Bootstrappers/RegisterExceptionHandler.php @@ -36,8 +36,8 @@ public function bootstrap(Application $app) } error_reporting(-1); - set_error_handler($this->handleError(...)); - set_exception_handler($this->handleException(...)); + set_error_handler([$this, 'handleError']); + set_exception_handler([$this, 'handleException']); register_shutdown_function([$this, 'handleShutdown']); } @@ -52,7 +52,7 @@ public function handleException($e) try { $request = $this->app->get('request'); - } catch (NotFoundException) { + } catch (NotFoundException $notFoundException) { $request = ServerRequestFactory::fromGlobals( $_SERVER, $_GET, diff --git a/src/Bootstrappers/RegisterFacades.php b/src/Bootstrappers/RegisterFacades.php index 11e8b60..53628a0 100644 --- a/src/Bootstrappers/RegisterFacades.php +++ b/src/Bootstrappers/RegisterFacades.php @@ -3,12 +3,12 @@ namespace Rareloop\Lumberjack\Bootstrappers; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; class RegisterFacades { public function bootstrap(Application $app) { - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); } } diff --git a/src/Contracts/Encrypter.php b/src/Contracts/Encrypter.php new file mode 100644 index 0000000..b6a00c1 --- /dev/null +++ b/src/Contracts/Encrypter.php @@ -0,0 +1,10 @@ +key = $key; + } + + public function encrypt($data) + { + return AesCbc::encrypt(@serialize($data), $this->key); + } + + public function decrypt($data) + { + return @unserialize(AesCbc::decrypt($data, $this->key)); + } +} diff --git a/src/Exceptions/Handler.php b/src/Exceptions/Handler.php index 99c794e..cc5409b 100644 --- a/src/Exceptions/Handler.php +++ b/src/Exceptions/Handler.php @@ -53,6 +53,6 @@ public function render(ServerRequestInterface $request, Exception $e): ResponseI protected function shouldNotReport(Exception $e) { - return in_array($e::class, $this->dontReport); + return in_array(get_class($e), $this->dontReport); } } diff --git a/src/FacadeManager.php b/src/FacadeFactory.php similarity index 96% rename from src/FacadeManager.php rename to src/FacadeFactory.php index 5892e7c..eb09caf 100644 --- a/src/FacadeManager.php +++ b/src/FacadeFactory.php @@ -4,7 +4,7 @@ use Psr\Container\ContainerInterface; -class FacadeManager +class FacadeFactory { protected static ?ContainerInterface $container = null; diff --git a/src/Facades/AbstractFacade.php b/src/Facades/AbstractFacade.php index 22697fa..72b81c4 100644 --- a/src/Facades/AbstractFacade.php +++ b/src/Facades/AbstractFacade.php @@ -2,18 +2,18 @@ namespace Rareloop\Lumberjack\Facades; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; abstract class AbstractFacade { public static function __callStatic($name, array $arguments = []) { - return FacadeManager::create(static::accessor(), $name, $arguments); + return FacadeFactory::create(static::accessor(), $name, $arguments); } public static function __instance() { - return FacadeManager::getContainer()->get(static::accessor()); + return FacadeFactory::getContainer()->get(static::accessor()); } abstract protected static function accessor(); diff --git a/src/Http/Lumberjack.php b/src/Http/Lumberjack.php index 8225b1b..df31f57 100644 --- a/src/Http/Lumberjack.php +++ b/src/Http/Lumberjack.php @@ -13,6 +13,8 @@ class Lumberjack { + private $app; + protected $bootstrappers = [ LoadConfiguration::class, RegisterExceptionHandler::class, @@ -23,8 +25,9 @@ class Lumberjack RegisterRequestHandler::class, ]; - public function __construct(private readonly Application $app) + public function __construct(Application $app) { + $this->app = $app; } public function bootstrap() diff --git a/src/Http/Middleware/PasswordProtected.php b/src/Http/Middleware/PasswordProtected.php index ef1bd96..23e886d 100644 --- a/src/Http/Middleware/PasswordProtected.php +++ b/src/Http/Middleware/PasswordProtected.php @@ -37,7 +37,7 @@ protected function handlePasswordProtected(): ?ResponseInterface try { return new TimberResponse($template, $context); - } catch (TwigTemplateNotFoundException) { + } catch (TwigTemplateNotFoundException $e) { return null; } } diff --git a/src/Http/MiddlewareAliasStore.php b/src/Http/MiddlewareAliasStore.php index b86eb5b..80fbc9d 100644 --- a/src/Http/MiddlewareAliasStore.php +++ b/src/Http/MiddlewareAliasStore.php @@ -17,7 +17,7 @@ public function set(string $name, $middleware) public function get(string $name) { - [$name, $params] = $this->parseName($name); + list($name, $params) = $this->parseName($name); $middleware = $this->aliases[$name]; @@ -32,18 +32,18 @@ public function get(string $name) return $middleware; } - protected function parseName($name) : array + protected function parseName($name): array { - [$name, $params] = array_pad(explode(':', (string) $name), 2, ''); + list($name, $params) = array_pad(explode(':', $name), 2, ''); $params = explode(',', $params); return [$name, $params]; } - public function has(string $name) : bool + public function has(string $name): bool { - [$name, $params] = $this->parseName($name); + list($name, $params) = $this->parseName($name); return isset($this->aliases[$name]); } diff --git a/src/Http/Router.php b/src/Http/Router.php index 3eb1d25..7753b07 100644 --- a/src/Http/Router.php +++ b/src/Http/Router.php @@ -17,7 +17,6 @@ class Router extends RareRouter * @param callable|string $callback * @return \Rareloop\Router\Route */ - #[\Override] public function map(array $verbs, string $uri, $callback): Route { if ($this->isControllerString($callback)) { @@ -35,7 +34,7 @@ public function map(array $verbs, string $uri, $callback): Route */ private function isControllerString($callback) : bool { - return is_string($callback) && str_contains($callback, '@'); + return is_string($callback) && strpos($callback, '@') !== false; } /** @@ -46,7 +45,7 @@ private function isControllerString($callback) : bool */ private function normaliseCallbackString(string $callback) : string { - @[$controller, $method] = explode('@', $callback); + @list($controller, $method) = explode('@', $callback); if (class_exists($this->defaultControllerNamespace . $controller)) { return $this->defaultControllerNamespace . $callback; diff --git a/src/Page.php b/src/Page.php index edaee2b..8398eea 100644 --- a/src/Page.php +++ b/src/Page.php @@ -11,7 +11,6 @@ class Page extends Post * * @return string */ - #[\Override] public static function getPostType() { return 'page'; diff --git a/src/Post.php b/src/Post.php index 2dada06..38f4258 100644 --- a/src/Post.php +++ b/src/Post.php @@ -2,7 +2,6 @@ namespace Rareloop\Lumberjack; -use Error; use Rareloop\Lumberjack\Exceptions\PostTypeRegistrationException; use Rareloop\Lumberjack\ScopedQueryBuilder; use Spatie\Macroable\Macroable; @@ -27,7 +26,6 @@ public function __construct(mixed $wpPost = null, $preventTimberInit = false) } } - #[\Override] public function __call($name, $arguments) { if (static::hasMacro($name)) { @@ -48,7 +46,7 @@ public static function __callStatic($name, $arguments) return call_user_func_array([$builder, $name], $arguments); } - throw new Error('Call to undefined method ' . self::class . '::' . $name . '()'); + trigger_error('Call to undefined method ' . __CLASS__ . '::' . $name . '()', E_USER_ERROR); } /** @@ -103,7 +101,7 @@ public static function register() throw new PostTypeRegistrationException('Config not set'); } - add_filter('timber/post/classmap', static::filterTimberPostClassMap(...)); + add_filter('timber/post/classmap', [static::class, 'filterTimberPostClassMap']); register_post_type($postType, $config); } @@ -121,7 +119,7 @@ public static function filterTimberPostClassMap(array $classMap): array */ public static function all($perPage = -1, $orderby = 'menu_order', $order = 'ASC') { - $order = strtoupper((string) $order); + $order = strtoupper($order); $args = [ 'posts_per_page' => $perPage, @@ -163,6 +161,6 @@ public static function query($args = null) */ private static function posts($args = null) { - return collect(Timber::get_posts($args, static::class)); + return collect(Timber::get_posts($args, get_called_class())); } } diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php index 1f462e1..ec762cc 100644 --- a/src/Providers/EncryptionServiceProvider.php +++ b/src/Providers/EncryptionServiceProvider.php @@ -2,8 +2,11 @@ namespace Rareloop\Lumberjack\Providers; -use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; -use Illuminate\Encryption\Encrypter; +use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; +use Rareloop\Lumberjack\Encrypter; +use Rareloop\Lumberjack\Facades\Config; +use Rareloop\Lumberjack\Session\SessionManager; class EncryptionServiceProvider extends ServiceProvider { diff --git a/src/Providers/SessionServiceProvider.php b/src/Providers/SessionServiceProvider.php index d7dca2d..8ca5715 100644 --- a/src/Providers/SessionServiceProvider.php +++ b/src/Providers/SessionServiceProvider.php @@ -43,8 +43,12 @@ public function boot() setcookie( $this->session->getName(), - (string) $this->session->getId(), - ['expires' => time() + ($cookieOptions['lifetime'] * 60), 'path' => $cookieOptions['path'], 'domain' => $cookieOptions['domain'], 'secure' => $cookieOptions['secure'], 'httponly' => $cookieOptions['httpOnly']] + $this->session->getId(), + time() + ($cookieOptions['lifetime'] * 60), + $cookieOptions['path'], + $cookieOptions['domain'], + $cookieOptions['secure'], + $cookieOptions['httpOnly'] ); $cookieSet = true; diff --git a/src/Providers/WordPressControllersServiceProvider.php b/src/Providers/WordPressControllersServiceProvider.php index 3b3dd2e..280e567 100644 --- a/src/Providers/WordPressControllersServiceProvider.php +++ b/src/Providers/WordPressControllersServiceProvider.php @@ -16,7 +16,7 @@ class WordPressControllersServiceProvider extends ServiceProvider { public function boot() { - add_filter('template_include', $this->handleTemplateInclude(...)); + add_filter('template_include', [$this, 'handleTemplateInclude']); } public function handleTemplateInclude($template) @@ -38,14 +38,14 @@ public function handleTemplateInclude($template) if ($response) { $this->app->shutdown($response); } else { - $this->app->bind('__wp-controller-miss-template', basename((string) $template)); + $this->app->bind('__wp-controller-miss-template', basename($template)); $this->app->bind('__wp-controller-miss-controller', $controller); } } public function getControllerClassFromTemplate($template) { - $controllerName = Stringy::create(basename((string) $template, '.php'))->upperCamelize() . 'Controller'; + $controllerName = Stringy::create(basename($template, '.php'))->upperCamelize() . 'Controller'; // Classes can't start with a number so we have to special case the behaviour here if ($controllerName === '404Controller') { @@ -76,7 +76,11 @@ public function handleRequest(RequestInterface $request, $controllerName, $metho if ($controller instanceof ProvidesControllerMiddleware) { $controllerMiddleware = new Collection($controller->getControllerMiddleware()); - $middlewares = $controllerMiddleware->reject(fn($cm) => $cm->excludedForMethod($methodName))->map(fn($cm) => $cm->middleware())->all(); + $middlewares = $controllerMiddleware->reject(function ($cm) use ($methodName) { + return $cm->excludedForMethod($methodName); + })->map(function ($cm) { + return $cm->middleware(); + })->all(); } $middlewares = [ @@ -90,7 +94,7 @@ function ($request) use ($controller, $methodName) { ]; $dispatcher = $this->createDispatcher($middlewares); - return $dispatcher->handle($request); + return $dispatcher->dispatch($request); } private function createDispatcher(array $middlewares): Dispatcher @@ -98,7 +102,9 @@ private function createDispatcher(array $middlewares): Dispatcher $resolver = null; if ($this->app->has('middleware-resolver')) { - $resolver = (fn($name) => $this->app->get('middleware-resolver')->resolve($name)); + $resolver = function ($name) { + return $this->app->get('middleware-resolver')->resolve($name); + }; } return new Dispatcher($middlewares, $resolver); diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 004f019..384fee6 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -82,7 +82,7 @@ public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuild return $this; } - public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilderContract + public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilderContract { $order = strtoupper($order); @@ -122,7 +122,7 @@ public function whereStatus(): QueryBuilderContract protected function initialiseMetaQuery() { - $this->params['meta_query'] ??= []; + $this->params['meta_query'] = $this->params['meta_query'] ?? []; } public function whereMeta($key, $value, $compare = '=', $type = null): QueryBuilderContract diff --git a/src/ScopedQueryBuilder.php b/src/ScopedQueryBuilder.php index b28a777..f6089c1 100644 --- a/src/ScopedQueryBuilder.php +++ b/src/ScopedQueryBuilder.php @@ -2,7 +2,6 @@ namespace Rareloop\Lumberjack; -use Error; use Rareloop\Lumberjack\Contracts\QueryBuilder as QueryBuilderContract; use Rareloop\Lumberjack\Exceptions\CannotRedeclarePostClassOnQueryException; use Rareloop\Lumberjack\Exceptions\CannotRedeclarePostTypeOnQueryException; @@ -13,10 +12,14 @@ class ScopedQueryBuilder { + protected $postClass; + protected $queryBuilder; - public function __construct(protected $postClass) + public function __construct($postClass) { + $this->postClass = $postClass; + $this->queryBuilder = Helpers::app(QueryBuilderContract::class); $this->queryBuilder @@ -35,18 +38,23 @@ public function __call($name, $arguments) } // See if this is a scope function that needs calling - $scopeFunctionName = 'scope' . ucfirst((string) $name); + $scopeFunctionName = 'scope' . ucfirst($name); $reflection = new ReflectionClass($this->postClass); - $publicMethods = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))->map(fn($method) => $method->getName())->toArray(); + $publicMethods = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))->map(function ($method) { + return $method->getName(); + })->toArray(); if (!in_array($scopeFunctionName, $publicMethods)) { - throw new Error('Call to undefined method ' . $this->postClass . '::' . $scopeFunctionName . '()'); + trigger_error( + 'Call to undefined method ' . $this->postClass . '::' . $scopeFunctionName . '()', + E_USER_ERROR + ); } array_unshift($arguments, $this); - return new $this->postClass(false, true)->{$scopeFunctionName}(...$arguments); + return (new $this->postClass(false, true))->{$scopeFunctionName}(...$arguments); } /** @@ -69,7 +77,7 @@ protected function hasQueryBuilderMethod(string $name): bool return false; } - public function wherePostType($postType): never + public function wherePostType($postType) { throw new CannotRedeclarePostTypeOnQueryException; } diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 6fccda0..856b7c1 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -3,8 +3,8 @@ namespace Rareloop\Lumberjack\Session; use Exception; -use Illuminate\Contracts\Encryption\Encrypter; use SessionHandlerInterface; +use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; class EncryptedStore extends Store @@ -17,7 +17,7 @@ public function __construct( SessionHandlerInterface $handler, Encrypter $encrypter, $id = null, - ?HandlerInterface $exceptionHandler = null + HandlerInterface $exceptionHandler = null ) { $this->encrypter = $encrypter; $this->exceptionHandler = $exceptionHandler; @@ -25,13 +25,11 @@ public function __construct( parent::__construct($name, $handler, $id); } - #[\Override] protected function prepareForStorage($data) { return $this->encrypter->encrypt($data); } - #[\Override] protected function prepareForUnserialize($data) { try { diff --git a/src/Session/FileSessionHandler.php b/src/Session/FileSessionHandler.php index 9efa24b..80d5a48 100644 --- a/src/Session/FileSessionHandler.php +++ b/src/Session/FileSessionHandler.php @@ -8,8 +8,13 @@ class FileSessionHandler implements SessionHandlerInterface { - public function __construct(protected $path, protected $prefix = 'lumberjack_session_') + protected $path; + protected $prefix; + + public function __construct($path, $prefix = 'lumberjack_session_') { + $this->path = $path; + $this->prefix = $prefix; } #[\ReturnTypeWillChange] @@ -41,7 +46,7 @@ public function write($sessionId, $data) { try { file_put_contents($this->getFilepath($sessionId), $data); - } catch (Exception) { + } catch (Exception $e) { Log::error('Failed to create session on disk'); } diff --git a/src/Session/SessionManager.php b/src/Session/SessionManager.php index 31bad1a..0926a03 100644 --- a/src/Session/SessionManager.php +++ b/src/Session/SessionManager.php @@ -2,9 +2,9 @@ namespace Rareloop\Lumberjack\Session; -use Illuminate\Contracts\Encryption\Encrypter; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; +use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; use Rareloop\Lumberjack\Manager; class SessionManager extends Manager @@ -53,7 +53,7 @@ protected function buildSession($handler) $sessionId = ($_COOKIE[$this->name] ?? null); if ($this->config->get('session.encrypt')) { - $encrypter = $this->app->get(Encrypter::class); + $encrypter = $this->app->get(EncrypterContract::class); return new EncryptedStore($this->name, $handler, $encrypter, $sessionId); } diff --git a/src/Session/Store.php b/src/Session/Store.php index 9e73bf9..9c58b64 100644 --- a/src/Session/Store.php +++ b/src/Session/Store.php @@ -36,7 +36,7 @@ protected function loadSession() protected function readFromHandler() { $data = $this->handler->read($this->id); - $data = $this->prepareForUnserialize($data); + $data = @unserialize($this->prepareForUnserialize($data)); if ($data !== false && !is_null($data) && is_array($data)) { return $data; @@ -49,7 +49,7 @@ public function save() { $this->ageFlashData(); - $this->handler->write($this->id, $this->prepareForStorage($this->attributes)); + $this->handler->write($this->id, $this->prepareForStorage(@serialize($this->attributes))); return $this; } @@ -194,7 +194,7 @@ public function getId() public function setId($id = null) { - $id ??= static::random(40); + $id = $id ?? static::random(40); $this->id = $id; } diff --git a/src/ViewModel.php b/src/ViewModel.php index 724cede..9ae3fa2 100644 --- a/src/ViewModel.php +++ b/src/ViewModel.php @@ -9,44 +9,56 @@ abstract class ViewModel implements Arrayable { - public function toArray() : array + public function toArray(): array { $propertyKeyValues = collect($this->validPropertyNames()) - ->mapWithKeys(fn($method) => [ - $method => $this->{$method}, - ]) + ->mapWithKeys(function ($method) { + return [ + $method => $this->{$method}, + ]; + }) ->toArray(); $methodKeyValues = collect($this->validMethodNames()) ->whereNotIn(null, $this->ignoredMethods()) - ->mapWithKeys(fn($method) => [ - $method => call_user_func([$this, $method]), - ]) + ->mapWithKeys(function ($method) { + return [ + $method => call_user_func([$this, $method]), + ]; + }) ->toArray(); return array_merge($propertyKeyValues, $methodKeyValues); } - protected function validMethodNames() : array + protected function validMethodNames(): array { $class = new ReflectionClass(static::class); return collect($class->getMethods(ReflectionMethod::IS_PUBLIC)) - ->reject(fn($method) => $method->isStatic() || $method->getNumberOfParameters() > 0) - ->map(fn($method) => $method->getName()) + ->reject(function ($method) { + return $method->isStatic() || $method->getNumberOfParameters() > 0; + }) + ->map(function ($method) { + return $method->getName(); + }) ->all(); } - protected function validPropertyNames() : array + protected function validPropertyNames(): array { $class = new ReflectionClass(static::class); return collect($class->getProperties(ReflectionProperty::IS_PUBLIC)) - ->reject(fn($property) => $property->isStatic()) - ->map(fn($property) => $property->getName()) + ->reject(function ($property) { + return $property->isStatic(); + }) + ->map(function ($property) { + return $property->getName(); + }) ->all(); } - protected function ignoredMethods() : array + protected function ignoredMethods(): array { return [ 'toArray', diff --git a/src/functions.php b/src/functions.php index 20acd20..92043dd 100644 --- a/src/functions.php +++ b/src/functions.php @@ -5,69 +5,69 @@ if (!function_exists('app')) { function app() { - return call_user_func_array(Helpers::app(...), func_get_args()); + return call_user_func_array([Helpers::class, 'app'], func_get_args()); } } if (!function_exists('config')) { function config() { - return call_user_func_array(Helpers::config(...), func_get_args()); + return call_user_func_array([Helpers::class, 'config'], func_get_args()); } } if (!function_exists('view')) { function view() { - return call_user_func_array(Helpers::view(...), func_get_args()); + return call_user_func_array([Helpers::class, 'view'], func_get_args()); } } if (!function_exists('route')) { function route() { - return call_user_func_array(Helpers::route(...), func_get_args()); + return call_user_func_array([Helpers::class, 'route'], func_get_args()); } } if (!function_exists('redirect')) { function redirect() { - return call_user_func_array(Helpers::redirect(...), func_get_args()); + return call_user_func_array([Helpers::class, 'redirect'], func_get_args()); } } if (!function_exists('report')) { function report() { - return call_user_func_array(Helpers::report(...), func_get_args()); + return call_user_func_array([Helpers::class, 'report'], func_get_args()); } } if (!function_exists('session')) { function session() { - return call_user_func_array(Helpers::session(...), func_get_args()); + return call_user_func_array([Helpers::class, 'session'], func_get_args()); } } if (!function_exists('back')) { function back() { - return call_user_func_array(Helpers::back(...), func_get_args()); + return call_user_func_array([Helpers::class, 'back'], func_get_args()); } } if (!function_exists('request')) { function request() { - return call_user_func_array(Helpers::request(...), func_get_args()); + return call_user_func_array([Helpers::class, 'request'], func_get_args()); } } if (!function_exists('logger')) { function logger() { - return call_user_func_array(Helpers::logger(...), func_get_args()); + return call_user_func_array([Helpers::class, 'logger'], func_get_args()); } } diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index 2e6bee6..4d935af 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -498,7 +498,9 @@ private function createPhpSapiNameMock($value, $namespace) $builder->setNamespace($namespace) ->setName('php_sapi_name') ->setFunction( - fn() => $value + function () use ($value) { + return $value; + } ); return $builder->build(); @@ -550,15 +552,21 @@ public function calling_detectWhenRequestHasNotBeenHandled_adds_actions() class BootstrapperBootstrapTester { - public function __construct(public $callback) + public $callback; + + public function __construct($callback) { + $this->callback = $callback; } } abstract class TestBootstrapperBase { - public function __construct(private readonly BootstrapperBootstrapTester $tester) + private BootstrapperBootstrapTester $tester; + + public function __construct(BootstrapperBootstrapTester $tester) { + $this->tester = $tester; } public function bootstrap(Application $app) @@ -567,58 +575,36 @@ public function bootstrap(Application $app) } } -class TestBootstrapper1 extends TestBootstrapperBase -{ -} +class TestBootstrapper1 extends TestBootstrapperBase {} -class TestBootstrapper2 extends TestBootstrapperBase -{ -} +class TestBootstrapper2 extends TestBootstrapperBase {} -interface TestInterface -{ -} +interface TestInterface {} -class TestInterfaceImplementation implements TestInterface -{ -} +class TestInterfaceImplementation implements TestInterface {} class TestInterfaceImplementationWithConstructorParams implements TestInterface { - public function __construct(TestServiceProvider $provider) - { - } + public function __construct(TestServiceProvider $provider) {} } -interface TestSubInterface -{ -} +interface TestSubInterface {} -class TestSubInterfaceImplementation implements TestSubInterface -{ -} +class TestSubInterfaceImplementation implements TestSubInterface {} class TestServiceProvider extends ServiceProvider { - public function register() - { - } - public function boot() - { - } + public function register() {} + public function boot() {} } -class EmptyServiceProvider extends ServiceProvider -{ -} +class EmptyServiceProvider extends ServiceProvider {} class TestBootServiceProvider extends ServiceProvider { private $bootCallback; - public function register() - { - } + public function register() {} public function boot(Application $app, TestInterface $test) { @@ -646,9 +632,13 @@ public function __construct(TestInterface $test) class RequiresAdditionalConstructorParams { public $param; + public $param1; + public $param2; - public function __construct(TestInterface $test, public $param1, public $param2) + public function __construct(TestInterface $test, $param1, $param2) { $this->param = $test; + $this->param1 = $param1; + $this->param2 = $param2; } } diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php index cf1c230..c19f006 100644 --- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php +++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php @@ -14,9 +14,10 @@ use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\Responsable; -use Laminas\Diactoros\Response; -use Laminas\Diactoros\Response\TextResponse; -use Laminas\Diactoros\ServerRequest; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Zend\Diactoros\Response; +use Zend\Diactoros\Response\TextResponse; +use Zend\Diactoros\ServerRequest; /** * @runTestsInSeparateProcesses @@ -56,7 +57,9 @@ public function E_USER_NOTICE_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_NOTICE && $e->getMessage() === 'Test Error')); + $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { + return $e->getSeverity() === E_USER_NOTICE && $e->getMessage() === 'Test Error'; + })); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -77,7 +80,9 @@ public function E_USER_DEPRECATED_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_DEPRECATED && $e->getMessage() === 'Test Error')); + $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { + return $e->getSeverity() === E_USER_DEPRECATED && $e->getMessage() === 'Test Error'; + })); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -98,7 +103,9 @@ public function E_DEPRECATED_errors_are_not_converted_to_exceptions() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_DEPRECATED && $e->getMessage() === 'Test Error')); + $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { + return $e->getSeverity() === E_DEPRECATED && $e->getMessage() === 'Test Error'; + })); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); @@ -121,7 +128,9 @@ public function custom_error_level_can_be_set_for_report_only() $app->bind('config', $config); $app->bind(Config::class, $config); - $handler->shouldReceive('report')->once()->with(Mockery::on(fn($e) => $e->getSeverity() === E_USER_ERROR && $e->getMessage() === 'Test Error')); + $handler->shouldReceive('report')->once()->with(Mockery::on(function ($e) { + return $e->getSeverity() === E_USER_ERROR && $e->getMessage() === 'Test Error'; + })); $bootstrapper = new RegisterExceptionHandler(); $bootstrapper->bootstrap($app); diff --git a/tests/Unit/Bootstrappers/RegisterFacadesTest.php b/tests/Unit/Bootstrappers/RegisterFacadesTest.php index 871442f..5a2f234 100644 --- a/tests/Unit/Bootstrappers/RegisterFacadesTest.php +++ b/tests/Unit/Bootstrappers/RegisterFacadesTest.php @@ -5,7 +5,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Bootstrappers\RegisterFacades; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; class RegisterFacadesTest extends TestCase { @@ -17,6 +17,6 @@ public function boots_all_registered_providers() $registerFacadesBootstrapper = new RegisterFacades; $registerFacadesBootstrapper->bootstrap($app); - $this->assertSame($app, FacadeManager::getContainer()); + $this->assertSame($app, FacadeFactory::getContainer()); } } diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php new file mode 100644 index 0000000..df52253 --- /dev/null +++ b/tests/Unit/EncrypterTest.php @@ -0,0 +1,81 @@ +shouldReceive('encrypt')->withArgs(function ($data, $key) { + if ($key !== 'secret-key') { + return false; + } + + if ($data !== @serialize('test-string')) { + return false; + } + + return true; + })->once(); + + $encrypter = new Encrypter($key); + $encrypter->encrypt('test-string'); + } + + /** @test */ + public function can_decrypt_data() + { + $key = 'secret-key'; + $dcrypt = Mockery::mock('alias:' . AesCbc::class); + $dcrypt->shouldReceive('decrypt')->with('test-string', $key)->once(); + + $encrypter = new Encrypter($key); + $encrypter->decrypt('test-string'); + } + + /** @test */ + public function can_process_strings() + { + $payload = 'test-string'; + $encrypter = new Encrypter('secret-key'); + + $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); + } + + /** @test */ + public function can_process_arrays() + { + $payload = ['foo' => 'bar']; + $encrypter = new Encrypter('secret-key'); + + $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); + } + + /** @test */ + public function can_process_objects() + { + $payload = new \stdClass; + $payload->foo = 'bar'; + $encrypter = new Encrypter('secret-key'); + + $output = $encrypter->decrypt($encrypter->encrypt($payload)); + + $this->assertInstanceOf(\stdClass::class, $output); + $this->assertSame('bar', $output->foo); + } +} diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index 113f5e0..5c424f9 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -10,7 +10,7 @@ use Rareloop\Lumberjack\Exceptions\Handler; use Laminas\Diactoros\Response\HtmlResponse; use Laminas\Diactoros\ServerRequest; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; class HandlerTest extends TestCase { @@ -52,7 +52,7 @@ public function blacklisted_exception_types_will_not_be_logged() public function render_should_return_an_html_response_when_debug_is_enabled() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config; $config->set('app.debug', true); $app->bind('config', $config); @@ -69,7 +69,7 @@ public function render_should_return_an_html_response_when_debug_is_enabled() public function render_should_return_an_html_response_when_debug_is_disabled() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config; $config->set('app.debug', false); $app->bind('config', $config); @@ -86,7 +86,7 @@ public function render_should_return_an_html_response_when_debug_is_disabled() public function render_should_include_stack_trace_when_debug_is_enabled() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config; $config->set('app.debug', true); $app->bind('config', $config); @@ -103,7 +103,7 @@ public function render_should_include_stack_trace_when_debug_is_enabled() public function render_should_not_include_stack_trace_when_debug_is_disabled() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config; $config->set('app.debug', false); $app->bind('config', $config); diff --git a/tests/Unit/Facades/ConfigTest.php b/tests/Unit/Facades/ConfigTest.php index bbcab55..29e5970 100644 --- a/tests/Unit/Facades/ConfigTest.php +++ b/tests/Unit/Facades/ConfigTest.php @@ -5,7 +5,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Config as ConfigFacade; class ConfigTest extends TestCase @@ -14,7 +14,7 @@ class ConfigTest extends TestCase public function test_facade() { $app = new Application(); - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config(); $config->set('app.environment', 'production'); diff --git a/tests/Unit/Facades/FacadeTest.php b/tests/Unit/Facades/FacadeTest.php index 3c3075c..2ff42ad 100644 --- a/tests/Unit/Facades/FacadeTest.php +++ b/tests/Unit/Facades/FacadeTest.php @@ -5,7 +5,7 @@ use DI\Container; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Facade; use Rareloop\Lumberjack\Test\Unit\Facades\Stubs\Foo; use Rareloop\Lumberjack\Test\Unit\Facades\Stubs\FooFacade; @@ -26,14 +26,14 @@ protected function setUp(): void /** @test */ public function can_initiate_facades() { - FacadeManager::setContainer($this->container); - $this->assertInstanceOf(ContainerInterface::class, FacadeManager::getContainer()); + FacadeFactory::setContainer($this->container); + $this->assertInstanceOf(ContainerInterface::class, FacadeFactory::getContainer()); } /** @test */ public function can_get_facade_instance() { - FacadeManager::setContainer($this->container); + FacadeFactory::setContainer($this->container); $instance = FooFacade::__instance(); @@ -44,7 +44,7 @@ public function can_get_facade_instance() /** @test */ public function can_swap_instances() { - FacadeManager::setContainer($this->container); + FacadeFactory::setContainer($this->container); $instance = FooFacade::__instance(); @@ -60,7 +60,7 @@ public function can_swap_instances() public function can_call_functions() { - FacadeManager::setContainer($this->container); + FacadeFactory::setContainer($this->container); $this->assertEquals('bar', forward_static_call([FooFacade::class, 'foo'])); $this->assertEquals('bar', call_user_func('FooFacade::class::foo')); diff --git a/tests/Unit/Facades/LogTest.php b/tests/Unit/Facades/LogTest.php index 4c42400..9105726 100644 --- a/tests/Unit/Facades/LogTest.php +++ b/tests/Unit/Facades/LogTest.php @@ -5,7 +5,7 @@ use Monolog\Logger; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Log as LogFacade; class LogTest extends TestCase @@ -14,7 +14,7 @@ class LogTest extends TestCase public function test_facade() { $app = new Application(); - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $logger = new Logger('app'); $app->bind('logger', $logger); diff --git a/tests/Unit/Facades/MiddlewareAliasesTest.php b/tests/Unit/Facades/MiddlewareAliasesTest.php index 04fdf64..cd847a0 100644 --- a/tests/Unit/Facades/MiddlewareAliasesTest.php +++ b/tests/Unit/Facades/MiddlewareAliasesTest.php @@ -4,7 +4,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\MiddlewareAliases; use Rareloop\Lumberjack\Http\MiddlewareAliasStore; @@ -14,7 +14,7 @@ class MiddlewareAliasesTest extends TestCase public function test_facade() { $app = new Application(); - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new MiddlewareAliasStore(); $app->bind('middleware-alias-store', $store); diff --git a/tests/Unit/Facades/RouterTest.php b/tests/Unit/Facades/RouterTest.php index ec92e5c..ebc39f6 100644 --- a/tests/Unit/Facades/RouterTest.php +++ b/tests/Unit/Facades/RouterTest.php @@ -4,7 +4,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Router as RouterFacade; use Rareloop\Lumberjack\Http\Router; @@ -14,7 +14,7 @@ class RouterTest extends TestCase public function test_facade() { $app = new Application(); - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $router = new Router(); $app->bind('router', $router); diff --git a/tests/Unit/Facades/SessionTest.php b/tests/Unit/Facades/SessionTest.php index 1b59eb1..95c7c3f 100644 --- a/tests/Unit/Facades/SessionTest.php +++ b/tests/Unit/Facades/SessionTest.php @@ -4,7 +4,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Session; use Rareloop\Lumberjack\Session\SessionManager; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; @@ -15,7 +15,7 @@ class SessionTest extends TestCase public function test_facade() { $app = new Application(); - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); diff --git a/tests/Unit/GlobalFunctionsTest.php b/tests/Unit/GlobalFunctionsTest.php index a0e7a5d..bc0d1d6 100644 --- a/tests/Unit/GlobalFunctionsTest.php +++ b/tests/Unit/GlobalFunctionsTest.php @@ -46,6 +46,8 @@ public static function globalHelperFunctions() { $reflection = new \ReflectionClass(Helpers::class); - return collect($reflection->getMethods(\ReflectionMethod::IS_STATIC))->map(fn($function) => [$function->name])->toArray(); + return collect($reflection->getMethods(\ReflectionMethod::IS_STATIC))->map(function ($function) { + return [$function->name]; + })->toArray(); } } diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index 129c43d..f61ad57 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -17,7 +17,7 @@ use Rareloop\Lumberjack\Http\Responses\TimberResponse; use Rareloop\Lumberjack\Http\Responses\RedirectResponse; use Monolog\Logger; -use Rareloop\Lumberjack\FacadeManager; +use Rareloop\Lumberjack\FacadeFactory; /** * @runTestsInSeparateProcesses @@ -48,7 +48,7 @@ public function can_resolve_something_from_the_container() public function can_retrieve_a_config_value() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config(); $config->set('app.environment', 'production'); @@ -61,7 +61,7 @@ public function can_retrieve_a_config_value() public function can_retrieve_a_default_when_no_config_value_is_set() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config(); $app->bind('config', $config); @@ -73,7 +73,7 @@ public function can_retrieve_a_default_when_no_config_value_is_set() public function can_set_a_config_value_when_array_passed_to_config_helper() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $config = new Config(); $app->bind('config', $config); @@ -139,7 +139,7 @@ public function can_get_a_timber_response_with_specific_headers() public function can_get_a_url_for_a_named_route() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $router = new Router; $router->get('test/route', function () {})->name('test.route'); $app->bind('router', $router); @@ -153,7 +153,7 @@ public function can_get_a_url_for_a_named_route() public function can_get_a_url_for_a_named_route_with_params() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $router = new Router; $router->get('test/{name}', function ($name) {})->name('test.route'); $app->bind('router', $router); @@ -205,7 +205,9 @@ public function can_report_an_exception() $handler = \Mockery::mock(TestExceptionHandler::class . '[report]', [$app]); $handler->shouldReceive('report')->with($exception)->once(); - $app->bind(HandlerInterface::class, fn() => $handler); + $app->bind(HandlerInterface::class, function () use ($handler) { + return $handler; + }); Helpers::report($exception); } @@ -214,7 +216,7 @@ public function can_report_an_exception() public function can_access_an_item_in_the_session_by_key() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -228,7 +230,7 @@ public function can_access_an_item_in_the_session_by_key() public function can_access_an_item_in_the_session_by_key_with_default() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -240,7 +242,7 @@ public function can_access_an_item_in_the_session_by_key_with_default() public function can_add_an_item_in_the_session() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -254,7 +256,7 @@ public function can_add_an_item_in_the_session() public function can_add_multiple_items_to_the_session() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -269,7 +271,7 @@ public function can_add_multiple_items_to_the_session() public function can_resolve_the_session_manager() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); @@ -281,7 +283,7 @@ public function can_resolve_the_session_manager() public function can_redirect_back() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $store = new SessionManager($app); $app->bind('session', $store); $store->setPreviousUrl('http://domain.com/previous/url'); @@ -296,7 +298,7 @@ public function can_redirect_back() public function can_get_server_request() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $request = new ServerRequest([], [], '/test/123', 'GET'); $app->bind('request', $request); @@ -308,7 +310,7 @@ public function can_get_server_request() public function can_get_logger() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $logger = new Logger('app'); $app->bind('logger', $logger); @@ -323,7 +325,7 @@ public function can_get_logger() public function can_write_debug_log() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $logger = \Mockery::mock(Logger::class)->makePartial(); $logger->shouldReceive('debug')->with('Example message', [])->once(); @@ -337,7 +339,7 @@ public function can_write_debug_log() public function can_write_debug_log_with_context() { $app = new Application; - FacadeManager::setContainer($app); + FacadeFactory::setContainer($app); $logger = \Mockery::mock(Logger::class)->makePartial(); $logger->shouldReceive('debug')->with('Example message', [ @@ -371,5 +373,12 @@ class TestExceptionHandler extends Handler class RequiresConstructorParams { - public function __construct(public $param1, public $param2) {} + public $param1; + public $param2; + + public function __construct($param1, $param2) + { + $this->param1 = $param1; + $this->param2 = $param2; + } } diff --git a/tests/Unit/Http/Middleware/PasswordProtectTest.php b/tests/Unit/Http/Middleware/PasswordProtectTest.php index 523e1b4..a21fc20 100644 --- a/tests/Unit/Http/Middleware/PasswordProtectTest.php +++ b/tests/Unit/Http/Middleware/PasswordProtectTest.php @@ -52,7 +52,9 @@ public function it_does_nothing_when_the_password_twig_file_is_not_found() $timber = \Mockery::mock('alias:' . Timber::class); $timber->shouldReceive('get_post')->once(); $timber->shouldReceive('compile') - ->withArgs(fn($template) => $template === 'single-password.twig') + ->withArgs(function ($template) { + return $template === 'single-password.twig'; + }) ->once() ->andReturn(false); @@ -82,7 +84,9 @@ public function it_renders_the_password_template_when_needed() $timber = \Mockery::mock('alias:' . Timber::class); $timber->shouldReceive('get_post')->once(); $timber->shouldReceive('compile') - ->withArgs(fn($template) => $template === 'single-password.twig') + ->withArgs(function ($template) { + return $template === 'single-password.twig'; + }) ->once() ->andReturn('testing123'); diff --git a/tests/Unit/Http/MiddlewareAliasStoreTest.php b/tests/Unit/Http/MiddlewareAliasStoreTest.php index 6818104..6d97f28 100644 --- a/tests/Unit/Http/MiddlewareAliasStoreTest.php +++ b/tests/Unit/Http/MiddlewareAliasStoreTest.php @@ -29,7 +29,9 @@ public function can_register_an_alias_for_a_middleware_closure_factory() $store = new MiddlewareAliasStore; $middleware = Mockery::mock(MiddlewareInterface::class); - $store->set('middlewarekey', fn() => $middleware); + $store->set('middlewarekey', function () use ($middleware) { + return $middleware; + }); $this->assertSame($middleware, $store->get('middlewarekey')); } @@ -106,7 +108,12 @@ class MASTestClass class MASTestClassWithConstructorParams { - public function __construct(public $param1, public $param2) + public $param1; + public $param2; + + public function __construct($param1, $param2) { + $this->param1 = $param1; + $this->param2 = $param2; } } diff --git a/tests/Unit/Http/RouterTest.php b/tests/Unit/Http/RouterTest.php index dd00b06..9447e00 100644 --- a/tests/Unit/Http/RouterTest.php +++ b/tests/Unit/Http/RouterTest.php @@ -36,6 +36,17 @@ public function controller_does_not_have_namespace_added_when_it_already_exists( $this->assertSame(RouterTestController::class . '@test', $route->getActionName()); } + /** @test */ + public function controller_does_not_have_namespace_added_when_it_is_callable() + { + $router = new Router; + $controller = new RouterTestController; + + $route = $router->get('/test/123', [$controller, 'test']); + + $this->assertSame(RouterTestController::class . '@test', $route->getActionName()); + } + /** @test */ public function controller_does_not_have_namespace_added_when_it_is_closure() { @@ -52,7 +63,9 @@ public function controller_does_not_have_namespace_added_when_it_is_closure() */ public function can_extend_post_behaviour_with_macros() { - Router::macro('testFunctionAddedByMacro', fn() => 'abc123'); + Router::macro('testFunctionAddedByMacro', function () { + return 'abc123'; + }); $queryBuilder = new Router(); @@ -77,7 +90,9 @@ class RouterMixin { function testFunctionAddedByMixin() { - return fn() => 'abc123'; + return function() { + return 'abc123'; + }; } } diff --git a/tests/Unit/PostQueryBuilderTest.php b/tests/Unit/PostQueryBuilderTest.php index a8af46b..7ea2c3d 100644 --- a/tests/Unit/PostQueryBuilderTest.php +++ b/tests/Unit/PostQueryBuilderTest.php @@ -50,9 +50,15 @@ public function can_create_a_builder_from_static_functions() */ public function throw_error_on_missing_static_function() { - $this->expectException(Throwable::class); + $errorThrown = false; - Post::missingStaticFunction(); + try { + Post::missingStaticFunction(); + } catch (Throwable $e) { + $errorThrown = true; + } + + $this->assertTrue($errorThrown); } private function assertQueryBuilder($function, $params, $postType) @@ -77,7 +83,6 @@ public static function setCreateBuilderResponse($builder) static::$injectedBuilder = $builder; } - #[\Override] public static function builder(): ScopedQueryBuilder { return static::$injectedBuilder; diff --git a/tests/Unit/PostTest.php b/tests/Unit/PostTest.php index e57e3d9..4aabdc7 100644 --- a/tests/Unit/PostTest.php +++ b/tests/Unit/PostTest.php @@ -30,7 +30,7 @@ public function register_function_calls_register_post_type_when_post_type_and_co RegisterablePostType::register(); - $this->assertNotFalse(has_filter('timber/post/classmap', RegisterablePostType::filterTimberPostClassMap(...))); + $this->assertNotFalse(has_filter('timber/post/classmap', [RegisterablePostType::class, 'filterTimberPostClassMap'])); } /** @test */ @@ -213,7 +213,9 @@ public function all_can_have_order_set() */ public function can_extend_post_behaviour_with_macros() { - Post::macro('testFunctionAddedByMacro', fn() => 'abc123'); + Post::macro('testFunctionAddedByMacro', function () { + return 'abc123'; + }); $post = new Post(null, true); @@ -226,7 +228,9 @@ public function can_extend_post_behaviour_with_macros() */ public function macros_set_correct_this_context_on_instances() { - PostWithPrivateData::macro('testFunctionAddedByMacro', fn() => $this->dummyData); + PostWithPrivateData::macro('testFunctionAddedByMacro', function () { + return $this->dummyData; + }); $post = new PostWithPrivateData(null, true); @@ -250,7 +254,9 @@ class PostMixin { function testFunctionAddedByMixin() { - return fn() => 'abc123'; + return function () { + return 'abc123'; + }; } } @@ -261,7 +267,6 @@ class PostWithPrivateData extends Post class RegisterablePostType extends Post { - #[\Override] public static function getPostType(): string { return 'registerable_post_type'; @@ -312,7 +317,6 @@ protected static function getPostTypeConfig(): array class UnregisterablePostTypeWithoutConfig extends Post { - #[\Override] public static function getPostType(): string { return 'post_type'; diff --git a/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php b/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php index ac2aa05..ae58e05 100644 --- a/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php +++ b/tests/Unit/Providers/CustomPostTypesServiceProviderTest.php @@ -37,7 +37,6 @@ public function should_call_register_post_type_for_each_configured_post_type() class CustomPost1 extends Post { - #[\Override] public static function getPostType() { return 'custom_post_1'; @@ -53,7 +52,6 @@ protected static function getPostTypeConfig() class CustomPost2 extends Post { - #[\Override] public static function getPostType() { return 'custom_post_1'; diff --git a/tests/Unit/Providers/EncryptionServiceProviderTest.php b/tests/Unit/Providers/EncryptionServiceProviderTest.php index 001b072..89ccfdb 100644 --- a/tests/Unit/Providers/EncryptionServiceProviderTest.php +++ b/tests/Unit/Providers/EncryptionServiceProviderTest.php @@ -2,11 +2,11 @@ namespace Rareloop\Lumberjack\Test\Providers; -use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; -use Illuminate\Encryption\Encrypter; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; +use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; +use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Providers\EncryptionServiceProvider; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; @@ -18,7 +18,7 @@ class EncryptionServiceProviderTest extends TestCase public function encryptor_is_registered_in_container_when_a_config_key_is_present() { $config = new Config; - $config->set('app.key', Encrypter::generateKey('aes-128-cbc')); + $config->set('app.key', 'encryption-key'); $app = new Application(); $app->bind('config', $config); diff --git a/tests/Unit/Providers/RouterServiceProviderTest.php b/tests/Unit/Providers/RouterServiceProviderTest.php index 7926a8c..5f7b813 100644 --- a/tests/Unit/Providers/RouterServiceProviderTest.php +++ b/tests/Unit/Providers/RouterServiceProviderTest.php @@ -101,7 +101,9 @@ public function basedir_is_set_from_wordpress_config() $lumberjack->bootstrap(); $router = $app->get('router'); - $router->get('/test/123', fn() => 'abc123'); + $router->get('/test/123', function () { + return 'abc123'; + }); $response = $router->match($request); $this->assertSame(200, $response->getStatusCode()); @@ -284,7 +286,14 @@ private function setSiteUrl($url) class RSPAddHeaderMiddleware implements MiddlewareInterface { - public function __construct(private $key, private $value) {} + private $key; + private $value; + + public function __construct($key, $value) + { + $this->key = $key; + $this->value = $value; + } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php index bee3e8b..713c84d 100644 --- a/tests/Unit/Providers/WordPressControllersServiceProviderTest.php +++ b/tests/Unit/Providers/WordPressControllersServiceProviderTest.php @@ -40,7 +40,7 @@ public function template_include_filter_is_applied_on_boot() $app->register($provider); $app->boot(); - $this->assertNotFalse(has_filter('template_include', $provider->handleTemplateInclude(...))); + $this->assertNotFalse(has_filter('template_include', [$provider, 'handleTemplateInclude'])); } /** @test */ @@ -195,7 +195,7 @@ public function handle_request_returns_response_when_controller_does_exist() $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestController::class, 'handle'); @@ -212,7 +212,7 @@ public function handle_request_returns_response_when_controller_returns_a_respon $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerReturningAResponsable::class, 'handle'); @@ -230,7 +230,7 @@ public function handle_request_resolves_constructor_params_from_container() $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithConstructorParams::class, 'handle'); @@ -247,7 +247,7 @@ public function handle_request_resolves_controller_method_params_from_container( $app = new Application(__DIR__ . '/../'); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithHandleParams::class, 'handle'); @@ -267,7 +267,7 @@ public function handle_request_supports_middleware() $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -288,7 +288,7 @@ public function handle_request_adds_password_protect_middleware() $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -308,7 +308,7 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_ $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -328,7 +328,7 @@ public function handle_request_supports_middleware_applied_to_a_specific_method_ $app->bind(TestControllerWithMiddleware::class, $controller); $provider = new WordPressControllersServiceProvider($app); - $provider->boot(); + $provider->boot($app); $response = $provider->handleRequest(new ServerRequest, TestControllerWithMiddleware::class, 'handle'); @@ -358,7 +358,7 @@ public function handle_request_supports_middleware_aliases() $provider = new WordPressControllersServiceProvider($app); $routerProvider->register(); $routerProvider->boot(); - $provider->boot(); + $provider->boot($app); $store = $app->get(MiddlewareAliases::class); $store->set('middleware-key', new AddHeaderMiddleware('X-Header', 'testing123')); @@ -399,19 +399,27 @@ public function handle_template_include_will_not_call_app_shutdown_when_it_has_n class TestController { - public function handle() {} + public function handle() + { + } } class TestControllerWithConstructorParams { - public function __construct(Application $app) {} + public function __construct(Application $app) + { + } - public function handle() {} + public function handle() + { + } } class TestControllerWithHandleParams { - public function handle(Application $app) {} + public function handle(Application $app) + { + } } class MyResponsable implements Responsable @@ -432,12 +440,21 @@ public function handle() class TestControllerWithMiddleware extends Controller { - public function handle() {} + public function handle() + { + } } class AddHeaderMiddleware implements MiddlewareInterface { - public function __construct(private $key, private $value) {} + private $key; + private $value; + + public function __construct($key, $value) + { + $this->key = $key; + $this->value = $value; + } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/tests/Unit/QueryBuilderTest.php b/tests/Unit/QueryBuilderTest.php index a059f5c..20a1426 100644 --- a/tests/Unit/QueryBuilderTest.php +++ b/tests/Unit/QueryBuilderTest.php @@ -526,7 +526,6 @@ function testFunctionAddedByMixin() class PostWithCustomPostType extends Post { - #[\Override] public static function getPostType() { return 'post_with_query_scope'; diff --git a/tests/Unit/ScopedQueryBuilderTest.php b/tests/Unit/ScopedQueryBuilderTest.php index 30afa52..592e8da 100644 --- a/tests/Unit/ScopedQueryBuilderTest.php +++ b/tests/Unit/ScopedQueryBuilderTest.php @@ -108,10 +108,16 @@ public function can_pass_params_into_a_query_scope_on_post_object() */ public function missing_query_scope_throws_an_error() { - $this->expectException(Throwable::class); + $errorThrown = false; - $builder = new ScopedQueryBuilder(PostWithQueryScope::class); - $builder->nonExistentScope(); + try { + $builder = new ScopedQueryBuilder(PostWithQueryScope::class); + $builder->nonExistentScope(); + } catch (Throwable $e) { + $errorThrown = true; + } + + $this->assertTrue($errorThrown); } /** @test */ @@ -154,7 +160,6 @@ public function nonStandardMethod() class PostWithQueryScope extends Post { - #[\Override] public static function getPostType() { return 'post_with_query_scope'; diff --git a/tests/Unit/Session/EncryptedStoreTest.php b/tests/Unit/Session/EncryptedStoreTest.php index d9ccb65..8cd64c4 100644 --- a/tests/Unit/Session/EncryptedStoreTest.php +++ b/tests/Unit/Session/EncryptedStoreTest.php @@ -3,11 +3,12 @@ namespace Rareloop\Lumberjack\Test; use Dcrypt\AesCbc; -use Illuminate\Encryption\Encrypter; use Mockery; use PHPUnit\Framework\TestCase; +use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Session\EncryptedStore; +use Rareloop\Lumberjack\Session\Store; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; class EncryptedStoreTest extends TestCase @@ -18,8 +19,10 @@ class EncryptedStoreTest extends TestCase public function data_is_encrypted_before_it_is_saved() { $serializedString = @serialize(['foo' => 'bar']); - $encrypter = Mockery::mock(Encrypter::class); - $encrypter->shouldReceive('encrypt')->withArgs(function ($array) { + $encrypter = Mockery::mock(Encrypter::class . '[encrypt]', ['encryption-key']); + $encrypter->shouldReceive('encrypt')->withArgs(function ($string) { + $array = @unserialize($string); + return $array['foo'] === 'bar'; })->once(); @@ -33,16 +36,17 @@ public function data_is_encrypted_before_it_is_saved() /** @test */ public function data_is_decrypted_before_it_is_loaded() { - $encrypter = new Encrypter(Encrypter::generateKey('aes-128-cbc')); + $encryptionKey = 'encryption-key'; + // Create the string that would have been stored by an encrypted store // Serialize once for the Encrypter and once for the Encrypted store - $encryptedString = $encrypter->encrypt(['foo' => 'bar']); + $encryptedString = AesCbc::encrypt(@serialize(@serialize(['foo' => 'bar'])), $encryptionKey); // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); $handler->shouldReceive('read')->andReturn($encryptedString); - $store = new EncryptedStore('session-name', $handler, $encrypter, 'session-id'); + $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id'); $store->start(); $this->assertSame('bar', $store->get('foo')); @@ -54,7 +58,7 @@ public function data_is_decrypted_before_it_is_loaded() */ public function unexpected_session_data_is_handled_gracefully($previousSessionValue) { - $encryptionKey = Encrypter::generateKey('aes-128-cbc'); + $encryptionKey = 'encryption-key'; // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); diff --git a/tests/Unit/Session/FileSessionHandlerTest.php b/tests/Unit/Session/FileSessionHandlerTest.php index 2a6ac05..c2801e9 100644 --- a/tests/Unit/Session/FileSessionHandlerTest.php +++ b/tests/Unit/Session/FileSessionHandlerTest.php @@ -32,7 +32,7 @@ public function close_returns_true() { $handler = new FileSessionHandler(vfsStream::url('exampleDir')); - $this->assertTrue($handler->close()); + $this->assertTrue($handler->close('save-path', 'session-name')); } /** @test */ diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index c5ba3e0..c46a2c9 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -3,17 +3,17 @@ namespace Rareloop\Lumberjack\Test; use Mockery; -use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; -use Rareloop\Lumberjack\Config; -use Illuminate\Encryption\Encrypter; use Rareloop\Lumberjack\Application; -use Rareloop\Lumberjack\Session\Store; +use Rareloop\Lumberjack\Config; +use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; +use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Session\EncryptedStore; -use Rareloop\Lumberjack\Session\SessionManager; use Rareloop\Lumberjack\Session\FileSessionHandler; +use Rareloop\Lumberjack\Session\SessionManager; +use Rareloop\Lumberjack\Session\Store; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; -use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract; +use org\bovigo\vfs\vfsStream; class SessionManagerTest extends TestCase { @@ -74,7 +74,9 @@ public function can_extend_list_of_drivers() $app = new Application; $manager = new SessionManager($app); - $manager->extend('test', fn() => new Store('name', new TestSessionHandler)); + $manager->extend('test', function () { + return new Store('name', new TestSessionHandler); + }); $this->assertInstanceOf(TestSessionHandler::class, $manager->driver('test')->getHandler()); } @@ -92,7 +94,7 @@ public function can_create_an_unencrypted_store() public function can_create_an_encrypted_store() { $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); - $app->bind(EncrypterContract::class, new Encrypter(Encrypter::generateKey('aes-128-cbc'))); + $app->bind(EncrypterContract::class, new Encrypter('encryption-key')); $manager = new SessionManager($app); diff --git a/tests/Unit/Session/StoreTest.php b/tests/Unit/Session/StoreTest.php index e399091..b167f8a 100644 --- a/tests/Unit/Session/StoreTest.php +++ b/tests/Unit/Session/StoreTest.php @@ -205,7 +205,7 @@ public function can_flush_all_values() public function starting_a_session_loads_data_from_handler() { $handler = Mockery::mock(NullSessionHandler::class . '[read]'); - $handler->shouldReceive('read')->once()->with('session-id')->andReturn(['foo' => 'bar']); + $handler->shouldReceive('read')->once()->with('session-id')->andReturn(serialize(['foo' => 'bar'])); $store = new Store('session-name', $handler, 'session-id'); @@ -219,7 +219,9 @@ public function saving_a_session_writes_data_to_handler() { $handler = Mockery::mock(NullSessionHandler::class . '[write]'); $handler->shouldReceive('write')->once()->with('session-id', Mockery::on(function ($argument) { - return isset($argument['foo']) && $argument['foo'] === 'bar'; + $array = @unserialize($argument); + + return isset($array['foo']) && $array['foo'] === 'bar'; })); $store = new Store('session-name', $handler, 'session-id'); From 492a9046bca0ecfc74863fd9aa785d42d6297853 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 16:20:01 +0100 Subject: [PATCH 23/43] Formatting tweaks --- tests/Unit/ApplicationTest.php | 44 ++++++++++++++++++++------- tests/Unit/Exceptions/HandlerTest.php | 4 ++- tests/Unit/HelpersTest.php | 14 +++++---- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/tests/Unit/ApplicationTest.php b/tests/Unit/ApplicationTest.php index 4d935af..cc5f13c 100644 --- a/tests/Unit/ApplicationTest.php +++ b/tests/Unit/ApplicationTest.php @@ -575,36 +575,58 @@ public function bootstrap(Application $app) } } -class TestBootstrapper1 extends TestBootstrapperBase {} +class TestBootstrapper1 extends TestBootstrapperBase +{ +} -class TestBootstrapper2 extends TestBootstrapperBase {} +class TestBootstrapper2 extends TestBootstrapperBase +{ +} -interface TestInterface {} +interface TestInterface +{ +} -class TestInterfaceImplementation implements TestInterface {} +class TestInterfaceImplementation implements TestInterface +{ +} class TestInterfaceImplementationWithConstructorParams implements TestInterface { - public function __construct(TestServiceProvider $provider) {} + public function __construct(TestServiceProvider $provider) + { + } } -interface TestSubInterface {} +interface TestSubInterface +{ +} -class TestSubInterfaceImplementation implements TestSubInterface {} +class TestSubInterfaceImplementation implements TestSubInterface +{ +} class TestServiceProvider extends ServiceProvider { - public function register() {} - public function boot() {} + public function register() + { + } + public function boot() + { + } } -class EmptyServiceProvider extends ServiceProvider {} +class EmptyServiceProvider extends ServiceProvider +{ +} class TestBootServiceProvider extends ServiceProvider { private $bootCallback; - public function register() {} + public function register() + { + } public function boot(Application $app, TestInterface $test) { diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index 5c424f9..e5aacec 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -124,4 +124,6 @@ class HandlerWithBlacklist extends Handler ]; } -class BlacklistedException extends \Exception {} +class BlacklistedException extends \Exception +{ +} diff --git a/tests/Unit/HelpersTest.php b/tests/Unit/HelpersTest.php index f61ad57..870ee98 100644 --- a/tests/Unit/HelpersTest.php +++ b/tests/Unit/HelpersTest.php @@ -3,11 +3,13 @@ namespace Rareloop\Lumberjack\Test; use Timber\Timber; +use Monolog\Logger; use Rareloop\Router\Router; use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Config; use Rareloop\Lumberjack\Helpers; use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\FacadeFactory; use Rareloop\Lumberjack\Facades\Session; use Rareloop\Lumberjack\Exceptions\Handler; use Rareloop\Lumberjack\Http\ServerRequest; @@ -16,8 +18,6 @@ use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Http\Responses\TimberResponse; use Rareloop\Lumberjack\Http\Responses\RedirectResponse; -use Monolog\Logger; -use Rareloop\Lumberjack\FacadeFactory; /** * @runTestsInSeparateProcesses @@ -141,12 +141,13 @@ public function can_get_a_url_for_a_named_route() $app = new Application; FacadeFactory::setContainer($app); $router = new Router; - $router->get('test/route', function () {})->name('test.route'); + $router->get('test/route', function () { + })->name('test.route'); $app->bind('router', $router); $url = Helpers::route('test.route'); - $this->assertSame('test/route', trim((string) $url, '/')); + $this->assertSame('test/route', trim($url, '/')); } /** @test */ @@ -155,14 +156,15 @@ public function can_get_a_url_for_a_named_route_with_params() $app = new Application; FacadeFactory::setContainer($app); $router = new Router; - $router->get('test/{name}', function ($name) {})->name('test.route'); + $router->get('test/{name}', function ($name) { + })->name('test.route'); $app->bind('router', $router); $url = Helpers::route('test.route', [ 'name' => 'route', ]); - $this->assertSame('test/route', trim((string) $url, '/')); + $this->assertSame('test/route', trim($url, '/')); } /** @test */ From 4222a3236309b718b85756397298b68a19cdd64e Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 17:08:46 +0100 Subject: [PATCH 24/43] Update EncryptionServiceProvider This ensures that the encrypter is only created at the point of it being used. --- src/Providers/EncryptionServiceProvider.php | 25 +++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php index ec762cc..5a4412b 100644 --- a/src/Providers/EncryptionServiceProvider.php +++ b/src/Providers/EncryptionServiceProvider.php @@ -2,11 +2,8 @@ namespace Rareloop\Lumberjack\Providers; -use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; use Rareloop\Lumberjack\Encrypter; -use Rareloop\Lumberjack\Facades\Config; -use Rareloop\Lumberjack\Session\SessionManager; class EncryptionServiceProvider extends ServiceProvider { @@ -17,10 +14,30 @@ public function register() if ($this->app->has('config')) { $encryptionKey = $this->app->get('config')->get('app.key'); - $encrypter = new Encrypter($encryptionKey); + $encrypter = function () use ($config) { + $key = $config->get('app.key'); + $cipher = $config->get('app.cipher', 'aes-256-cbc'); + + // Throw own exception + // Add unit test for legacy decryption + // test base64 and nonbase64 + + return new Encrypter($this->parseKey($key), $cipher); + }; $this->app->bind(EncrypterContract::class, $encrypter); $this->app->bind('encrypter', $encrypter); } } + + protected function parseKey(string $key): string + { + $prefix = 'base64:'; + + if (str_starts_with($key, $prefix)) { + $key = base64_decode(str_replace($prefix, '', $key)); + } + + return $key; + } } From d69d636fb16c811ec3d73661db4956db1eed7fbb Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 17:10:54 +0100 Subject: [PATCH 25/43] Fix bug --- src/Providers/EncryptionServiceProvider.php | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php index 5a4412b..e9c9dfd 100644 --- a/src/Providers/EncryptionServiceProvider.php +++ b/src/Providers/EncryptionServiceProvider.php @@ -12,17 +12,10 @@ class EncryptionServiceProvider extends ServiceProvider public function register() { if ($this->app->has('config')) { - $encryptionKey = $this->app->get('config')->get('app.key'); + $encrypter = function () { + $encryptionKey = $this->app->get('config')->get('app.key'); - $encrypter = function () use ($config) { - $key = $config->get('app.key'); - $cipher = $config->get('app.cipher', 'aes-256-cbc'); - - // Throw own exception - // Add unit test for legacy decryption - // test base64 and nonbase64 - - return new Encrypter($this->parseKey($key), $cipher); + return new Encrypter($this->parseKey($encryptionKey), 'aes-256-cbc'); }; $this->app->bind(EncrypterContract::class, $encrypter); From 366d39fe8655ed232f70b582f40a8be6e3488cb9 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 18:37:59 +0100 Subject: [PATCH 26/43] Fix PHP errors --- src/Application.php | 3 +-- src/QueryBuilder.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Application.php b/src/Application.php index 9714ab2..599f9fd 100644 --- a/src/Application.php +++ b/src/Application.php @@ -8,7 +8,6 @@ use Laminas\HttpHandlerRunner\Emitter\SapiEmitter; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; -use function Http\Response\send; class Application implements ContainerInterface { @@ -151,7 +150,7 @@ private function isSingletonClassBind($id) * * @return bool */ - public function has($id) + public function has($id): bool { return $this->container->has($id); } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 384fee6..f51ad99 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -82,7 +82,7 @@ public function orderBy($orderBy, string $order = QueryBuilder::ASC): QueryBuild return $this; } - public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, string $type = null): QueryBuilderContract + public function orderByMeta($metaKey, string $order = QueryBuilder::ASC, ?string $type = null): QueryBuilderContract { $order = strtoupper($order); From 2af859cd94f84da1587fe15abdadbc9f8d35135b Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 18:38:20 +0100 Subject: [PATCH 27/43] Fix namespaces --- tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php index c19f006..ff38c42 100644 --- a/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php +++ b/tests/Unit/Bootstrappers/RegisterExceptionHandlerTest.php @@ -14,10 +14,9 @@ use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Test\Unit\BrainMonkeyPHPUnitIntegration; use Rareloop\Router\Responsable; -use Symfony\Component\Debug\Exception\FatalErrorException; -use Zend\Diactoros\Response; -use Zend\Diactoros\Response\TextResponse; -use Zend\Diactoros\ServerRequest; +use Laminas\Diactoros\Response; +use Laminas\Diactoros\Response\TextResponse; +use Laminas\Diactoros\ServerRequest; /** * @runTestsInSeparateProcesses From 0b1e0369368747822541c4204560a43cb75d77bf Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Thu, 9 Oct 2025 18:38:39 +0100 Subject: [PATCH 28/43] WIP refactor encrypter and tests --- src/Encrypter.php | 22 +++++++++++++--- src/Providers/EncryptionServiceProvider.php | 13 +-------- tests/Unit/EncrypterTest.php | 29 ++++++++++----------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Encrypter.php b/src/Encrypter.php index 12e3049..c9f707a 100644 --- a/src/Encrypter.php +++ b/src/Encrypter.php @@ -2,25 +2,39 @@ namespace Rareloop\Lumberjack; -use Dcrypt\AesCbc; +use Illuminate\Encryption\Encrypter as IlluminateEncrypter; +use Illuminate\Contracts\Encryption\Encrypter as IlluminateEncrypterContract; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; class Encrypter implements EncrypterContract { protected $key; + protected static $cipher = 'aes-256-cbc'; + protected $encrypter; public function __construct($key) { - $this->key = $key; + $this->encrypter = new IlluminateEncrypter($this->parseKey($key), static::$cipher); } public function encrypt($data) { - return AesCbc::encrypt(@serialize($data), $this->key); + return $this->encrypter->encrypt($data); } public function decrypt($data) { - return @unserialize(AesCbc::decrypt($data, $this->key)); + return $this->encrypter->decrypt($data); + } + + protected function parseKey(string $key): string + { + $prefix = 'base64:'; + + if (str_starts_with($key, $prefix)) { + $key = base64_decode(str_replace($prefix, '', $key)); + } + + return $key; } } diff --git a/src/Providers/EncryptionServiceProvider.php b/src/Providers/EncryptionServiceProvider.php index e9c9dfd..87b7c3f 100644 --- a/src/Providers/EncryptionServiceProvider.php +++ b/src/Providers/EncryptionServiceProvider.php @@ -15,22 +15,11 @@ public function register() $encrypter = function () { $encryptionKey = $this->app->get('config')->get('app.key'); - return new Encrypter($this->parseKey($encryptionKey), 'aes-256-cbc'); + return new Encrypter($encryptionKey); }; $this->app->bind(EncrypterContract::class, $encrypter); $this->app->bind('encrypter', $encrypter); } } - - protected function parseKey(string $key): string - { - $prefix = 'base64:'; - - if (str_starts_with($key, $prefix)) { - $key = base64_decode(str_replace($prefix, '', $key)); - } - - return $key; - } } diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php index df52253..1df453a 100644 --- a/tests/Unit/EncrypterTest.php +++ b/tests/Unit/EncrypterTest.php @@ -2,14 +2,13 @@ namespace Rareloop\Lumberjack\Test; -use Dcrypt\AesCbc; use Mockery; +use ReflectionProperty; use PHPUnit\Framework\TestCase; -use Rareloop\Lumberjack\Config; use Rareloop\Lumberjack\Encrypter; +use Illuminate\Encryption\Encrypter as IlluminateEncrypter; /** - * @runTestsInSeparateProcesses * @preserveGlobalState disabled */ class EncrypterTest extends TestCase @@ -19,21 +18,21 @@ class EncrypterTest extends TestCase /** @test */ public function can_encrypt_data() { - $key = 'secret-key'; - $dcrypt = Mockery::mock('alias:' . AesCbc::class); - $dcrypt->shouldReceive('encrypt')->withArgs(function ($data, $key) { - if ($key !== 'secret-key') { - return false; - } - - if ($data !== @serialize('test-string')) { - return false; - } + $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; - return true; - })->once(); + $illuminateMock = Mockery::mock(IlluminateEncrypter::class); + $illuminateMock + ->shouldReceive('encrypt') + ->with('test-string') + ->once(); $encrypter = new Encrypter($key); + + // Replace the internal Illuminate encrypter + $ref = new ReflectionProperty($encrypter, 'encrypter'); + $ref->setAccessible(true); + $ref->setValue($encrypter, $illuminateMock); + $encrypter->encrypt('test-string'); } From 42a3199bbf99bc4f1eb33fef16b5bcf5e7bf0e54 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 13:24:29 +0100 Subject: [PATCH 29/43] Fix encryption and session storage --- src/Encrypter.php | 27 ++++++- src/Exceptions/DecryptException.php | 8 ++ src/Exceptions/EncryptException.php | 8 ++ .../InvalidEncryptionKeyException.php | 8 ++ .../WordPressControllersServiceProvider.php | 2 +- src/Session/EncryptedStore.php | 4 +- tests/Unit/EncrypterTest.php | 81 ++++++++++++++++--- .../EncryptionServiceProviderTest.php | 2 +- tests/Unit/Session/EncryptedStoreTest.php | 24 +++--- tests/Unit/Session/SessionManagerTest.php | 2 +- 10 files changed, 137 insertions(+), 29 deletions(-) create mode 100644 src/Exceptions/DecryptException.php create mode 100644 src/Exceptions/EncryptException.php create mode 100644 src/Exceptions/InvalidEncryptionKeyException.php diff --git a/src/Encrypter.php b/src/Encrypter.php index c9f707a..59cfdff 100644 --- a/src/Encrypter.php +++ b/src/Encrypter.php @@ -3,8 +3,10 @@ namespace Rareloop\Lumberjack; use Illuminate\Encryption\Encrypter as IlluminateEncrypter; -use Illuminate\Contracts\Encryption\Encrypter as IlluminateEncrypterContract; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; +use Rareloop\Lumberjack\Exceptions\DecryptException; +use Rareloop\Lumberjack\Exceptions\EncryptException; +use Rareloop\Lumberjack\Exceptions\InvalidEncryptionKeyException; class Encrypter implements EncrypterContract { @@ -14,17 +16,34 @@ class Encrypter implements EncrypterContract public function __construct($key) { - $this->encrypter = new IlluminateEncrypter($this->parseKey($key), static::$cipher); + try { + $this->encrypter = new IlluminateEncrypter($this->parseKey($key), static::$cipher); + } catch (\Throwable $th) { + throw new InvalidEncryptionKeyException; + } } public function encrypt($data) { - return $this->encrypter->encrypt($data); + try { + return $this->encrypter->encrypt($data); + } catch (\Throwable $th) { + throw new EncryptException('Unable to encrypt the data.'); + } } public function decrypt($data) { - return $this->encrypter->decrypt($data); + try { + return $this->encrypter->decrypt($data); + } catch (\Throwable $th) { + throw new DecryptException('Unable to decrypt the data: ' . $th->getMessage()); + } + } + + public static function generateKey() + { + return IlluminateEncrypter::generateKey(static::$cipher); } protected function parseKey(string $key): string diff --git a/src/Exceptions/DecryptException.php b/src/Exceptions/DecryptException.php new file mode 100644 index 0000000..64f7607 --- /dev/null +++ b/src/Exceptions/DecryptException.php @@ -0,0 +1,8 @@ +createDispatcher($middlewares); - return $dispatcher->dispatch($request); + return $dispatcher->handle($request); } private function createDispatcher(array $middlewares): Dispatcher diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 856b7c1..50f8b9c 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -3,8 +3,8 @@ namespace Rareloop\Lumberjack\Session; use Exception; +use Rareloop\Lumberjack\Contracts\Encrypter; use SessionHandlerInterface; -use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; class EncryptedStore extends Store @@ -17,7 +17,7 @@ public function __construct( SessionHandlerInterface $handler, Encrypter $encrypter, $id = null, - HandlerInterface $exceptionHandler = null + ?HandlerInterface $exceptionHandler = null ) { $this->encrypter = $encrypter; $this->exceptionHandler = $exceptionHandler; diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php index 1df453a..00c5f7c 100644 --- a/tests/Unit/EncrypterTest.php +++ b/tests/Unit/EncrypterTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use Rareloop\Lumberjack\Encrypter; use Illuminate\Encryption\Encrypter as IlluminateEncrypter; +use Illuminate\Contracts\Encryption\EncryptException as IlluminateEncryptException; /** * @preserveGlobalState disabled @@ -28,7 +29,6 @@ public function can_encrypt_data() $encrypter = new Encrypter($key); - // Replace the internal Illuminate encrypter $ref = new ReflectionProperty($encrypter, 'encrypter'); $ref->setAccessible(true); $ref->setValue($encrypter, $illuminateMock); @@ -37,21 +37,46 @@ public function can_encrypt_data() } /** @test */ - public function can_decrypt_data() + public function can_decrypt_data_using_old_key() { - $key = 'secret-key'; - $dcrypt = Mockery::mock('alias:' . AesCbc::class); - $dcrypt->shouldReceive('decrypt')->with('test-string', $key)->once(); + $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; + // $string = 'ixTgrcIr6+GvOB/S0Fdt4e4zbeLthAfqE/IINyhLI8zvLEgna/7yS7/uP1p7fZVdDDXuOlXutgkBJ4JnzFMnUD844YNa5b6k6aSk/v3+n4g='; + $string = 'eyJpdiI6InVjVHNkSWtlbnRESHZOVzlWMG9JcUE9PSIsInZhbHVlIjoiUDV4d3BlSy9jMTlpRTFNUGtHM0hmd3h6SHNHYTg1TzAyWmJHaWNSWi9iST0iLCJtYWMiOiJkNDgyNTc3YTk3ZTMwMjNmYjFkOWRiZDg3MTI5NDliMzhiMmU3N2ZmZWRhMGRhNGI5ZTc0MWMxYjdlODA5MmZmIiwidGFnIjoiIn0='; + + $encrypter = new Encrypter($key); + + $this->assertSame('test-string', $encrypter->decrypt($string)); + } + + /** @test */ + public function can_decrypt_data_that_was_encrypted_with_dcrypt_package() + { + $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; + $string = '3yHCC7zeDOxdAfJLy8np/yynZ/0YoLFoE9xdDrHaJD8to7+QyyMtyXjiZ16UXXxwxEnQc7jzZJ9w3dAIokA03WpahumAZO2JFHgTQTL/DKc='; + + $encrypter = new Encrypter($key); + $encrypter->encrypt('test-string'); + + $this->markTestIncomplete('Data is serialised differently between packages'); + $this->assertSame('test-string', $encrypter->decrypt($string)); + } + + /** @test */ + public function can_decrypt_data_using_base64_key() + { + $key = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; + $string = 'eyJpdiI6IklGL2JDOUVoK1ZucU5Na1lFdjRjOXc9PSIsInZhbHVlIjoiVDA3OGtPZUE2a2wrdUE3K2t1c2RoSkhaREJmRzVUY3NGaS8xdmpuR2tIND0iLCJtYWMiOiI0N2Y2NjJmYzZiMWFiN2I0ZGNiZjFmNWJiNzIzOWQ5ZGI5M2FjM2VhZDRmNDZiMDk5M2YwNDFhMWU2OGEwMjVhIiwidGFnIjoiIn0='; $encrypter = new Encrypter($key); - $encrypter->decrypt('test-string'); + + $this->assertSame('test-string', $encrypter->decrypt($string)); } /** @test */ public function can_process_strings() { $payload = 'test-string'; - $encrypter = new Encrypter('secret-key'); + $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); } @@ -60,7 +85,7 @@ public function can_process_strings() public function can_process_arrays() { $payload = ['foo' => 'bar']; - $encrypter = new Encrypter('secret-key'); + $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); } @@ -70,11 +95,49 @@ public function can_process_objects() { $payload = new \stdClass; $payload->foo = 'bar'; - $encrypter = new Encrypter('secret-key'); + $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); $output = $encrypter->decrypt($encrypter->encrypt($payload)); $this->assertInstanceOf(\stdClass::class, $output); $this->assertSame('bar', $output->foo); } + + /** @test */ + public function cannot_use_invalid_key() + { + $this->expectException(\Rareloop\Lumberjack\Exceptions\InvalidEncryptionKeyException::class); + + new Encrypter('invalid-key'); + } + + /** @test */ + public function cannot_decrypt_invalid_string() + { + $this->expectException(\Rareloop\Lumberjack\Exceptions\DecryptException::class); + + $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + $encrypter->decrypt('foo'); + } + + /** @test */ + public function cannot_encrypt_thing() + { + $this->expectException(\Rareloop\Lumberjack\Exceptions\EncryptException::class); + + $illuminateMock = Mockery::mock(IlluminateEncrypter::class); + $illuminateMock + ->shouldReceive('encrypt') + ->with('test-string') + ->once() + ->andThrow(new IlluminateEncryptException('Could not encrypt the data.')); + + $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + + $ref = new ReflectionProperty($encrypter, 'encrypter'); + $ref->setAccessible(true); + $ref->setValue($encrypter, $illuminateMock); + + $encrypter->encrypt('test-string'); + } } diff --git a/tests/Unit/Providers/EncryptionServiceProviderTest.php b/tests/Unit/Providers/EncryptionServiceProviderTest.php index 89ccfdb..2cbe034 100644 --- a/tests/Unit/Providers/EncryptionServiceProviderTest.php +++ b/tests/Unit/Providers/EncryptionServiceProviderTest.php @@ -18,7 +18,7 @@ class EncryptionServiceProviderTest extends TestCase public function encryptor_is_registered_in_container_when_a_config_key_is_present() { $config = new Config; - $config->set('app.key', 'encryption-key'); + $config->set('app.key', 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); $app = new Application(); $app->bind('config', $config); diff --git a/tests/Unit/Session/EncryptedStoreTest.php b/tests/Unit/Session/EncryptedStoreTest.php index 8cd64c4..5e71963 100644 --- a/tests/Unit/Session/EncryptedStoreTest.php +++ b/tests/Unit/Session/EncryptedStoreTest.php @@ -18,13 +18,14 @@ class EncryptedStoreTest extends TestCase /** @test */ public function data_is_encrypted_before_it_is_saved() { - $serializedString = @serialize(['foo' => 'bar']); - $encrypter = Mockery::mock(Encrypter::class . '[encrypt]', ['encryption-key']); - $encrypter->shouldReceive('encrypt')->withArgs(function ($string) { - $array = @unserialize($string); + $encrypter = Mockery::mock(Encrypter::class); + $encrypter->shouldReceive('encrypt') + ->withArgs(function ($string) { + $array = @unserialize($string); - return $array['foo'] === 'bar'; - })->once(); + return $array['foo'] === 'bar'; + }) + ->once(); $store = new EncryptedStore('session-name', new NullSessionHandler, $encrypter, 'session-id'); @@ -36,11 +37,12 @@ public function data_is_encrypted_before_it_is_saved() /** @test */ public function data_is_decrypted_before_it_is_loaded() { - $encryptionKey = 'encryption-key'; + $encryptionKey = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; - // Create the string that would have been stored by an encrypted store - // Serialize once for the Encrypter and once for the Encrypted store - $encryptedString = AesCbc::encrypt(@serialize(@serialize(['foo' => 'bar'])), $encryptionKey); + // Use a string that has been previously been encrypted by the store with the key above. It was encrypted using the following code: + // $encrypter = new Encrypter($encryptionKey); + // $value = $encrypter->encrypt(@serialize(['foo' => 'bar'])); + $encryptedString = 'eyJpdiI6IlAvOURGM3FPck1PU2hQTG9ScFVaMEE9PSIsInZhbHVlIjoiRmFmdmt6ZnVrNWo5c0JaQkNnSHBMTG42czhEWVlXWGZFTnBBWTdCdzBwNUxYZkFQdU9jd21CSCtocFhSMXZ5ayIsIm1hYyI6IjY4NzhkZjFiMDAyM2Y4ODI3MzQ1MTA5YmU3MDQ5ODljYTY5ZDFjNGFiNzdkYjRjNTBlMTgxZTE0MWY2ODIxYjUiLCJ0YWciOiIifQ=='; // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); @@ -58,7 +60,7 @@ public function data_is_decrypted_before_it_is_loaded() */ public function unexpected_session_data_is_handled_gracefully($previousSessionValue) { - $encryptionKey = 'encryption-key'; + $encryptionKey = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index c46a2c9..717f5f5 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -94,7 +94,7 @@ public function can_create_an_unencrypted_store() public function can_create_an_encrypted_store() { $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); - $app->bind(EncrypterContract::class, new Encrypter('encryption-key')); + $app->bind(EncrypterContract::class, new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w=')); $manager = new SessionManager($app); From c4fb85b79c62573375bb2d15f4eb54085eab5d85 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 15:31:27 +0100 Subject: [PATCH 30/43] Revert encryption changes --- composer.json | 8 +- src/Encrypter.php | 41 +-------- src/Session/EncryptedStore.php | 2 +- src/Session/FileSessionHandler.php | 9 +- tests/Unit/EncrypterTest.php | 106 +++++----------------- tests/Unit/Session/EncryptedStoreTest.php | 24 +++-- tests/Unit/Session/SessionManagerTest.php | 2 +- 7 files changed, 47 insertions(+), 145 deletions(-) diff --git a/composer.json b/composer.json index a1fd5a6..ac34e32 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "illuminate/encryption": "^10", + "mmeyer2k/dcrypt": "dev-master as 8.3.2", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^6.0" @@ -52,11 +52,15 @@ "repositories": [ { "type": "vcs", - "url": "https://github.com/rareloop/router" + "url": "https://github.com/Rareloop/router" }, { "type": "vcs", "url": "https://github.com/tommitchelmore/psr7-server-request-extension" + }, + { + "type": "vcs", + "url": "https://github.com/Rareloop/dcrypt" } ] } \ No newline at end of file diff --git a/src/Encrypter.php b/src/Encrypter.php index 59cfdff..12e3049 100644 --- a/src/Encrypter.php +++ b/src/Encrypter.php @@ -2,58 +2,25 @@ namespace Rareloop\Lumberjack; -use Illuminate\Encryption\Encrypter as IlluminateEncrypter; +use Dcrypt\AesCbc; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; -use Rareloop\Lumberjack\Exceptions\DecryptException; -use Rareloop\Lumberjack\Exceptions\EncryptException; -use Rareloop\Lumberjack\Exceptions\InvalidEncryptionKeyException; class Encrypter implements EncrypterContract { protected $key; - protected static $cipher = 'aes-256-cbc'; - protected $encrypter; public function __construct($key) { - try { - $this->encrypter = new IlluminateEncrypter($this->parseKey($key), static::$cipher); - } catch (\Throwable $th) { - throw new InvalidEncryptionKeyException; - } + $this->key = $key; } public function encrypt($data) { - try { - return $this->encrypter->encrypt($data); - } catch (\Throwable $th) { - throw new EncryptException('Unable to encrypt the data.'); - } + return AesCbc::encrypt(@serialize($data), $this->key); } public function decrypt($data) { - try { - return $this->encrypter->decrypt($data); - } catch (\Throwable $th) { - throw new DecryptException('Unable to decrypt the data: ' . $th->getMessage()); - } - } - - public static function generateKey() - { - return IlluminateEncrypter::generateKey(static::$cipher); - } - - protected function parseKey(string $key): string - { - $prefix = 'base64:'; - - if (str_starts_with($key, $prefix)) { - $key = base64_decode(str_replace($prefix, '', $key)); - } - - return $key; + return @unserialize(AesCbc::decrypt($data, $this->key)); } } diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 50f8b9c..7eff287 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -3,8 +3,8 @@ namespace Rareloop\Lumberjack\Session; use Exception; -use Rareloop\Lumberjack\Contracts\Encrypter; use SessionHandlerInterface; +use Rareloop\Lumberjack\Encrypter; use Rareloop\Lumberjack\Exceptions\HandlerInterface; class EncryptedStore extends Store diff --git a/src/Session/FileSessionHandler.php b/src/Session/FileSessionHandler.php index 80d5a48..9efa24b 100644 --- a/src/Session/FileSessionHandler.php +++ b/src/Session/FileSessionHandler.php @@ -8,13 +8,8 @@ class FileSessionHandler implements SessionHandlerInterface { - protected $path; - protected $prefix; - - public function __construct($path, $prefix = 'lumberjack_session_') + public function __construct(protected $path, protected $prefix = 'lumberjack_session_') { - $this->path = $path; - $this->prefix = $prefix; } #[\ReturnTypeWillChange] @@ -46,7 +41,7 @@ public function write($sessionId, $data) { try { file_put_contents($this->getFilepath($sessionId), $data); - } catch (Exception $e) { + } catch (Exception) { Log::error('Failed to create session on disk'); } diff --git a/tests/Unit/EncrypterTest.php b/tests/Unit/EncrypterTest.php index 00c5f7c..df52253 100644 --- a/tests/Unit/EncrypterTest.php +++ b/tests/Unit/EncrypterTest.php @@ -2,14 +2,14 @@ namespace Rareloop\Lumberjack\Test; +use Dcrypt\AesCbc; use Mockery; -use ReflectionProperty; use PHPUnit\Framework\TestCase; +use Rareloop\Lumberjack\Config; use Rareloop\Lumberjack\Encrypter; -use Illuminate\Encryption\Encrypter as IlluminateEncrypter; -use Illuminate\Contracts\Encryption\EncryptException as IlluminateEncryptException; /** + * @runTestsInSeparateProcesses * @preserveGlobalState disabled */ class EncrypterTest extends TestCase @@ -19,64 +19,40 @@ class EncrypterTest extends TestCase /** @test */ public function can_encrypt_data() { - $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; + $key = 'secret-key'; + $dcrypt = Mockery::mock('alias:' . AesCbc::class); + $dcrypt->shouldReceive('encrypt')->withArgs(function ($data, $key) { + if ($key !== 'secret-key') { + return false; + } - $illuminateMock = Mockery::mock(IlluminateEncrypter::class); - $illuminateMock - ->shouldReceive('encrypt') - ->with('test-string') - ->once(); + if ($data !== @serialize('test-string')) { + return false; + } - $encrypter = new Encrypter($key); - - $ref = new ReflectionProperty($encrypter, 'encrypter'); - $ref->setAccessible(true); - $ref->setValue($encrypter, $illuminateMock); - - $encrypter->encrypt('test-string'); - } - - /** @test */ - public function can_decrypt_data_using_old_key() - { - $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; - // $string = 'ixTgrcIr6+GvOB/S0Fdt4e4zbeLthAfqE/IINyhLI8zvLEgna/7yS7/uP1p7fZVdDDXuOlXutgkBJ4JnzFMnUD844YNa5b6k6aSk/v3+n4g='; - $string = 'eyJpdiI6InVjVHNkSWtlbnRESHZOVzlWMG9JcUE9PSIsInZhbHVlIjoiUDV4d3BlSy9jMTlpRTFNUGtHM0hmd3h6SHNHYTg1TzAyWmJHaWNSWi9iST0iLCJtYWMiOiJkNDgyNTc3YTk3ZTMwMjNmYjFkOWRiZDg3MTI5NDliMzhiMmU3N2ZmZWRhMGRhNGI5ZTc0MWMxYjdlODA5MmZmIiwidGFnIjoiIn0='; - - $encrypter = new Encrypter($key); - - $this->assertSame('test-string', $encrypter->decrypt($string)); - } - - /** @test */ - public function can_decrypt_data_that_was_encrypted_with_dcrypt_package() - { - $key = 'YurEU4attIXDGJWTL5VNvEXkMTosdBah'; - $string = '3yHCC7zeDOxdAfJLy8np/yynZ/0YoLFoE9xdDrHaJD8to7+QyyMtyXjiZ16UXXxwxEnQc7jzZJ9w3dAIokA03WpahumAZO2JFHgTQTL/DKc='; + return true; + })->once(); $encrypter = new Encrypter($key); $encrypter->encrypt('test-string'); - - $this->markTestIncomplete('Data is serialised differently between packages'); - $this->assertSame('test-string', $encrypter->decrypt($string)); } /** @test */ - public function can_decrypt_data_using_base64_key() + public function can_decrypt_data() { - $key = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; - $string = 'eyJpdiI6IklGL2JDOUVoK1ZucU5Na1lFdjRjOXc9PSIsInZhbHVlIjoiVDA3OGtPZUE2a2wrdUE3K2t1c2RoSkhaREJmRzVUY3NGaS8xdmpuR2tIND0iLCJtYWMiOiI0N2Y2NjJmYzZiMWFiN2I0ZGNiZjFmNWJiNzIzOWQ5ZGI5M2FjM2VhZDRmNDZiMDk5M2YwNDFhMWU2OGEwMjVhIiwidGFnIjoiIn0='; + $key = 'secret-key'; + $dcrypt = Mockery::mock('alias:' . AesCbc::class); + $dcrypt->shouldReceive('decrypt')->with('test-string', $key)->once(); $encrypter = new Encrypter($key); - - $this->assertSame('test-string', $encrypter->decrypt($string)); + $encrypter->decrypt('test-string'); } /** @test */ public function can_process_strings() { $payload = 'test-string'; - $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + $encrypter = new Encrypter('secret-key'); $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); } @@ -85,7 +61,7 @@ public function can_process_strings() public function can_process_arrays() { $payload = ['foo' => 'bar']; - $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + $encrypter = new Encrypter('secret-key'); $this->assertSame($payload, $encrypter->decrypt($encrypter->encrypt($payload))); } @@ -95,49 +71,11 @@ public function can_process_objects() { $payload = new \stdClass; $payload->foo = 'bar'; - $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + $encrypter = new Encrypter('secret-key'); $output = $encrypter->decrypt($encrypter->encrypt($payload)); $this->assertInstanceOf(\stdClass::class, $output); $this->assertSame('bar', $output->foo); } - - /** @test */ - public function cannot_use_invalid_key() - { - $this->expectException(\Rareloop\Lumberjack\Exceptions\InvalidEncryptionKeyException::class); - - new Encrypter('invalid-key'); - } - - /** @test */ - public function cannot_decrypt_invalid_string() - { - $this->expectException(\Rareloop\Lumberjack\Exceptions\DecryptException::class); - - $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); - $encrypter->decrypt('foo'); - } - - /** @test */ - public function cannot_encrypt_thing() - { - $this->expectException(\Rareloop\Lumberjack\Exceptions\EncryptException::class); - - $illuminateMock = Mockery::mock(IlluminateEncrypter::class); - $illuminateMock - ->shouldReceive('encrypt') - ->with('test-string') - ->once() - ->andThrow(new IlluminateEncryptException('Could not encrypt the data.')); - - $encrypter = new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); - - $ref = new ReflectionProperty($encrypter, 'encrypter'); - $ref->setAccessible(true); - $ref->setValue($encrypter, $illuminateMock); - - $encrypter->encrypt('test-string'); - } } diff --git a/tests/Unit/Session/EncryptedStoreTest.php b/tests/Unit/Session/EncryptedStoreTest.php index 5e71963..8cd64c4 100644 --- a/tests/Unit/Session/EncryptedStoreTest.php +++ b/tests/Unit/Session/EncryptedStoreTest.php @@ -18,14 +18,13 @@ class EncryptedStoreTest extends TestCase /** @test */ public function data_is_encrypted_before_it_is_saved() { - $encrypter = Mockery::mock(Encrypter::class); - $encrypter->shouldReceive('encrypt') - ->withArgs(function ($string) { - $array = @unserialize($string); + $serializedString = @serialize(['foo' => 'bar']); + $encrypter = Mockery::mock(Encrypter::class . '[encrypt]', ['encryption-key']); + $encrypter->shouldReceive('encrypt')->withArgs(function ($string) { + $array = @unserialize($string); - return $array['foo'] === 'bar'; - }) - ->once(); + return $array['foo'] === 'bar'; + })->once(); $store = new EncryptedStore('session-name', new NullSessionHandler, $encrypter, 'session-id'); @@ -37,12 +36,11 @@ public function data_is_encrypted_before_it_is_saved() /** @test */ public function data_is_decrypted_before_it_is_loaded() { - $encryptionKey = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; + $encryptionKey = 'encryption-key'; - // Use a string that has been previously been encrypted by the store with the key above. It was encrypted using the following code: - // $encrypter = new Encrypter($encryptionKey); - // $value = $encrypter->encrypt(@serialize(['foo' => 'bar'])); - $encryptedString = 'eyJpdiI6IlAvOURGM3FPck1PU2hQTG9ScFVaMEE9PSIsInZhbHVlIjoiRmFmdmt6ZnVrNWo5c0JaQkNnSHBMTG42czhEWVlXWGZFTnBBWTdCdzBwNUxYZkFQdU9jd21CSCtocFhSMXZ5ayIsIm1hYyI6IjY4NzhkZjFiMDAyM2Y4ODI3MzQ1MTA5YmU3MDQ5ODljYTY5ZDFjNGFiNzdkYjRjNTBlMTgxZTE0MWY2ODIxYjUiLCJ0YWciOiIifQ=='; + // Create the string that would have been stored by an encrypted store + // Serialize once for the Encrypter and once for the Encrypted store + $encryptedString = AesCbc::encrypt(@serialize(@serialize(['foo' => 'bar'])), $encryptionKey); // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); @@ -60,7 +58,7 @@ public function data_is_decrypted_before_it_is_loaded() */ public function unexpected_session_data_is_handled_gracefully($previousSessionValue) { - $encryptionKey = 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='; + $encryptionKey = 'encryption-key'; // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index 717f5f5..c46a2c9 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -94,7 +94,7 @@ public function can_create_an_unencrypted_store() public function can_create_an_encrypted_store() { $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); - $app->bind(EncrypterContract::class, new Encrypter('base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w=')); + $app->bind(EncrypterContract::class, new Encrypter('encryption-key')); $manager = new SessionManager($app); From bba95261666d5da899326f2d62ad54b3f6f66a7c Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 15:32:55 +0100 Subject: [PATCH 31/43] Remove unused exceptions --- src/Exceptions/DecryptException.php | 8 -------- src/Exceptions/EncryptException.php | 8 -------- src/Exceptions/InvalidEncryptionKeyException.php | 8 -------- 3 files changed, 24 deletions(-) delete mode 100644 src/Exceptions/DecryptException.php delete mode 100644 src/Exceptions/EncryptException.php delete mode 100644 src/Exceptions/InvalidEncryptionKeyException.php diff --git a/src/Exceptions/DecryptException.php b/src/Exceptions/DecryptException.php deleted file mode 100644 index 64f7607..0000000 --- a/src/Exceptions/DecryptException.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Mon, 13 Oct 2025 15:34:46 +0100 Subject: [PATCH 32/43] Revert encryption-key change --- tests/Unit/Providers/EncryptionServiceProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/Providers/EncryptionServiceProviderTest.php b/tests/Unit/Providers/EncryptionServiceProviderTest.php index 2cbe034..89ccfdb 100644 --- a/tests/Unit/Providers/EncryptionServiceProviderTest.php +++ b/tests/Unit/Providers/EncryptionServiceProviderTest.php @@ -18,7 +18,7 @@ class EncryptionServiceProviderTest extends TestCase public function encryptor_is_registered_in_container_when_a_config_key_is_present() { $config = new Config; - $config->set('app.key', 'base64:ydjOTEkQq2WsMwzhSgq4xuv392AdyjENcO8/VrNl37w='); + $config->set('app.key', 'encryption-key'); $app = new Application(); $app->bind('config', $config); From b043f1fb57ac9dec4ffd42595572dc27ebfbe8eb Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 16:52:26 +0100 Subject: [PATCH 33/43] Update composer file to use pinned versions --- composer.json | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index ac34e32..c9f76f8 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": ">=8.1", "php-di/php-di": "^7", - "rareloop/router": "dev-feature/upgrade-dependencies as 6.0.3", + "rareloop/router": "^6.0.3", "psr/container": "^2", "psr/http-message": "^2", "psr/http-server-middleware": "^1.0.2", @@ -14,12 +14,12 @@ "illuminate/collections": "^10", "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", - "rareloop/psr7-server-request-extension": "dev-master as 3.0", + "rareloop/psr7-server-request-extension": "^2.2.0", "spatie/macroable": "^1.0.1", "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "mmeyer2k/dcrypt": "dev-master as 8.3.2", + "mmeyer2k/dcrypt": "^8.3.2", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^6.0" @@ -50,14 +50,6 @@ } }, "repositories": [ - { - "type": "vcs", - "url": "https://github.com/Rareloop/router" - }, - { - "type": "vcs", - "url": "https://github.com/tommitchelmore/psr7-server-request-extension" - }, { "type": "vcs", "url": "https://github.com/Rareloop/dcrypt" From 521bf22649acab772bc8e6f054c2a79f55a4ba6d Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 16:58:53 +0100 Subject: [PATCH 34/43] Tweak CI file --- .github/workflows/ci.yml | 82 ++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4598b5..7208d53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,44 +3,44 @@ name: CI on: [push, pull_request] jobs: - build-test: - runs-on: ubuntu-latest - strategy: - matrix: - php_version: [8.1, 8.2, 8.3, 8.4] - composer_flags: ["", "--prefer-lowest"] - - steps: - - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php_version }} - extensions: xdebug - - - name: Install dependencies - uses: php-actions/composer@v5 - with: - php_version: ${{ matrix.php_version }} - args: ${{ matrix.composer_flags }} - command: update - - - name: Run tests - run: ./vendor/bin/phpunit --coverage-clover ./tests/logs/clover.xml - env: - XDEBUG_MODE: coverage - - - name: Run Codesniffer - run: vendor/bin/phpcs --standard=PSR2 ./src - - # - name: Submit coverage to Coveralls - # # We use php-coveralls library for this, as the official Coveralls GitHub Action lacks support for clover reports: - # # https://github.com/coverallsapp/github-action/issues/15 - # env: - # COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # COVERALLS_PARALLEL: true - # COVERALLS_FLAG_NAME: ${{ github.job }}-PHP-${{ matrix.php_version }} ${{ matrix.composer_flags }} - # run: | - # composer global require php-coveralls/php-coveralls - # ~/.composer/vendor/bin/php-coveralls -v + build-test: + runs-on: ubuntu-latest + strategy: + matrix: + php_version: [8.1, 8.2, 8.3, 8.4] + composer_flags: ['', '--prefer-lowest'] + + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + extensions: xdebug + + - name: Install dependencies + uses: php-actions/composer@v5 + with: + php_version: ${{ matrix.php_version }} + args: ${{ matrix.composer_flags }} + command: update + + - name: Run tests + run: ./vendor/bin/phpunit --coverage-clover ./tests/logs/clover.xml + env: + XDEBUG_MODE: coverage + + - name: Run Codesniffer + run: vendor/bin/phpcs --standard=PSR2 ./src + + # - name: Submit coverage to Coveralls + # # We use php-coveralls library for this, as the official Coveralls GitHub Action lacks support for clover reports: + # # https://github.com/coverallsapp/github-action/issues/15 + # env: + # COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # COVERALLS_PARALLEL: true + # COVERALLS_FLAG_NAME: ${{ github.job }}-PHP-${{ matrix.php_version }} ${{ matrix.composer_flags }} + # run: | + # composer global require php-coveralls/php-coveralls + # ~/.composer/vendor/bin/php-coveralls -v From 7863e62d6c0e8a11f2df09833565151e2995a65c Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 17:28:44 +0100 Subject: [PATCH 35/43] Use php-actions/composer@v6 --- .github/workflows/ci.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7208d53..816f93d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: extensions: xdebug - name: Install dependencies - uses: php-actions/composer@v5 + uses: php-actions/composer@v6 with: php_version: ${{ matrix.php_version }} args: ${{ matrix.composer_flags }} diff --git a/composer.json b/composer.json index c9f76f8..9997391 100644 --- a/composer.json +++ b/composer.json @@ -55,4 +55,4 @@ "url": "https://github.com/Rareloop/dcrypt" } ] -} \ No newline at end of file +} From efbb11fa57928908943c27f7e41b91839bea85d9 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 17:45:44 +0100 Subject: [PATCH 36/43] Test removing repositories --- composer.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 9997391..d5cad26 100644 --- a/composer.json +++ b/composer.json @@ -49,10 +49,5 @@ "composer/installers": true } }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/Rareloop/dcrypt" - } - ] + "repositories": [] } From 3614c0e7c2249fece0b566d292325f6749e47dae Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 17:46:58 +0100 Subject: [PATCH 37/43] Test --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d5cad26..bbaa32a 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "mmeyer2k/dcrypt": "^8.3.2", + "mmeyer2k/dcrypt": "^8.3.1", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^6.0" @@ -50,4 +50,4 @@ } }, "repositories": [] -} +} \ No newline at end of file From 96088c83f0629ace38af4a9f146af2c6fe6c79d2 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Mon, 13 Oct 2025 17:50:44 +0100 Subject: [PATCH 38/43] Test --- composer.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index bbaa32a..bcff375 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "mmeyer2k/dcrypt": "^8.3.1", + "mmeyer2k/dcrypt": "^8.3.2", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^6.0" @@ -49,5 +49,10 @@ "composer/installers": true } }, - "repositories": [] + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:Rareloop/dcrypt.git" + } + ] } \ No newline at end of file From 1058e8c8b09fdab793df4048a642ec8f7fa6a3a6 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Tue, 14 Oct 2025 09:25:30 +0100 Subject: [PATCH 39/43] Add repository back in --- composer.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index bcff375..f20ab62 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,12 @@ "name": "rareloop/lumberjack-core", "description": "A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code", "license": "MIT", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/Rareloop/dcrypt" + } + ], "require": { "php": ">=8.1", "php-di/php-di": "^7", @@ -48,11 +54,5 @@ "allow-plugins": { "composer/installers": true } - }, - "repositories": [ - { - "type": "vcs", - "url": "git@github.com:Rareloop/dcrypt.git" - } - ] + } } \ No newline at end of file From 0b6e032c6a6270b603fb03e66df24f34d1e34841 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Tue, 14 Oct 2025 09:44:27 +0100 Subject: [PATCH 40/43] Bring forked dcrypt library into core --- composer.json | 9 +- src/Dcrypt/AesCbc.php | 42 +++++ src/Dcrypt/AesCtr.php | 35 ++++ src/Dcrypt/Hash.php | 193 ++++++++++++++++++++++ src/Dcrypt/OpensslBridge.php | 191 +++++++++++++++++++++ src/Dcrypt/OpensslWrapper.php | 67 ++++++++ src/Dcrypt/Otp.php | 50 ++++++ src/Dcrypt/Pkcs7.php | 76 +++++++++ src/Dcrypt/Rc4.php | 76 +++++++++ src/Dcrypt/Spritz.php | 59 +++++++ src/Dcrypt/Str.php | 92 +++++++++++ src/Encrypter.php | 2 +- tests/Unit/Dcrypt/AesCbcTest.php | 49 ++++++ tests/Unit/Dcrypt/AesCtrTest.php | 44 +++++ tests/Unit/Dcrypt/HashTest.php | 64 +++++++ tests/Unit/Dcrypt/OtpTest.php | 38 +++++ tests/Unit/Dcrypt/Pkcs7Test.php | 19 +++ tests/Unit/Dcrypt/Rc4Test.php | 39 +++++ tests/Unit/Dcrypt/SpritzTest.php | 27 +++ tests/Unit/Dcrypt/StrTest.php | 19 +++ tests/Unit/Dcrypt/SwapTest.php | 15 ++ tests/Unit/Dcrypt/TestSupport.php | 30 ++++ tests/Unit/EncrypterTest.php | 2 +- tests/Unit/Session/EncryptedStoreTest.php | 2 +- 24 files changed, 1229 insertions(+), 11 deletions(-) create mode 100644 src/Dcrypt/AesCbc.php create mode 100644 src/Dcrypt/AesCtr.php create mode 100644 src/Dcrypt/Hash.php create mode 100644 src/Dcrypt/OpensslBridge.php create mode 100644 src/Dcrypt/OpensslWrapper.php create mode 100644 src/Dcrypt/Otp.php create mode 100644 src/Dcrypt/Pkcs7.php create mode 100644 src/Dcrypt/Rc4.php create mode 100644 src/Dcrypt/Spritz.php create mode 100644 src/Dcrypt/Str.php create mode 100644 tests/Unit/Dcrypt/AesCbcTest.php create mode 100644 tests/Unit/Dcrypt/AesCtrTest.php create mode 100644 tests/Unit/Dcrypt/HashTest.php create mode 100644 tests/Unit/Dcrypt/OtpTest.php create mode 100644 tests/Unit/Dcrypt/Pkcs7Test.php create mode 100644 tests/Unit/Dcrypt/Rc4Test.php create mode 100644 tests/Unit/Dcrypt/SpritzTest.php create mode 100644 tests/Unit/Dcrypt/StrTest.php create mode 100644 tests/Unit/Dcrypt/SwapTest.php create mode 100644 tests/Unit/Dcrypt/TestSupport.php diff --git a/composer.json b/composer.json index f20ab62..ec14ca9 100644 --- a/composer.json +++ b/composer.json @@ -2,12 +2,6 @@ "name": "rareloop/lumberjack-core", "description": "A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code", "license": "MIT", - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/Rareloop/dcrypt" - } - ], "require": { "php": ">=8.1", "php-di/php-di": "^7", @@ -25,7 +19,6 @@ "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", "symfony/var-dumper": "^5.0||^6.3.6", - "mmeyer2k/dcrypt": "^8.3.2", "spatie/ignition": "^1.15", "laminas/laminas-httphandlerrunner": "^2.12", "symfony/error-handler": "^6.0" @@ -55,4 +48,4 @@ "composer/installers": true } } -} \ No newline at end of file +} diff --git a/src/Dcrypt/AesCbc.php b/src/Dcrypt/AesCbc.php new file mode 100644 index 0000000..bcf544c --- /dev/null +++ b/src/Dcrypt/AesCbc.php @@ -0,0 +1,42 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * Symmetric AES-256-CBC encryption functions powered by OpenSSL. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class AesCbc extends OpensslBridge +{ + /** + * AES-256 cipher identifier that will be passed to openssl + * + * @var string + */ + const CIPHER = 'aes-256-cbc'; + + /** + * Specify sha256 for message authentication + * + * @var string + */ + const CHKSUM = 'sha256'; +} diff --git a/src/Dcrypt/AesCtr.php b/src/Dcrypt/AesCtr.php new file mode 100644 index 0000000..8ed8814 --- /dev/null +++ b/src/Dcrypt/AesCtr.php @@ -0,0 +1,35 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * Symmetric AES-256-CTR encryption functions powered by OpenSSL. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class AesCtr extends AesCbc +{ + /** + * AES-256 cipher identifier that will be passed to openssl + * + * @var string + */ + const CIPHER = 'aes-256-ctr'; +} diff --git a/src/Dcrypt/Hash.php b/src/Dcrypt/Hash.php new file mode 100644 index 0000000..1177d46 --- /dev/null +++ b/src/Dcrypt/Hash.php @@ -0,0 +1,193 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * An opaque 512 bit iterative hash function. + * + * 16 bytes => iv + * 12 bytes => cost checksum + * 4 bytes => cost + * 32 bytes => hmac + * + * ivivivivivivivivsssssssssssscosthmachmachmachmachmachmachmachmac + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Hash +{ + const ALGO = 'sha256'; + + /** + * Internal function used to build the actual hash. + * + * @param string $input Data to hash + * @param string $password Password to use in HMAC call + * @param int $cost Number of iterations to use + * @param string|null $salt Initialization vector to use in HMAC calls + * @return string + */ + private static function build(string $input, string $password, int $cost, ?string $salt = null): string + { + // Generate salt if needed + $salt = $salt ?? \random_bytes(16); + + // Verify and normalize cost value + $cost = self::cost($cost); + + // Create key to use for hmac operations + $key = self::hmac($salt, $password, self::ALGO); + + // Perform hash iterations. Get a 32 byte output value + $hash = self::ihmac($input, $key, $cost, self::ALGO); + + // Return the salt + cost blob + hmac + return $salt . self::costHash($cost, $salt, $password) . $hash; + } + + /** + * Return a normalized cost value. + * + * @param int $cost Number of iterations to use. + * @return int + */ + private static function cost(int $cost): int + { + return $cost % \pow(2, 32); + } + + /** + * Performs hashing functions + * + * @param int $cost + * @param string $salt + * @param string $password + * @return string + */ + private static function costHash(int $cost, string $salt, string $password): string + { + // Hash and return first 12 bytes + $hash = Str::substr(self::hmac($cost, $salt, self::ALGO), 0, 12); + + // Convert cost to base 256 then encrypt with OTP stream cipher + $cost = Otp::crypt(self::dec2bin($cost), $password); + + return $hash . $cost; + } + + /** + * Perform a raw iterative HMAC operation with a configurable algo. + * + * @param string $data Data to hash. + * @param string $key Key to use to authenticate the hash. + * @param int $iter Number of times to iteratate the hash + * @param string $algo Name of algo (sha256 or sha512 recommended) + * @return string + */ + public static function ihmac(string $data, string $key, int $iter, string $algo = 'sha256'): string + { + // Can't perform negative iterations + $iter = \abs($iter); + + // Perform iterative hmac calls + // Make sure $iter value of 0 is handled + for ($i = 0; $i <= $iter; $i++) { + $data = self::hmac($data . $i . $iter, $key, $algo); + } + + return $data; + } + + /** + * Perform a single hmac iteration. This adds an extra layer of safety because hash_hmac can return false if algo + * is not valid. Return type hint will throw an exception if this happens. + * + * @param string $data Data to hash. + * @param string $key Key to use to authenticate the hash. + * @param string $algo Name of algo + * @return string + */ + public static function hmac(string $data, string $key, string $algo): string + { + return \hash_hmac($algo, $data, $key, true); + } + + /** + * Hash an input string into a salted 512 byte hash. + * + * @param string $input Data to hash. + * @param string $password HMAC validation password. + * @param int $cost Cost value of the hash. + * @return string + */ + public static function make(string $input, string $password, int $cost = 250000): string + { + return self::build($input, $password, $cost, null); + } + + /** + * Check the validity of a hash. + * + * @param string $input Input to test. + * @param string $hash Known hash to validate against. + * @param string $password HMAC password to use during iterative hash. + * @return boolean + */ + public static function verify(string $input, string $hash, string $password): bool + { + // Get the salt value from the decrypted prefix + $salt = Str::substr($hash, 0, 16); + + // Get the encrypted cost bytes + $cost = self::bin2dec(Otp::crypt(Str::substr($hash, 28, 4), $password)); + + // Get the entire cost+hash blob for comparison + $blob = Str::substr($hash, 16, 16); + + if (!Str::equal(self::costHash($cost, $salt, $password), $blob)) { + return false; + } + + // Return the boolean equivalence + return Str::equal($hash, self::build($input, $password, $cost, $salt)); + } + + /** + * Turns an integer into a 4 byte binary representation + * + * @param int $dec Integer to convert to binary + * @return string + */ + private static function dec2bin(int $dec): string + { + return \hex2bin(\str_pad(\dechex($dec), 8, '0', STR_PAD_LEFT)); + } + + /** + * Reverses dec2bin + * + * @param string $bin Binary string to convert to decimal + * @return string + */ + private static function bin2dec(string $bin): string + { + return \hexdec(\bin2hex($bin)); + } +} diff --git a/src/Dcrypt/OpensslBridge.php b/src/Dcrypt/OpensslBridge.php new file mode 100644 index 0000000..ebe329b --- /dev/null +++ b/src/Dcrypt/OpensslBridge.php @@ -0,0 +1,191 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * Provides functionality common to the dcrypt AES block ciphers. Extend this class to customize your cipher suite. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class OpensslBridge +{ + /** + * This string is used when hashing to ensure cross compatibility between + * dcrypt\mcrypt and dcrypt\aes. Since v7, this is only needed for backwards + * compatibility with older versions + */ + const RIJNDA = 'rijndael-128'; + + /** + * Decrypt cyphertext + * + * @param string $data Cyphertext to decrypt + * @param string $pass Password that should be used to decrypt input data + * @param int $cost Number of extra HMAC iterations to perform on key + * @return string + */ + public static function decrypt(string $data, string $pass, int $cost = 0): string + { + // Find the IV at the beginning of the cypher text + $ivr = Str::substr($data, 0, self::ivsize()); + + // Gather the checksum portion of the ciphertext + $sum = Str::substr($data, self::ivsize(), self::cksize()); + + // Gather message portion of ciphertext after iv and checksum + $msg = Str::substr($data, self::ivsize() + self::cksize()); + + // Derive key from password + $key = self::key($pass, $ivr, $cost); + + // Calculate verification checksum + $chk = self::checksum($msg, $ivr, $key); + + // Verify HMAC before decrypting + self::checksumVerify($chk, $sum); + + // Decrypt message and return + return OpensslWrapper::decrypt($msg, static::CIPHER, $key, $ivr); + } + + /** + * Encrypt plaintext + * + * @param string $data Plaintext string to encrypt. + * @param string $pass Password used to encrypt data. + * @param int $cost Number of extra HMAC iterations to perform on key + * @return string + */ + public static function encrypt(string $data, string $pass, int $cost = 0): string + { + // Generate IV of appropriate size. + $ivr = \random_bytes(self::ivsize()); + + // Derive key from password + $key = self::key($pass, $ivr, $cost); + + // Encrypt the plaintext + $msg = OpensslWrapper::encrypt($data, static::CIPHER, $key, $ivr); + + // Create the cypher text prefix (iv + checksum) + $pre = $ivr . self::checksum($msg, $ivr, $key); + + // Return prefix + cyphertext + return $pre . $msg; + } + + /** + * Create a message authentication checksum. + * + * @param string $data Ciphertext that needs a checksum. + * @param string $iv Initialization vector. + * @param string $key HMAC key + * @return string + */ + private static function checksum(string $data, string $iv, string $key): string + { + // Prevent multiple potentially large string concats by hmac-ing the input data + // by itself first... + $sum = Hash::hmac($data, $key, static::CHKSUM); + + // Then add the other input elements together before performing the final hash + $sum = $sum . $iv . self::mode() . self::RIJNDA; + + // ... then hash other elements with previous hmac and return + return Hash::hmac($sum, $key, static::CHKSUM); + } + + /** + * Transform password into key and perform iterative HMAC (if specified) + * + * @param string $pass Encryption key + * @param string $iv Initialization vector + * @param int $cost Number of HMAC iterations to perform on key + * @return string + */ + private static function key(string $pass, string $iv, int $cost): string + { + // Create the authentication string to be hashed + $data = $iv . self::RIJNDA . self::mode(); + + return Hash::ihmac($data, $pass, $cost, static::CHKSUM); + } + + /** + * Verify checksum during decryption step and throw error if mismatching. + * + * @param string $calculated + * @param string $supplied + * @throws \InvalidArgumentException + */ + private static function checksumVerify(string $calculated, string $supplied) + { + if (!Str::equal($calculated, $supplied)) { + $e = 'Decryption can not proceed due to invalid cyphertext checksum.'; + throw new \InvalidArgumentException($e); + } + } + + /** + * Return the encryption mode string. This function is really only needed for backwards + * compatibility. + * + * @return string + */ + private static function mode(): string + { + // To prevent legacy blobs from not decoding, these ciphers (which were implemented before 8.3) have hard coded + // return values. Luckily, this integrates gracefully with overloading. + $legacy = [ + 'bf-cbc' => 'cbc', + 'bf-ofb' => 'ofb', + 'aes-256-cbc' => 'cbc', + 'aes-256-ctr' => 'ctr', + ]; + + $cipher = \strtolower(static::CIPHER); + + if (isset($legacy[$cipher])) { + return $legacy[$cipher]; + } + + return $cipher; + } + + /** + * Calculate checksum size + * + * @return int + */ + private static function cksize(): int + { + return Str::hashSize(static::CHKSUM); + } + + /** + * Get IV size + * + * @return int + */ + private static function ivsize(): int + { + return \openssl_cipher_iv_length(static::CIPHER); + } +} diff --git a/src/Dcrypt/OpensslWrapper.php b/src/Dcrypt/OpensslWrapper.php new file mode 100644 index 0000000..bea6638 --- /dev/null +++ b/src/Dcrypt/OpensslWrapper.php @@ -0,0 +1,67 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +class OpensslWrapper +{ + + /** + * OpenSSL encrypt wrapper function + * + * @param string $data Data to decrypt + * @param string $method Cipher method to use + * @param string $key Key string + * @param string $iv Initialization vector + * @return string + */ + public static function encrypt(string $data, string $method, string $key, string $iv): string + { + $ret = \openssl_encrypt($data, $method, $key, 1, $iv); + + return self::returnOrException($ret); + } + + /** + * OpenSSL decrypt wrapper function + * + * @param string $data Data to decrypt + * @param string $method Cipher method to use + * @param string $key Key string + * @param string $iv Initialization vector + * @return string + */ + public static function decrypt(string $data, string $method, string $key, string $iv): string + { + $ret = \openssl_decrypt($data, $method, $key, 1, $iv); + + return self::returnOrException($ret); + } + + /** + * Throw an exception if openssl function returns false + * + * @param string|bool $data + * @return string + * @throws \Exception + */ + private static function returnOrException($data): string + { + if ($data === false) { + throw new \Exception('OpenSSL failed to encrypt/decrypt message.'); + } + + return $data; + } +} diff --git a/src/Dcrypt/Otp.php b/src/Dcrypt/Otp.php new file mode 100644 index 0000000..2836e4f --- /dev/null +++ b/src/Dcrypt/Otp.php @@ -0,0 +1,50 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * A one time pad stream encryption class. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link http://en.wikipedia.org/wiki/Stream_cipher + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Otp +{ + /** + * Encrypt or decrypt a binary input string. + * + * @param string $input Input data to encrypt + * @param string $password Encryption/decryption key to use on input + * @param string $algo Hashing algo to generate keystream + * @return string + */ + public static function crypt(string $input, string $password, string $algo = 'sha512'): string + { + $chunks = \str_split($input, Str::hashSize($algo)); + + $length = Str::strlen($input); + + foreach ($chunks as $i => &$chunk) { + $chunk = $chunk ^ \hash_hmac($algo, $password . $length, $i, true); + } + + return \implode($chunks); + } +} diff --git a/src/Dcrypt/Pkcs7.php b/src/Dcrypt/Pkcs7.php new file mode 100644 index 0000000..37fc9ca --- /dev/null +++ b/src/Dcrypt/Pkcs7.php @@ -0,0 +1,76 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * Provides PKCS #7 padding functionality. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Pkcs7 +{ + /** + * PKCS #7 padding function. + * + * @param string $input String to pad + * @param int $blocksize Block size in bytes + * @return string + */ + public static function pad(string $input, int $blocksize): string + { + // Determine the padding string that needs to be appended. + $pad = self::paddingString(Str::strlen($input), $blocksize); + + // Return input + padding + return $input . $pad; + } + + /** + * Create the padding string that will be appended to the input. + * + * @param int $inputsize Size of the input in bytes + * @param int $blocksize Blocksize in bytes + * @return string + */ + private static function paddingString(int $inputsize, int $blocksize): string + { + // Determine the amount of padding to use + $pad = $blocksize - ($inputsize % $blocksize); + + // Create and return the padding string + return \str_repeat(\chr($pad), $pad); + } + + /** + * PKCS #7 unpadding function. + * + * @param string $input Padded string to unpad + * @return string + */ + public static function unpad(string $input): string + { + // Determine the padding size by converting the final byte of the + // input to its decimal value + $padsize = \ord(Str::substr($input, -1)); + + // Return string minus the padding amount + return Str::substr($input, 0, Str::strlen($input) - $padsize); + } +} diff --git a/src/Dcrypt/Rc4.php b/src/Dcrypt/Rc4.php new file mode 100644 index 0000000..8587437 --- /dev/null +++ b/src/Dcrypt/Rc4.php @@ -0,0 +1,76 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * An implementation of RC4 symmetric encryption. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link http://en.wikipedia.org/wiki/Stream_cipher + * @link https://en.wikipedia.org/wiki/RC4 + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Rc4 +{ + /** + * Perform (en/de)cryption + * + * @param string $str String to be encrypted + * @param string $key Key to use for encryption + * @return string + */ + public static function crypt(string $str, string $key): string + { + $s = self::initializeState($key); + $i = $j = 0; + $res = ''; + $size = Str::strlen($str); + for ($y = 0; $y < $size; $y++) { + $i = ($i + 1) % 256; + $j = ($j + $s[$i]) % 256; + $x = $s[$i]; + $s[$i] = $s[$j]; + $s[$j] = $x; + $res .= $str[$y] ^ \chr($s[($s[$i] + $s[$j]) % 256]); + } + + return $res; + } + + /** + * Create the initial byte matrix that will be used for swaps. This code + * is identical between RC4 and Spritz. + * + * @param string $key + * @return array + */ + protected static function initializeState(string $key): array + { + $s = \range(0, 255); + $j = 0; + foreach (\range(0, 255) as $i) { + $j = ($j + $s[$i] + \ord($key[$i % Str::strlen($key)])) % 256; + $x = $s[$i]; + $s[$i] = $s[$j]; + $s[$j] = $x; + } + + return $s; + } +} diff --git a/src/Dcrypt/Spritz.php b/src/Dcrypt/Spritz.php new file mode 100644 index 0000000..78d6726 --- /dev/null +++ b/src/Dcrypt/Spritz.php @@ -0,0 +1,59 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * An implementation of Spritz symmetric encryption. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link http://en.wikipedia.org/wiki/Stream_cipher + * @link https://en.wikipedia.org/wiki/RC4 + * @link http://people.csail.mit.edu/rivest/pubs/RS14.pdf + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Spritz extends Rc4 +{ + /** + * Perform (en/de)cryption + * + * @param string $str String to be encrypted + * @param string $key Key to use for encryption + * @return string + */ + public static function crypt(string $str, string $key): string + { + $s = self::initializeState($key); + $i = $j = $k = $z = 0; + $w = 1; + $res = ''; + $size = Str::strlen($str); + for ($y = 0; $y < $size; $y++) { + $i = ($i + $w) % 256; + $j = ($k + $s[($j + $s[$i]) % 256]) % 256; + $k = ($i + $k + $s[$j]) % 256; + $x = $s[$i]; + $s[$i] = $s[$j]; + $s[$j] = $x; + $z = $s[($j + $s[($i + $s[($z + $k) % 256]) % 256]) % 256]; + $res .= $str[$y] ^ \chr($z); + } + + return $res; + } +} diff --git a/src/Dcrypt/Str.php b/src/Dcrypt/Str.php new file mode 100644 index 0000000..b9f1b89 --- /dev/null +++ b/src/Dcrypt/Str.php @@ -0,0 +1,92 @@ + + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + */ + +namespace Rareloop\Lumberjack\Dcrypt; + +/** + * Provides time-safe string comparison facilities, and safe string operations + * on systems that have mb_* function overloading enabled. + * + * The functions in this class were inspired by the symfony's StringUtils class. + * + * @category Dcrypt + * @package Dcrypt + * @author Michael Meyer (mmeyer2k) + * @license http://opensource.org/licenses/MIT The MIT License (MIT) + * @link https://github.com/mmeyer2k/dcrypt + * @link https://github.com/symfony/Security/blob/master/Core/Util/StringUtils.php + * @link https://php.net/manual/en/mbstring.overload.php + * @link https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html + */ +class Str +{ + /** + * Compares two strings in constant time. Strings are hashed before + * comparison so information is not leaked when strings are not of + * equal length. + * + * @param string $known The string of known length to compare against + * @param string $given The string that the user can control + * @return bool + */ + public static function equal(string $known, string $given): bool + { + // Create some entropy + $nonce = \random_bytes(32); + + // We hash the 2 inputs at this point because hash_equals is still + // vulnerable to timing attacks when the inputs have different sizes. + // Inputs are also cast to string like in symfony stringutils. + $known = Hash::hmac($known, $nonce, 'sha256'); + $given = Hash::hmac($given, $nonce, 'sha256'); + + return \hash_equals($known, $given); + } + + /** + * Determine the length of the output of a given hash algorithm in bytes. + * + * @param string $algo Name of algorithm to look up + * @return int + */ + public static function hashSize(string $algo): int + { + return self::strlen(\hash($algo, 'hash me', true)); + } + + /** + * Returns the number of bytes in a string. + * + * @param string $string The string whose length we wish to obtain + * @return int + */ + public static function strlen(string $string): int + { + return \mb_strlen($string, '8bit'); + } + + /** + * Returns part of a string. + * + * @param string $string The string whose length we wish to obtain + * @param int $start + * @param int $length + * + * @return string the extracted part of string; or FALSE on failure, or an empty string. + */ + public static function substr(string $string, int $start, ?int $length = null): string + { + return \mb_substr($string, $start, $length, '8bit'); + } +} diff --git a/src/Encrypter.php b/src/Encrypter.php index 12e3049..034da2a 100644 --- a/src/Encrypter.php +++ b/src/Encrypter.php @@ -2,7 +2,7 @@ namespace Rareloop\Lumberjack; -use Dcrypt\AesCbc; +use Rareloop\Lumberjack\Dcrypt\AesCbc; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; class Encrypter implements EncrypterContract diff --git a/tests/Unit/Dcrypt/AesCbcTest.php b/tests/Unit/Dcrypt/AesCbcTest.php new file mode 100644 index 0000000..e7a4cb7 --- /dev/null +++ b/tests/Unit/Dcrypt/AesCbcTest.php @@ -0,0 +1,49 @@ +expectException(\InvalidArgumentException::class); + + $input = 'AAAAAAAA'; + $key = 'AAAAAAAA'; + $encrypted = AesCbc::encrypt($input, $key, 10); + $this->assertEquals($input, AesCbc::decrypt($encrypted, $key, 10)); + + $corrupt = self::swaprandbyte($encrypted); + AesCbc::decrypt($corrupt, $key, 10); + } + + public function testEngine() + { + $this->expectException(\InvalidArgumentException::class); + + $input = 'AAAAAAAA'; + $key = 'AAAAAAAA'; + + $encrypted = AesCbc::encrypt($input, $key); + $this->assertEquals($input, AesCbc::decrypt($encrypted, $key)); + + // Perform a validation by replacing a random byte to make sure + // the decryption fails. After enough successful runs, + // all areas of the cypher text will have been tested + // for integrity + $corrupt = self::swaprandbyte($encrypted); + AesCbc::decrypt($corrupt, $key); + } + + public function testVector() + { + $input = 'hello world'; + $pass = 'password'; + $vector = \base64_decode('eZu2DqB2gYhdA2YkjagLNJJVMVo1BbpJ75tW/PO2bGIY98XHD+Gp+YlO5cv/rHzo45LHMCxL2qOircdST1w5hg=='); + + $this->assertEquals($input, AesCbc::decrypt($vector, $pass)); + } +} diff --git a/tests/Unit/Dcrypt/AesCtrTest.php b/tests/Unit/Dcrypt/AesCtrTest.php new file mode 100644 index 0000000..e640a2f --- /dev/null +++ b/tests/Unit/Dcrypt/AesCtrTest.php @@ -0,0 +1,44 @@ +input, $this->key, 10); + $this->assertEquals($this->input, AesCtr::decrypt($encrypted, $this->key, 10)); + } + + public function testEngine() + { + $encrypted = AesCtr::encrypt($this->input, $this->key); + $this->assertEquals($this->input, AesCtr::decrypt($encrypted, $this->key)); + } + + public function testCorrupt() + { + $this->expectException(\InvalidArgumentException::class); + + $encrypted = AesCtr::encrypt($this->input, $this->key); + + // Perform a validation by replacing a random byte to make sure + // the decryption fails. After enough successful runs, + // all areas of the cypher text will have been tested + // for integrity + $corrupt = self::swaprandbyte($encrypted); + AesCtr::decrypt($corrupt, $this->key); + } + + public function testVector() + { + $input = 'hello world'; + $pass = 'password'; + $vector = \base64_decode('Vpbd71CIVcRPALeSg126DhRKYozXlbusn/eSSxrQPtzj/U7hOhlN8D21Y0gmlmUKorpoXuDS6bklvD8='); + $this->assertEquals($input, AesCtr::decrypt($vector, $pass)); + } +} diff --git a/tests/Unit/Dcrypt/HashTest.php b/tests/Unit/Dcrypt/HashTest.php new file mode 100644 index 0000000..3e1ba86 --- /dev/null +++ b/tests/Unit/Dcrypt/HashTest.php @@ -0,0 +1,64 @@ +assertNotEquals('aaaa', Hash::ihmac('aaaa', 'bbbb', 0)); + $this->assertNotEquals('aaaa', Hash::ihmac('aaaa', 'bbbb', -1)); + } + + public function testBadCost() + { + $this->assertEquals(64, strlen(Hash::make('test', '1234', 0))); + } + + public function testLength() + { + $this->assertEquals(64, strlen(Hash::make('test', '1234'))); + } + + public function testCycle() + { + $input = 'input test'; + $key = 'key123'; + $hash = Hash::make($input, $key, 1); + $this->assertTrue(Hash::verify($input, $hash, $key)); + } + + public function testHmacAlgoFailure() + { + $this->expectException(\ValueError::class); + + Hash::hmac('test', '1234', 'an algo that does not exist'); + } + + public function testFail() + { + $input = str_repeat('A', rand(0, 10000)); + $key = str_repeat('A', rand(10, 100)); + $cost = 1; + + $output = Hash::make($input, $key, $cost); + $this->assertTrue(Hash::verify($input, $output, $key)); + + for ($i = 0; $i < 10; $i++) { + $corrupt = self::swaprandbyte($output); + $this->assertFalse(Hash::verify($input, $corrupt, $key)); + } + } + + public function testVector() + { + $input = 'hello world'; + $key = 'password'; + $vector = base64_decode('dvvWMEFPCo9EV+l+htGGcoK5Uj8zrh6bfxCh16NOjJxuugObuidTQ3+R3qiyZLnHl7zRxSmfHRasEJQpTymZDw=='); + $this->assertTrue(Hash::verify($input, $vector, $key)); + } +} diff --git a/tests/Unit/Dcrypt/OtpTest.php b/tests/Unit/Dcrypt/OtpTest.php new file mode 100644 index 0000000..38b910a --- /dev/null +++ b/tests/Unit/Dcrypt/OtpTest.php @@ -0,0 +1,38 @@ +assertEquals(strlen($input), strlen($encrypted)); + $this->assertNotEquals($input, $encrypted); + + /* + * Test decryption + */ + $decrypted = Otp::crypt($encrypted, $key); + $this->assertEquals($input, $decrypted); + } + } + + public function testVector() + { + $input = 'hello world'; + $pass = 'password'; + $vector = base64_decode('Cf6ULwbiZEbJr1w='); + + $this->assertEquals($input, Otp::crypt($vector, $pass)); + } +} diff --git a/tests/Unit/Dcrypt/Pkcs7Test.php b/tests/Unit/Dcrypt/Pkcs7Test.php new file mode 100644 index 0000000..6f8ed2e --- /dev/null +++ b/tests/Unit/Dcrypt/Pkcs7Test.php @@ -0,0 +1,19 @@ +assertEquals(Pkcs7::pad('aaaabbbb', 3), "aaaabbbb\x01"); + + $this->assertEquals(Pkcs7::pad('aaaabbbb', 4), "aaaabbbb\x04\x04\x04\x04"); + + $this->assertEquals(Pkcs7::unpad("aaaabbbb\x01"), "aaaabbbb"); + + $this->assertEquals(Pkcs7::unpad("aaaabbbb\x04\x04\x04\x04"), "aaaabbbb"); + } +} diff --git a/tests/Unit/Dcrypt/Rc4Test.php b/tests/Unit/Dcrypt/Rc4Test.php new file mode 100644 index 0000000..eb1dd49 --- /dev/null +++ b/tests/Unit/Dcrypt/Rc4Test.php @@ -0,0 +1,39 @@ +assertEquals(strlen($input), strlen($encrypted)); + $this->assertNotEquals($input, $encrypted); + + /* + * Test decryption + */ + $decrypted = Rc4::crypt($encrypted, $key); + $this->assertEquals($input, $decrypted); + } + + public function testVector() + { + /* + * Test that known cypher text decrypts properly + */ + $cyphertext = hex2bin('140ad3d278a229ff3c487d'); + $plain = 'Hello World'; + $key = 'asdf'; + + $this->assertEquals($plain, Rc4::crypt($cyphertext, $key)); + } +} diff --git a/tests/Unit/Dcrypt/SpritzTest.php b/tests/Unit/Dcrypt/SpritzTest.php new file mode 100644 index 0000000..d2bc421 --- /dev/null +++ b/tests/Unit/Dcrypt/SpritzTest.php @@ -0,0 +1,27 @@ +assertEquals(strlen($input), strlen($encrypted)); + $this->assertNotEquals($input, $encrypted); + + /* + * Test decryption + */ + $decrypted = Spritz::crypt($encrypted, $key); + $this->assertEquals($input, $decrypted); + } +} diff --git a/tests/Unit/Dcrypt/StrTest.php b/tests/Unit/Dcrypt/StrTest.php new file mode 100644 index 0000000..19022e0 --- /dev/null +++ b/tests/Unit/Dcrypt/StrTest.php @@ -0,0 +1,19 @@ +assertTrue(Str::equal('2222', '2222', true)); + $this->assertFalse(Str::equal('2222', '3333', true)); + + // Test without hash_equals + $this->assertTrue(Str::equal('2222', '2222', false)); + $this->assertFalse(Str::equal('2222', '3333', false)); + } +} diff --git a/tests/Unit/Dcrypt/SwapTest.php b/tests/Unit/Dcrypt/SwapTest.php new file mode 100644 index 0000000..8267ea3 --- /dev/null +++ b/tests/Unit/Dcrypt/SwapTest.php @@ -0,0 +1,15 @@ +assertFalse($orig === self::swaprandbyte($orig)); + $this->assertEquals(levenshtein($orig, self::swaprandbyte($orig)), 1); + } + } +} diff --git a/tests/Unit/Dcrypt/TestSupport.php b/tests/Unit/Dcrypt/TestSupport.php new file mode 100644 index 0000000..dcf1c8f --- /dev/null +++ b/tests/Unit/Dcrypt/TestSupport.php @@ -0,0 +1,30 @@ + Date: Tue, 14 Oct 2025 16:03:16 +0100 Subject: [PATCH 41/43] Update dependencies --- composer.json | 36 +++++++++++++++--------- tests/Unit/Providers/includes/single.php | 1 + 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/composer.json b/composer.json index ec14ca9..6e44d49 100644 --- a/composer.json +++ b/composer.json @@ -4,34 +4,41 @@ "license": "MIT", "require": { "php": ">=8.1", - "php-di/php-di": "^7", + "php-di/php-di": "^7.1.1", "rareloop/router": "^6.0.3", - "psr/container": "^2", + "psr/container": "^2.0.2", "psr/http-message": "^2", "psr/http-server-middleware": "^1.0.2", - "timber/timber": "^2.3.0", + "timber/timber": "^2.3.3", "monolog/monolog": "^3.9", - "illuminate/collections": "^10", + "illuminate/collections": "^10.49", "statamic/stringy": "^3.1.3", "laminas/laminas-diactoros": "^3.6.0", "rareloop/psr7-server-request-extension": "^2.2.0", "spatie/macroable": "^1.0.1", "mindplay/middleman": "^4.0.4", "psr/log": "^2.0.0", - "symfony/var-dumper": "^5.0||^6.3.6", - "spatie/ignition": "^1.15", - "laminas/laminas-httphandlerrunner": "^2.12", - "symfony/error-handler": "^6.0" + "symfony/var-dumper": "^6.4.26", + "spatie/ignition": "^1.15.1", + "laminas/laminas-httphandlerrunner": "^2.13", + "symfony/error-handler": "^6.4.26", + "illuminate/support": "^10.49", + "illuminate/pipeline": "^10.49", + "spatie/backtrace": "^1.8.1", + "illuminate/conditionable": "^10.49", + "guzzlehttp/guzzle": "^7.10", + "spatie/flare-client-php": "^1.10.1" }, "require-dev": { - "phpunit/phpunit": "^9.6.13", - "php-coveralls/php-coveralls": "^2.6", - "mockery/mockery": "^1.6.6", - "brain/monkey": "^2.6.1", - "squizlabs/php_codesniffer": "^3.7.2", + "phpunit/phpunit": "^9.6.29", + "php-coveralls/php-coveralls": "^2.8", + "mockery/mockery": "^1.6.12", + "brain/monkey": "^2.6.2", + "squizlabs/php_codesniffer": "^3.13.4", "php-mock/php-mock": "^2.6.2", "mikey179/vfsstream": "^1.6.12", - "dms/phpunit-arraysubset-asserts": "^0.3.1" + "dms/phpunit-arraysubset-asserts": "^0.5.0", + "antecedent/patchwork": "^2.2.3" }, "autoload": { "psr-4": { @@ -44,6 +51,7 @@ } }, "config": { + "preferred-install": "dist", "allow-plugins": { "composer/installers": true } diff --git a/tests/Unit/Providers/includes/single.php b/tests/Unit/Providers/includes/single.php index e69de29..b3d9bbc 100644 --- a/tests/Unit/Providers/includes/single.php +++ b/tests/Unit/Providers/includes/single.php @@ -0,0 +1 @@ + Date: Tue, 14 Oct 2025 16:05:47 +0100 Subject: [PATCH 42/43] Fix PHPCS issue --- src/Facades/MiddlewareAliases.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Facades/MiddlewareAliases.php b/src/Facades/MiddlewareAliases.php index dece0e4..72d1080 100644 --- a/src/Facades/MiddlewareAliases.php +++ b/src/Facades/MiddlewareAliases.php @@ -2,7 +2,6 @@ namespace Rareloop\Lumberjack\Facades; - class MiddlewareAliases extends AbstractFacade { protected static function accessor() From f961127fba1d21181dc0c70bfcd9ea7fe2244ae0 Mon Sep 17 00:00:00 2001 From: Adam Tomat Date: Wed, 15 Oct 2025 11:45:27 +0100 Subject: [PATCH 43/43] Make session storage a bit more resilliant --- src/Session/EncryptedStore.php | 6 +++- src/Session/FileSessionHandler.php | 7 +++- src/Session/SessionManager.php | 4 ++- tests/Unit/Exceptions/HandlerTest.php | 2 +- tests/Unit/Session/EncryptedStoreTest.php | 44 ++++++++++++++++++----- tests/Unit/Session/SessionManagerTest.php | 27 ++++++++++---- 6 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/Session/EncryptedStore.php b/src/Session/EncryptedStore.php index 7eff287..d6388f0 100644 --- a/src/Session/EncryptedStore.php +++ b/src/Session/EncryptedStore.php @@ -32,10 +32,14 @@ protected function prepareForStorage($data) protected function prepareForUnserialize($data) { + if ($data === '') { + return ''; + } + try { return $this->encrypter->decrypt($data); } catch (Exception $e) { - $this->exceptionHandler->report($e); + $this->exceptionHandler?->report($e); return ''; } } diff --git a/src/Session/FileSessionHandler.php b/src/Session/FileSessionHandler.php index 9efa24b..56f3b5e 100644 --- a/src/Session/FileSessionHandler.php +++ b/src/Session/FileSessionHandler.php @@ -8,8 +8,13 @@ class FileSessionHandler implements SessionHandlerInterface { - public function __construct(protected $path, protected $prefix = 'lumberjack_session_') + protected $path; + protected $prefix; + + public function __construct($path, $prefix = 'lumberjack_session_') { + $this->path = $path; + $this->prefix = $prefix; } #[\ReturnTypeWillChange] diff --git a/src/Session/SessionManager.php b/src/Session/SessionManager.php index 0926a03..6359bae 100644 --- a/src/Session/SessionManager.php +++ b/src/Session/SessionManager.php @@ -5,6 +5,7 @@ use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; +use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Manager; class SessionManager extends Manager @@ -54,8 +55,9 @@ protected function buildSession($handler) if ($this->config->get('session.encrypt')) { $encrypter = $this->app->get(EncrypterContract::class); + $exceptionHandler = $this->app->get(HandlerInterface::class); - return new EncryptedStore($this->name, $handler, $encrypter, $sessionId); + return new EncryptedStore($this->name, $handler, $encrypter, $sessionId, $exceptionHandler); } return new Store($this->name, $handler, $sessionId); diff --git a/tests/Unit/Exceptions/HandlerTest.php b/tests/Unit/Exceptions/HandlerTest.php index e5aacec..f393f91 100644 --- a/tests/Unit/Exceptions/HandlerTest.php +++ b/tests/Unit/Exceptions/HandlerTest.php @@ -124,6 +124,6 @@ class HandlerWithBlacklist extends Handler ]; } -class BlacklistedException extends \Exception +class BlacklistedException extends \Exception { } diff --git a/tests/Unit/Session/EncryptedStoreTest.php b/tests/Unit/Session/EncryptedStoreTest.php index 63ccbcd..f1217aa 100644 --- a/tests/Unit/Session/EncryptedStoreTest.php +++ b/tests/Unit/Session/EncryptedStoreTest.php @@ -54,15 +54,14 @@ public function data_is_decrypted_before_it_is_loaded() /** * @test - * @dataProvider unexpectedSessionData */ - public function unexpected_session_data_is_handled_gracefully($previousSessionValue) + public function unexpected_session_data_is_handled_gracefully() { $encryptionKey = 'encryption-key'; // Use a mock handler to fake a previously stored state $handler = Mockery::mock(NullSessionHandler::class . '[read]'); - $handler->shouldReceive('read')->andReturn($previousSessionValue); + $handler->shouldReceive('read')->andReturn(@serialize(['foo' => 'bar'])); $errorHandler = Mockery::mock(HandlerInterface::class); $errorHandler->shouldReceive('report')->once(); @@ -73,11 +72,40 @@ public function unexpected_session_data_is_handled_gracefully($previousSessionVa $this->assertSame(null, $store->get('foo')); } - public function unexpectedSessionData() + /** + * @test + */ + public function gracefully_handle_case_with_no_exception_handler() { - return [ - [@serialize(['foo' => 'bar'])], - [''], - ]; + $encryptionKey = 'encryption-key'; + + // Use a mock handler to fake a previously stored state + $handler = Mockery::mock(NullSessionHandler::class . '[read]'); + $handler->shouldReceive('read')->andReturn(@serialize(['foo' => 'bar'])); + + $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id'); + $store->start(); + + $this->assertSame(null, $store->get('foo')); + } + + /** + * @test + */ + public function empty_session_data_is_ignored() + { + $encryptionKey = 'encryption-key'; + + // Use a mock handler to fake a previously stored state + $handler = Mockery::mock(NullSessionHandler::class . '[read]'); + $handler->shouldReceive('read')->andReturn(''); + + $errorHandler = Mockery::mock(HandlerInterface::class); + $errorHandler->shouldNotHaveReceived('report'); + + $store = new EncryptedStore('session-name', $handler, new Encrypter($encryptionKey), 'session-id', $errorHandler); + $store->start(); + + $this->assertSame(null, $store->get('foo')); } } diff --git a/tests/Unit/Session/SessionManagerTest.php b/tests/Unit/Session/SessionManagerTest.php index c46a2c9..f1e34aa 100644 --- a/tests/Unit/Session/SessionManagerTest.php +++ b/tests/Unit/Session/SessionManagerTest.php @@ -3,17 +3,20 @@ namespace Rareloop\Lumberjack\Test; use Mockery; +use ReflectionClass; +use org\bovigo\vfs\vfsStream; use PHPUnit\Framework\TestCase; -use Rareloop\Lumberjack\Application; use Rareloop\Lumberjack\Config; -use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; use Rareloop\Lumberjack\Encrypter; +use Rareloop\Lumberjack\Application; +use Rareloop\Lumberjack\Session\Store; +use Rareloop\Lumberjack\Exceptions\Handler; use Rareloop\Lumberjack\Session\EncryptedStore; -use Rareloop\Lumberjack\Session\FileSessionHandler; use Rareloop\Lumberjack\Session\SessionManager; -use Rareloop\Lumberjack\Session\Store; +use Rareloop\Lumberjack\Session\FileSessionHandler; +use Rareloop\Lumberjack\Exceptions\HandlerInterface; use Rareloop\Lumberjack\Test\Unit\Session\NullSessionHandler; -use org\bovigo\vfs\vfsStream; +use Rareloop\Lumberjack\Contracts\Encrypter as EncrypterContract; class SessionManagerTest extends TestCase { @@ -93,12 +96,22 @@ public function can_create_an_unencrypted_store() /** @test */ public function can_create_an_encrypted_store() { - $app = $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); + $app = $this->appWithSessionDriverConfig('file', 'lumberjack', $encrypted = true); $app->bind(EncrypterContract::class, new Encrypter('encryption-key')); + $handler = Mockery::mock(Handler::class); + $app->bind(HandlerInterface::class, $handler); + $manager = new SessionManager($app); - $this->assertInstanceOf(EncryptedStore::class, $manager->driver()); + $driver = $manager->driver(); + $this->assertInstanceOf(EncryptedStore::class, $driver); + + $reflection = new ReflectionClass($driver); + $property = $reflection->getProperty('exceptionHandler'); + $property->setAccessible(true); + + $this->assertInstanceOf(HandlerInterface::class, $property->getValue($driver)); } }