Skip to content

Commit 5a2edda

Browse files
committed
Enhancement: Implement JsonPointer
1 parent 46547c2 commit 5a2edda

File tree

6 files changed

+394
-2
lines changed

6 files changed

+394
-2
lines changed

CHANGELOG.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## Unreleased
88

9-
For a full diff see [`a5ba52c...main`][a5ba52c...main].
9+
For a full diff see [`1.0.0...main`][1.0.0...main].
10+
11+
## [`1.0.0`][1.0.0]
12+
13+
For a full diff see [`a5ba52c...1.0.0`][a5ba52c...1.0.0].
1014

1115
### Added
1216

1317
- Added `ReferenceToken` as a value object ([#1]), by [@localheinz]
18+
- Added `JsonPointer` as a value object ([#2]), by [@localheinz]
1419

15-
[a5ba52c...main]: https://github.com/ergebnis/json-pointer/compare/a5ba52c...main
20+
[a5ba52c...1.0.0]: https://github.com/ergebnis/json-pointer/compare/a5ba52c...1.0.0
21+
[1.0.0...main]: https://github.com/ergebnis/json-pointer/compare/1.0.0...main
1622

1723
[#1]: https://github.com/ergebnis/json-pointer/pull/1
24+
[#2]: https://github.com/ergebnis/json-pointer/pull/2
1825

1926
[@localheinz]: https://github.com/localheinz

README.md

+63
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,69 @@ $two = Pointer\ReferenceToken::fromEscapedString('foo~1bar');
8585
$one->equals($two); // true
8686
```
8787

88+
### `JsonPointer`
89+
90+
You can create a `JsonPointer` referencing a document:
91+
92+
```php
93+
<?php
94+
95+
declare(strict_types=1);
96+
97+
use Ergebnis\Json\Pointer;
98+
99+
$jsonPointer = Pointer\JsonPointer::document();
100+
101+
$jsonPointer->toString(); // ''
102+
```
103+
104+
You can create a `JsonPointer` from a `string` value:
105+
106+
```php
107+
<?php
108+
109+
declare(strict_types=1);
110+
111+
use Ergebnis\Json\Pointer;
112+
113+
$jsonPointer = Pointer\JsonPointer::fromString('/foo/bar');
114+
115+
$jsonPointer->toString(); // '/foo/bar'
116+
```
117+
118+
You can compare `JsonPointer`s:
119+
120+
```php
121+
<?php
122+
123+
declare(strict_types=1);
124+
125+
use Ergebnis\Json\Pointer;
126+
127+
$one = Pointer\JsonPointer::fromString('/foo/bar');
128+
$two = Pointer\JsonPointer::fromString('/foo~1bar');
129+
130+
$one->equals($two); // false
131+
```
132+
133+
You can append a `ReferenceToken` to a `JsonPointer`:
134+
135+
```php
136+
<?php
137+
138+
declare(strict_types=1);
139+
140+
use Ergebnis\Json\Pointer;
141+
142+
$jsonPointer = Pointer\JsonPointer::fromString('/foo/bar');
143+
144+
$referenceToken = Pointer\ReferenceToken::fromUnescapedString('baz');
145+
146+
$newJsonPointer = $jsonPointer->append($referenceToken);
147+
148+
$newJsonPointer->toString(); // '/foo/bar/baz'
149+
```
150+
88151
## Changelog
89152

90153
Please have a look at [`CHANGELOG.md`](CHANGELOG.md).

src/Exception/InvalidJsonPointer.php

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2020-2022 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/json-pointer
12+
*/
13+
14+
namespace Ergebnis\Json\Pointer\Exception;
15+
16+
final class InvalidJsonPointer extends \InvalidArgumentException implements Exception
17+
{
18+
public static function fromString(string $value): self
19+
{
20+
return new self(\sprintf(
21+
'Value "%s" does not appear to be a valid JSON Pointer.',
22+
$value,
23+
));
24+
}
25+
}

src/JsonPointer.php

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2020-2022 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/json-pointer
12+
*/
13+
14+
namespace Ergebnis\Json\Pointer;
15+
16+
/**
17+
* @psalm-immutable
18+
*
19+
* @see https://datatracker.ietf.org/doc/html/rfc6901
20+
*/
21+
final class JsonPointer
22+
{
23+
private string $value;
24+
25+
private function __construct(string $value)
26+
{
27+
$this->value = $value;
28+
}
29+
30+
/**
31+
* @see https://datatracker.ietf.org/doc/html/rfc6901#section-3
32+
* @see https://www.php.net/manual/en/regexp.reference.unicode.php
33+
*
34+
* @throws Exception\InvalidJsonPointer
35+
*/
36+
public static function fromString(string $value): self
37+
{
38+
if (1 !== \preg_match('/^(\/(?P<referenceToken>((?P<unescaped>[\x00-\x2E]|[\x30-\x7D]|[\x7F-\x{10FFFF}])|(?P<escaped>~[01]))*))*$/u', $value, $matches)) {
39+
throw Exception\InvalidJsonPointer::fromString($value);
40+
}
41+
42+
return new self($value);
43+
}
44+
45+
public static function document(): self
46+
{
47+
return new self('');
48+
}
49+
50+
public function append(ReferenceToken $referenceToken): self
51+
{
52+
return new self(\sprintf(
53+
'%s/%s',
54+
$this->value,
55+
$referenceToken->toEscapedString(),
56+
));
57+
}
58+
59+
public function toString(): string
60+
{
61+
return $this->value;
62+
}
63+
64+
public function equals(self $other): bool
65+
{
66+
return $this->value === $other->value;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright (c) 2020-2022 Andreas Möller
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE.md file that was distributed with this source code.
10+
*
11+
* @see https://github.com/ergebnis/json-pointer
12+
*/
13+
14+
namespace Ergebnis\Json\Pointer\Test\Unit\Exception;
15+
16+
use Ergebnis\Json\Pointer\Exception;
17+
use Ergebnis\Json\Pointer\Test;
18+
use PHPUnit\Framework;
19+
20+
/**
21+
* @internal
22+
*
23+
* @covers \Ergebnis\Json\Pointer\Exception\InvalidJsonPointer
24+
*/
25+
final class InvalidJsonPointerTest extends Framework\TestCase
26+
{
27+
use Test\Util\Helper;
28+
29+
public function testFromValueReturnsInvalidJsonPointerException(): void
30+
{
31+
$value = self::faker()->sentence();
32+
33+
$exception = Exception\InvalidJsonPointer::fromString($value);
34+
35+
$message = \sprintf(
36+
'Value "%s" does not appear to be a valid JSON Pointer.',
37+
$value,
38+
);
39+
40+
self::assertSame($message, $exception->getMessage());
41+
}
42+
}

0 commit comments

Comments
 (0)