Skip to content

Commit

Permalink
Introduce factory for the server url helper
Browse files Browse the repository at this point in the history
Adds laminas-diactoros as a dependency so that detection of the host URI can be delegated to existing code

Signed-off-by: George Steel <[email protected]>
  • Loading branch information
gsteel committed Mar 24, 2022
1 parent 977708b commit 0106980
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 2 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"ext-filter": "*",
"ext-json": "*",
"container-interop/container-interop": "^1.2",
"laminas/laminas-diactoros": "^2.8",
"laminas/laminas-escaper": "^2.5",
"laminas/laminas-eventmanager": "^3.4",
"laminas/laminas-json": "^3.3",
Expand Down
209 changes: 208 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions src/Helper/Service/ServerUrlFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Laminas\View\Helper\Service;

use ArrayAccess;
use Laminas\View\Exception\RuntimeException;
use Laminas\View\Helper\ServerUrl;
use Psr\Container\ContainerInterface;

use function assert;
use function is_array;
use function is_string;
use function Laminas\Diactoros\marshalHeadersFromSapi;
use function Laminas\Diactoros\marshalUriFromSapi;

final class ServerUrlFactory
{
public function __invoke(ContainerInterface $container): ServerUrl
{
return new ServerUrl(
$this->fetchConfiguredServerUrl($container) ?: $this->detectServerUrlFromEnvironment()
);
}

private function fetchConfiguredServerUrl(ContainerInterface $container): ?string
{
$config = $container->has('config') ? $container->get('config') : [];
assert(is_array($config) || $config instanceof ArrayAccess);

$helperConfig = $config['view_helper_config'] ?? [];
assert(is_array($helperConfig));

$serverUrl = $helperConfig['server_url'] ?? null;
assert(is_string($serverUrl) || $serverUrl === null);

return $serverUrl;
}

private function detectServerUrlFromEnvironment(): string
{
$uri = marshalUriFromSapi($_SERVER, marshalHeadersFromSapi($_SERVER))
->withPath('')
->withQuery('')
->withFragment('');

if (! $uri->getHost() || ! $uri->getScheme()) {
throw new RuntimeException(
'The current host or scheme cannot be detected from the environment'
);
}

return (string) $uri;
}
}
2 changes: 1 addition & 1 deletion src/HelperPluginManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class HelperPluginManager extends AbstractPluginManager
Helper\Placeholder::class => InvokableFactory::class,
Helper\RenderChildModel::class => InvokableFactory::class,
Helper\RenderToPlaceholder::class => InvokableFactory::class,
Helper\ServerUrl::class => InvokableFactory::class,
Helper\ServerUrl::class => Helper\Service\ServerUrlFactory::class,
Helper\Url::class => InvokableFactory::class,
Helper\ViewModel::class => InvokableFactory::class,

Expand Down
89 changes: 89 additions & 0 deletions test/Helper/Service/ServerUrlFactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace LaminasTest\View\Helper\Service;

use Laminas\ServiceManager\ServiceManager;
use Laminas\View\Exception\RuntimeException;
use Laminas\View\Helper\Service\ServerUrlFactory;
use PHPUnit\Framework\TestCase;

class ServerUrlFactoryTest extends TestCase
{
/** @var array<array-key, mixed> */
private array $serverVariables;

protected function setUp(): void
{
parent::setUp();
$this->serverVariables = $_SERVER;
}

protected function tearDown(): void
{
$_SERVER = $this->serverVariables;
parent::tearDown();
}

public function testThatWhenThereIsNoConfigurationTheHostUriWillBeDetectedFromGlobals(): void
{
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['HTTPS'] = true;
$_SERVER['SERVER_PORT'] = 443;

$helper = (new ServerUrlFactory())(new ServiceManager());
self::assertEquals('https://example.com', $helper->__invoke());
}

public function testThatWhenThereIsNoConfigurationDetectedPathQueryAndFragmentWillBeOmitted(): void
{
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['HTTPS'] = true;
$_SERVER['SERVER_PORT'] = 443;
$_SERVER['REQUEST_URI'] = '/some/#thing?foo=bar';

$helper = (new ServerUrlFactory())(new ServiceManager());
self::assertEquals('https://example.com', $helper->__invoke());
}

public function testThatConfiguredHostIsPreferred(): void
{
$container = new ServiceManager();
$container->setService('config', [
'view_helper_config' => [
'server_url' => 'https://other.example.com',
],
]);

$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['HTTPS'] = true;
$_SERVER['SERVER_PORT'] = 443;

$helper = (new ServerUrlFactory())($container);
self::assertEquals('https://other.example.com', $helper->__invoke());
}

public function testUndetectableEnvironmentAndZeroConfigurationYieldsException(): void
{
$_SERVER = [];

$this->expectException(RuntimeException::class);
$this->expectExceptionMessage('The current host or scheme cannot be detected from the environment');

(new ServerUrlFactory())(new ServiceManager());
}

public function testAnEmptyEnvironmentIsAcceptableWhenConfigurationIsFound(): void
{
$_SERVER = [];
$container = new ServiceManager();
$container->setService('config', [
'view_helper_config' => [
'server_url' => 'https://other.example.com',
],
]);
$helper = (new ServerUrlFactory())($container);
self::assertEquals('https://other.example.com', $helper->__invoke());
}
}

0 comments on commit 0106980

Please sign in to comment.