Skip to content

Commit d413d79

Browse files
skydudieclaude
andcommitted
v0.65.0: toRdf — drop literals with malformed language tags (+1 toRdf)
- A language-tagged literal whose @language is not a well-formed BCP47 tag (e.g. "a b", containing a space) is now dropped: the statement carries no valid RDF language, instead of emitting an invalid N-Quads literal (#twf05, §7.3). valueToLiteral returns null for the malformed case and objectToRdf propagates it so the quad is skipped. W3C (toEqual gate): toRdf 453->454 (+1); expand 378, compact 246 (100%) unchanged. Unit 282 green; phpstan max clean; pint clean; 0 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9fd3848 commit d413d79

4 files changed

Lines changed: 34 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.65.0] - 2026-06-10
11+
12+
toRdf: drop literals with malformed language tags.
13+
14+
W3C JSON-LD 1.1 test suite (corrected `toEqual` gate):
15+
16+
```
17+
expand compact toRdf
18+
v0.64.0: 378 246 453
19+
v0.65.0: 378 246 454 (+1 toRdf)
20+
```
21+
22+
### Fixed (toRdf, §7.3)
23+
24+
- A language-tagged literal whose `@language` is not a well-formed BCP47
25+
language tag (e.g. `"a b"`, which contains a space) is now dropped — the
26+
statement carries no valid RDF language — instead of emitting an invalid
27+
N-Quads literal (`#twf05`).
28+
1029
## [0.64.0] - 2026-06-10
1130

1231
toRdf harness: sound blank-node isomorphism comparison.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ A PHP implementation of the [JSON-LD 1.1](https://www.w3.org/TR/json-ld11/) spec
3131
- [x] Custom `DocumentLoader` interface
3232
- [~] Expansion (§5.5) — implemented; ~378/385 of the W3C expand suite (the remaining 7 are environment/spec-accommodation blockers)
3333
- [x] Compaction (§5.6) — **246/246 of the W3C compact suite (100%)**: container-maps (incl. property-valued `@index`, `@type`-map node refs), keyword recursion, `@nest`, nested `@list`, `@graph` maps, `@reverse` (incl. per-value term selection and containers), full inverse-context term scoring (list/direction-aware, `@json` literals), property-/type-scoped contexts with scoped `@base`/`@vocab`, IRI relativisation, expand-first normalisation, the `compactArrays` option, §5.7 prefix rules and error conditions
34-
- [~] Serialize JSON-LD to RDF (§7 / `toRdf`) — implemented; ~453/467 of the W3C toRdf suite (N-Quads output incl. `rdfDirection` and `produceGeneralizedRdf`)
34+
- [~] Serialize JSON-LD to RDF (§7 / `toRdf`) — implemented; ~454/467 of the W3C toRdf suite (N-Quads output incl. `rdfDirection` and `produceGeneralizedRdf`)
3535

3636
> Suite numbers from v0.42.0 use the spec-accurate `toEqual` comparison
3737
> (object-key order insignificant, array order significant); earlier numbers

src/Algorithms/ToRdf.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,10 @@ private function listToRdf(array $list, array &$listQuads, BlankNodeIssuer $issu
193193
* @param list<RdfQuad> $listQuads Receives any auxiliary triples (used
194194
* by the compound-literal rdfDirection
195195
* mode).
196+
* @return RdfTerm|null Null when the value carries a malformed language
197+
* tag and the statement must be dropped (#twf05).
196198
*/
197-
private function valueToLiteral(array $item, array &$listQuads, BlankNodeIssuer $issuer): RdfTerm
199+
private function valueToLiteral(array $item, array &$listQuads, BlankNodeIssuer $issuer): ?RdfTerm
198200
{
199201
$value = $item[Keyword::Value->value];
200202

@@ -268,6 +270,13 @@ private function valueToLiteral(array $item, array &$listQuads, BlankNodeIssuer
268270
// String value.
269271
$stringValue = is_scalar($value) ? (string) $value : '';
270272

273+
// A language-tagged literal whose tag is not a well-formed BCP47
274+
// language tag carries no valid RDF language: the statement is dropped
275+
// (#twf05). A well-formed tag is ALPHA{1,8} (-(ALPHANUM){1,8})*.
276+
if ($language !== null && preg_match('/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/', $language) !== 1) {
277+
return null;
278+
}
279+
271280
return RdfTerm::literal($stringValue, $datatype, $language);
272281
}
273282

tests/W3c/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ composer test:all
5050
## Current score
5151

5252
```
53-
Expansion: 378 passed / 7 failed (v0.64.0)
54-
Compaction: 246 passed / 0 failed (v0.64.0) ← 100%
55-
toRdf: 453 passed / 14 failed (v0.64.0)
53+
Expansion: 378 passed / 7 failed (v0.65.0)
54+
Compaction: 246 passed / 0 failed (v0.65.0) ← 100%
55+
toRdf: 454 passed / 13 failed (v0.65.0)
5656
```
5757

5858
> v0.42.0 corrected the expand/compact comparison from `toEqualCanonicalizing`
@@ -133,6 +133,7 @@ Each Phase 4 PR:
133133
| v0.62.0 | 378 | +6 | expansion: expandContext option, @context:null full reset, @base:null, scoped @nest (+5 toRdf)|
134134
| v0.63.0 | 378 | +0 | toRdf: produceGeneralizedRdf option — keep blank-node predicates (+2 toRdf)|
135135
| v0.64.0 | 378 | +0 | toRdf harness: sound blank-node isomorphism compare — rescues 10 spec-correct false-failures (+10 toRdf)|
136+
| v0.65.0 | 378 | +0 | toRdf: drop literals with malformed BCP47 language tags (+1 toRdf)|
136137

137138
### Notes on v0.4.0
138139

0 commit comments

Comments
 (0)