Skip to content
  •  
  •  
  •  
84 changes: 45 additions & 39 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: build

on:
push:
branches: [ master, development, '[0-9]+.[0-9]+', '[0-9]+.[0-9]+.[0-9]+' ]
branches: [ master, main, development, '[0-9]+.[0-9]+', '[0-9]+.[0-9]+.[0-9]+' ]
pull_request:
branches: [ master, development, '[0-9]+.[0-9]', '[0-9]+.[0-9]+.[0-9]+' ]
branches: [ master, main, development, '[0-9]+.[0-9]+', '[0-9]+.[0-9]+.[0-9]+' ]
schedule:
- cron: '30 4 * * *'

Expand All @@ -16,9 +16,8 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- php: "8.1"
typo3: "^11.5"
php: ["8.2", "8.3", "8.4", "8.5"]
typo3: ["^13.4", "^14.3"]

runs-on: ubuntu-latest

Expand All @@ -35,10 +34,19 @@ jobs:
tools: composer:v2.2

- name: Select TYPO3 version
run: "composer require typo3/cms-core:${{ matrix.typo3 }} -W --no-update"
run: >
composer require
typo3/cms-core:${{ matrix.typo3 }}
typo3/cms-extbase:${{ matrix.typo3 }}
typo3/cms-fluid:${{ matrix.typo3 }}
typo3/cms-frontend:${{ matrix.typo3 }}
typo3/cms-backend:${{ matrix.typo3 }}
-W --no-update

- name: Composer install
uses: ramsey/composer-install@v2
with:
composer-options: --ignore-platform-req=php+

- name: Run phpcs
run: vendor/bin/phpcs Classes --standard=PSR2
Expand All @@ -50,21 +58,8 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- php: "7.4"
typo3: "^10.4"
- php: "8.0"
typo3: "^11.5"
- php: "8.1"
typo3: "^11.5"
- php: "8.1"
typo3: "^12.4"
- php: "8.2"
typo3: "^12.4"
- php: "8.3"
typo3: "^12.4"
- php: "8.3"
typo3: "^13.4"
php: ["8.2", "8.3", "8.4", "8.5"]
typo3: ["^13.4", "^14.3"]

runs-on: ubuntu-latest

Expand All @@ -81,10 +76,19 @@ jobs:
tools: composer:v2.2

- name: Select TYPO3 version
run: "composer require typo3/cms-core:${{ matrix.typo3 }} -W --no-update"
run: >
composer require
typo3/cms-core:${{ matrix.typo3 }}
typo3/cms-extbase:${{ matrix.typo3 }}
typo3/cms-fluid:${{ matrix.typo3 }}
typo3/cms-frontend:${{ matrix.typo3 }}
typo3/cms-backend:${{ matrix.typo3 }}
-W --no-update

- name: Composer install
uses: ramsey/composer-install@v2
with:
composer-options: --ignore-platform-req=php+

- name: Run phpstan
run: vendor/bin/phpstan analyze
Expand All @@ -96,21 +100,8 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- php: "7.4"
typo3: "^10.4"
- php: "8.0"
typo3: "^11.5"
- php: "8.1"
typo3: "^11.5"
- php: "8.1"
typo3: "^12.4"
- php: "8.2"
typo3: "^12.4"
- php: "8.3"
typo3: "^12.4"
- php: "8.3"
typo3: "^13.4"
php: ["8.2", "8.3", "8.4", "8.5"]
typo3: ["^13.4", "^14.3"]

runs-on: ubuntu-latest

Expand All @@ -122,19 +113,34 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, json
extensions: mbstring, json, pdo_sqlite
ini-values: date.timezone="Europe/Copenhagen", opcache.fast_shutdown=0
tools: composer:v2.2

