Skip to content

Commit

Permalink
Add URL comparator
Browse files Browse the repository at this point in the history
  • Loading branch information
tr33m4n committed May 1, 2021
1 parent d227794 commit e7ad3a4
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 20 deletions.
46 changes: 43 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# url-object
An URL value object to use in your favourite functional err, functions! Some inspiration taken from https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
A URL value object. Some inspiration taken from https://developer.mozilla.org/en-US/docs/Web/API/URL/URL

## Installing
```sh
Expand All @@ -19,7 +19,7 @@ echo $url->getHost();
```
### `create`
```php
$url = (string) Url::create()
echo \tr33m4n\UrlObject\Url::create()
->withScheme('https')
->withHost('example.com')
->withUser('test')
Expand All @@ -30,7 +30,7 @@ $url = (string) Url::create()
### Replacing parts of the URL
```php
$url = \tr33m4n\UrlObject\Url::fromString('https://example.com:1234');
echo (string) $url->withHost('not-example.com');
echo $url->withHost('not-example.com');
// https://not-example.com:1234
```
### Getting URL params
Expand All @@ -44,3 +44,43 @@ $aSpecificParameter = $url->getParameter('this');

echo $aSpecificParameter->getValue();
// "test"
```
### Comparing URL's
```php
$url = Url::fromString('https://example.com:1234?this=test&another=something');
$comparator = $url->compareWith(
'https://example.com:1234',
'https://another-example.com:1234?this=test&another=something',
'https://example.com:1234',
Url::fromString('https://example.com:1234?this=test&another=something'),
\tr33m4n\UrlObject\Url::create()
->withScheme('https')
->withHost('example.com')
->withUser('test')
->withPass('example')
->withPort(1234)
// etc...
);

var_dump($comparator->matchPort());
var_dump($comparator->matchHost());
var_dump($comparator->matchScheme());
var_dump($comparator->matchParameters());
// bool(true)
// bool(false)
// bool(true)
// bool(false)

// Calling the comparator outside of the URL object
$comparator = \tr33m4n\UrlObject\Comparator::compare(
'https://example.com:1234',
Url::fromString('https://example.com:1234'),
\tr33m4n\UrlObject\Url::create()
->withScheme('https')
->withHost('example.com')
->withUser('test')
->withPass('example')
->withPort(1234)
// etc...
);
```
186 changes: 186 additions & 0 deletions src/Comparator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<?php

declare(strict_types=1);

namespace tr33m4n\UrlObject;

use Closure;

/**
* Class Comparator
*
* @package tr33m4n\UrlObject
*/
class Comparator
{
/**
* @var \tr33m4n\UrlObject\Url[]
*/
private $urls;

/**
* Comparator constructor.
*/
private function __construct()
{
//
}

/**
* Compare multiple URLs
*
* @throws \tr33m4n\UrlObject\Exception\UrlException
* @param string|\tr33m4n\UrlObject\Url ...$urls
* @return \tr33m4n\UrlObject\Comparator
*/
public static function compare(...$urls): Comparator
{
$comparator = new self();
$comparator->urls = array_map(static function ($url) {
return !$url instanceof Url ? Url::fromString($url) : $url;
}, $urls);

return $comparator;
}

/**
* Check whether entire URL strings match
*
* @return bool
*/
public function match(): bool
{
return $this->equals(static function (Url $url): string {
return (string) $url;
});
}

/**
* Check whether all schemes match
*
* @return bool
*/
public function matchScheme(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getScheme();
});
}

/**
* Check whether all users match
*
* @return bool
*/
public function matchUser(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getUser();
});
}

/**
* Check whether all passwords match
*
* @return bool
*/
public function matchPass(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getPass();
});
}

/**
* Check whether all hosts match
*
* @return bool
*/
public function matchHost(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getHost();
});
}

/**
* Check whether all ports match
*
* @return bool
*/
public function matchPort(): bool
{
return $this->equals(static function (Url $url): ?int {
return $url->getPort();
});
}

