JVal icon indicating copy to clipboard operation
JVal copied to clipboard

Resolver relies on getPrimaryResourceIdentifier() being unique for each schema

Open JanTvrdik opened this issue 9 years ago • 7 comments

Resolver relies e.g. in registerSchema on getPrimaryResourceIdentifier being unique for each schema however when Validator::validate is called without the third argument, the getPrimaryResourceIdentifier() returns empty string every time.

My guess is that we must either make the 3rd parameter of Validator::validate required or generate primary resource identifier differently (maybe random?) when empty Uri is passed.

JanTvrdik avatar Apr 10 '16 12:04 JanTvrdik

Maybe we can just put sth like this to Validator::validate

if ($schemaUri === '') {
    $schemaUri = '/schema/' . md5(spl_object_hash($schema));
}

JanTvrdik avatar Apr 10 '16 12:04 JanTvrdik

Unless a $ref pointing to another document is encountered, we never leave the current schema, so it's expected to get always the same identifier (here the empty string) and the same schema from the cache. If an external $ref is met, the identifier returned by getPrimaryResourceIdentifier() must be different. Isn't this the case? Can you provide a schema illustrating the issue?

stefk avatar Apr 10 '16 14:04 stefk

I guess you're right. The registerSchema behavior is wrong (relying on uniqueness of primary resource identifier even when it's empty string), but resolve does never actually read those schemas with empty identifier so it never actually breaks anything.

Therefore the best solution would likely be to just ignore empty identifiers in registerSchema as caching them is both wrong and pointless.

$id = $uri->getPrimaryResourceIdentifier();
if ($id !== '' && !isset($this->schemas[$id])) {
    $this->schemas[$id] = $schema;
}

JanTvrdik avatar Apr 10 '16 14:04 JanTvrdik

The registerSchema behavior is wrong (relying on uniqueness of primary resource identifier even when it's empty string)

I don't agree. The empty string is also a valid identifier. It's unique and will always denote the document from which we started, in case no path/url was provided.

resolve does never actually read those schemas

It will when a solution will be provided for #6.

stefk avatar Apr 10 '16 15:04 stefk

The collision with #6 is a good observation.

It's unique and will always denote the document from which we started

When you call validate with schema A without URL and then validate with schema B without URL, references such as {"$ref": "#"} in schema B would resolve against schema A.

JanTvrdik avatar Apr 10 '16 15:04 JanTvrdik

You're right, and actually even for #6, the schema should probably be taken from $stack and not $schemas, so we can safely exclude the empty string as you suggested.

stefk avatar Apr 10 '16 15:04 stefk

What about implementing check in registerSchema to prevent accidental overrides

public function registerSchema(stdClass $schema, Uri $uri)
{
    $identifier = $uri->getPrimaryResourceIdentifier();

    if ($identifier === '') {
        // ignore        
    } elseif (!isset($this->schemas[$identifier])) {
        $this->schemas[$identifier] = $schema;
    } elseif ($this->schemas[$identifier] != $schema) { // intentionally ==, instead of ===
        throw new \LogicException('Different schema is already registered with given URI.');
    }
}

JanTvrdik avatar Apr 10 '16 15:04 JanTvrdik