- name: Select TYPO3 version
run: "composer require typo3/cms-core:${{ matrix.typo3 }} --no-update"
run: >
composer require
typo3/cms-core:${{ matrix.typo3 }}
typo3/cms-extbase:${{ matrix.typo3 }}
typo3/cms-fluid:${{ matrix.typo3 }}
typo3/cms-frontend:${{ matrix.typo3 }}
typo3/cms-backend:${{ matrix.typo3 }}
-W --no-update

- name: Composer install
uses: ramsey/composer-install@v2
with:
composer-options: --ignore-platform-req=php+

- name: Restore source Composer manifest
run: git checkout -- composer.json

- name: Run tests
run: vendor/bin/phpunit --coverage-clover=build/logs/clover.xml

- name: Run functional tests
run: typo3DatabaseDriver=pdo_sqlite vendor/bin/phpunit -c phpunit-functional.xml.dist

- name: Upload test coverage
run: |
composer global require php-coveralls/php-coveralls
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ vendor
.phpunit.result.cache
composer.lock
preflight
/var
/build
/Documentation-GENERATED-temp/
/index.php
Expand Down
14 changes: 14 additions & 0 deletions Classes/Asset.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?php
declare(strict_types=1);

namespace FluidTYPO3\Vhs;

/*
Expand Down Expand Up @@ -82,6 +84,7 @@ class Asset implements AssetInterface
protected bool $async = false;
protected bool $defer = false;
protected bool $rewrite = true;
protected ?bool $csp = null;
private static ?array $settingsCache = null;

public function __construct()
Expand Down Expand Up @@ -265,6 +268,17 @@ public function getDefer(): bool
return $this->defer;
}

public function setCsp(?bool $csp): self
{
$this->csp = $csp;
return $this;
}

public function getCsp(): ?bool
{
return $this->csp;
}

public function getName(): string
{
return $this->name;
Expand Down
10 changes: 3 additions & 7 deletions Classes/Core/ViewHelper/AbstractViewHelper.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
namespace FluidTYPO3\Vhs\Core\ViewHelper;
declare(strict_types=1);

use TYPO3Fluid\Fluid\Component\Argument\ArgumentCollection;
namespace FluidTYPO3\Vhs\Core\ViewHelper;

class AbstractViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper
{
Expand All @@ -10,10 +10,6 @@ class AbstractViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewH
*/
public function render()
{
return static::renderStatic(
$this->arguments instanceof ArgumentCollection ? $this->arguments->getArrayCopy() : $this->arguments,
$this->buildRenderChildrenClosure(),
$this->renderingContext
);
return static::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext);
}
}
22 changes: 21 additions & 1 deletion Classes/Events/AfterCacheableContentIsGeneratedEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace FluidTYPO3\Vhs\Events;

use FluidTYPO3\Vhs\Service\AssetService;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent;

class AfterCacheableContentIsGeneratedEventListener
Expand All @@ -16,6 +17,25 @@ public function __construct(AssetService $assetService)

public function insertVhsAssetHeaderAndFooterCode(AfterCacheableContentIsGeneratedEvent $event): void
{
$this->assetService->buildAll([], $event->getController(), $event->isCachingEnabled());
if ($this->isAssetHandlingDisabled()) {
return;
}
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '14.0', '<')) {
return;
}
// @phpstan-ignore-next-line TYPO3 14-only event content API.
$content = $event->getContent();
$this->assetService->buildAll([], $event->getRequest(), $event->isCachingEnabled(), $content);
// @phpstan-ignore-next-line TYPO3 14-only event content API.
$event->setContent($content);
}

private function isAssetHandlingDisabled(): bool
{
$disabled = $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['vhs']['disableAssetHandling']
?? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['vhs']['setup']['disableAssetHandling']
?? false;

return filter_var($disabled, \FILTER_VALIDATE_BOOL);
}
}
20 changes: 16 additions & 4 deletions Classes/Middleware/AssetInclusion.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\Stream;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class AssetInclusion implements MiddlewareInterface
{
public function __construct(private readonly AssetService $assetService)
{
}

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);

