Skip to content

Commit

Permalink
Adding UriStringProvider interface
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed Dec 25, 2024
1 parent 93ef923 commit b7b312e
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 10 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"require": {
"php": "^8.1",
"ext-bcmath": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-gmp": "*",
"ext-intl": "*",
Expand Down
3 changes: 3 additions & 0 deletions docs/uri/7.0/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ as an IPv4 address.
In order to create Data URI from the content of a file you are required to also
install the `fileinfo` extension otherwise an exception will be thrown.

To use the `toAnchor` method you need to have the `ext-dpm` extension
installed in your system.

Installation
--------

Expand Down
18 changes: 17 additions & 1 deletion docs/uri/7.0/rfc3986.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ echo $uri->toNormalizedString(); //displays 'example://a/b/c/%7Bfoo%7D?foo%5B%5D
echo $uri->toDisplayString(); //displays 'example://a/b/c/{foo}?foo[]=bar'
````

File specific representation are also added to allow representing Unix and Windows Path.
File specific representation are added to allow representing Unix and Windows Path.

```php
use League\Uri\Uri;
Expand All @@ -164,6 +164,22 @@ $uri = Uri::new('file://localhost/etc/fstab');
echo $uri->toRfc8089(); //display 'file:/etc/fstab'
```

HTML specific representation are added to allow adding URI to your HTML/Markdown page.

```php
use League\Uri\Uri;

$uri = Uri::new('eXAMPLE://a/./b/../b/%63/%7bfoo%7d?foo[]=bar');
echo $uri->toMarkdown();
//display '[example://a/b/c/{foo}?foo[]=bar](example://a/./b/../b/%63/%7bfoo%7d?foo%5B%5D=bar)
echo $uri->toMarkdown('my link');
//display '[my link](example://a/./b/../b/%63/%7bfoo%7d?foo%5B%5D=bar)
echo $uri->toAnchorTag();
// display '<a href="example://a/./b/../b/%63/%7bfoo%7d?foo%5B%5D=bar">example://a/b/c/{foo}?foo[]=bar</a>'
echo $uri->toAnchorTag('my link');
// display '<a href="example://a/./b/../b/%63/%7bfoo%7d?foo%5B%5D=bar">my link</a>'
```

## Accessing URI properties

Let's examine the result of building a URI:
Expand Down
5 changes: 1 addition & 4 deletions interfaces/Contracts/UriInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@
*
* @method string|null getUsername() returns the user component of the URI.
* @method string|null getPassword() returns the scheme-specific information about how to gain authorization to access the resource.
* @method string|null toUnixPath() returns the Unix filesystem path. The method returns null for any other scheme
* @method string|null toWindowsPath() returns the Windows filesystem path. The method returns null for any other scheme
* @method string|null toRfc8089() returns a string representation of a File URI according to RFC8089. The method returns null for any other scheme
* @method string|null getOrigin() returns the URI origin as described in the WHATWG URL Living standard specification
* @method string toNormalizedString() returns the normalized string representation of the URI
* @method array toComponents() returns an associative array containing all the URI components.
* @method self normalize() returns a new URI instance with normalized components
* @method self resolve(UriInterface $uri) resolves a URI against a base URI using RFC3986 rules
* @method self relativize(UriInterface $uri) relativize a URI against a base URI using RFC3986 rules
* @method self|null getOrigin() returns the URI origin as described in the WHATWG URL Living standard specification
* @method bool isOpaque() tells whether the given URI object represents an opaque URI.
* @method bool isAbsolute() tells whether the URI represents an absolute URI.
* @method bool isNetworkPath() tells whether the URI represents a network path URI.
Expand Down
71 changes: 71 additions & 0 deletions interfaces/Contracts/UriStringProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

/**
* League.Uri (https://uri.thephpleague.com)
*
* (c) Ignace Nyamagana Butera <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace League\Uri\Contracts;

use DOMException;
use JsonSerializable;

interface UriStringProvider extends JsonSerializable
{
/**
* Returns the string representation as a URI reference.
*
* @see http://tools.ietf.org/html/rfc3986#section-4.1
*/
public function toString(): string;

/**
* Returns the normalized string representation of the URI.
*
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
*/
public function toNormalizedString(): ?string;

/**
* Returns the human-readable string representation of the URI.
*
* @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.2
*/
public function toDisplayString(): ?string;

/**
* Returns the string representation as a URI reference.
*
* @see http://tools.ietf.org/html/rfc3986#section-4.1
* @see ::__toString
*/
public function jsonSerialize(): string;

/**
* Returns the HTML string representation of the anchor tag with the current instance as its href attribute.
*
* @throws DOMException
*/
public function toAnchorTag(?string $content = null, ?string $class = null, ?string $target = null): string;

/**
* Returns the markdown string representation of the anchor tag with the current instance as its href attribute.
*/
public function toMarkdown(?string $content = null): string;

/**
* Returns the Unix filesystem path. The method returns null for any other scheme.
*/
public function toUnixPath(): ?string;

/**
* Returns the Windows filesystem path. The method returns null for any other scheme.
*/
public function toWindowsPath(): ?string;
}
2 changes: 2 additions & 0 deletions uri/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ All Notable changes to `League\Uri` will be documented in this file
- `Uri::toUnixPath` returns the URI path as a Unix Path or `null`
- `Uri::toWindowsPath` returns the URI path as a Windows Path or `null`
- `Uri::toRfc8089` return the URI in a RFC8089 formator `null`
- `Uri::toAnchor` returns the HTML anchor string using the instance as the href attribute value
- `Uri::toMarkdown` returns the markdown link construct using the instance as the href attribute value

