Skip to content

Commit

Permalink
[TASK] Change Site Base Handling to PSR-7 URI Interface
Browse files Browse the repository at this point in the history
While working with Site Handling API, it's more than obvious
that we should utilize the UriInterface, to integrate PSR-7
more natively.

The Interface changes, however, as it was only introduced
recently, this should not be an issue.

Resolves: #86027
Releases: master
Change-Id: Id06ccbcf2bbced8408342866d295edc3f11a376a
Reviewed-on: https://review.typo3.org/58070
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: TYPO3com <no-reply@typo3.com>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
  • Loading branch information
bmack authored and andreaskienast committed Aug 29, 2018
1 parent aef8cfb commit 51a7bb5
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 82 deletions.
10 changes: 4 additions & 6 deletions typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php
Expand Up @@ -141,13 +141,11 @@ public function render()
protected function getPrefix(SiteInterface $site, int $requestLanguageId = 0): string
{
$language = $site->getLanguageById($requestLanguageId);
$baseUrl = $language->getBase();
$base = $language->getBase();
$baseUrl = (string)$base;
$baseUrl = rtrim($baseUrl, '/');
if (!empty($baseUrl)) {
$urlParts = parse_url($baseUrl);
if (!isset($urlParts['scheme']) && isset($urlParts['host'])) {
$baseUrl = 'http:' . $baseUrl;
}
if (!empty($baseUrl) && empty($base->getScheme()) && $base->getHost() !== '') {
$baseUrl = 'http:' . $baseUrl;
}
return $baseUrl;
}
Expand Down
Expand Up @@ -18,6 +18,7 @@

use TYPO3\CMS\Backend\View\PageLayoutView;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
Expand All @@ -42,16 +43,16 @@ protected function setUp(): void

$this->subject = $this->getAccessibleMock(PageLayoutView::class, ['dummy']);
$this->subject->_set('siteLanguages', [
0 => new SiteLanguage(0, '', '/', [
0 => new SiteLanguage(0, '', new Uri('/'), [
'title' => 'default',
]),
1 => new SiteLanguage(1, '', '/', [
1 => new SiteLanguage(1, '', new Uri('/'), [
'title' => 'german',
]),
2 => new SiteLanguage(2, '', '/', [
2 => new SiteLanguage(2, '', new Uri('/'), [
'title' => 'french',
]),
3 => new SiteLanguage(3, '', '/', [
3 => new SiteLanguage(3, '', new Uri('/'), [
'title' => 'polish',
]),
]);
Expand Down
2 changes: 1 addition & 1 deletion typo3/sysext/core/Classes/Http/Uri.php
Expand Up @@ -610,7 +610,7 @@ public function __toString()
protected function isNonStandardPort($scheme, $host, $port)
{
if (empty($scheme)) {
return true;
return empty($host) || !empty($port);
}

if (empty($host) || empty($port)) {
Expand Down
2 changes: 1 addition & 1 deletion typo3/sysext/core/Classes/Routing/PageUriBuilder.php
Expand Up @@ -100,7 +100,7 @@ public function buildUri(int $pageId, array $queryParameters = [], string $fragm
} else {
$pageRecord = BackendUtility::getRecord('pages', $pageId);
}
$prefix = $siteLanguage->getBase();
$prefix = (string)$siteLanguage->getBase();
if (!empty($pageRecord['slug'] ?? '')) {
$prefix = rtrim($prefix, '/') . '/' . ltrim($pageRecord['slug'], '/');
} else {
Expand Down
43 changes: 21 additions & 22 deletions typo3/sysext/core/Classes/Routing/SiteMatcher.php
Expand Up @@ -190,30 +190,30 @@ protected function getRouteCollectionForAllSites(): RouteCollection
$groupedRoutes = [];
foreach ($this->finder->getAllSites() as $site) {
// Add the site as entrypoint
$urlParts = parse_url($site->getBase());
$uri = $site->getBase();
$route = new Route(
($urlParts['path'] ?? '/') . '{tail}',
($uri->getPath() ?: '/') . '{tail}',
['site' => $site, 'language' => null, 'tail' => ''],
array_filter(['tail' => '.*', 'port' => (string)($urlParts['port'] ?? '')]),
array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
['utf8' => true],
$urlParts['host'] ?? '',
!empty($urlParts['scheme']) ? [$urlParts['scheme']] : null
$uri->getHost() ?: '',
$uri->getScheme()
);
$identifier = 'site_' . $site->getIdentifier();
$groupedRoutes[($urlParts['scheme'] ?? '-') . ($urlParts['host'] ?? '-')][$urlParts['path'] ?? '/'][$identifier] = $route;
$groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
// Add all languages
foreach ($site->getAllLanguages() as $siteLanguage) {
$urlParts = parse_url($siteLanguage->getBase());
$uri = $siteLanguage->getBase();
$route = new Route(
($urlParts['path'] ?? '/') . '{tail}',
($uri->getPath() ?: '/') . '{tail}',
['site' => $site, 'language' => $siteLanguage, 'tail' => ''],
array_filter(['tail' => '.*', 'port' => (string)($urlParts['port'] ?? '')]),
array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
['utf8' => true],
$urlParts['host'] ?? '',
!empty($urlParts['scheme']) ? [$urlParts['scheme']] : null
$uri->getHost() ?: '',
$uri->getScheme()
);
$identifier = 'site_' . $site->getIdentifier() . '_' . $siteLanguage->getLanguageId();
$groupedRoutes[($urlParts['scheme'] ?? '-') . ($urlParts['host'] ?? '-')][$urlParts['path'] ?? '/'][$identifier] = $route;
$groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
}
}
return $this->createRouteCollectionFromGroupedRoutes($groupedRoutes);
Expand All @@ -234,23 +234,22 @@ protected function getRouteCollectionForVisibleSysDomains(): RouteCollection
if (!$site instanceof PseudoSite) {
continue;
}
foreach ($site->getEntryPoints() as $domainName) {
foreach ($site->getEntryPoints() as $uri) {
// Site has no sys_domain record, it is not valid for a routing entrypoint, but only available
// via "id" GET parameter which is handled before
if ($domainName === '/') {
// via "id" GET parameter which is handled separately
if (!$uri->getHost()) {
continue;
}
$urlParts = parse_url($domainName);
$route = new Route(
($urlParts['path'] ?? '/') . '{tail}',
($uri->getPath() ?: '/') . '{tail}',
['site' => $site, 'language' => null, 'tail' => ''],
array_filter(['tail' => '.*', 'port' => (string)($urlParts['port'] ?? '')]),
array_filter(['tail' => '.*', 'port' => (string)$uri->getPort()]),
['utf8' => true],
$urlParts['host'] ?? '',
!empty($urlParts['scheme']) ? [$urlParts['scheme']] : null
$uri->getHost(),
$uri->getScheme()
);
$identifier = 'site_' . $site->getIdentifier() . '_' . $domainName;
$groupedRoutes[($urlParts['scheme'] ?? '-') . ($urlParts['host'] ?? '-')][$urlParts['path'] ?? '/'][$identifier] = $route;
$identifier = 'site_' . $site->getIdentifier() . '_' . (string)$uri;
$groupedRoutes[($uri->getScheme() ?: '-') . ($uri->getHost() ?: '-')][$uri->getPath() ?: '/'][$identifier] = $route;
}
}
return $this->createRouteCollectionFromGroupedRoutes($groupedRoutes);
Expand Down
9 changes: 5 additions & 4 deletions typo3/sysext/core/Classes/Site/Entity/NullSite.php
Expand Up @@ -16,9 +16,11 @@
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;

Expand Down Expand Up @@ -48,11 +50,10 @@ public function __construct(array $languages = null)
$languageUid = (int)$languageConfiguration['languageId'];
// Language configuration does not have a base defined
// So the main site base is used (usually done for default languages)
$base = '/';
$this->languages[$languageUid] = new SiteLanguage(
$languageUid,
$languageConfiguration['locale'] ?? '',
$base,
new Uri('/'),
$languageConfiguration
);
}
Expand All @@ -71,9 +72,9 @@ public function getIdentifier(): string
/**
* Always "/"
*/
public function getBase(): string
public function getBase(): UriInterface
{
return '/';
return new Uri('/');
}

/**
Expand Down
16 changes: 9 additions & 7 deletions typo3/sysext/core/Classes/Site/Entity/PseudoSite.php
Expand Up @@ -16,6 +16,9 @@
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Core\Http\Uri;

/**
* Entity representing a site with legacy configuration (sys_domain) and all available
* languages in the system (sys_language)
Expand Down Expand Up @@ -48,21 +51,20 @@ public function __construct(int $rootPageId, array $configuration)
continue;
}
$this->domainRecords[] = $domain;
$this->entryPoints[] = $this->sanitizeBaseUrl($domain['domainName'] ?: '');
$this->entryPoints[] = new Uri($this->sanitizeBaseUrl($domain['domainName'] ?: ''));
}
if (empty($this->entryPoints)) {
$this->entryPoints = ['/'];
$this->entryPoints = [new Uri('/')];
}
$baseEntryPoint = reset($this->entryPoints);
foreach ($configuration['languages'] as $languageConfiguration) {
$languageUid = (int)$languageConfiguration['languageId'];
// Language configuration does not have a base defined
// So the main site base is used (usually done for default languages)
$base = $this->sanitizeBaseUrl(rtrim($baseEntryPoint, '/') . '/');
$this->languages[$languageUid] = new SiteLanguage(
$languageUid,
$languageConfiguration['locale'] ?? '',
$base,
$baseEntryPoint,
$languageConfiguration
);
}
Expand All @@ -81,15 +83,15 @@ public function getIdentifier(): string
/**
* Returns the first base URL of this site, falls back to "/"
*/
public function getBase(): string
public function getBase(): UriInterface
{
return $this->entryPoints[0] ?? '/';
return $this->entryPoints[0] ?? new Uri('/');
}

/**
* Returns the base URLs of this site, if none given, it's always "/"
*
* @return array
* @return UriInterface[]
*/
public function getEntryPoints(): array
{
Expand Down
22 changes: 11 additions & 11 deletions typo3/sysext/core/Classes/Site/Entity/Site.php
Expand Up @@ -16,12 +16,14 @@
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\UriInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Error\PageErrorHandler\FluidPageErrorHandler;
use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageContentErrorHandler;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Routing\PageRouter;
use TYPO3\CMS\Core\Routing\RouterInterface;
Expand All @@ -42,7 +44,7 @@ class Site implements SiteInterface
protected $identifier;

/**
* @var string
* @var UriInterface
*/
protected $base;

Expand Down Expand Up @@ -92,23 +94,21 @@ public function __construct(string $identifier, int $rootPageId, array $configur
'direction' => '',
]
];
$this->base = $this->sanitizeBaseUrl($configuration['base'] ?? '');
$this->base = new Uri($this->sanitizeBaseUrl($configuration['base'] ?? ''));
foreach ($configuration['languages'] as $languageConfiguration) {
$languageUid = (int)$languageConfiguration['languageId'];
// site language has defined its own base, this is the case most of the time.
if (!empty($languageConfiguration['base'])) {
$base = $languageConfiguration['base'];
$base = $this->sanitizeBaseUrl($base);
$baseParts = parse_url($base);
$base = new Uri($this->sanitizeBaseUrl($languageConfiguration['base']));
// no host given by the language-specific base, so lets prefix the main site base
if (empty($baseParts['scheme']) && empty($baseParts['host'])) {
$base = rtrim($this->base, '/') . '/' . ltrim($base, '/');
$base = $this->sanitizeBaseUrl($base);
if ($base->getScheme() === null && $base->getHost() === '') {
$base = rtrim((string)$this->base, '/') . '/' . ltrim((string)$base, '/');
$base = new Uri($this->sanitizeBaseUrl($base));
}
} else {
// Language configuration does not have a base defined
// So the main site base is used (usually done for default languages)
$base = $this->sanitizeBaseUrl(rtrim($this->base, '/') . '/');
$base = new Uri($this->sanitizeBaseUrl(rtrim((string)$this->base, '/') . '/'));
}
if (!empty($languageConfiguration['flag'])) {
if ($languageConfiguration['flag'] === 'global') {
Expand Down Expand Up @@ -145,9 +145,9 @@ public function getIdentifier(): string
/**
* Returns the base URL of this site
*
* @return string
* @return UriInterface
*/
public function getBase(): string
public function getBase(): UriInterface
{
return $this->base;
}
Expand Down
14 changes: 8 additions & 6 deletions typo3/sysext/core/Classes/Site/Entity/SiteLanguage.php
Expand Up @@ -16,6 +16,8 @@
* The TYPO3 project - inspiring people to share!
*/

use Psr\Http\Message\UriInterface;

/**
* Entity representing a site_language configuration of a site object.
*/
Expand All @@ -38,7 +40,7 @@ class SiteLanguage
/**
* The Base URL for this language
*
* @var string
* @var UriInterface
*/
protected $base;

Expand Down Expand Up @@ -115,10 +117,10 @@ class SiteLanguage
*
* @param int $languageId
* @param string $locale
* @param string $base
* @param UriInterface $base
* @param array $attributes
*/
public function __construct(int $languageId, string $locale, string $base, array $attributes)
public function __construct(int $languageId, string $locale, UriInterface $base, array $attributes)
{
$this->languageId = $languageId;
$this->locale = $locale;
Expand Down Expand Up @@ -167,7 +169,7 @@ public function toArray(): array
return [
'languageId' => $this->getLanguageId(),
'locale' => $this->getLocale(),
'base' => $this->getBase(),
'base' => (string)$this->getBase(),
'title' => $this->getTitle(),
'navigationTitle' => $this->getNavigationTitle(),
'twoLetterIsoCode' => $this->getTwoLetterIsoCode(),
Expand Down Expand Up @@ -198,9 +200,9 @@ public function getLocale(): string
}

/**
* @return string
* @return UriInterface
*/
public function getBase(): string
public function getBase(): UriInterface
{
return $this->base;
}
Expand Down
27 changes: 27 additions & 0 deletions typo3/sysext/core/Tests/Unit/Http/UriTest.php
Expand Up @@ -200,6 +200,33 @@ public function withPortRaisesExceptionForInvalidPortsByRange($port)
$uri->withPort($port);
}

/**
* @test
*/
public function standardPortAndSchemeDoesNotRenderPort()
{
$subject = new Uri('http://www.example.com:80');
$this->assertEquals('http://www.example.com', (string)$subject);
}

/**
* @test
*/
public function standardPortAndNoSchemeDoesRenderPort()
{
$subject = new Uri('www.example.com:80');
$this->assertEquals('//www.example.com:80', (string)$subject);
}

/**
* @test
*/
public function noPortAndNoSchemeDoesNotRenderPort()
{
$subject = new Uri('www.example.com');
$this->assertEquals('/www.example.com', (string)$subject);
}

/**
* @test
*/
Expand Down

0 comments on commit 51a7bb5

Please sign in to comment.