if (!$this->isHtmlResponse($response)) {
return $response;
}

$body = $response->getBody();
$body->rewind();
$contents = $body->getContents();
$contentsBefore = $contents;

/** @var AssetService $assetService */
$assetService = GeneralUtility::makeInstance(AssetService::class);
$assetService->buildAllUncached([], $GLOBALS['TSFE'], $contents);
$this->assetService->buildAllUncached([], $request, $contents);

if ($contentsBefore === $contents) {
// Content is unchanged, return the original response since there is no need to modify it, or the
Expand All @@ -38,4 +43,11 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface

return $response->withBody(new Stream($stream));
}

private function isHtmlResponse(ResponseInterface $response): bool
{
$contentType = strtolower($response->getHeaderLine('Content-Type'));

return $contentType === '' || str_contains($contentType, 'text/html');
}
}
4 changes: 3 additions & 1 deletion Classes/Middleware/RequestAvailability.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php

declare(strict_types=1);

namespace FluidTYPO3\Vhs\Middleware;

use Psr\Http\Message\ResponseInterface;
Expand All @@ -10,7 +13,6 @@ class RequestAvailability implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$GLOBALS['TYPO3_REQUEST'] = $GLOBALS['TYPO3_REQUEST'] ?? $request;
return $handler->handle($request);
}
}
7 changes: 3 additions & 4 deletions Classes/Proxy/DispatcherProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
* LICENSE.md file that was distributed with this source code.
*/

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Dispatcher;
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
use TYPO3\CMS\Extbase\Mvc\Response;
use Psr\Http\Message\ResponseInterface;

class DispatcherProxy
{
Expand All @@ -22,8 +21,8 @@ public function __construct(Dispatcher $dispatcher)
$this->dispatcher = $dispatcher;
}

public function dispatch(RequestInterface $request, ?Response $response = null): ?ResponseInterface
public function dispatch(RequestInterface $request): ResponseInterface
{
return $this->dispatcher->dispatch($request, $response);
return $this->dispatcher->dispatch($request);
}
}
41 changes: 6 additions & 35 deletions Classes/Proxy/DoctrineQueryProxy.php
Original file line number Diff line number Diff line change
@@ -1,52 +1,23 @@
<?php
namespace FluidTYPO3\Vhs\Proxy;

use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\Result;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;

class DoctrineQueryProxy
{
/**
* Returns \Doctrine\DBAL\Result on v11+, \Doctrine\DBAL\Driver\ResultStatement on v10
*
* @return Result|ResultStatement
*/
public static function executeQueryOnQueryBuilder(QueryBuilder $queryBuilder)
public static function executeQueryOnQueryBuilder(QueryBuilder $queryBuilder): Result
{
if (method_exists($queryBuilder, 'executeQuery')) {
return $queryBuilder->executeQuery();
}
/** @var Result $result */
$result = $queryBuilder->execute();
return $result;
return $queryBuilder->executeQuery();
}

/**
* @param Result|ResultStatement $result
*/
public static function fetchAssociative($result): ?array
public static function fetchAssociative(Result $result): ?array
{
if (method_exists($result, 'fetchAssociative')) {
/** @var array|null $output */
$output = $result->fetchAssociative() ?: null;
} else {
/** @var array|null $output */
$output = $result->fetch(FetchMode::ASSOCIATIVE);
}

return $output;
return $result->fetchAssociative() ?: null;
}

/**
* @param Result|ResultStatement $result
*/
public static function fetchAllAssociative($result): array
public static function fetchAllAssociative(Result $result): array
{
if (method_exists($result, 'fetchAllAssociative')) {
return $result->fetchAllAssociative() ?: [];
}
return $result->fetchAll(FetchMode::ASSOCIATIVE) ?: [];
return $result->fetchAllAssociative() ?: [];
}
}
Loading
Loading