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

Reply via email to