/**
* Check whether all paths match
*
* @return bool
*/
public function matchPath(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getPath();
});
}

/**
* Check whether all parameters match
*
* @return bool
*/
public function matchParameters(): bool
{
return $this->equals(static function (Url $url) {
$parameters = $url->getParameters();
usort($parameters, static function (UrlParameter $urlParameterA, UrlParameter $urlParameterB) {
return strcmp($urlParameterA->getKey(), $urlParameterB->getKey());
});

return http_build_query(
array_reduce(
$parameters,
static function (array $parametersAsArray, UrlParameter $urlParameter): array {
return $parametersAsArray = array_merge(
$parametersAsArray,
[$urlParameter->getKey() => $urlParameter->getValue()]
);
},
[]
)
);
});
}

/**
* Check whether all fragments match
*
* @return bool
*/
public function matchFragment(): bool
{
return $this->equals(static function (Url $url): ?string {
return $url->getFragment();
});
}

/**
* Compare array of values returned from passed callback
*
* @param \Closure $callback
* @return bool
*/
private function equals(Closure $callback): bool
{
return count(
array_unique(
array_map(static function (Url $url) use ($callback) {
return $callback($url);
}, $this->urls)
)
) === 1;
}
}
40 changes: 26 additions & 14 deletions src/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,18 @@ public static function fromString(string $url): Url
throw new UrlException(sprintf('"%s" is not a valid URL', $url));
}

$urlParts = parse_url($url);
if (!$urlParts) {
if (!$urlParts = parse_url($url)) {
throw new UrlException(sprintf('"%s" is not a valid URL', $url));
}

return array_reduce(
array_keys($urlParts),
static function (Url $url, string $urlPartKey) use ($urlParts) {
if (!method_exists($url, 'with' . ucfirst($urlPartKey))) {
if (!method_exists($url, $method = 'with' . ucfirst($urlPartKey))) {
return $url;
}

return $url = $url->{'with' . ucfirst($urlPartKey)}($urlParts[$urlPartKey]);
return $url = $url->$method($urlParts[$urlPartKey]);
},
new self()
);
Expand Down Expand Up @@ -341,6 +340,17 @@ public function getFragment(): ?string
return $this->fragment;
}

/**
* Compare this URL with others
*
* @param string|\tr33m4n\UrlObject\Url ...$urls
* @return \tr33m4n\UrlObject\Comparator
*/
public function compareWith(...$urls): Comparator
{
return call_user_func_array([Comparator::class, 'compare'], array_merge([$this], $urls));
}

/**
* Concatenate properties to string
*
Expand All @@ -357,16 +367,18 @@ public function __toString(): string
$this->getPort() ? ':' . $this->getPort() : '',
$this->getPath() ? ltrim($this->getPath() ?? '', '/') : '',
!empty($this->getParameters())
? '?' . http_build_query(array_reduce(
$this->getParameters(),
static function (array $parametersAsArray, UrlParameter $urlParameter): array {
return $parametersAsArray = array_merge(
$parametersAsArray,
[$urlParameter->getKey() => $urlParameter->getValue()]
);
},
[]
))
? '?' . http_build_query(
array_reduce(
$this->getParameters(),
static function (array $parametersAsArray, UrlParameter $urlParameter): array {
return $parametersAsArray = array_merge(
$parametersAsArray,
[$urlParameter->getKey() => $urlParameter->getValue()]
);
},
[]
)
)
: '',
$this->getFragment() ? '#' . $this->getFragment() : ''
);
Expand Down
6 changes: 3 additions & 3 deletions src/UrlParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
class UrlParameter
{
/**
* @var string|null
* @var string
*/
private $key;

Expand Down Expand Up @@ -62,9 +62,9 @@ public function withValue(string $value): UrlParameter
/**
* Get key
*
* @return string|null
* @return string
*/
public function getKey(): ?string
public function getKey(): string
{
return $this->key;
}
Expand Down

0 comments on commit e7ad3a4

Please sign in to comment.