diff --git a/src/Rxn/Framework/Codegen/PolyparityExporter.php b/src/Rxn/Framework/Codegen/PolyparityExporter.php index 7fe93d4..e974748 100644 --- a/src/Rxn/Framework/Codegen/PolyparityExporter.php +++ b/src/Rxn/Framework/Codegen/PolyparityExporter.php @@ -11,6 +11,7 @@ use Rxn\Framework\Http\Attribute\Required; use Rxn\Framework\Http\Attribute\Url; use Rxn\Framework\Http\Binding\RequestDto; +use Rxn\Framework\Http\Binding\Validates; /** * Emit a polyparity YAML spec from a Rxn `RequestDto`. The same @@ -212,6 +213,15 @@ private function refuse(string $attrName, string $propName): ?string . 'Add a mapping or document the DTO as PHP-only.', ); } + + if (is_subclass_of($attrName, Validates::class)) { + throw new \RuntimeException( + "PolyparityExporter: $attrName on property '$propName' is a custom validator. " + . 'Refusing to emit silently-divergent spec. ' + . 'Add a polyparity mapping or keep this DTO PHP-only.' + ); + } + return null; } diff --git a/src/Rxn/Framework/Tests/Codegen/PolyparityExporterTest.php b/src/Rxn/Framework/Tests/Codegen/PolyparityExporterTest.php index 2e7689b..52bcb8d 100644 --- a/src/Rxn/Framework/Tests/Codegen/PolyparityExporterTest.php +++ b/src/Rxn/Framework/Tests/Codegen/PolyparityExporterTest.php @@ -183,6 +183,14 @@ public function testRefusesUnsupportedAttribute(): void $exporter->emit(Fixture\UnsupportedDto::class); } + + public function testRefusesCustomValidatesAttribute(): void + { + $exporter = new PolyparityExporter(); + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessageMatches('/CustomValidatesAttribute.*custom validator/i'); + $exporter->emit(Fixture\CustomUnsupportedDto::class); + } public function testRejectsNonRequestDto(): void { $exporter = new PolyparityExporter();