diff --git a/.github/workflows/tasks.yml b/.github/workflows/tasks.yml index ec17e3c..fcdc2c2 100644 --- a/.github/workflows/tasks.yml +++ b/.github/workflows/tasks.yml @@ -4,19 +4,30 @@ on: [push, pull_request] jobs: lint-php: - name: Linting php with grumphp + name: "php: ${{ matrix.php }} TYPO3: ${{ matrix.typo3 }}" runs-on: ubuntu-latest - container: - image: kanti/buildy:7.4 + strategy: + fail-fast: false + matrix: + php: [ '8.1', '8.2', '8.3' ] + typo3: [ '11', '12' ] + exclude: + - php: '8.1' + typo3: '13' steps: + - name: Setup PHP with PECL extension + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} - uses: actions/checkout@v2 - uses: actions/cache@v2 with: path: ~/.composer/cache/files - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} restore-keys: | - ${{ runner.os }}-composer- - - run: composer install --no-interaction --no-progress --ignore-platform-req=ext* + ${{ runner.os }}-${{ matrix.php }}-composer- + - run: composer require typo3/minimal="^${{ matrix.typo3 }}" -W --dev + - run: composer install --no-interaction --no-progress - run: ./vendor/bin/grumphp run --ansi ter-release: @@ -40,7 +51,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '7.4' + php-version: '8.1' extensions: intl, mbstring, xml, soap, zip, curl tools: composer diff --git a/.gitignore b/.gitignore index 42c24f1..756ae3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ composer.lock public/ vendor/ +var/ diff --git a/Classes/Cache/ClearCache.php b/Classes/Cache/ClearCache.php new file mode 100644 index 0000000..84cc4f7 --- /dev/null +++ b/Classes/Cache/ClearCache.php @@ -0,0 +1,39 @@ + $parameters */ + public function clearCache(array $parameters): void + { + if (isset($parameters['cacheCmd']) && ($parameters['cacheCmd'] === 'pages' || $parameters['cacheCmd'] === 'all')) { + $path = Environment::getPublicPath() . RenderIncludeViewHelper::SSI_INCLUDE_DIR; + $this->removeFiles($path); + } + } + + protected function removeFiles(string $dir): void + { + if (is_dir($dir)) { + $objects = scandir($dir); + if (!$objects) { + return; + } + + foreach ($objects as $object) { + if ($object !== '.' && $object !== '..') { + $filePath = $dir . DIRECTORY_SEPARATOR . $object; + if (is_file($filePath) && is_writable($filePath)) { + unlink($filePath); + } + } + } + } + } +} diff --git a/Classes/Event/RenderedEvent.php b/Classes/Event/RenderedEvent.php index 222342d..ec522dc 100644 --- a/Classes/Event/RenderedEvent.php +++ b/Classes/Event/RenderedEvent.php @@ -6,11 +6,8 @@ class RenderedEvent { - protected string $html; - - public function __construct(string $html) + public function __construct(protected string $html) { - $this->html = $html; } public function getHtml(): string diff --git a/Classes/Middleware/InternalSsiRedirectMiddleware.php b/Classes/Middleware/InternalSsiRedirectMiddleware.php index a78f031..858bf34 100644 --- a/Classes/Middleware/InternalSsiRedirectMiddleware.php +++ b/Classes/Middleware/InternalSsiRedirectMiddleware.php @@ -18,17 +18,20 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface { if (isset($request->getQueryParams()['ssi_include'])) { $ssiInclude = $request->getQueryParams()['ssi_include']; - if (!preg_match('/^([a-zA-Z0-9_]+)$/', $ssiInclude)) { + if (!preg_match('/^(\w+)$/', (string) $ssiInclude)) { return new HtmlResponse('ssi_include invalid', 400); } + $cacheFileName = RenderIncludeViewHelper::SSI_INCLUDE_DIR . $ssiInclude; $absolutePath = Environment::getPublicPath() . $cacheFileName; if (!file_exists($absolutePath)) { // ignore response use the content of the file: $handler->handle($request->withAttribute('noCache', true)); } + return new HtmlResponse(file_get_contents($absolutePath) ?: ''); } + return $handler->handle($request); } } diff --git a/Classes/Utility/VersionUtility.php b/Classes/Utility/VersionUtility.php index 056a16e..2d967dd 100644 --- a/Classes/Utility/VersionUtility.php +++ b/Classes/Utility/VersionUtility.php @@ -18,7 +18,7 @@ public static function getVersion(): string $str = 'dev'; try { return explode('@', Versions::getVersion('andersundsehr/ssi-include'))[0] ?? $str; - } catch (Throwable $e) { + } catch (Throwable) { return $str; } } diff --git a/Classes/ViewHelpers/RenderIncludeViewHelper.php b/Classes/ViewHelpers/RenderIncludeViewHelper.php index 1853510..9daca3d 100644 --- a/Classes/ViewHelpers/RenderIncludeViewHelper.php +++ b/Classes/ViewHelpers/RenderIncludeViewHelper.php @@ -4,6 +4,8 @@ namespace AUS\SsiInclude\ViewHelpers; +use Webimpress\SafeWriter\Exception\ExceptionInterface; +use Closure; use AUS\SsiInclude\Event\RenderedEvent; use Exception; use TYPO3\CMS\Core\Context\Context; @@ -33,14 +35,12 @@ public function initializeArguments(): void /** * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return string - * @throws \Webimpress\SafeWriter\Exception\ExceptionInterface + * @throws Exception + * @throws ExceptionInterface */ - public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string + public static function renderStatic(array $arguments, Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string { - $name = static::validateName($arguments); + $name = self::validateName($arguments); $filename = static::getSiteName() . '_' . static::getLangauge() . '_' . $name; $basePath = self::SSI_INCLUDE_DIR . $filename; @@ -56,10 +56,11 @@ public static function renderStatic(array $arguments, \Closure $renderChildrenCl $eventDispatcher->dispatch($renderedHtmlEvent); $html = $renderedHtmlEvent->getHtml(); - @mkdir(dirname($absolutePath), octdec($GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']), true); + @mkdir(dirname($absolutePath), (int)octdec((string)$GLOBALS['TYPO3_CONF_VARS']['SYS']['folderCreateMask']), true); FileWriter::writeFile($absolutePath, $html); GeneralUtility::fixPermissions($absolutePath); } + return ''; } @@ -68,25 +69,24 @@ private static function shouldRenderFile(string $absolutePath, int $cacheLifeTim if (!file_exists($absolutePath)) { return true; } + if ((filemtime($absolutePath) + $cacheLifeTime) < time()) { return true; } - if (self::isBackendUser()) { - return true; - } - return false; + + return self::isBackendUser(); } /** * @param array $arguments - * @return string * @throws Exception */ private static function validateName(array $arguments): string { - if (ctype_alnum($arguments['name'])) { + if (ctype_alnum((string) $arguments['name'])) { return $arguments['name']; } + throw new Exception(sprintf('Only Alphanumeric characters allowed got: "%s"', $arguments['name'])); } diff --git a/Configuration/RequestMiddlewares.php b/Configuration/RequestMiddlewares.php index cfa41fe..f503e75 100644 --- a/Configuration/RequestMiddlewares.php +++ b/Configuration/RequestMiddlewares.php @@ -1,9 +1,11 @@ [ - \AUS\SsiInclude\Middleware\InternalSsiRedirectMiddleware::class => [ - 'target' => \AUS\SsiInclude\Middleware\InternalSsiRedirectMiddleware::class, + InternalSsiRedirectMiddleware::class => [ + 'target' => InternalSsiRedirectMiddleware::class, 'before' => [ 'typo3/cms-core/normalized-params-attribute' ], diff --git a/composer.json b/composer.json index c337fb7..e6a0b66 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "name": "andersundsehr/ssi-include", "description": "Allows to periodically create ssi includes from anders und sehr GmbH", - "type": "typo3-cms-extension", "license": "GPL-3.0-or-later", + "type": "typo3-cms-extension", "authors": [ { "name": "Matthias Vogel", @@ -10,48 +10,44 @@ "homepage": "https://andersundsehr.com" } ], + "require": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "ocramius/package-versions": "^2.1.0", + "typo3/cms-fluid": "^10.4.0 || ^11.5.0 || ^12.4.0", + "typo3/cms-frontend": "^10.4.0 || ^11.5.0 || ^12.4.0", + "webimpress/safe-writer": "^2.2.0" + }, + "require-dev": { + "composer/composer": "^2.5.5", + "pluswerk/grumphp-config": "^7.0", + "saschaegerer/phpstan-typo3": "^1.10.0", + "ssch/typo3-rector": "^2.5.0" + }, "replace": { "typo3-ter/ssi-include": "self.version" }, - "extra": { - "typo3/cms": { - "extension-key": "ssi_include" - }, - "pluswerk/grumphp-config": { - "auto-setting": true - }, - "grumphp": { - "config-default-path": "vendor/pluswerk/grumphp-config/grumphp.yml" + "autoload": { + "psr-4": { + "AUS\\SsiInclude\\": "Classes/" } }, "config": { - "sort-packages": true, "allow-plugins": { + "ergebnis/composer-normalize": true, "phpro/grumphp": true, - "typo3/class-alias-loader": true, - "typo3/cms-composer-installers": true, + "phpstan/extension-installer": true, "pluswerk/grumphp-config": true, - "phpstan/extension-installer": true - } + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true + }, + "sort-packages": true }, - "autoload": { - "psr-4": { - "AUS\\SsiInclude\\": "Classes/" + "extra": { + "typo3/cms": { + "extension-key": "ssi_include" } }, "ter-require": { "webimpress/safe-writer": "^2.2.0" - }, - "require": { - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0", - "ocramius/package-versions": "^2.1.0", - "typo3/cms-fluid": "^10.4.0 || ^11.5.0 || ^12.4.0", - "typo3/cms-frontend": "^10.4.0 || ^11.5.0 || ^12.4.0", - "webimpress/safe-writer": "^2.2.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.1.0", - "pluswerk/grumphp-config": "^5.0", - "saschaegerer/phpstan-typo3": "^0.13.3" } } diff --git a/ext_emconf.php b/ext_emconf.php index f995f6f..d5190fc 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,5 +1,7 @@ 'SSI Include - Render your includes', @@ -10,7 +12,7 @@ 'author_company' => 'anders und sehr GmbH', 'state' => 'stable', 'clearCacheOnLoad' => 0, - 'version' => \AUS\SsiInclude\Utility\VersionUtility::getVersion(), + 'version' => VersionUtility::getVersion(), 'constraints' => [ 'depends' => [ 'typo3' => '10.4.0-11.99.99', diff --git a/ext_localconf.php b/ext_localconf.php index 82517ea..ca4e54f 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,6 +1,10 @@ clearCache'; diff --git a/grumphp.yml b/grumphp.yml new file mode 100644 index 0000000..3a60a5a --- /dev/null +++ b/grumphp.yml @@ -0,0 +1,16 @@ +imports: + - { resource: vendor/pluswerk/grumphp-config/grumphp.yml } +parameters: + convention.process_timeout: 240 + convention.security_checker_blocking: true + convention.jsonlint_ignore_pattern: { } + convention.xmllint_ignore_pattern: { } + convention.yamllint_ignore_pattern: { } + convention.phpcslint_ignore_pattern: { } + convention.phpcslint_exclude: { } + convention.xlifflint_ignore_pattern: { } + convention.rector_ignore_pattern: { } + convention.rector_enabled: true + convention.rector_config: rector.php + convention.rector_clear-cache: false + convention.phpstan_level: null diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 0000000..9631d44 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1 @@ +parameters: diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..e178ba6 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +includes: + - phpstan-baseline.neon + - vendor/andersundsehr/phpstan-git-files/extension.php + +parameters: + level: 8 + reportUnmatchedIgnoredErrors: false diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..0f267f7 --- /dev/null +++ b/rector.php @@ -0,0 +1,42 @@ +parallel(); + $rectorConfig->importNames(); + $rectorConfig->importShortClasses(); + $rectorConfig->cacheClass(FileCacheStorage::class); + $rectorConfig->cacheDirectory('./var/cache/rector'); + + $rectorConfig->paths( + array_filter(explode("\n", (string)shell_exec("git ls-files | xargs ls -d 2>/dev/null | grep -E '\.(php)$'"))) + ); + + // define sets of rules + $rectorConfig->sets( + [ + ...RectorSettings::sets(true), + ...RectorSettings::setsTypo3(false), + ] + ); + + // remove some rules + // ignore some files + $rectorConfig->skip( + [ + ...RectorSettings::skip(), + ...RectorSettings::skipTypo3(), + + /** + * rector should not touch these files + */ + //__DIR__ . '/src/Example', + //__DIR__ . '/src/Example.php', + ] + ); +};