Skip to content

Instantly share code, notes, and snippets.

@fjahn
Created April 20, 2021 08:40
Show Gist options
  • Save fjahn/5cb87c568b82c89ae582e9b4068bfd92 to your computer and use it in GitHub Desktop.
Save fjahn/5cb87c568b82c89ae582e9b4068bfd92 to your computer and use it in GitHub Desktop.
Builder class for building clean and simple URIs every time
<?php
namespace App\Util;
class UriBuilder
{
private string|null $protocol;
private string|null $host = null;
private array $path_segments = [];
private array $query_parameters = [];
private function __construct(string|null $protocol)
{
$this->protocol = $protocol;
}
public static function https(): UriBuilder
{
return new UriBuilder('https');
}
public static function http(): UriBuilder
{
return new UriBuilder('http');
}
public static function noProtocol(): UriBuilder
{
return new UriBuilder(null);
}
public function build(): string
{
$result = '';
if (!is_null($this->protocol))
$result .= $this->protocol . '://';
if (!is_null($this->host))
$result .= $this->host;
if (!empty($this->path_segments)) {
$result .= '/';
$result .= join('/', $this->path_segments);
}
if (!empty($this->query_parameters)) {
$result .= '?';
$first = true;
foreach ($this->query_parameters as $key => $value) {
if ($first) {
$first = false;
} else {
$result .= '&';
}
$result .= $key . '=' . $value;
}
}
return $result;
}
public function host(string $host): UriBuilder
{
$encoded_host = $this->encode($host);
$this->host = $encoded_host;
return $this;
}
private function encode(string $unencoded): string
{
return rawurlencode($unencoded);
}
private function encodeArray(array $unencoded): string
{
$encodedArray = array_map(function ($element) {
return $this->encode($element);
}, $unencoded);
return implode(',', $encodedArray);
}
public function path(string $path): UriBuilder
{
if (str_contains($path, '/')) {
return $this->pathCombination($path);
} else {
$encoded_path = $this->encode($path);
$this->path_segments[] = $encoded_path;
return $this;
}
}
private function pathCombination(string $path): UriBuilder
{
$new_path_segments = explode('/', $path);
foreach ($new_path_segments as $segment) {
if ($segment != '') {
$this->path($segment);
}
}
return $this;
}
public function query(string $key, $value): UriBuilder
{
if (is_array($value)) {
$stringifiedValues = array_map(function ($element) {
return strval($element);
}, $value);
$encoded_value = $this->encodeArray($stringifiedValues);
} else
$encoded_value = $this->encode(strval($value));
$this->query_parameters[$key] = $encoded_value;
return $this;
}
}
<?php
namespace Tests\Unit\app\Util;
use App\Util\UriBuilder;
use Tests\TestCase;
class UriBuilderTest extends TestCase
{
public function testBasicHttpsUrl()
{
$url = UriBuilder::https()->host('google.com')->build();
$this->assertEquals('https://google.com', $url);
}
public function testBasicHttpUrl()
{
$url = UriBuilder::http()->host('google.com')->build();
$this->assertEquals('http://google.com', $url);
}
public function testWithSinglePath()
{
$url = UriBuilder::https()
->host('google.com')
->path('account')
->build();
$this->assertEquals('https://google.com/account', $url);
}
public function testWithMultiplePath()
{
$url = UriBuilder::https()
->host('google.com')
->path('account')
->path('settings')
->build();
$this->assertEquals('https://google.com/account/settings', $url);
}
public function testWithPathCombination()
{
$url = UriBuilder::https()
->host('google.com')
->path('account/settings')
->build();
$this->assertEquals('https://google.com/account/settings', $url);
}
public function testWithLeadingSlash()
{
$url = UriBuilder::https()
->host('google.com')
->path('/account')
->build();
$this->assertEquals('https://google.com/account', $url);
}
public function testWithTrailingSlash()
{
$url = UriBuilder::https()
->host('google.com')
->path('account/')
->build();
$this->assertEquals('https://google.com/account', $url);
}
public function testWithQueryParameter()
{
$url = UriBuilder::https()
->host('google.com')
->query('page', '5')
->build();
$this->assertEquals('https://google.com?page=5', $url);
}
public function testWithPathAndQueryParameter()
{
$url = UriBuilder::https()
->host('google.com')
->path('account')
->query('page', '5')
->build();
$this->assertEquals('https://google.com/account?page=5', $url);
}
public function testWithQueryParameterInt()
{
$url = UriBuilder::https()
->host('google.com')
->query('page', 5)
->build();
$this->assertEquals('https://google.com?page=5', $url);
}
public function testWithQueryMultipleParameters()
{
$url = UriBuilder::https()
->host('google.com')
->query('page', 5)
->query('offset', 1)
->build();
$this->assertEquals('https://google.com?page=5&offset=1', $url);
}
public function testWithEncodedPath()
{
$url = UriBuilder::https()
->host('google.com')
->path('account info')
->build();
$this->assertEquals('https://google.com/account%20info', $url);
}
public function testWithEncodedQuery()
{
$url = UriBuilder::https()
->host('google.com')
->query('page', 'number five')
->build();
$this->assertEquals('https://google.com?page=number%20five', $url);
}
public function testWithNoProtocol()
{
$url = UriBuilder::noProtocol()
->host('google.com')
->build();
$this->assertEquals('google.com', $url);
}
public function testWithNoProtocolAndPath()
{
$url = UriBuilder::noProtocol()
->host('google.com')
->path('accounts')
->path('me')
->build();
$this->assertEquals('google.com/accounts/me', $url);
}
public function testWithPathOnly()
{
$url = UriBuilder::noProtocol()
->path('accounts')
->path('me')
->build();
$this->assertEquals('/accounts/me', $url);
}
public function testNothing() {
$url = UriBuilder::noProtocol()->build();
$this->assertEquals('', $url);
}
public function testQueryWithArray() {
$url = UriBuilder::https()
->host('graph.facebook.com')
->path('me')
->query('fields', [
'name',
'email'
])
->build();
$this->assertEquals('https://graph.facebook.com/me?fields=name,email', $url);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment