On Fri, Mar 3, 2023, at 6:19 AM, Jakub Zelenka wrote:
>> You mean using the version from the JSON string, and allowing an
>> override? Like this?
>>
>>
> It should never allow overriding the $schema as it would go against spec so
> this would be just default if $schema is not specified. Just the default
> could be overridden.
That sounds like it could be hard to explain why a parameter only sometimes
does something... Which suggests we should take a different approach.
>> new JsonSchema($schema_string, version: JsonSchema::DRAFT_4);
>>
> Would be probably called more JsonSchema::VERSION_DRAFT_04 but essentially
> yeah I was thinking either that or something like just global constant
> JSON_SCHEMA_VERSION_DRAFT_04 which is currently more convention in json
> extension. I wouldn't really mind using the class constant though.
Class constants FTW.
> As I said my main point was that custom defined schema should contain
> version in the schema - I realise that it might not be used in wild but
> this is defined as SHOULD in all drafts so we should follow that
> recommendation in our API design. When this is the case, there is no point
> for user to explicitly pick the schema class IMO.
>
> Another thing to note is that we might want to introduce some sort of a
> factory method or factory class instead of using constructor because as I
> said before we would probably like to introduce more sources for schema
> than just string in the future. It means it could be automatically
> generated schema from a class so only the class name would be passed or for
> convenience it could be just passed directly from the assoc array. It is
> basically pointless to always convert it to string because internally it
> will just decode the json string to object (stdClass) or more likely array
> and parse the schema internal representation from that. If we had this, we
> could maybe introduce a different schema classes as well but it would be
> more invisible for users and could be just subclasses of JsonSchema or
> JsonSchema would be just an interface.
I'm totally fine with factory methods. I'm less enamored with a factory class,
but open to discussing it.
>> I see two issues there.
>>
>> 1. If I want to see if DRAFT_6 is available, I have to use defined()[1]
>> with strings. This is fugly.
>>
>>
> This is a good point as for class with autoloader you don't need strings.
>
> Maybe we could also introduce JsonSchema::VERSION_LATEST which would have
> value of the last supported draft. Then you could check if draft-06 is
> supported by just doing something like
>
> if (JsonSchema::VERSION_LATEST > JsonSchema::VERSION_DRAFT_04) {
> // at lest draft 06
> } else if (JsonSchema::VERSION_LATEST > JsonSchema::VERSION_DRAFT_06) {
> // at lest draft 07
> } else ...
>
>
> We could also support string for the actual version and allowing passing
> the well defined values for $schema. The it would throw exception if it's
> not supported. We could even have static method to check whether version is
> supported and then for example doing something like
>
> if (JsonSchema::isVersionSupported("
> https://json-schema.org/draft/2020-12/schema")) { ... }
thinking-face.gif
That could work. For simplicity someone in user space (FIG?) could release a
set of constants that can be rapidly updated, but the actual PHP API is just
looking at the URL. If we expect it to be a rarely-used feature, I'd be on
board with that.
>> 2. I don't know how to polyfill newer spec versions if I don't want to
>> wait for internals to get around to adding a new version.
>>
>
> I guess it could be possible to support custom user validators (e.g.
> instances implementing JsonSchema interface if above concept is used) in
> the future but it would be of course more limited. That's not something
> that would happen initially but it might be good design an API with that in
> mind.
>
> So to sum it up, maybe the rough structure could be something like
>
> interface JsonSchema {
> const VERSION_DRAFT_04 = 1;
> const VERSION_DRAFT_06 = 2;
> const VERSION_DRAFT_07 = 3;
> const VERSION_LATEST = 3;
>
> public function validate(array|stdClass $data): bool;
> }
>
> class JsonSchemaForVersionDraft04 implements JsonSchema {
> public function validate(array|stdClass $data): bool {}
> }
>
> class JsonSchemaForVersionDraft06 implements JsonSchema {
> public function validate(array|stdClass $data): bool {}
> }
>
> class JsonSchemaForVersionDraft07 implements JsonSchema {
> public function validate(array|stdClass $data): bool {}
> }
>
> class JsonSchemaFactory {
> public static function createFromJsonString(string $jsonString,
> int|string $version = JsonSchema::VERSION_LATEST): JsonSchema {}
>
> public static function createFromClasl(string $className, int|string
> $version = JsonSchema::VERSION_LATEST): JsonSchema {}
>
> public static function createFromArray(array $schemaData, int|string
> $version = JsonSchema::VERSION_LATEST): JsonSchema {}
>
> public static function isVersionSupported(int|string $version): bool {}
> }
>
> Just a draft of course so any ideas how to improve it are welcome.
>
> What do you think?
Hm. So you're idea is that you'd parse the JSON string with json_decode()
first, then pass that to the validator? Is that really the most
performant/convenient approach? (I don't know, but I question if it is.)
Thinking about it another way, I see three base primitives that are better done
in C than in PHP.
1) validate(SchemaDefinition $schema_definition, $json_value): bool
2) make_schema(string $jsonSchemaString): SchemaDefinition (and possibly
alternates with other typed parameters)
3) make_schema_from_class(string $className, string $version = latest
available): SchemaDefinition
Everything else could be done in user-space without any significant performance
impact. Even 3 could technically be done in user space if SchemaDefinition had
a robust API that could be populated from user space, rather than being opaque.
So a major question is what level of exposed API we want the schema object to
have. (I could probably argue both ways here.)
(If you want to continue this real-time, I'm available in most of the PHP chat
fora these days.)
--Larry Garfield
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php