### Fixed

Expand Down
47 changes: 43 additions & 4 deletions uri/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
namespace League\Uri;

use Deprecated;
use DOMDocument;
use DOMException;
use finfo;
use League\Uri\Contracts\Conditionable;
use League\Uri\Contracts\UriComponentInterface;
use League\Uri\Contracts\UriException;
use League\Uri\Contracts\UriInterface;
use League\Uri\Contracts\UriStringProvider;
use League\Uri\Exceptions\ConversionFailed;
use League\Uri\Exceptions\MissingFeature;
use League\Uri\Exceptions\SyntaxError;
Expand Down Expand Up @@ -75,7 +78,7 @@
* @phpstan-import-type ComponentMap from UriString
* @phpstan-import-type InputComponentMap from UriString
*/
final class Uri implements UriInterface, Conditionable
final class Uri implements UriInterface, Conditionable, UriStringProvider
{
/**
* RFC3986 invalid characters.
Expand Down Expand Up @@ -1039,6 +1042,42 @@ public function toDisplayString(): string
return UriString::build($components);
}

/**
* Returns the HTML string representation of the anchor tag with the current instance as its href attribute.
*
* @throws DOMException
*/
public function toAnchorTag(?string $content = null, ?string $class = null, ?string $target = null): string
{
$doc = new DOMDocument('1.0', 'utf-8');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$anchor = $doc->createElement('a');
$anchor->setAttribute('href', $this->toString());
if (null !== $class) {
$anchor->setAttribute('class', $class);
}
if (null !== $target) {
$anchor->setAttribute('target', $target);
}

$anchor->appendChild($doc->createTextNode($content ?? $this->toDisplayString()));
$anchor = $doc->saveHTML($anchor);
if (false === $anchor) {
throw new DOMException('The link generation failed.');
}

return $anchor;
}

/**
* Returns the markdown string representation of the anchor tag with the current instance as its href attribute.
*/
public function toMarkdown(?string $content = null): string
{
return '['.($content ?? $this->toDisplayString()).']('.$this->toString().')';
}

/**
* Returns the Unix filesystem path.
*
Expand Down Expand Up @@ -1208,9 +1247,9 @@ public function getFragment(): ?string
return $this->fragment;
}

public function getOrigin(): ?self
public function getOrigin(): ?string
{
return null === $this->origin ? null : Uri::new($this->origin);
return $this->origin;
}

public function when(callable|bool $condition, callable $onSuccess, ?callable $onFail = null): static
Expand Down Expand Up @@ -1429,7 +1468,7 @@ public function isCrossOrigin(UriInterface|Stringable|string $uri): bool
return true;
}

return $this->origin !== (string) $origin;
return $this->origin !== $origin;
}

public function isSameOrigin(Stringable|string $uri): bool
Expand Down
93 changes: 92 additions & 1 deletion uri/UriTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ public static function sameValueAsProvider(): array
#[DataProvider('getOriginProvider')]
public function testGetOrigin(Psr7UriInterface|Uri|string $uri, ?string $expectedOrigin): void
{
self::assertSame($expectedOrigin, Uri::new($uri)->getOrigin()?->toString());
self::assertSame($expectedOrigin, Uri::new($uri)->getOrigin());
}

public static function getOriginProvider(): array
Expand Down Expand Up @@ -981,4 +981,95 @@ public static function providesUriToDisplay(): iterable
'output' => 'http://bébé.be',
];
}

#[Test]
#[DataProvider('providesUriToMarkdown')]
public function it_will_generate_the_markdown_code_for_the_instance(string $uri, ?string $content, string $expected): void
{
self::assertSame($expected, Uri::new($uri)->toMarkdown($content));
}

public static function providesUriToMarkdown(): iterable
{
yield 'empty string' => [
'uri' => '',
'content' => '',
'expected' => '[]()',
];

yield 'URI with a specific content' => [
'uri' => 'http://example.com/foo/bar',
'content' => 'this is a link',
'expected' => '[this is a link](http://example.com/foo/bar)',
];

yield 'URI without content' => [
'uri' => 'http://Bébé.be',
'content' => null,
'expected' => '[http://bébé.be](http://xn--bb-bjab.be)',
];
}

#[Test]
#[DataProvider('providesUriToHTML')]
public function it_will_generate_the_html_code_for_the_instance(
string $uri,
?string $content,
?string $class,
?string $target,
string $expected
): void {
self::assertSame($expected, Uri::new($uri)->toAnchorTag($content, $class, $target));
}

public static function providesUriToHTML(): iterable
{
yield 'empty string' => [
'uri' => '',
'content' => '',
'class' => null,
'target' => null,
'expected' => '<a href=""></a>',
];

yield 'URI with a specific content' => [
'uri' => 'http://example.com/foo/bar',
'content' => 'this is a link',
'class' => null,
'target' => null,
'expected' => '<a href="http://example.com/foo/bar">this is a link</a>',
];

yield 'URI without content' => [
'uri' => 'http://Bébé.be',
'content' => null,
'class' => null,
'target' => null,
'expected' => '<a href="http://xn--bb-bjab.be">http://bébé.be</a>',
];

yield 'URI without content and with class' => [
'uri' => 'http://Bébé.be',
'content' => null,
'class' => 'foo bar',
'target' => null,
'expected' => '<a href="http://xn--bb-bjab.be" class="foo bar">http://bébé.be</a>',
];

yield 'URI without content and with target' => [
'uri' => 'http://Bébé.be',
'content' => null,
'class' => null,
'target' => '_blank',
'expected' => '<a href="http://xn--bb-bjab.be" target="_blank">http://bébé.be</a>',
];

yield 'URI without content, with target and class' => [
'uri' => 'http://Bébé.be',
'content' => null,
'class' => 'foo bar',
'target' => '_blank',
'expected' => '<a href="http://xn--bb-bjab.be" class="foo bar" target="_blank">http://bébé.be</a>',
];
}
}
1 change: 1 addition & 0 deletions uri/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"league/uri-schemes": "^1.0"
},
"suggest": {
"ext-dom": "to convert the URI into an HTML anchor tag",
"ext-bcmath": "to improve IPV4 host parsing",
"ext-fileinfo": "to create Data URI from file contennts",
"ext-gmp": "to improve IPV4 host parsing",
Expand Down

0 comments on commit b7b312e

Please sign in to comment.