diff --git a/json-schema-validator/schema/v4/README.md b/json-schema-validator/schema/v4/README.md new file mode 100644 index 0000000..c021196 --- /dev/null +++ b/json-schema-validator/schema/v4/README.md @@ -0,0 +1,5 @@ +The schema files should be named as +* file name should be all lowercase +* file name should exactly match the event type. eg: be_object_lifecycle, cp_interact +* file should have dot json extension + diff --git a/json-schema-validator/schema/v4/be_object_lifecycle.json b/json-schema-validator/schema/v4/be_object_lifecycle.json new file mode 100644 index 0000000..69d9d3c --- /dev/null +++ b/json-schema-validator/schema/v4/be_object_lifecycle.json @@ -0,0 +1,170 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": {}, + "id": "BE_OBJECT_LIFECYCLE", + "properties": { + "cdata": { + "id": "/properties/cdata", + "items": { + "id": "/properties/cdata/items", + "properties": { + "id": { + "id": "/properties/cdata/items/properties/id", + "type": "string" + }, + "type": { + "id": "/properties/cdata/items/properties/type", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "context": { + "id": "/properties/context", + "properties": { + "content_id": { + "id": "/properties/context/properties/content_id", + "type": "string" + }, + "sid": { + "id": "/properties/context/properties/sid", + "type": "string" + } + }, + "required": [ + "sid" + ], + "type": "object" + }, + "edata": { + "id": "/properties/edata", + "properties": { + "eks": { + "id": "/properties/edata/properties/eks", + "properties": { + "code": { + "id": "/properties/edata/properties/eks/properties/code", + "type": "string" + }, + "id": { + "id": "/properties/edata/properties/eks/properties/id", + "type": "string" + }, + "name": { + "id": "/properties/edata/properties/eks/properties/name", + "type": "string" + }, + "parentid": { + "id": "/properties/edata/properties/eks/properties/parentid", + "type": "string" + }, + "parenttype": { + "id": "/properties/edata/properties/eks/properties/parenttype", + "type": "string" + }, + "prevstate": { + "id": "/properties/edata/properties/eks/properties/prevstate", + "type": "string" + }, + "state": { + "id": "/properties/edata/properties/eks/properties/state", + "type": "string" + }, + "subtype": { + "id": "/properties/edata/properties/eks/properties/subtype", + "type": "string" + }, + "type": { + "id": "/properties/edata/properties/eks/properties/type", + "type": "string" + } + }, + "required": [ + "code", + "name", + "parenttype", + "subtype", + "state", + "prevstate", + "parentid", + "type", + "id" + ], + "type": "object" + } + }, + "required": [ + "eks" + ], + "type": "object" + }, + "eid": { + "id": "/properties/eid", + "type": "string" + }, + "ets": { + "id": "/properties/ets", + "type": "number" + }, + "mid": { + "id": "/properties/mid", + "type": "string" + }, + "pdata": { + "id": "/properties/pdata", + "properties": { + "id": { + "id": "/properties/pdata/properties/id", + "type": "string" + }, + "pid": { + "id": "/properties/pdata/properties/pid", + "type": "string" + }, + "ver": { + "id": "/properties/pdata/properties/ver", + "type": "string" + } + }, + "required": [ + "ver", + "pid", + "id" + ], + "type": "object" + }, + "rid": { + "id": "/properties/rid", + "type": "string" + }, + "tags": { + "id": "/properties/tags", + "items": {}, + "type": "array" + }, + "uid": { + "id": "/properties/uid", + "type": "string" + }, + "ver": { + "id": "/properties/ver", + "type": "string" + } + }, + "required": [ + "ets", + "context", + "ver", + "uid", + "tags", + "pdata", + "mid", + "eid", + "cdata", + "edata", + "rid" + ], + "type": "object" +} diff --git a/json-schema-validator/schema/v4/ce_api_call.json b/json-schema-validator/schema/v4/ce_api_call.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_api_call.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/ce_end.json b/json-schema-validator/schema/v4/ce_end.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_end.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/ce_error.json b/json-schema-validator/schema/v4/ce_error.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_error.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/ce_interact.json b/json-schema-validator/schema/v4/ce_interact.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_interact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/ce_plugin_lifecycle.json b/json-schema-validator/schema/v4/ce_plugin_lifecycle.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_plugin_lifecycle.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/ce_start.json b/json-schema-validator/schema/v4/ce_start.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/json-schema-validator/schema/v4/ce_start.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/cp_impression.json b/json-schema-validator/schema/v4/cp_impression.json new file mode 100644 index 0000000..8f7a2f0 --- /dev/null +++ b/json-schema-validator/schema/v4/cp_impression.json @@ -0,0 +1,279 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": {}, + "id": "CP_IMPRESSION", + "properties": { + "cdata": { + "id": "/properties/cdata", + "items": { + "id": "/properties/cdata/items", + "properties": { + "id": { + "id": "/properties/cdata/items/properties/id", + "type": "string" + }, + "type": { + "id": "/properties/cdata/items/properties/type", + "type": "string" + } + }, + "required": [ + "type", + "id" + ], + "type": "object" + }, + "type": "array", + "uniqueItems": true + }, + "context": { + "id": "/properties/context", + "properties": { + "content_id": { + "id": "/properties/context/properties/content_id", + "type": "string" + }, + "sid": { + "id": "/properties/context/properties/sid", + "type": "string" + } + }, + "required": [ + "content_id", + "sid" + ], + "type": "object" + }, + "edata": { + "id": "/properties/edata", + "properties": { + "eks": { + "id": "/properties/edata/properties/eks", + "properties": { + "env": { + "id": "/properties/edata/properties/eks/properties/env", + "oneOf": [ + {"pattern": "^blog$"}, + {"pattern": "^community$"}, + {"pattern": "^concept$"}, + {"pattern": "^content$"}, + {"pattern": "^developer$"}, + {"pattern": "^discussion$"}, + {"pattern": "^editor$"}, + {"pattern": "^home$"}, + {"pattern": "^item$"}, + {"pattern": "^other$"}, + {"pattern": "^partner$"}, + {"pattern": "^textbook$"}, + {"pattern": "^tutorial$"}, + {"pattern": "^domain$"}, + {"pattern": "^contentrequest$"}, + {"pattern": "^word$"} + ], + "type": "string" + }, + "id": { + "id": "/properties/edata/properties/eks/properties/id", + "type": "string" + }, + "name": { + "id": "/properties/edata/properties/eks/properties/name", + "type": "string" + }, + "pageid": { + "id": "/properties/edata/properties/eks/properties/pageid", + "oneOf": [ + {"pattern" : "^com_content$"}, + {"pattern" : "^com_content.ekstep.homepage$"}, + {"pattern" : "^com_subusers.organizations.category$"}, + {"pattern" : "^com_subusers.organization$"}, + {"pattern" : "^com_easysocial.groups.category$"}, + {"pattern" : "^com_content.article$"}, + {"pattern" : "^com_content.category$"}, + {"pattern" : "^com_content.category.blog$"}, + {"pattern" : "^com_content.featured$"}, + {"pattern" : "^com_easyblog$"}, + {"pattern" : "^com_easyblog.dashboard.write$"}, + {"pattern" : "^com_easyblog.entry$"}, + {"pattern" : "^com_easyblog.latest$"}, + {"pattern" : "^com_easyblog.search$"}, + {"pattern" : "^com_easydiscuss$"}, + {"pattern" : "^com_easydiscuss.index$"}, + {"pattern" : "^com_easydiscuss.post$"}, + {"pattern" : "^com_easysocial$"}, + {"pattern" : "^com_easysocial.groups$"}, + {"pattern" : "^com_easysocial.profile$"}, + {"pattern" : "^com_ekcontent$"}, + {"pattern" : "^com_ekcontent.concept$"}, + {"pattern" : "^com_ekcontent.concept.details$"}, + {"pattern" : "^com_ekcontent.conceptform.create$"}, + {"pattern" : "^com_ekcontent.concepts$"}, + {"pattern" : "^com_ekcontent.concepts.search$"}, + {"pattern" : "^com_ekcontent.consumers$"}, + {"pattern" : "^com_ekcontent.consumers.list$"}, + {"pattern" : "^com_ekcontent.content$"}, + {"pattern" : "^com_ekcontent.content.details$"}, + {"pattern" : "^com_ekcontent.content.search$"}, + {"pattern" : "^com_ekcontent.contentform$"}, + {"pattern" : "^com_ekcontent.contentform.collection$"}, + {"pattern" : "^com_ekcontent.contentform.create$"}, + {"pattern" : "^com_ekcontent.contentform.initcontent$"}, + {"pattern" : "^com_ekcontent.contentform.lesson$"}, + {"pattern" : "^com_ekcontent.contentform.lessoncollection$"}, + {"pattern" : "^com_ekcontent.contentform.textbook$"}, + {"pattern" : "^com_ekcontent.contentform.textbook.unit$"}, + {"pattern" : "^com_ekcontent.contentform.upload$"}, + {"pattern" : "^com_ekcontent.contentrequestform$"}, + {"pattern" : "^com_ekcontent.contentrequestform.create$"}, + {"pattern" : "^com_ekcontent.contentrequests$"}, + {"pattern" : "^com_ekcontent.contentrequests.list$"}, + {"pattern" : "^com_ekcontent.contents$"}, + {"pattern" : "^com_ekcontent.dashboard$"}, + {"pattern" : "^com_ekcontent.dashboard.domain$"}, + {"pattern" : "^com_ekcontent.dashboard.language$"}, + {"pattern" : "^com_ekcontent.domain.dashboard$"}, + {"pattern" : "^com_ekcontent.editor$"}, + {"pattern" : "^com_ekcontent.itemform$"}, + {"pattern" : "^com_ekcontent.itemform.create$"}, + {"pattern" : "^com_ekcontent.items$"}, + {"pattern" : "^com_ekcontent.items.list$"}, + {"pattern" : "^com_ekcontent.medialibrary$"}, + {"pattern" : "^com_ekcontent.search$"}, + {"pattern" : "^com_ekcontent.search.etextbook$"}, + {"pattern" : "^com_ekcontent.tbtags$"}, + {"pattern" : "^com_ekcontent.tbtagsform$"}, + {"pattern" : "^com_ekcontent.tbtagsform.create$"}, + {"pattern" : "^com_ekcontent.terms$"}, + {"pattern" : "^com_ekcontent.textbook.search$"}, + {"pattern" : "^com_ekcontent.word$"}, + {"pattern" : "^com_ekcontent.word.details$"}, + {"pattern" : "^com_ekcontent.wordform$"}, + {"pattern" : "^com_ekcontent.wordform.create$"}, + {"pattern" : "^com_ekcontent.words$"}, + {"pattern" : "^com_ekcontent.words.language.dashboard$"}, + {"pattern" : "^com_ekcontent.words.list$"}, + {"pattern" : "^com_media$"}, + {"pattern" : "^com_newsfeeds$"}, + {"pattern" : "^com_rsticketspro$"}, + {"pattern" : "^com_subusers$"}, + {"pattern" : "^com_subusers.initiativeform.create$"}, + {"pattern" : "^com_subusers.organization.details$"}, + {"pattern" : "^com_subusers.organizationform.create$"}, + {"pattern" : "^com_subusers.organizations.list$"}, + {"pattern" : "^com_subusers.organizations.pin$"}, + {"pattern" : "^com_subusers.programform.create$"}, + {"pattern" : "^com_subusers.programtagform.create$"}, + {"pattern" : "^com_subusers.programtags.list$"}, + {"pattern" : "^com_subusers.userform.create$"}, + {"pattern" : "^com_subusers.users.list$"}, + {"pattern" : "^com_ekcontent.search.content$"}, + {"pattern" : "^com_subusers.programtagform$"}, + {"pattern" : "^com_ekcontent.recommendation.display$"}, + {"pattern" : "^com_easysocial.groups.item$"}, + {"pattern" : "^com_ekcontent.search.word$"}, + {"pattern" : "^com_ekcontent.contentform.default$"}, + {"pattern" : "^com_easysocial.profile.edit$"}, + {"pattern" : "^com_tags$"}, + {"pattern" : "^com_users$"} + ], + "type": "string" + }, + "type": { + "id": "/properties/edata/properties/eks/properties/type", + "oneOf": [ + {"pattern": "^view$"}, + {"pattern": "^edit$"}, + {"pattern": "^list$"} + ], + "type": "string" + }, + "url": { + "id": "/properties/edata/properties/eks/properties/url", + "type": "string" + } + }, + "required": [ + "pageid", + "name", + "url", + "env", + "type", + "id" + ], + "type": "object" + } + }, + "required": [ + "eks" + ], + "type": "object" + }, + "eid": { + "id": "/properties/eid", + "type": "string" + }, + "ets": { + "id": "/properties/ets", + "type": "number" + }, + "mid": { + "id": "/properties/mid", + "type": "string" + }, + "pdata": { + "id": "/properties/pdata", + "properties": { + "id": { + "id": "/properties/pdata/properties/id", + "type": "string" + }, + "pid": { + "id": "/properties/pdata/properties/pid", + "type": "string" + }, + "ver": { + "id": "/properties/pdata/properties/ver", + "type": "string" + } + }, + "required": [ + "ver", + "pid", + "id" + ], + "type": "object" + }, + "rid": { + "id": "/properties/rid", + "type": "string" + }, + "tags": { + "id": "/properties/tags", + "items": {}, + "type": "array", + "uniqueItems": true + }, + "uid": { + "id": "/properties/uid", + "type": "string" + }, + "ver": { + "id": "/properties/ver", + "type": "string" + } + }, + "required": [ + "ets", + "context", + "ver", + "uid", + "tags", + "pdata", + "mid", + "eid", + "cdata", + "edata", + "rid" + ], + "type": "object" +} diff --git a/json-schema-validator/schema/v4/cp_interact.json b/json-schema-validator/schema/v4/cp_interact.json new file mode 100644 index 0000000..4906ecf --- /dev/null +++ b/json-schema-validator/schema/v4/cp_interact.json @@ -0,0 +1,207 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": {}, + "id": "CP_INTERACT", + "properties": { + "cdata": { + "id": "/properties/cdata", + "items": { + "id": "/properties/cdata/items", + "properties": { + "id": { + "id": "/properties/cdata/items/properties/id", + "type": "string" + }, + "type": { + "id": "/properties/cdata/items/properties/type", + "type": "string" + } + }, + "required": [ + "type", + "id" + ], + "type": "object" + }, + "type": "array", + "uniqueItems": true + }, + "context": { + "id": "/properties/context", + "properties": { + "content_id": { + "id": "/properties/context/properties/content_id", + "type": "string" + }, + "sid": { + "id": "/properties/context/properties/sid", + "type": "string" + } + }, + "required": [ + "content_id", + "sid" + ], + "type": "object" + }, + "edata": { + "id": "/properties/edata", + "properties": { + "eks": { + "id": "/properties/edata/properties/eks", + "properties": { + "context": { + "id": "/properties/edata/properties/eks/properties/context", + "type": "string" + }, + "env": { + "id": "/properties/edata/properties/eks/properties/env", + "oneOf": [ + {"pattern": "^blog$"}, + {"pattern": "^community$"}, + {"pattern": "^concept$"}, + {"pattern": "^content$"}, + {"pattern": "^developer$"}, + {"pattern": "^discussion$"}, + {"pattern": "^editor$"}, + {"pattern": "^home$"}, + {"pattern": "^item$"}, + {"pattern": "^other$"}, + {"pattern": "^partner$"}, + {"pattern": "^textbook$"}, + {"pattern": "^tutorial$"}, + {"pattern": "^domain$"}, + {"pattern": "^word$"} + ], + "type": "string" + }, + "subtype": { + "id": "/properties/edata/properties/eks/properties/subtype", + "type": "string" + }, + "target": { + "id": "/properties/edata/properties/eks/properties/target", + "oneOf": [ + {"pattern": "^asset$"}, + {"pattern": "^concept$"}, + {"pattern": "^content$"}, + {"pattern": "^lesson$"}, + {"pattern": "^recommendation$"}, + {"pattern": "^textbooksubunit$"}, + {"pattern": "^textbookunit$"}, + {"pattern": "^lessonpicker$"}, + {"pattern": "^tab$"}, + {"pattern": "^alert$"}, + {"pattern": "^textbook$"}, + {"pattern": "^$"} + ], + "type": "string" + }, + "targetid": { + "id": "/properties/edata/properties/eks/properties/targetid", + "type": "string" + }, + "type": { + "id": "/properties/edata/properties/eks/properties/type", + "oneOf": [ + {"pattern": "^change$"}, + {"pattern": "^click$"}, + {"pattern": "^drag$"}, + {"pattern": "^popup$"}, + {"pattern": "^action$"}, + {"pattern": "^select$"} + ], + "type": "string" + }, + "values": { + "id": "/properties/edata/properties/eks/properties/values", + "items": {}, + "type": "array" + } + }, + "required": [ + "env", + "targetid", + "subtype", + "values", + "context", + "type", + "target" + ], + "type": "object" + } + }, + "required": [ + "eks" + ], + "type": "object" + }, + "eid": { + "id": "/properties/eid", + "type": "string" + }, + "ets": { + "id": "/properties/ets", + "type": "number" + }, + "mid": { + "id": "/properties/mid", + "type": "string" + }, + "pdata": { + "id": "/properties/pdata", + "properties": { + "id": { + "id": "/properties/pdata/properties/id", + "type": "string" + }, + "pid": { + "id": "/properties/pdata/properties/pid", + "type": "string" + }, + "ver": { + "id": "/properties/pdata/properties/ver", + "type": "string" + } + }, + "required": [ + "ver", + "pid", + "id" + ], + "type": "object" + }, + "rid": { + "id": "/properties/rid", + "type": "string" + }, + "tags": { + "id": "/properties/tags", + "items": {}, + "type": "array", + "uniqueItems": true + }, + "uid": { + "id": "/properties/uid", + "type": "string" + }, + "ver": { + "id": "/properties/ver", + "type": "string" + } + }, + "required": [ + "ets", + "context", + "ver", + "uid", + "tags", + "pdata", + "mid", + "eid", + "cdata", + "edata", + "rid" + ], + "type": "object" +} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/cp_session_start.json b/json-schema-validator/schema/v4/cp_session_start.json new file mode 100644 index 0000000..c26c54e --- /dev/null +++ b/json-schema-validator/schema/v4/cp_session_start.json @@ -0,0 +1,129 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": {}, + "id": "CP_SESSION_START", + "properties": { + "cdata": { + "id": "/properties/cdata", + "items": { + "id": "/properties/cdata/items", + "properties": { + "id": { + "id": "/properties/cdata/items/properties/id", + "type": "string" + }, + "type": { + "id": "/properties/cdata/items/properties/type", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "context": { + "id": "/properties/context", + "properties": { + "content_id": { + "id": "/properties/context/properties/content_id", + "type": "string" + }, + "sid": { + "id": "/properties/context/properties/sid", + "type": "string" + } + }, + "type": "object" + }, + "edata": { + "id": "/properties/edata", + "properties": { + "eks": { + "id": "/properties/edata/properties/eks", + "properties": { + "authprovider": { + "id": "/properties/edata/properties/eks/properties/authprovider", + "type": "string" + }, + "uaspec": { + "id": "/properties/edata/properties/eks/properties/uaspec", + "properties": { + "agent": { + "id": "/properties/edata/properties/eks/properties/uaspec/properties/agent", + "type": "string" + }, + "platform": { + "id": "/properties/edata/properties/eks/properties/uaspec/properties/platform", + "type": "string" + }, + "raw": { + "id": "/properties/edata/properties/eks/properties/uaspec/properties/raw", + "type": "string" + }, + "system": { + "id": "/properties/edata/properties/eks/properties/uaspec/properties/system", + "type": "string" + }, + "ver": { + "id": "/properties/edata/properties/eks/properties/uaspec/properties/ver", + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "eid": { + "id": "/properties/eid", + "type": "string" + }, + "ets": { + "id": "/properties/ets", + "type": "number" + }, + "mid": { + "id": "/properties/mid", + "type": "string" + }, + "pdata": { + "id": "/properties/pdata", + "properties": { + "id": { + "id": "/properties/pdata/properties/id", + "type": "string" + }, + "pid": { + "id": "/properties/pdata/properties/pid", + "type": "string" + }, + "ver": { + "id": "/properties/pdata/properties/ver", + "type": "string" + } + }, + "type": "object" + }, + "rid": { + "id": "/properties/rid", + "type": "string" + }, + "tags": { + "id": "/properties/tags", + "items": {}, + "type": "array" + }, + "uid": { + "id": "/properties/uid", + "type": "string" + }, + "ver": { + "id": "/properties/ver", + "type": "string" + } + }, + "type": "object" +} \ No newline at end of file diff --git a/json-schema-validator/schema/v4/cp_update_profile.json b/json-schema-validator/schema/v4/cp_update_profile.json new file mode 100644 index 0000000..8961994 --- /dev/null +++ b/json-schema-validator/schema/v4/cp_update_profile.json @@ -0,0 +1,184 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "definitions": {}, + "id": "CP_UPDATE_PROFILE", + "properties": { + "cdata": { + "id": "/properties/cdata", + "items": { + "id": "/properties/cdata/items", + "properties": { + "id": { + "id": "/properties/cdata/items/properties/id", + "type": "string" + }, + "type": { + "id": "/properties/cdata/items/properties/type", + "type": "string" + } + }, + "required": [ + "type", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "context": { + "id": "/properties/context", + "properties": { + "content_id": { + "id": "/properties/context/properties/content_id", + "type": "string" + }, + "sid": { + "id": "/properties/context/properties/sid", + "type": "string" + } + }, + "required": [ + "content_id", + "sid" + ], + "type": "object" + }, + "edata": { + "id": "/properties/edata", + "properties": { + "eks": { + "id": "/properties/edata/properties/eks", + "properties": { + "access": { + "id": "/properties/edata/properties/eks/properties/access", + "items": { + "id": "/properties/edata/properties/eks/properties/access/items", + "properties": { + "id": { + "id": "/properties/edata/properties/eks/properties/access/items/properties/id", + "type": "string" + }, + "value": { + "id": "/properties/edata/properties/eks/properties/access/items/properties/value", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "email": { + "id": "/properties/edata/properties/eks/properties/email", + "type": "string" + }, + "name": { + "id": "/properties/edata/properties/eks/properties/name", + "type": "string" + }, + "partners": { + "id": "/properties/edata/properties/eks/properties/partners", + "items": { + "id": "/properties/edata/properties/eks/properties/partners/items", + "properties": { + "id": { + "id": "/properties/edata/properties/eks/properties/partners/items/properties/id", + "type": "string" + }, + "value": { + "id": "/properties/edata/properties/eks/properties/partners/items/properties/value", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "profile": { + "id": "/properties/edata/properties/eks/properties/profile", + "items": {}, + "type": "array" + } + }, + "required": [ + "access", + "partners", + "profile", + "name", + "email" + ], + "type": "object" + } + }, + "required": [ + "eks" + ], + "type": "object" + }, + "eid": { + "id": "/properties/eid", + "type": "string" + }, + "ets": { + "id": "/properties/ets", + "type": "number" + }, + "mid": { + "id": "/properties/mid", + "type": "string" + }, + "pdata": { + "id": "/properties/pdata", + "properties": { + "id": { + "id": "/properties/pdata/properties/id", + "type": "string" + }, + "pid": { + "id": "/properties/pdata/properties/pid", + "type": "string" + }, + "ver": { + "id": "/properties/pdata/properties/ver", + "type": "string" + } + }, + "required": [ + "ver", + "pid", + "id" + ], + "type": "object" + }, + "rid": { + "id": "/properties/rid", + "type": "string" + }, + "tags": { + "id": "/properties/tags", + "items": {}, + "type": "array" + }, + "uid": { + "id": "/properties/uid", + "type": "string" + }, + "ver": { + "id": "/properties/ver", + "type": "string" + } + }, + "required": [ + "ets", + "context", + "ver", + "uid", + "tags", + "pdata", + "mid", + "eid", + "cdata", + "edata", + "rid" + ], + "type": "object" +} \ No newline at end of file diff --git a/json-schema-validator/tools/php/LICENSE b/json-schema-validator/tools/php/LICENSE new file mode 100644 index 0000000..fa020fc --- /dev/null +++ b/json-schema-validator/tools/php/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/json-schema-validator/tools/php/README.md b/json-schema-validator/tools/php/README.md new file mode 100644 index 0000000..03a691f --- /dev/null +++ b/json-schema-validator/tools/php/README.md @@ -0,0 +1,24 @@ +# JSON Schema for PHP + +A PHP Implementation for validating `JSON` Structures against a given `Schema`. + +See [json-schema](http://json-schema.org/) for more details. + +## Installation + +``` +composer install +``` + +## Usage + +``` +cd ekstep +php validate.php +``` + +## Schema +The schema files should be kept ../../schema/v and should be named as: +* file name should be all lowercase +* file name should exactly match the event type. eg: be_object_lifecycle, cp_interact +* file should have dot json extension diff --git a/json-schema-validator/tools/php/bin/validate-json b/json-schema-validator/tools/php/bin/validate-json new file mode 100755 index 0000000..e9c1809 --- /dev/null +++ b/json-schema-validator/tools/php/bin/validate-json @@ -0,0 +1,237 @@ +#!/usr/bin/env php + + */ + +/** + * Dead simple autoloader + * + * @param string $className Name of class to load + * + * @return void + */ +function __autoload($className) +{ + $className = ltrim($className, '\\'); + $fileName = ''; + $namespace = ''; + if ($lastNsPos = strrpos($className, '\\')) { + $namespace = substr($className, 0, $lastNsPos); + $className = substr($className, $lastNsPos + 1); + $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; + } + $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + if (stream_resolve_include_path($fileName)) { + require_once $fileName; + } +} + +/** + * Show the json parse error that happened last + * + * @return void + */ +function showJsonError() +{ + $constants = get_defined_constants(true); + $json_errors = array(); + foreach ($constants['json'] as $name => $value) { + if (!strncmp($name, 'JSON_ERROR_', 11)) { + $json_errors[$value] = $name; + } + } + + echo 'JSON parse error: ' . $json_errors[json_last_error()] . "\n"; +} + +function getUrlFromPath($path) +{ + if (parse_url($path, PHP_URL_SCHEME) !== null) { + //already an URL + return $path; + } + if ($path{0} == '/') { + //absolute path + return 'file://' . $path; + } + + //relative path: make absolute + return 'file://' . getcwd() . '/' . $path; +} + +/** + * Take a HTTP header value and split it up into parts. + * + * @param $headerValue + * @return array Key "_value" contains the main value, all others + * as given in the header value + */ +function parseHeaderValue($headerValue) +{ + if (strpos($headerValue, ';') === false) { + return array('_value' => $headerValue); + } + + $parts = explode(';', $headerValue); + $arData = array('_value' => array_shift($parts)); + foreach ($parts as $part) { + list($name, $value) = explode('=', $part); + $arData[$name] = trim($value, ' "\''); + } + return $arData; +} + + +// support running this tool from git checkout +if (is_dir(__DIR__ . '/../src/JsonSchema')) { + set_include_path(__DIR__ . '/../src' . PATH_SEPARATOR . get_include_path()); +} + +$arOptions = array(); +$arArgs = array(); +array_shift($argv);//script itself +foreach ($argv as $arg) { + if ($arg{0} == '-') { + $arOptions[$arg] = true; + } else { + $arArgs[] = $arg; + } +} + +if (count($arArgs) == 0 + || isset($arOptions['--help']) || isset($arOptions['-h']) +) { + echo << array( + 'header' => array( + 'Accept: */*', + 'Connection: Close' + ), + 'max_redirects' => 5 + ) + ) +); +$dataString = file_get_contents($pathData, false, $context); +if ($dataString == '') { + echo "Data file is not readable or empty.\n"; + exit(3); +} + +$data = json_decode($dataString); +unset($dataString); +if ($data === null) { + echo "Error loading JSON data file\n"; + showJsonError(); + exit(5); +} + +if ($pathSchema === null) { + if (isset($http_response_header)) { + array_shift($http_response_header);//HTTP/1.0 line + foreach ($http_response_header as $headerLine) { + list($hName, $hValue) = explode(':', $headerLine, 2); + $hName = strtolower($hName); + if ($hName == 'link') { + //Link: ; rel="describedBy" + $hParts = parseHeaderValue($hValue); + if (isset($hParts['rel']) && $hParts['rel'] == 'describedBy') { + $pathSchema = trim($hParts['_value'], ' <>'); + } + } else if ($hName == 'content-type') { + //Content-Type: application/my-media-type+json; + // profile=http://example.org/schema# + $hParts = parseHeaderValue($hValue); + if (isset($hParts['profile'])) { + $pathSchema = $hParts['profile']; + } + + } + } + } + if (is_object($data) && property_exists($data, '$schema')) { + $pathSchema = $data->{'$schema'}; + } + + //autodetect schema + if ($pathSchema === null) { + echo "JSON data must be an object and have a \$schema property.\n"; + echo "You can pass the schema file on the command line as well.\n"; + echo "Schema autodetection failed.\n"; + exit(6); + } +} +if ($pathSchema{0} == '/') { + $pathSchema = 'file://' . $pathSchema; +} + +$resolver = new JsonSchema\Uri\UriResolver(); +$retriever = new JsonSchema\Uri\UriRetriever(); +try { + $urlSchema = $resolver->resolve($pathSchema, $urlData); + + if (isset($arOptions['--dump-schema-url'])) { + echo $urlSchema . "\n"; + exit(); + } +} catch (Exception $e) { + echo "Error loading JSON schema file\n"; + echo $urlSchema . "\n"; + echo $e->getMessage() . "\n"; + exit(2); +} +$refResolver = new JsonSchema\SchemaStorage($retriever, $resolver); +$schema = $refResolver->resolveRef($urlSchema); + +if (isset($arOptions['--dump-schema'])) { + $options = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; + echo json_encode($schema, $options) . "\n"; + exit(); +} + +try { + $validator = new JsonSchema\Validator(); + $validator->check($data, $schema); + + if ($validator->isValid()) { + echo "OK. The supplied JSON validates against the schema.\n"; + } else { + echo "JSON does not validate. Violations:\n"; + foreach ($validator->getErrors() as $error) { + echo sprintf("[%s] %s\n", $error['property'], $error['message']); + } + exit(23); + } +} catch (Exception $e) { + echo "JSON does not validate. Error:\n"; + echo $e->getMessage() . "\n"; + echo "Error code: " . $e->getCode() . "\n"; + exit(24); +} diff --git a/json-schema-validator/tools/php/composer.json b/json-schema-validator/tools/php/composer.json new file mode 100644 index 0000000..86cb2d1 --- /dev/null +++ b/json-schema-validator/tools/php/composer.json @@ -0,0 +1,65 @@ +{ + "name": "justinrainbow/json-schema", + "description": "A library to validate a json schema.", + "keywords": ["json", "schema"], + "homepage": "https://github.com/justinrainbow/json-schema", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "repositories": [{ + "type": "package", + "package": { + "name": "json-schema/JSON-Schema-Test-Suite", + "version": "1.2.0", + "source": { + "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", + "type": "git", + "reference": "1.2.0" + } + } + }], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "json-schema/JSON-Schema-Test-Suite": "1.2.0", + "phpunit/phpunit": "^4.8.22", + "friendsofphp/php-cs-fixer": "^2.1" + }, + "autoload": { + "psr-4": { "JsonSchema\\": "src/JsonSchema/" } + }, + "autoload-dev": { + "psr-4": { "JsonSchema\\Tests\\": "tests/" } + }, + "bin": ["bin/validate-json"], + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "scripts": { + "test" : "vendor/bin/phpunit", + "testOnly" : "vendor/bin/phpunit --colors --filter", + "coverage" : "vendor/bin/phpunit --coverage-text", + "style-check" : "vendor/bin/php-cs-fixer fix --dry-run --verbose --diff", + "style-fix" : "vendor/bin/php-cs-fixer fix --verbose" + } +} diff --git a/json-schema-validator/tools/php/composer.lock b/json-schema-validator/tools/php/composer.lock new file mode 100644 index 0000000..d8c2000 --- /dev/null +++ b/json-schema-validator/tools/php/composer.lock @@ -0,0 +1,2096 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "2a13b69da9e743e67f603cdd49a0d96a", + "content-hash": "62105cdd36db417d043f55918cd318d1", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/annotations", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-02-24 16:22:25" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.3.2", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", + "reference": "597745f744bcce1aed59dfd1bb4603de2a06cda9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/597745f744bcce1aed59dfd1bb4603de2a06cda9", + "reference": "597745f744bcce1aed59dfd1bb4603de2a06cda9", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.2", + "ext-json": "*", + "ext-tokenizer": "*", + "gecko-packages/gecko-php-unit": "^2.0", + "php": "^5.6 || >=7.0 <7.2", + "sebastian/diff": "^1.4", + "symfony/console": "^3.0", + "symfony/event-dispatcher": "^3.0", + "symfony/filesystem": "^3.0", + "symfony/finder": "^3.0", + "symfony/options-resolver": "^3.0", + "symfony/polyfill-php70": "^1.0", + "symfony/polyfill-xml": "^1.3", + "symfony/process": "^3.0", + "symfony/stopwatch": "^3.0" + }, + "conflict": { + "hhvm": "<3.18" + }, + "require-dev": { + "johnkary/phpunit-speedtrap": "^1.1", + "justinrainbow/json-schema": "^5.0", + "mi-schi/phpmd-extension": "^4.2", + "phpmd/phpmd": "^2.4.3", + "phpunit/phpunit": "^4.8.35 || ^5.4.3", + "satooshi/php-coveralls": "^1.0", + "symfony/phpunit-bridge": "^3.2.2" + }, + "suggest": { + "ext-mbstring": "For handling non-UTF8 characters in cache signature.", + "ext-xml": "For better performance.", + "symfony/polyfill-mbstring": "When enabling `ext-mbstring` is not possible." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "time": "2017-05-24 21:59:38" + }, + { + "name": "gecko-packages/gecko-php-unit", + "version": "v2.0", + "source": { + "type": "git", + "url": "https://github.com/GeckoPackages/GeckoPHPUnit.git", + "reference": "40a697ec261f3526e8196363b481b24383740c13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GeckoPackages/GeckoPHPUnit/zipball/40a697ec261f3526e8196363b481b24383740c13", + "reference": "40a697ec261f3526e8196363b481b24383740c13", + "shasum": "" + }, + "require": { + "php": "^5.3.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "GeckoPackages\\PHPUnit\\": "src\\PHPUnit" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Additional PHPUnit tests.", + "homepage": "https://github.com/GeckoPackages", + "keywords": [ + "extension", + "filesystem", + "phpunit" + ], + "time": "2016-11-22 11:01:27" + }, + { + "name": "json-schema/JSON-Schema-Test-Suite", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/json-schema/JSON-Schema-Test-Suite", + "reference": "1.2.0" + }, + "type": "library" + }, + { + "name": "paragonie/random_compat", + "version": "v2.0.10", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2017-03-13 16:27:32" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2015-12-27 11:43:31" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.2.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2016-09-30 07:12:33" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "shasum": "" + }, + "require": { + "php": ">=5.5", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2016-11-25 06:54:22" + }, + { + "name": "phpspec/prophecy", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8 || ^5.6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2017-03-02 20:05:34" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2015-10-06 15:47:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2016-10-03 07:40:28" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26 11:10:40" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-02-27 10:12:30" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.35", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/791b1a67c25af50e230f841ee7a9c6eba507dc87", + "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.2.2", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-02-06 05:18:07" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02 06:51:40" + }, + { + "name": "psr/log", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10 12:19:37" + }, + { + "name": "sebastian/comparator", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-01-29 09:50:25" + }, + { + "name": "sebastian/diff", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-05-22 07:24:03" + }, + { + "name": "sebastian/environment", + "version": "1.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-08-18 05:49:44" + }, + { + "name": "sebastian/exporter", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2016-06-17 09:04:28" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2016-10-03 07:41:43" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21 13:59:46" + }, + { + "name": "symfony/console", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/70d2a29b2911cbdc91a7e268046c395278238b2e", + "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/debug": "~2.8|~3.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3", + "symfony/dependency-injection": "~3.3", + "symfony/event-dispatcher": "~2.8|~3.0", + "symfony/filesystem": "~2.8|~3.0", + "symfony/http-kernel": "~2.8|~3.0", + "symfony/process": "~2.8|~3.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/filesystem": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2017-06-02 19:24:58" + }, + { + "name": "symfony/debug", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/e9c50482841ef696e8fa1470d950a79c8921f45d", + "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "time": "2017-06-01 21:01:25" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "4054a102470665451108f9b59305c79176ef98f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4054a102470665451108f9b59305c79176ef98f0", + "reference": "4054a102470665451108f9b59305c79176ef98f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0", + "symfony/dependency-injection": "~3.3", + "symfony/expression-language": "~2.8|~3.0", + "symfony/stopwatch": "~2.8|~3.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2017-06-04 18:15:29" + }, + { + "name": "symfony/filesystem", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "c709670bf64721202ddbe4162846f250735842c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c709670bf64721202ddbe4162846f250735842c0", + "reference": "c709670bf64721202ddbe4162846f250735842c0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2017-05-28 14:08:56" + }, + { + "name": "symfony/finder", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/baea7f66d30854ad32988c11a09d7ffd485810c4", + "reference": "baea7f66d30854ad32988c11a09d7ffd485810c4", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2017-06-01 21:01:25" + }, + { + "name": "symfony/options-resolver", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ff48982d295bcac1fd861f934f041ebc73ae40f0", + "reference": "ff48982d295bcac1fd861f934f041ebc73ae40f0", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2017-04-12 14:14:56" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", + "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14 01:06:16" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", + "reference": "13ce343935f0f91ca89605a2f6ca6f5c2f3faac2", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14 01:06:16" + }, + { + "name": "symfony/polyfill-xml", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-xml.git", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-xml/zipball/64b6a864f18ab4fddad49f5025f805f6781dfabd", + "reference": "64b6a864f18ab4fddad49f5025f805f6781dfabd", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-xml": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Xml\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for xml's utf8_encode and utf8_decode functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-11-14 01:06:16" + }, + { + "name": "symfony/process", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/8e30690c67aafb6c7992d6d8eb0d707807dd3eaf", + "reference": "8e30690c67aafb6c7992d6d8eb0d707807dd3eaf", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2017-05-22 12:32:03" + }, + { + "name": "symfony/stopwatch", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/602a15299dc01556013b07167d4f5d3a60e90d15", + "reference": "602a15299dc01556013b07167d4f5d3a60e90d15", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Stopwatch Component", + "homepage": "https://symfony.com", + "time": "2017-04-12 14:14:56" + }, + { + "name": "symfony/yaml", + "version": "v3.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063", + "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-06-02 22:05:06" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2016-11-23 20:04:58" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [] +} diff --git a/json-schema-validator/tools/php/ekstep/validate.php b/json-schema-validator/tools/php/ekstep/validate.php new file mode 100644 index 0000000..100f72a --- /dev/null +++ b/json-schema-validator/tools/php/ekstep/validate.php @@ -0,0 +1,101 @@ +eid; + $schemaFile = strtolower($eventType) . '.json'; + $schema = $schemaPath.$schemaFile; + + if(!file_exists(realpath($schema))) { + $nonSchemaErrorCount++; + echo FORMAT_LINE_SEPARATOR . FORMAT_COLOR_YELLOW . "JSON [$eventType] at line: {$lineNumber} cannot be validated because of non-availability of schema. Please ensure you have '{$schemaFile}' available and readable at '".realpath($schemaPath)."'" . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + continue; + } + + // Validate + $validator = new JsonSchema\Validator(); + $validator->check($lineData, (object) array('$ref' => 'file://' . $schema)); + + if ($validator->isValid()) { + // UNCOMMENT Below line to display positive messages as well. + // echo "The supplied JSON [$eventType] validates against the schema.\n"; + } else { + $errorCount++; + echo FORMAT_LINE_SEPARATOR . FORMAT_COLOR_RED . "JSON [$eventType] at line: {$lineNumber} does not validate." . FORMAT_LINE_BREAK . FORMAT_COLOR_CYAN . "Provided Data: $line " . FORMAT_LINE_BREAK . + FORMAT_COLOR_RED . "Violations:" . FORMAT_LINE_BREAK; + foreach ($validator->getErrors() as $error) { + echo sprintf("[%s] %s" . FORMAT_LINE_BREAK, $error['property'], $error['message']); + } + echo FORMAT_COLOR_END; + } + } + + //update color of last progress. + echo "\033[32D"; //move back the cursor + echo FORMAT_COLOR_GREEN . "Progress: " . $progress_formatted . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + + + if ($errorCount) { + echo FORMAT_LINE_BREAK . FORMAT_COLOR_RED . "Found error(s) in $errorCount record(s)." . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + } else { + echo FORMAT_LINE_BREAK . FORMAT_COLOR_GREEN . "No errors found!" . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + } + + if ($nonSchemaErrorCount) { + echo FORMAT_LINE_BREAK . FORMAT_COLOR_YELLOW . "WARNING: Could not validate $nonSchemaErrorCount records - Schema not available." . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + } else { + echo FORMAT_LINE_BREAK . FORMAT_COLOR_GREEN . "No warnings generated!" . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + } + + echo FORMAT_LINE_BREAK . FORMAT_COLOR_GREEN . "INFO: Completed validating $lineNumber record(s)." . FORMAT_COLOR_END . FORMAT_LINE_BREAK; + + fclose($dataHandle); +} else { + echo FORMAT_LINE_BREAK . FORMAT_COLOR_RED . "Error: Could not read data file. Please ensure the file path is valid and readable." . FORMAT_COLOR_END . FORMAT_LINE_BREAK; +} + +$timer[] = microtime(true); +$diff = end($timer) - prev($timer); +$timetaken = number_format($diff, 6); +echo FORMAT_COLOR_GREEN . "INFO: Task completed in " . FORMAT_COLOR_PURPLE . $timetaken . FORMAT_COLOR_END . "s" . FORMAT_LINE_BREAK . FORMAT_LINE_BREAK; \ No newline at end of file diff --git a/json-schema-validator/tools/php/phpunit.xml.dist b/json-schema-validator/tools/php/phpunit.xml.dist new file mode 100644 index 0000000..0136d8e --- /dev/null +++ b/json-schema-validator/tools/php/phpunit.xml.dist @@ -0,0 +1,26 @@ + + + + + + tests + + + + + + ./src/JsonSchema/ + + + diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/BaseConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/BaseConstraint.php new file mode 100644 index 0000000..ef1bdc5 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/BaseConstraint.php @@ -0,0 +1,84 @@ +factory = $factory ?: new Factory(); + } + + public function addError(JsonPointer $path = null, $message, $constraint = '', array $more = null) + { + $error = array( + 'property' => $this->convertJsonPointerIntoPropertyPath($path ?: new JsonPointer('')), + 'pointer' => ltrim(strval($path ?: new JsonPointer('')), '#'), + 'message' => $message, + 'constraint' => $constraint, + ); + + if ($this->factory->getConfig(Constraint::CHECK_MODE_EXCEPTIONS)) { + throw new ValidationException(sprintf('Error validating %s: %s', $error['pointer'], $error['message'])); + } + + if (is_array($more) && count($more) > 0) { + $error += $more; + } + + $this->errors[] = $error; + } + + public function addErrors(array $errors) + { + if ($errors) { + $this->errors = array_merge($this->errors, $errors); + } + } + + public function getErrors() + { + return $this->errors; + } + + public function isValid() + { + return !$this->getErrors(); + } + + /** + * Clears any reported errors. Should be used between + * multiple validation checks. + */ + public function reset() + { + $this->errors = array(); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/CollectionConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/CollectionConstraint.php new file mode 100644 index 0000000..3c594b3 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/CollectionConstraint.php @@ -0,0 +1,147 @@ + + * @author Bruno Prieto Reis + */ +class CollectionConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + // Verify minItems + if (isset($schema->minItems) && count($value) < $schema->minItems) { + $this->addError($path, 'There must be a minimum of ' . $schema->minItems . ' items in the array', 'minItems', array('minItems' => $schema->minItems)); + } + + // Verify maxItems + if (isset($schema->maxItems) && count($value) > $schema->maxItems) { + $this->addError($path, 'There must be a maximum of ' . $schema->maxItems . ' items in the array', 'maxItems', array('maxItems' => $schema->maxItems)); + } + + // Verify uniqueItems + if (isset($schema->uniqueItems) && $schema->uniqueItems) { + $unique = $value; + if (is_array($value) && count($value)) { + $unique = array_map(function ($e) { + return var_export($e, true); + }, $value); + } + if (count(array_unique($unique)) != count($value)) { + $this->addError($path, 'There are no duplicates allowed in the array', 'uniqueItems'); + } + } + + // Verify items + if (isset($schema->items)) { + $this->validateItems($value, $schema, $path, $i); + } + } + + /** + * Validates the items + * + * @param array $value + * @param \stdClass $schema + * @param JsonPointer|null $path + * @param string $i + */ + protected function validateItems(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + if (is_object($schema->items)) { + // just one type definition for the whole array + + if (isset($schema->items->type) + && ( + $schema->items->type == 'string' + || $schema->items->type == 'number' + || $schema->items->type == 'integer' + ) + && !isset($schema->additionalItems) + ) { + // performance optimization + $type = $schema->items->type; + $typeValidator = $this->factory->createInstanceFor('type'); + $validator = $this->factory->createInstanceFor($type === 'integer' ? 'number' : $type); + + foreach ($value as $k => &$v) { + $k_path = $this->incrementPath($path, $k); + $typeValidator->check($v, $schema->items, $k_path, $i); + + $validator->check($v, $schema->items, $k_path, $i); + } + unset($v); // remove dangling reference to prevent any future bugs + // caused by accidentally using $v elsewhere + $this->addErrors($typeValidator->getErrors()); + $this->addErrors($validator->getErrors()); + } else { + foreach ($value as $k => &$v) { + $initErrors = $this->getErrors(); + + // First check if its defined in "items" + $this->checkUndefined($v, $schema->items, $path, $k); + + // Recheck with "additionalItems" if the first test fails + if (count($initErrors) < count($this->getErrors()) && (isset($schema->additionalItems) && $schema->additionalItems !== false)) { + $secondErrors = $this->getErrors(); + $this->checkUndefined($v, $schema->additionalItems, $path, $k); + } + + // Reset errors if needed + if (isset($secondErrors) && count($secondErrors) < count($this->getErrors())) { + $this->errors = $secondErrors; + } elseif (isset($secondErrors) && count($secondErrors) === count($this->getErrors())) { + $this->errors = $initErrors; + } + } + unset($v); // remove dangling reference to prevent any future bugs + // caused by accidentally using $v elsewhere + } + } else { + // Defined item type definitions + foreach ($value as $k => &$v) { + if (array_key_exists($k, $schema->items)) { + $this->checkUndefined($v, $schema->items[$k], $path, $k); + } else { + // Additional items + if (property_exists($schema, 'additionalItems')) { + if ($schema->additionalItems !== false) { + $this->checkUndefined($v, $schema->additionalItems, $path, $k); + } else { + $this->addError( + $path, 'The item ' . $i . '[' . $k . '] is not defined and the definition does not allow additional items', 'additionalItems', array('additionalItems' => $schema->additionalItems)); + } + } else { + // Should be valid against an empty schema + $this->checkUndefined($v, new \stdClass(), $path, $k); + } + } + } + unset($v); // remove dangling reference to prevent any future bugs + // caused by accidentally using $v elsewhere + + // Treat when we have more schema definitions than values, not for empty arrays + if (count($value) > 0) { + for ($k = count($value); $k < count($schema->items); $k++) { + $undefinedInstance = $this->factory->createInstanceFor('undefined'); + $this->checkUndefined($undefinedInstance, $schema->items[$k], $path, $k); + } + } + } + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/Constraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/Constraint.php new file mode 100644 index 0000000..7fa0a99 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/Constraint.php @@ -0,0 +1,211 @@ + + * @author Bruno Prieto Reis + */ +abstract class Constraint extends BaseConstraint implements ConstraintInterface +{ + protected $inlineSchemaProperty = '$schema'; + + const CHECK_MODE_NONE = 0x00000000; + const CHECK_MODE_NORMAL = 0x00000001; + const CHECK_MODE_TYPE_CAST = 0x00000002; + const CHECK_MODE_COERCE_TYPES = 0x00000004; + const CHECK_MODE_APPLY_DEFAULTS = 0x00000008; + const CHECK_MODE_EXCEPTIONS = 0x00000010; + + /** + * Bubble down the path + * + * @param JsonPointer|null $path Current path + * @param mixed $i What to append to the path + * + * @return JsonPointer; + */ + protected function incrementPath(JsonPointer $path = null, $i) + { + $path = $path ?: new JsonPointer(''); + $path = $path->withPropertyPaths( + array_merge( + $path->getPropertyPaths(), + array_filter(array($i), 'strlen') + ) + ); + + return $path; + } + + /** + * Validates an array + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkArray(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('collection'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Validates an object + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + * @param mixed $patternProperties + */ + protected function checkObject(&$value, $schema = null, JsonPointer $path = null, $i = null, $patternProperties = null) + { + $validator = $this->factory->createInstanceFor('object'); + $validator->check($value, $schema, $path, $i, $patternProperties); + + $this->addErrors($validator->getErrors()); + } + + /** + * Validates the type of a property + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkType(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('type'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Checks a undefined element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkUndefined(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('undefined'); + + $validator->check($value, $this->factory->getSchemaStorage()->resolveRefSchema($schema), $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Checks a string element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkString($value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('string'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Checks a number element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param mixed $i + */ + protected function checkNumber($value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('number'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Checks a enum element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkEnum($value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('enum'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Checks format of an element + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + */ + protected function checkFormat($value, $schema = null, JsonPointer $path = null, $i = null) + { + $validator = $this->factory->createInstanceFor('format'); + $validator->check($value, $schema, $path, $i); + + $this->addErrors($validator->getErrors()); + } + + /** + * Get the type check based on the set check mode. + * + * @return TypeCheck\TypeCheckInterface + */ + protected function getTypeCheck() + { + return $this->factory->getTypeCheck(); + } + + /** + * @param JsonPointer $pointer + * + * @return string property path + */ + protected function convertJsonPointerIntoPropertyPath(JsonPointer $pointer) + { + $result = array_map( + function ($path) { + return sprintf(is_numeric($path) ? '[%d]' : '.%s', $path); + }, + $pointer->getPropertyPaths() + ); + + return trim(implode('', $result), '.'); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/ConstraintInterface.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/ConstraintInterface.php new file mode 100644 index 0000000..442268e --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/ConstraintInterface.php @@ -0,0 +1,65 @@ + + */ +interface ConstraintInterface +{ + /** + * returns all collected errors + * + * @return array + */ + public function getErrors(); + + /** + * adds errors to this validator + * + * @param array $errors + */ + public function addErrors(array $errors); + + /** + * adds an error + * + * @param JsonPointer|null $path + * @param string $message + * @param string $constraint the constraint/rule that is broken, e.g.: 'minLength' + * @param array $more more array elements to add to the error + */ + public function addError(JsonPointer $path = null, $message, $constraint='', array $more = null); + + /** + * checks if the validator has not raised errors + * + * @return bool + */ + public function isValid(); + + /** + * invokes the validation of an element + * + * @abstract + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer|null $path + * @param mixed $i + * + * @throws \JsonSchema\Exception\ExceptionInterface + */ + public function check(&$value, $schema = null, JsonPointer $path = null, $i = null); +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/EnumConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/EnumConstraint.php new file mode 100644 index 0000000..0fd2b6a --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/EnumConstraint.php @@ -0,0 +1,54 @@ + + * @author Bruno Prieto Reis + */ +class EnumConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + // Only validate enum if the attribute exists + if ($element instanceof UndefinedConstraint && (!isset($schema->required) || !$schema->required)) { + return; + } + $type = gettype($element); + + foreach ($schema->enum as $enum) { + $enumType = gettype($enum); + if ($this->factory->getConfig(self::CHECK_MODE_TYPE_CAST) && $type == 'array' && $enumType == 'object') { + if ((object) $element == $enum) { + return; + } + } + + if ($type === gettype($enum)) { + if ($type == 'object') { + if ($element == $enum) { + return; + } + } elseif ($element === $enum) { + return; + } + } + } + + $this->addError($path, 'Does not have a value in the enumeration ' . json_encode($schema->enum), 'enum', array('enum' => $schema->enum)); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/Factory.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/Factory.php new file mode 100644 index 0000000..8c24873 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/Factory.php @@ -0,0 +1,196 @@ + 'JsonSchema\Constraints\CollectionConstraint', + 'collection' => 'JsonSchema\Constraints\CollectionConstraint', + 'object' => 'JsonSchema\Constraints\ObjectConstraint', + 'type' => 'JsonSchema\Constraints\TypeConstraint', + 'undefined' => 'JsonSchema\Constraints\UndefinedConstraint', + 'string' => 'JsonSchema\Constraints\StringConstraint', + 'number' => 'JsonSchema\Constraints\NumberConstraint', + 'enum' => 'JsonSchema\Constraints\EnumConstraint', + 'format' => 'JsonSchema\Constraints\FormatConstraint', + 'schema' => 'JsonSchema\Constraints\SchemaConstraint', + 'validator' => 'JsonSchema\Validator' + ); + + /** + * @var array + */ + private $instanceCache = array(); + + /** + * @param SchemaStorage $schemaStorage + * @param UriRetrieverInterface $uriRetriever + * @param int $checkMode + */ + public function __construct( + SchemaStorageInterface $schemaStorage = null, + UriRetrieverInterface $uriRetriever = null, + $checkMode = Constraint::CHECK_MODE_NORMAL + ) { + // set provided config options + $this->setConfig($checkMode); + + $this->uriRetriever = $uriRetriever ?: new UriRetriever(); + $this->schemaStorage = $schemaStorage ?: new SchemaStorage($this->uriRetriever); + } + + /** + * Set config values + * + * @param int $checkMode Set checkMode options - does not preserve existing flags + */ + public function setConfig($checkMode = Constraint::CHECK_MODE_NORMAL) + { + $this->checkMode = $checkMode; + } + + /** + * Enable checkMode flags + * + * @param int $options + */ + public function addConfig($options) + { + $this->checkMode |= $options; + } + + /** + * Disable checkMode flags + * + * @param int $options + */ + public function removeConfig($options) + { + $this->checkMode &= ~$options; + } + + /** + * Get checkMode option + * + * @param int $options Options to get, if null then return entire bitmask + * + * @return int + */ + public function getConfig($options = null) + { + if ($options === null) { + return $this->checkMode; + } + + return $this->checkMode & $options; + } + + /** + * @return UriRetrieverInterface + */ + public function getUriRetriever() + { + return $this->uriRetriever; + } + + public function getSchemaStorage() + { + return $this->schemaStorage; + } + + public function getTypeCheck() + { + if (!isset($this->typeCheck[$this->checkMode])) { + $this->typeCheck[$this->checkMode] = ($this->checkMode & Constraint::CHECK_MODE_TYPE_CAST) + ? new TypeCheck\LooseTypeCheck() + : new TypeCheck\StrictTypeCheck(); + } + + return $this->typeCheck[$this->checkMode]; + } + + /** + * @param string $name + * @param string $class + * + * @return Factory + */ + public function setConstraintClass($name, $class) + { + // Ensure class exists + if (!class_exists($class)) { + throw new InvalidArgumentException('Unknown constraint ' . $name); + } + // Ensure class is appropriate + if (!in_array('JsonSchema\Constraints\ConstraintInterface', class_implements($class))) { + throw new InvalidArgumentException('Invalid class ' . $name); + } + $this->constraintMap[$name] = $class; + + return $this; + } + + /** + * Create a constraint instance for the given constraint name. + * + * @param string $constraintName + * + * @throws InvalidArgumentException if is not possible create the constraint instance + * + * @return ConstraintInterface|ObjectConstraint + */ + public function createInstanceFor($constraintName) + { + if (!isset($this->constraintMap[$constraintName])) { + throw new InvalidArgumentException('Unknown constraint ' . $constraintName); + } + + if (!isset($this->instanceCache[$constraintName])) { + $this->instanceCache[$constraintName] = new $this->constraintMap[$constraintName]($this); + } + + return clone $this->instanceCache[$constraintName]; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/FormatConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/FormatConstraint.php new file mode 100644 index 0000000..ad192b5 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/FormatConstraint.php @@ -0,0 +1,202 @@ + + * + * @see http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.23 + */ +class FormatConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + if (!isset($schema->format)) { + return; + } + + switch ($schema->format) { + case 'date': + if (!$date = $this->validateDateTime($element, 'Y-m-d')) { + $this->addError($path, sprintf('Invalid date %s, expected format YYYY-MM-DD', json_encode($element)), 'format', array('format' => $schema->format)); + } + break; + + case 'time': + if (!$this->validateDateTime($element, 'H:i:s')) { + $this->addError($path, sprintf('Invalid time %s, expected format hh:mm:ss', json_encode($element)), 'format', array('format' => $schema->format)); + } + break; + + case 'date-time': + if (null === Rfc3339::createFromString($element)) { + $this->addError($path, sprintf('Invalid date-time %s, expected format YYYY-MM-DDThh:mm:ssZ or YYYY-MM-DDThh:mm:ss+hh:mm', json_encode($element)), 'format', array('format' => $schema->format)); + } + break; + + case 'utc-millisec': + if (!$this->validateDateTime($element, 'U')) { + $this->addError($path, sprintf('Invalid time %s, expected integer of milliseconds since Epoch', json_encode($element)), 'format', array('format' => $schema->format)); + } + break; + + case 'regex': + if (!$this->validateRegex($element)) { + $this->addError($path, 'Invalid regex format ' . $element, 'format', array('format' => $schema->format)); + } + break; + + case 'color': + if (!$this->validateColor($element)) { + $this->addError($path, 'Invalid color', 'format', array('format' => $schema->format)); + } + break; + + case 'style': + if (!$this->validateStyle($element)) { + $this->addError($path, 'Invalid style', 'format', array('format' => $schema->format)); + } + break; + + case 'phone': + if (!$this->validatePhone($element)) { + $this->addError($path, 'Invalid phone number', 'format', array('format' => $schema->format)); + } + break; + + case 'uri': + if (null === filter_var($element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE)) { + // FILTER_VALIDATE_URL does not conform to RFC-3986, and cannot handle relative URLs, but + // the json-schema spec uses RFC-3986, so need a bit of hackery to properly validate them. + // See https://tools.ietf.org/html/rfc3986#section-4.2 for additional information. + if (substr($element, 0, 2) === '//') { // network-path reference + $validURL = filter_var('scheme:' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); + } elseif (substr($element, 0, 1) === '/') { // absolute-path reference + $validURL = filter_var('scheme://host' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); + } elseif (strlen($element)) { // relative-path reference + $pathParts = explode('/', $element, 2); + if (strpos($pathParts[0], ':') !== false) { + $validURL = null; + } else { + $validURL = filter_var('scheme://host/' . $element, FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE); + } + } else { + $validURL = null; + } + if ($validURL === null) { + $this->addError($path, 'Invalid URL format', 'format', array('format' => $schema->format)); + } + } + break; + + case 'email': + if (null === filter_var($element, FILTER_VALIDATE_EMAIL, FILTER_NULL_ON_FAILURE)) { + $this->addError($path, 'Invalid email', 'format', array('format' => $schema->format)); + } + break; + + case 'ip-address': + case 'ipv4': + if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV4)) { + $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); + } + break; + + case 'ipv6': + if (null === filter_var($element, FILTER_VALIDATE_IP, FILTER_NULL_ON_FAILURE | FILTER_FLAG_IPV6)) { + $this->addError($path, 'Invalid IP address', 'format', array('format' => $schema->format)); + } + break; + + case 'host-name': + case 'hostname': + if (!$this->validateHostname($element)) { + $this->addError($path, 'Invalid hostname', 'format', array('format' => $schema->format)); + } + break; + + default: + // Empty as it should be: + // The value of this keyword is called a format attribute. It MUST be a string. + // A format attribute can generally only validate a given set of instance types. + // If the type of the instance to validate is not in this set, validation for + // this format attribute and instance SHOULD succeed. + // http://json-schema.org/latest/json-schema-validation.html#anchor105 + break; + } + } + + protected function validateDateTime($datetime, $format) + { + $dt = \DateTime::createFromFormat($format, $datetime); + + if (!$dt) { + return false; + } + + if ($datetime === $dt->format($format)) { + return true; + } + + // handles the case where a non-6 digit microsecond datetime is passed + // which will fail the above string comparison because the passed + // $datetime may be '2000-05-01T12:12:12.123Z' but format() will return + // '2000-05-01T12:12:12.123000Z' + if ((strpos('u', $format) !== -1) && (preg_match('/\.\d+Z$/', $datetime))) { + return true; + } + + return false; + } + + protected function validateRegex($regex) + { + return false !== @preg_match('/' . $regex . '/u', ''); + } + + protected function validateColor($color) + { + if (in_array(strtolower($color), array('aqua', 'black', 'blue', 'fuchsia', + 'gray', 'green', 'lime', 'maroon', 'navy', 'olive', 'orange', 'purple', + 'red', 'silver', 'teal', 'white', 'yellow'))) { + return true; + } + + return preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $color); + } + + protected function validateStyle($style) + { + $properties = explode(';', rtrim($style, ';')); + $invalidEntries = preg_grep('/^\s*[-a-z]+\s*:\s*.+$/i', $properties, PREG_GREP_INVERT); + + return empty($invalidEntries); + } + + protected function validatePhone($phone) + { + return preg_match('/^\+?(\(\d{3}\)|\d{3}) \d{3} \d{4}$/', $phone); + } + + protected function validateHostname($host) + { + $hostnameRegex = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/i'; + + return preg_match($hostnameRegex, $host); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/NumberConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/NumberConstraint.php new file mode 100644 index 0000000..5a80977 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/NumberConstraint.php @@ -0,0 +1,86 @@ + + * @author Bruno Prieto Reis + */ +class NumberConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + // Verify minimum + if (isset($schema->exclusiveMinimum)) { + if (isset($schema->minimum)) { + if ($schema->exclusiveMinimum && $element <= $schema->minimum) { + $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'exclusiveMinimum', array('minimum' => $schema->minimum)); + } elseif ($element < $schema->minimum) { + $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum)); + } + } else { + $this->addError($path, 'Use of exclusiveMinimum requires presence of minimum', 'missingMinimum'); + } + } elseif (isset($schema->minimum) && $element < $schema->minimum) { + $this->addError($path, 'Must have a minimum value of ' . $schema->minimum, 'minimum', array('minimum' => $schema->minimum)); + } + + // Verify maximum + if (isset($schema->exclusiveMaximum)) { + if (isset($schema->maximum)) { + if ($schema->exclusiveMaximum && $element >= $schema->maximum) { + $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'exclusiveMaximum', array('maximum' => $schema->maximum)); + } elseif ($element > $schema->maximum) { + $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum)); + } + } else { + $this->addError($path, 'Use of exclusiveMaximum requires presence of maximum', 'missingMaximum'); + } + } elseif (isset($schema->maximum) && $element > $schema->maximum) { + $this->addError($path, 'Must have a maximum value of ' . $schema->maximum, 'maximum', array('maximum' => $schema->maximum)); + } + + // Verify divisibleBy - Draft v3 + if (isset($schema->divisibleBy) && $this->fmod($element, $schema->divisibleBy) != 0) { + $this->addError($path, 'Is not divisible by ' . $schema->divisibleBy, 'divisibleBy', array('divisibleBy' => $schema->divisibleBy)); + } + + // Verify multipleOf - Draft v4 + if (isset($schema->multipleOf) && $this->fmod($element, $schema->multipleOf) != 0) { + $this->addError($path, 'Must be a multiple of ' . $schema->multipleOf, 'multipleOf', array('multipleOf' => $schema->multipleOf)); + } + + $this->checkFormat($element, $schema, $path, $i); + } + + private function fmod($number1, $number2) + { + $number1 = abs($number1); + $modulus = fmod($number1, $number2); + $precision = abs(0.0000000001); + $diff = (float) ($modulus - $number2); + + if (-$precision < $diff && $diff < $precision) { + return 0.0; + } + + $decimals1 = mb_strpos($number1, '.') ? mb_strlen($number1) - mb_strpos($number1, '.') - 1 : 0; + $decimals2 = mb_strpos($number2, '.') ? mb_strlen($number2) - mb_strpos($number2, '.') - 1 : 0; + + return (float) round($modulus, max($decimals1, $decimals2)); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/ObjectConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/ObjectConstraint.php new file mode 100644 index 0000000..5ea94f7 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/ObjectConstraint.php @@ -0,0 +1,181 @@ + + * @author Bruno Prieto Reis + */ +class ObjectConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $definition = null, JsonPointer $path = null, $additionalProp = null, $patternProperties = null) + { + if ($element instanceof UndefinedConstraint) { + return; + } + + $matches = array(); + if ($patternProperties) { + $matches = $this->validatePatternProperties($element, $path, $patternProperties); + } + + if ($definition) { + // validate the definition properties + $this->validateDefinition($element, $definition, $path); + } + + // additional the element properties + $this->validateElement($element, $matches, $definition, $path, $additionalProp); + } + + public function validatePatternProperties($element, JsonPointer $path = null, $patternProperties) + { + $try = array('/', '#', '+', '~', '%'); + $matches = array(); + foreach ($patternProperties as $pregex => $schema) { + $delimiter = '/'; + // Choose delimiter. Necessary for patterns like ^/ , otherwise you get error + foreach ($try as $delimiter) { + if (strpos($pregex, $delimiter) === false) { // safe to use + break; + } + } + + // Validate the pattern before using it to test for matches + if (@preg_match($delimiter . $pregex . $delimiter . 'u', '') === false) { + $this->addError($path, 'The pattern "' . $pregex . '" is invalid', 'pregex', array('pregex' => $pregex)); + continue; + } + foreach ($element as $i => $value) { + if (preg_match($delimiter . $pregex . $delimiter . 'u', $i)) { + $matches[] = $i; + $this->checkUndefined($value, $schema ?: new \stdClass(), $path, $i); + } + } + } + + return $matches; + } + + /** + * Validates the element properties + * + * @param \stdClass $element Element to validate + * @param array $matches Matches from patternProperties (if any) + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsonPointer|null $path Path to test? + * @param mixed $additionalProp Additional properties + */ + public function validateElement($element, $matches, $objectDefinition = null, JsonPointer $path = null, $additionalProp = null) + { + $this->validateMinMaxConstraint($element, $objectDefinition, $path); + + foreach ($element as $i => $value) { + $definition = $this->getProperty($objectDefinition, $i); + + // no additional properties allowed + if (!in_array($i, $matches) && $additionalProp === false && $this->inlineSchemaProperty !== $i && !$definition) { + $this->addError($path, 'The property ' . $i . ' is not defined and the definition does not allow additional properties', 'additionalProp'); + } + + // additional properties defined + if (!in_array($i, $matches) && $additionalProp && !$definition) { + if ($additionalProp === true) { + $this->checkUndefined($value, null, $path, $i); + } else { + $this->checkUndefined($value, $additionalProp, $path, $i); + } + } + + // property requires presence of another + $require = $this->getProperty($definition, 'requires'); + if ($require && !$this->getProperty($element, $require)) { + $this->addError($path, 'The presence of the property ' . $i . ' requires that ' . $require . ' also be present', 'requires'); + } + + $property = $this->getProperty($element, $i, $this->factory->createInstanceFor('undefined')); + if (is_object($property)) { + $this->validateMinMaxConstraint(!($property instanceof UndefinedConstraint) ? $property : $element, $definition, $path); + } + } + } + + /** + * Validates the definition properties + * + * @param \stdClass $element Element to validate + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsonPointer|null $path Path? + */ + public function validateDefinition(&$element, $objectDefinition = null, JsonPointer $path = null) + { + $undefinedConstraint = $this->factory->createInstanceFor('undefined'); + + foreach ($objectDefinition as $i => $value) { + $property = &$this->getProperty($element, $i, $undefinedConstraint); + $definition = $this->getProperty($objectDefinition, $i); + + if (is_object($definition)) { + // Undefined constraint will check for is_object() and quit if is not - so why pass it? + $this->checkUndefined($property, $definition, $path, $i); + } + } + } + + /** + * retrieves a property from an object or array + * + * @param mixed $element Element to validate + * @param string $property Property to retrieve + * @param mixed $fallback Default value if property is not found + * + * @return mixed + */ + protected function &getProperty(&$element, $property, $fallback = null) + { + if (is_array($element) && (isset($element[$property]) || array_key_exists($property, $element)) /*$this->checkMode == self::CHECK_MODE_TYPE_CAST*/) { + return $element[$property]; + } elseif (is_object($element) && property_exists($element, $property)) { + return $element->$property; + } + + return $fallback; + } + + /** + * validating minimum and maximum property constraints (if present) against an element + * + * @param \stdClass $element Element to validate + * @param \stdClass $objectDefinition ObjectConstraint definition + * @param JsonPointer|null $path Path to test? + */ + protected function validateMinMaxConstraint($element, $objectDefinition, JsonPointer $path = null) + { + // Verify minimum number of properties + if (isset($objectDefinition->minProperties) && !is_object($objectDefinition->minProperties)) { + if ($this->getTypeCheck()->propertyCount($element) < $objectDefinition->minProperties) { + $this->addError($path, 'Must contain a minimum of ' . $objectDefinition->minProperties . ' properties', 'minProperties', array('minProperties' => $objectDefinition->minProperties)); + } + } + // Verify maximum number of properties + if (isset($objectDefinition->maxProperties) && !is_object($objectDefinition->maxProperties)) { + if ($this->getTypeCheck()->propertyCount($element) > $objectDefinition->maxProperties) { + $this->addError($path, 'Must contain no more than ' . $objectDefinition->maxProperties . ' properties', 'maxProperties', array('maxProperties' => $objectDefinition->maxProperties)); + } + } + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/SchemaConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/SchemaConstraint.php new file mode 100644 index 0000000..c33fe8c --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/SchemaConstraint.php @@ -0,0 +1,42 @@ + + * @author Bruno Prieto Reis + */ +class SchemaConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + if ($schema !== null) { + // passed schema + $this->checkUndefined($element, $schema, $path, $i); + } elseif ($this->getTypeCheck()->propertyExists($element, $this->inlineSchemaProperty)) { + $inlineSchema = $this->getTypeCheck()->propertyGet($element, $this->inlineSchemaProperty); + if (is_array($inlineSchema)) { + $inlineSchema = json_decode(json_encode($inlineSchema)); + } + // inline schema + $this->checkUndefined($element, $inlineSchema, $path, $i); + } else { + throw new InvalidArgumentException('no schema found to verify against'); + } + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/StringConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/StringConstraint.php new file mode 100644 index 0000000..5b15de7 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/StringConstraint.php @@ -0,0 +1,59 @@ + + * @author Bruno Prieto Reis + */ +class StringConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$element, $schema = null, JsonPointer $path = null, $i = null) + { + // Verify maxLength + if (isset($schema->maxLength) && $this->strlen($element) > $schema->maxLength) { + $this->addError($path, 'Must be at most ' . $schema->maxLength . ' characters long', 'maxLength', array( + 'maxLength' => $schema->maxLength, + )); + } + + //verify minLength + if (isset($schema->minLength) && $this->strlen($element) < $schema->minLength) { + $this->addError($path, 'Must be at least ' . $schema->minLength . ' characters long', 'minLength', array( + 'minLength' => $schema->minLength, + )); + } + + // Verify a regex pattern + if (isset($schema->pattern) && !preg_match('#' . str_replace('#', '\\#', $schema->pattern) . '#u', $element)) { + $this->addError($path, 'Does not match the regex pattern ' . $schema->pattern, 'pattern', array( + 'pattern' => $schema->pattern, + )); + } + + $this->checkFormat($element, $schema, $path, $i); + } + + private function strlen($string) + { + if (extension_loaded('mbstring')) { + return mb_strlen($string, mb_detect_encoding($string)); + } + + return strlen($string); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php new file mode 100644 index 0000000..9842885 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/LooseTypeCheck.php @@ -0,0 +1,68 @@ +{$property}; + } + + return $value[$property]; + } + + public static function propertySet(&$value, $property, $data) + { + if (is_object($value)) { + $value->{$property} = $data; + } else { + $value[$property] = $data; + } + } + + public static function propertyExists($value, $property) + { + if (is_object($value)) { + return property_exists($value, $property); + } + + return array_key_exists($property, $value); + } + + public static function propertyCount($value) + { + if (is_object($value)) { + return count(get_object_vars($value)); + } + + return count($value); + } + + /** + * Check if the provided array is associative or not + * + * @param array $arr + * + * @return bool + */ + private static function isAssociativeArray($arr) + { + return array_keys($arr) !== range(0, count($arr) - 1); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php new file mode 100644 index 0000000..a6303a7 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/StrictTypeCheck.php @@ -0,0 +1,36 @@ +{$property}; + } + + public static function propertySet(&$value, $property, $data) + { + $value->{$property} = $data; + } + + public static function propertyExists($value, $property) + { + return property_exists($value, $property); + } + + public static function propertyCount($value) + { + return count(get_object_vars($value)); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php new file mode 100644 index 0000000..10b40ea --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/TypeCheck/TypeCheckInterface.php @@ -0,0 +1,18 @@ + + * @author Bruno Prieto Reis + */ +class TypeConstraint extends Constraint +{ + /** + * @var array|string[] type wordings for validation error messages + */ + public static $wording = array( + 'integer' => 'an integer', + 'number' => 'a number', + 'boolean' => 'a boolean', + 'object' => 'an object', + 'array' => 'an array', + 'string' => 'a string', + 'null' => 'a null', + 'any' => null, // validation of 'any' is always true so is not needed in message wording + 0 => null, // validation of a false-y value is always true, so not needed as well + ); + + /** + * {@inheritdoc} + */ + public function check(&$value = null, $schema = null, JsonPointer $path = null, $i = null) + { + $type = isset($schema->type) ? $schema->type : null; + $isValid = false; + $wording = array(); + + if (is_array($type)) { + $this->validateTypesArray($value, $type, $wording, $isValid, $path); + } elseif (is_object($type)) { + $this->checkUndefined($value, $type, $path); + + return; + } else { + $isValid = $this->validateType($value, $type); + } + + if ($isValid === false) { + if (!is_array($type)) { + $this->validateTypeNameWording($type); + $wording[] = self::$wording[$type]; + } + $this->addError($path, ucwords(gettype($value)) . ' value found, but ' . + $this->implodeWith($wording, ', ', 'or') . ' is required', 'type'); + } + } + + /** + * Validates the given $value against the array of types in $type. Sets the value + * of $isValid to true, if at least one $type mateches the type of $value or the value + * passed as $isValid is already true. + * + * @param mixed $value Value to validate + * @param array $type TypeConstraints to check agains + * @param array $validTypesWording An array of wordings of the valid types of the array $type + * @param bool $isValid The current validation value + * @param $path + */ + protected function validateTypesArray(&$value, array $type, &$validTypesWording, &$isValid, $path) + { + foreach ($type as $tp) { + // $tp can be an object, if it's a schema instead of a simple type, validate it + // with a new type constraint + if (is_object($tp)) { + if (!$isValid) { + $validator = $this->factory->createInstanceFor('type'); + $subSchema = new \stdClass(); + $subSchema->type = $tp; + $validator->check($value, $subSchema, $path, null); + $error = $validator->getErrors(); + $isValid = !(bool) $error; + $validTypesWording[] = self::$wording['object']; + } + } else { + $this->validateTypeNameWording($tp); + $validTypesWording[] = self::$wording[$tp]; + if (!$isValid) { + $isValid = $this->validateType($value, $tp); + } + } + } + } + + /** + * Implodes the given array like implode() with turned around parameters and with the + * difference, that, if $listEnd isn't false, the last element delimiter is $listEnd instead of + * $delimiter. + * + * @param array $elements The elements to implode + * @param string $delimiter The delimiter to use + * @param bool $listEnd The last delimiter to use (defaults to $delimiter) + * + * @return string + */ + protected function implodeWith(array $elements, $delimiter = ', ', $listEnd = false) + { + if ($listEnd === false || !isset($elements[1])) { + return implode($delimiter, $elements); + } + $lastElement = array_slice($elements, -1); + $firsElements = join($delimiter, array_slice($elements, 0, -1)); + $implodedElements = array_merge(array($firsElements), $lastElement); + + return join(" $listEnd ", $implodedElements); + } + + /** + * Validates the given $type, if there's an associated self::$wording. If not, throws an + * exception. + * + * @param string $type The type to validate + * + * @throws StandardUnexpectedValueException + */ + protected function validateTypeNameWording($type) + { + if (!isset(self::$wording[$type])) { + throw new StandardUnexpectedValueException( + sprintf( + 'No wording for %s available, expected wordings are: [%s]', + var_export($type, true), + implode(', ', array_filter(self::$wording))) + ); + } + } + + /** + * Verifies that a given value is of a certain type + * + * @param mixed $value Value to validate + * @param string $type TypeConstraint to check against + * + * @throws InvalidArgumentException + * + * @return bool + */ + protected function validateType(&$value, $type) + { + //mostly the case for inline schema + if (!$type) { + return true; + } + + if ('any' === $type) { + return true; + } + + if ('object' === $type) { + return $this->getTypeCheck()->isObject($value); + } + + if ('array' === $type) { + return $this->getTypeCheck()->isArray($value); + } + + $coerce = $this->factory->getConfig(Constraint::CHECK_MODE_COERCE_TYPES); + + if ('integer' === $type) { + if ($coerce) { + $value = $this->toInteger($value); + } + + return is_int($value); + } + + if ('number' === $type) { + if ($coerce) { + $value = $this->toNumber($value); + } + + return is_numeric($value) && !is_string($value); + } + + if ('boolean' === $type) { + if ($coerce) { + $value = $this->toBoolean($value); + } + + return is_bool($value); + } + + if ('string' === $type) { + return is_string($value); + } + + if ('email' === $type) { + return is_string($value); + } + + if ('null' === $type) { + return is_null($value); + } + + throw new InvalidArgumentException((is_object($value) ? 'object' : $value) . ' is an invalid type for ' . $type); + } + + /** + * Converts a value to boolean. For example, "true" becomes true. + * + * @param $value The value to convert to boolean + * + * @return bool|mixed + */ + protected function toBoolean($value) + { + if ($value === 'true') { + return true; + } + + if ($value === 'false') { + return false; + } + + return $value; + } + + /** + * Converts a numeric string to a number. For example, "4" becomes 4. + * + * @param mixed $value the value to convert to a number + * + * @return int|float|mixed + */ + protected function toNumber($value) + { + if (is_numeric($value)) { + return $value + 0; // cast to number + } + + return $value; + } + + protected function toInteger($value) + { + if (is_numeric($value) && (int) $value == $value) { + return (int) $value; // cast to number + } + + return $value; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Constraints/UndefinedConstraint.php b/json-schema-validator/tools/php/src/JsonSchema/Constraints/UndefinedConstraint.php new file mode 100644 index 0000000..147e5bc --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Constraints/UndefinedConstraint.php @@ -0,0 +1,326 @@ + + * @author Bruno Prieto Reis + */ +class UndefinedConstraint extends Constraint +{ + /** + * {@inheritdoc} + */ + public function check(&$value, $schema = null, JsonPointer $path = null, $i = null) + { + if (is_null($schema) || !is_object($schema)) { + return; + } + + $path = $this->incrementPath($path ?: new JsonPointer(''), $i); + + // check special properties + $this->validateCommonProperties($value, $schema, $path, $i); + + // check allOf, anyOf, and oneOf properties + $this->validateOfProperties($value, $schema, $path, ''); + + // check known types + $this->validateTypes($value, $schema, $path, $i); + } + + /** + * Validates the value against the types + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i + */ + public function validateTypes(&$value, $schema = null, JsonPointer $path, $i = null) + { + // check array + if ($this->getTypeCheck()->isArray($value)) { + $this->checkArray($value, $schema, $path, $i); + } + + // check object + if (LooseTypeCheck::isObject($value)) { // object processing should always be run on assoc arrays, + // so use LooseTypeCheck here even if CHECK_MODE_TYPE_CAST + // is not set (i.e. don't use $this->getTypeCheck() here). + $this->checkObject( + $value, + isset($schema->properties) ? $this->factory->getSchemaStorage()->resolveRefSchema($schema->properties) : $schema, + $path, + isset($schema->additionalProperties) ? $schema->additionalProperties : null, + isset($schema->patternProperties) ? $schema->patternProperties : null + ); + } + + // check string + if (is_string($value)) { + $this->checkString($value, $schema, $path, $i); + } + + // check numeric + if (is_numeric($value)) { + $this->checkNumber($value, $schema, $path, $i); + } + + // check enum + if (isset($schema->enum)) { + $this->checkEnum($value, $schema, $path, $i); + } + } + + /** + * Validates common properties + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i + */ + protected function validateCommonProperties(&$value, $schema = null, JsonPointer $path, $i = '') + { + // if it extends another schema, it must pass that schema as well + if (isset($schema->extends)) { + if (is_string($schema->extends)) { + $schema->extends = $this->validateUri($schema, $schema->extends); + } + if (is_array($schema->extends)) { + foreach ($schema->extends as $extends) { + $this->checkUndefined($value, $extends, $path, $i); + } + } else { + $this->checkUndefined($value, $schema->extends, $path, $i); + } + } + + // Apply default values from schema + if ($this->factory->getConfig(self::CHECK_MODE_APPLY_DEFAULTS)) { + if ($this->getTypeCheck()->isObject($value) && isset($schema->properties)) { + // $value is an object, so apply default properties if defined + foreach ($schema->properties as $currentProperty => $propertyDefinition) { + if (!$this->getTypeCheck()->propertyExists($value, $currentProperty) && isset($propertyDefinition->default)) { + if (is_object($propertyDefinition->default)) { + $this->getTypeCheck()->propertySet($value, $currentProperty, clone $propertyDefinition->default); + } else { + $this->getTypeCheck()->propertySet($value, $currentProperty, $propertyDefinition->default); + } + } + } + } elseif ($this->getTypeCheck()->isArray($value)) { + if (isset($schema->properties)) { + // $value is an array, but default properties are defined, so treat as assoc + foreach ($schema->properties as $currentProperty => $propertyDefinition) { + if (!isset($value[$currentProperty]) && isset($propertyDefinition->default)) { + if (is_object($propertyDefinition->default)) { + $value[$currentProperty] = clone $propertyDefinition->default; + } else { + $value[$currentProperty] = $propertyDefinition->default; + } + } + } + } elseif (isset($schema->items)) { + // $value is an array, and default items are defined - treat as plain array + foreach ($schema->items as $currentProperty => $itemDefinition) { + if (!isset($value[$currentProperty]) && isset($itemDefinition->default)) { + if (is_object($itemDefinition->default)) { + $value[$currentProperty] = clone $itemDefinition->default; + } else { + $value[$currentProperty] = $itemDefinition->default; + } + } + } + } + } elseif (($value instanceof self || $value === null) && isset($schema->default)) { + // $value is a leaf, not a container - apply the default directly + $value = is_object($schema->default) ? clone $schema->default : $schema->default; + } + } + + // Verify required values + if ($this->getTypeCheck()->isObject($value)) { + if (!($value instanceof self) && isset($schema->required) && is_array($schema->required)) { + // Draft 4 - Required is an array of strings - e.g. "required": ["foo", ...] + foreach ($schema->required as $required) { + if (!$this->getTypeCheck()->propertyExists($value, $required)) { + $this->addError( + $this->incrementPath($path ?: new JsonPointer(''), $required), + 'The property ' . $required . ' is required', + 'required' + ); + } + } + } elseif (isset($schema->required) && !is_array($schema->required)) { + // Draft 3 - Required attribute - e.g. "foo": {"type": "string", "required": true} + if ($schema->required && $value instanceof self) { + $this->addError($path, 'Is missing and it is required', 'required'); + } + } + } + + // Verify type + if (!($value instanceof self)) { + $this->checkType($value, $schema, $path, $i); + } + + // Verify disallowed items + if (isset($schema->disallow)) { + $initErrors = $this->getErrors(); + + $typeSchema = new \stdClass(); + $typeSchema->type = $schema->disallow; + $this->checkType($value, $typeSchema, $path); + + // if no new errors were raised it must be a disallowed value + if (count($this->getErrors()) == count($initErrors)) { + $this->addError($path, 'Disallowed value was matched', 'disallow'); + } else { + $this->errors = $initErrors; + } + } + + if (isset($schema->not)) { + $initErrors = $this->getErrors(); + $this->checkUndefined($value, $schema->not, $path, $i); + + // if no new errors were raised then the instance validated against the "not" schema + if (count($this->getErrors()) == count($initErrors)) { + $this->addError($path, 'Matched a schema which it should not', 'not'); + } else { + $this->errors = $initErrors; + } + } + + // Verify that dependencies are met + if (isset($schema->dependencies) && $this->getTypeCheck()->isObject($value)) { + $this->validateDependencies($value, $schema->dependencies, $path); + } + } + + /** + * Validate allOf, anyOf, and oneOf properties + * + * @param mixed $value + * @param mixed $schema + * @param JsonPointer $path + * @param string $i + */ + protected function validateOfProperties(&$value, $schema, JsonPointer $path, $i = '') + { + // Verify type + if ($value instanceof self) { + return; + } + + if (isset($schema->allOf)) { + $isValid = true; + foreach ($schema->allOf as $allOf) { + $initErrors = $this->getErrors(); + $this->checkUndefined($value, $allOf, $path, $i); + $isValid = $isValid && (count($this->getErrors()) == count($initErrors)); + } + if (!$isValid) { + $this->addError($path, 'Failed to match all schemas', 'allOf'); + } + } + + if (isset($schema->anyOf)) { + $isValid = false; + $startErrors = $this->getErrors(); + foreach ($schema->anyOf as $anyOf) { + $initErrors = $this->getErrors(); + $this->checkUndefined($value, $anyOf, $path, $i); + if ($isValid = (count($this->getErrors()) == count($initErrors))) { + break; + } + } + if (!$isValid) { + $this->addError($path, 'Failed to match at least one schema', 'anyOf'); + } else { + $this->errors = $startErrors; + } + } + + if (isset($schema->oneOf)) { + $allErrors = array(); + $matchedSchemas = 0; + $startErrors = $this->getErrors(); + foreach ($schema->oneOf as $oneOf) { + $this->errors = array(); + $this->checkUndefined($value, $oneOf, $path, $i); + if (count($this->getErrors()) == 0) { + $matchedSchemas++; + } + $allErrors = array_merge($allErrors, array_values($this->getErrors())); + } + if ($matchedSchemas !== 1) { + $this->addErrors(array_merge($allErrors, $startErrors)); + $this->addError($path, 'Failed to match exactly one schema', 'oneOf'); + } else { + $this->errors = $startErrors; + } + } + } + + /** + * Validate dependencies + * + * @param mixed $value + * @param mixed $dependencies + * @param JsonPointer $path + * @param string $i + */ + protected function validateDependencies($value, $dependencies, JsonPointer $path, $i = '') + { + foreach ($dependencies as $key => $dependency) { + if ($this->getTypeCheck()->propertyExists($value, $key)) { + if (is_string($dependency)) { + // Draft 3 string is allowed - e.g. "dependencies": {"bar": "foo"} + if (!$this->getTypeCheck()->propertyExists($value, $dependency)) { + $this->addError($path, "$key depends on $dependency and $dependency is missing", 'dependencies'); + } + } elseif (is_array($dependency)) { + // Draft 4 must be an array - e.g. "dependencies": {"bar": ["foo"]} + foreach ($dependency as $d) { + if (!$this->getTypeCheck()->propertyExists($value, $d)) { + $this->addError($path, "$key depends on $d and $d is missing", 'dependencies'); + } + } + } elseif (is_object($dependency)) { + // Schema - e.g. "dependencies": {"bar": {"properties": {"foo": {...}}}} + $this->checkUndefined($value, $dependency, $path, $i); + } + } + } + } + + protected function validateUri($schema, $schemaUri = null) + { + $resolver = new UriResolver(); + $retriever = $this->factory->getUriRetriever(); + + $jsonSchema = null; + if ($resolver->isValid($schemaUri)) { + $schemaId = property_exists($schema, 'id') ? $schema->id : null; + $jsonSchema = $retriever->retrieve($schemaId, $schemaUri); + } + + return $jsonSchema; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Entity/JsonPointer.php b/json-schema-validator/tools/php/src/JsonSchema/Entity/JsonPointer.php new file mode 100644 index 0000000..31c753b --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Entity/JsonPointer.php @@ -0,0 +1,136 @@ + + */ +class JsonPointer +{ + /** @var string */ + private $filename; + + /** @var string[] */ + private $propertyPaths = array(); + + /** + * @param string $value + * + * @throws \InvalidArgumentException when $value is not a string + */ + public function __construct($value) + { + if (!is_string($value)) { + throw new \InvalidArgumentException('Ref value must be a string'); + } + + $splitRef = explode('#', $value, 2); + $this->filename = $splitRef[0]; + if (array_key_exists(1, $splitRef)) { + $this->propertyPaths = $this->decodePropertyPaths($splitRef[1]); + } + } + + /** + * @param string $propertyPathString + * + * @return string[] + */ + private function decodePropertyPaths($propertyPathString) + { + $paths = array(); + foreach (explode('/', trim($propertyPathString, '/')) as $path) { + $path = $this->decodePath($path); + if (is_string($path) && '' !== $path) { + $paths[] = $path; + } + } + + return $paths; + } + + /** + * @return array + */ + private function encodePropertyPaths() + { + return array_map( + array($this, 'encodePath'), + $this->getPropertyPaths() + ); + } + + /** + * @param string $path + * + * @return string + */ + private function decodePath($path) + { + return strtr($path, array('~1' => '/', '~0' => '~', '%25' => '%')); + } + + /** + * @param string $path + * + * @return string + */ + private function encodePath($path) + { + return strtr($path, array('/' => '~1', '~' => '~0', '%' => '%25')); + } + + /** + * @return string + */ + public function getFilename() + { + return $this->filename; + } + + /** + * @return string[] + */ + public function getPropertyPaths() + { + return $this->propertyPaths; + } + + /** + * @param array $propertyPaths + * + * @return JsonPointer + */ + public function withPropertyPaths(array $propertyPaths) + { + $new = clone $this; + $new->propertyPaths = $propertyPaths; + + return $new; + } + + /** + * @return string + */ + public function getPropertyPathAsString() + { + return rtrim('#/' . implode('/', $this->encodePropertyPaths()), '/'); + } + + /** + * @return string + */ + public function __toString() + { + return $this->getFilename() . $this->getPropertyPathAsString(); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Exception/ExceptionInterface.php b/json-schema-validator/tools/php/src/JsonSchema/Exception/ExceptionInterface.php new file mode 100644 index 0000000..439bd27 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Exception/ExceptionInterface.php @@ -0,0 +1,7 @@ + + */ +class UnresolvableJsonPointerException extends InvalidArgumentException +{ +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Exception/UriResolverException.php b/json-schema-validator/tools/php/src/JsonSchema/Exception/UriResolverException.php new file mode 100644 index 0000000..13b7303 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Exception/UriResolverException.php @@ -0,0 +1,17 @@ + + */ +class ObjectIterator implements \Iterator, \Countable +{ + /** @var object */ + private $object; + + /** @var int */ + private $position = 0; + + /** @var array */ + private $data = array(); + + /** @var bool */ + private $initialized = false; + + /** + * @param object $object + */ + public function __construct($object) + { + $this->object = $object; + } + + /** + * {@inheritdoc} + */ + public function current() + { + $this->initialize(); + + return $this->data[$this->position]; + } + + /** + * {@inheritdoc} + */ + public function next() + { + $this->initialize(); + $this->position++; + } + + /** + * {@inheritdoc} + */ + public function key() + { + $this->initialize(); + + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function valid() + { + $this->initialize(); + + return isset($this->data[$this->position]); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->initialize(); + $this->position = 0; + } + + /** + * {@inheritdoc} + */ + public function count() + { + $this->initialize(); + + return count($this->data); + } + + /** + * Initializer + */ + private function initialize() + { + if (!$this->initialized) { + $this->data = $this->buildDataFromObject($this->object); + $this->initialized = true; + } + } + + /** + * @param object $object + * + * @return array + */ + private function buildDataFromObject($object) + { + $result = array(); + + $stack = new \SplStack(); + $stack->push($object); + + while (!$stack->isEmpty()) { + $current = $stack->pop(); + if (is_object($current)) { + array_push($result, $current); + } + + foreach ($this->getDataFromItem($current) as $propertyName => $propertyValue) { + if (is_object($propertyValue) || is_array($propertyValue)) { + $stack->push($propertyValue); + } + } + } + + return $result; + } + + /** + * @param object|array $item + * + * @return array + */ + private function getDataFromItem($item) + { + if (!is_object($item) && !is_array($item)) { + return array(); + } + + return is_object($item) ? get_object_vars($item) : $item; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Rfc3339.php b/json-schema-validator/tools/php/src/JsonSchema/Rfc3339.php new file mode 100644 index 0000000..fb2eb7d --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Rfc3339.php @@ -0,0 +1,30 @@ +uriRetriever = $uriRetriever ?: new UriRetriever(); + $this->uriResolver = $uriResolver ?: new UriResolver(); + } + + /** + * @return UriRetrieverInterface + */ + public function getUriRetriever() + { + return $this->uriRetriever; + } + + /** + * @return UriResolverInterface + */ + public function getUriResolver() + { + return $this->uriResolver; + } + + /** + * {@inheritdoc} + */ + public function addSchema($id, $schema = null) + { + if (is_null($schema)) { + $schema = $this->uriRetriever->retrieve($id); + } + $objectIterator = new ObjectIterator($schema); + foreach ($objectIterator as $toResolveSchema) { + if (property_exists($toResolveSchema, '$ref') && is_string($toResolveSchema->{'$ref'})) { + $jsonPointer = new JsonPointer($this->uriResolver->resolve($toResolveSchema->{'$ref'}, $id)); + $toResolveSchema->{'$ref'} = (string) $jsonPointer; + } + } + $this->schemas[$id] = $schema; + } + + /** + * {@inheritdoc} + */ + public function getSchema($id) + { + if (!array_key_exists($id, $this->schemas)) { + $this->addSchema($id); + } + + return $this->schemas[$id]; + } + + /** + * {@inheritdoc} + */ + public function resolveRef($ref) + { + $jsonPointer = new JsonPointer($ref); + $refSchema = $this->getSchema($jsonPointer->getFilename()); + + foreach ($jsonPointer->getPropertyPaths() as $path) { + if (is_object($refSchema) && property_exists($refSchema, $path)) { + $refSchema = $this->resolveRefSchema($refSchema->{$path}); + } elseif (is_array($refSchema) && array_key_exists($path, $refSchema)) { + $refSchema = $this->resolveRefSchema($refSchema[$path]); + } else { + throw new UnresolvableJsonPointerException(sprintf( + 'File: %s is found, but could not resolve fragment: %s', + $jsonPointer->getFilename(), + $jsonPointer->getPropertyPathAsString() + )); + } + } + + return $refSchema; + } + + /** + * {@inheritdoc} + */ + public function resolveRefSchema($refSchema) + { + if (is_object($refSchema) && property_exists($refSchema, '$ref') && is_string($refSchema->{'$ref'})) { + $newSchema = $this->resolveRef($refSchema->{'$ref'}); + $refSchema = (object) (get_object_vars($refSchema) + get_object_vars($newSchema)); + unset($refSchema->{'$ref'}); + } + + return $refSchema; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/SchemaStorageInterface.php b/json-schema-validator/tools/php/src/JsonSchema/SchemaStorageInterface.php new file mode 100644 index 0000000..ddaf663 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/SchemaStorageInterface.php @@ -0,0 +1,41 @@ + + */ +abstract class AbstractRetriever implements UriRetrieverInterface +{ + /** + * Media content type + * + * @var string + */ + protected $contentType; + + /** + * {@inheritdoc} + * + * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::getContentType() + */ + public function getContentType() + { + return $this->contentType; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/Curl.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/Curl.php new file mode 100644 index 0000000..a4125aa --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/Curl.php @@ -0,0 +1,81 @@ + + */ +class Curl extends AbstractRetriever +{ + protected $messageBody; + + public function __construct() + { + if (!function_exists('curl_init')) { + throw new \RuntimeException('cURL not installed'); + } + } + + /** + * {@inheritdoc} + * + * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() + */ + public function retrieve($uri) + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $uri); + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: ' . Validator::SCHEMA_MEDIA_TYPE)); + + $response = curl_exec($ch); + if (false === $response) { + throw new \JsonSchema\Exception\ResourceNotFoundException('JSON schema not found'); + } + + $this->fetchMessageBody($response); + $this->fetchContentType($response); + + curl_close($ch); + + return $this->messageBody; + } + + /** + * @param string $response cURL HTTP response + */ + private function fetchMessageBody($response) + { + preg_match("/(?:\r\n){2}(.*)$/ms", $response, $match); + $this->messageBody = $match[1]; + } + + /** + * @param string $response cURL HTTP response + * + * @return bool Whether the Content-Type header was found or not + */ + protected function fetchContentType($response) + { + if (0 < preg_match("/Content-Type:(\V*)/ims", $response, $match)) { + $this->contentType = trim($match[1]); + + return true; + } + + return false; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/FileGetContents.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/FileGetContents.php new file mode 100644 index 0000000..7f0c399 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/FileGetContents.php @@ -0,0 +1,91 @@ + + */ +class FileGetContents extends AbstractRetriever +{ + protected $messageBody; + + /** + * {@inheritdoc} + * + * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() + */ + public function retrieve($uri) + { + $errorMessage = null; + set_error_handler(function ($errno, $errstr) use (&$errorMessage) { + $errorMessage = $errstr; + }); + $response = file_get_contents($uri); + restore_error_handler(); + + if ($errorMessage) { + throw new ResourceNotFoundException($errorMessage); + } + + if (false === $response) { + throw new ResourceNotFoundException('JSON schema not found at ' . $uri); + } + + if ($response == '' + && substr($uri, 0, 7) == 'file://' && substr($uri, -1) == '/' + ) { + throw new ResourceNotFoundException('JSON schema not found at ' . $uri); + } + + $this->messageBody = $response; + if (!empty($http_response_header)) { + $this->fetchContentType($http_response_header); + } else { + // Could be a "file://" url or something else - fake up the response + $this->contentType = null; + } + + return $this->messageBody; + } + + /** + * @param array $headers HTTP Response Headers + * + * @return bool Whether the Content-Type header was found or not + */ + private function fetchContentType(array $headers) + { + foreach ($headers as $header) { + if ($this->contentType = self::getContentTypeMatchInHeader($header)) { + return true; + } + } + + return false; + } + + /** + * @param string $header + * + * @return string|null + */ + protected static function getContentTypeMatchInHeader($header) + { + if (0 < preg_match("/Content-Type:(\V*)/ims", $header, $match)) { + return trim($match[1]); + } + + return null; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/PredefinedArray.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/PredefinedArray.php new file mode 100644 index 0000000..a663d34 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/PredefinedArray.php @@ -0,0 +1,56 @@ + '{ ... }', + * 'http://acme.com/schemas/address#' => '{ ... }', + * )) + * + * $schema = $retriever->retrieve('http://acme.com/schemas/person#'); + */ +class PredefinedArray extends AbstractRetriever +{ + /** + * Contains schemas as URI => JSON + * + * @var array + */ + private $schemas; + + /** + * Constructor + * + * @param array $schemas + * @param string $contentType + */ + public function __construct(array $schemas, $contentType = Validator::SCHEMA_MEDIA_TYPE) + { + $this->schemas = $schemas; + $this->contentType = $contentType; + } + + /** + * {@inheritdoc} + * + * @see \JsonSchema\Uri\Retrievers\UriRetrieverInterface::retrieve() + */ + public function retrieve($uri) + { + if (!array_key_exists($uri, $this->schemas)) { + throw new \JsonSchema\Exception\ResourceNotFoundException(sprintf( + 'The JSON schema "%s" was not found.', + $uri + )); + } + + return $this->schemas[$uri]; + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php new file mode 100644 index 0000000..2cc40cf --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/Retrievers/UriRetrieverInterface.php @@ -0,0 +1,36 @@ + + */ +interface UriRetrieverInterface +{ + /** + * Retrieve a schema from the specified URI + * + * @param string $uri URI that resolves to a JSON schema + * + * @throws \JsonSchema\Exception\ResourceNotFoundException + * + * @return mixed string|null + */ + public function retrieve($uri); + + /** + * Get media content type + * + * @return string + */ + public function getContentType(); +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/UriResolver.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/UriResolver.php new file mode 100644 index 0000000..7d6e793 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/UriResolver.php @@ -0,0 +1,160 @@ + + */ +class UriResolver implements UriResolverInterface +{ + /** + * Parses a URI into five main components + * + * @param string $uri + * + * @return array + */ + public function parse($uri) + { + preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match); + + $components = array(); + if (5 < count($match)) { + $components = array( + 'scheme' => $match[2], + 'authority' => $match[4], + 'path' => $match[5] + ); + } + if (7 < count($match)) { + $components['query'] = $match[7]; + } + if (9 < count($match)) { + $components['fragment'] = $match[9]; + } + + return $components; + } + + /** + * Builds a URI based on n array with the main components + * + * @param array $components + * + * @return string + */ + public function generate(array $components) + { + $uri = $components['scheme'] . '://' + . $components['authority'] + . $components['path']; + + if (array_key_exists('query', $components)) { + $uri .= $components['query']; + } + if (array_key_exists('fragment', $components)) { + $uri .= '#' . $components['fragment']; + } + + return $uri; + } + + /** + * {@inheritdoc} + */ + public function resolve($uri, $baseUri = null) + { + if ($uri == '') { + return $baseUri; + } + + $components = $this->parse($uri); + $path = $components['path']; + + if (!empty($components['scheme'])) { + return $uri; + } + $baseComponents = $this->parse($baseUri); + $basePath = $baseComponents['path']; + + $baseComponents['path'] = self::combineRelativePathWithBasePath($path, $basePath); + if (isset($components['fragment'])) { + $baseComponents['fragment'] = $components['fragment']; + } + + return $this->generate($baseComponents); + } + + /** + * Tries to glue a relative path onto an absolute one + * + * @param string $relativePath + * @param string $basePath + * + * @throws UriResolverException + * + * @return string Merged path + */ + public static function combineRelativePathWithBasePath($relativePath, $basePath) + { + $relativePath = self::normalizePath($relativePath); + if ($relativePath == '') { + return $basePath; + } + if ($relativePath[0] == '/') { + return $relativePath; + } + + $basePathSegments = explode('/', $basePath); + + preg_match('|^/?(\.\./(?:\./)*)*|', $relativePath, $match); + $numLevelUp = strlen($match[0]) /3 + 1; + if ($numLevelUp >= count($basePathSegments)) { + throw new UriResolverException(sprintf("Unable to resolve URI '%s' from base '%s'", $relativePath, $basePath)); + } + + $basePathSegments = array_slice($basePathSegments, 0, -$numLevelUp); + $path = preg_replace('|^/?(\.\./(\./)*)*|', '', $relativePath); + + return implode('/', $basePathSegments) . '/' . $path; + } + + /** + * Normalizes a URI path component by removing dot-slash and double slashes + * + * @param string $path + * + * @return string + */ + private static function normalizePath($path) + { + $path = preg_replace('|((?parse($uri); + + return !empty($components); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/Uri/UriRetriever.php b/json-schema-validator/tools/php/src/JsonSchema/Uri/UriRetriever.php new file mode 100644 index 0000000..ebb7eb3 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/Uri/UriRetriever.php @@ -0,0 +1,294 @@ + + */ +class UriRetriever implements BaseUriRetrieverInterface +{ + /** + * @var null|UriRetrieverInterface + */ + protected $uriRetriever = null; + + /** + * @var array|object[] + * + * @see loadSchema + */ + private $schemaCache = array(); + + /** + * Guarantee the correct media type was encountered + * + * @param UriRetrieverInterface $uriRetriever + * @param string $uri + * + * @return bool|void + */ + public function confirmMediaType($uriRetriever, $uri) + { + $contentType = $uriRetriever->getContentType(); + + if (is_null($contentType)) { + // Well, we didn't get an invalid one + return; + } + + if (in_array($contentType, array(Validator::SCHEMA_MEDIA_TYPE, 'application/json'))) { + return; + } + + if (substr($uri, 0, 23) == 'http://json-schema.org/') { + //HACK; they deliver broken content types + return true; + } + + throw new InvalidSchemaMediaTypeException(sprintf('Media type %s expected', Validator::SCHEMA_MEDIA_TYPE)); + } + + /** + * Get a URI Retriever + * + * If none is specified, sets a default FileGetContents retriever and + * returns that object. + * + * @return UriRetrieverInterface + */ + public function getUriRetriever() + { + if (is_null($this->uriRetriever)) { + $this->setUriRetriever(new FileGetContents()); + } + + return $this->uriRetriever; + } + + /** + * Resolve a schema based on pointer + * + * URIs can have a fragment at the end in the format of + * #/path/to/object and we are to look up the 'path' property of + * the first object then the 'to' and 'object' properties. + * + * @param object $jsonSchema JSON Schema contents + * @param string $uri JSON Schema URI + * + * @throws ResourceNotFoundException + * + * @return object JSON Schema after walking down the fragment pieces + */ + public function resolvePointer($jsonSchema, $uri) + { + $resolver = new UriResolver(); + $parsed = $resolver->parse($uri); + if (empty($parsed['fragment'])) { + return $jsonSchema; + } + + $path = explode('/', $parsed['fragment']); + while ($path) { + $pathElement = array_shift($path); + if (!empty($pathElement)) { + $pathElement = str_replace('~1', '/', $pathElement); + $pathElement = str_replace('~0', '~', $pathElement); + if (!empty($jsonSchema->$pathElement)) { + $jsonSchema = $jsonSchema->$pathElement; + } else { + throw new ResourceNotFoundException( + 'Fragment "' . $parsed['fragment'] . '" not found' + . ' in ' . $uri + ); + } + + if (!is_object($jsonSchema)) { + throw new ResourceNotFoundException( + 'Fragment part "' . $pathElement . '" is no object ' + . ' in ' . $uri + ); + } + } + } + + return $jsonSchema; + } + + /** + * {@inheritdoc} + */ + public function retrieve($uri, $baseUri = null) + { + $resolver = new UriResolver(); + $resolvedUri = $fetchUri = $resolver->resolve($uri, $baseUri); + + //fetch URL without #fragment + $arParts = $resolver->parse($resolvedUri); + if (isset($arParts['fragment'])) { + unset($arParts['fragment']); + $fetchUri = $resolver->generate($arParts); + } + + $jsonSchema = $this->loadSchema($fetchUri); + + // Use the JSON pointer if specified + $jsonSchema = $this->resolvePointer($jsonSchema, $resolvedUri); + + if ($jsonSchema instanceof \stdClass) { + $jsonSchema->id = $resolvedUri; + } + + return $jsonSchema; + } + + /** + * Fetch a schema from the given URI, json-decode it and return it. + * Caches schema objects. + * + * @param string $fetchUri Absolute URI + * + * @return object JSON schema object + */ + protected function loadSchema($fetchUri) + { + if (isset($this->schemaCache[$fetchUri])) { + return $this->schemaCache[$fetchUri]; + } + + $uriRetriever = $this->getUriRetriever(); + $contents = $this->uriRetriever->retrieve($fetchUri); + $this->confirmMediaType($uriRetriever, $fetchUri); + $jsonSchema = json_decode($contents); + + if (JSON_ERROR_NONE < $error = json_last_error()) { + throw new JsonDecodingException($error); + } + + $this->schemaCache[$fetchUri] = $jsonSchema; + + return $jsonSchema; + } + + /** + * Set the URI Retriever + * + * @param UriRetrieverInterface $uriRetriever + * + * @return $this for chaining + */ + public function setUriRetriever(UriRetrieverInterface $uriRetriever) + { + $this->uriRetriever = $uriRetriever; + + return $this; + } + + /** + * Parses a URI into five main components + * + * @param string $uri + * + * @return array + */ + public function parse($uri) + { + preg_match('|^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?|', $uri, $match); + + $components = array(); + if (5 < count($match)) { + $components = array( + 'scheme' => $match[2], + 'authority' => $match[4], + 'path' => $match[5] + ); + } + + if (7 < count($match)) { + $components['query'] = $match[7]; + } + + if (9 < count($match)) { + $components['fragment'] = $match[9]; + } + + return $components; + } + + /** + * Builds a URI based on n array with the main components + * + * @param array $components + * + * @return string + */ + public function generate(array $components) + { + $uri = $components['scheme'] . '://' + . $components['authority'] + . $components['path']; + + if (array_key_exists('query', $components)) { + $uri .= $components['query']; + } + + if (array_key_exists('fragment', $components)) { + $uri .= $components['fragment']; + } + + return $uri; + } + + /** + * Resolves a URI + * + * @param string $uri Absolute or relative + * @param string $baseUri Optional base URI + * + * @return string + */ + public function resolve($uri, $baseUri = null) + { + $components = $this->parse($uri); + $path = $components['path']; + + if ((array_key_exists('scheme', $components)) && ('http' === $components['scheme'])) { + return $uri; + } + + $baseComponents = $this->parse($baseUri); + $basePath = $baseComponents['path']; + + $baseComponents['path'] = UriResolver::combineRelativePathWithBasePath($path, $basePath); + + return $this->generate($baseComponents); + } + + /** + * @param string $uri + * + * @return bool + */ + public function isValid($uri) + { + $components = $this->parse($uri); + + return !empty($components); + } +} diff --git a/json-schema-validator/tools/php/src/JsonSchema/UriResolverInterface.php b/json-schema-validator/tools/php/src/JsonSchema/UriResolverInterface.php new file mode 100644 index 0000000..4eb50c0 --- /dev/null +++ b/json-schema-validator/tools/php/src/JsonSchema/UriResolverInterface.php @@ -0,0 +1,26 @@ + + * @author Bruno Prieto Reis + * + * @see README.md + */ +class Validator extends BaseConstraint +{ + const SCHEMA_MEDIA_TYPE = 'application/schema+json'; + + /** + * Validates the given data against the schema and returns an object containing the results + * Both the php object and the schema are supposed to be a result of a json_decode call. + * The validation works as defined by the schema proposal in http://json-schema.org. + * + * Note that the first argument is passwd by reference, so you must pass in a variable. + * + * {@inheritdoc} + */ + public function validate(&$value, $schema = null, $checkMode = null) + { + $initialCheckMode = $this->factory->getConfig(); + if ($checkMode !== null) { + $this->factory->setConfig($checkMode); + } + + $validator = $this->factory->createInstanceFor('schema'); + $validator->check($value, $schema); + + $this->factory->setConfig($initialCheckMode); + + $this->addErrors(array_unique($validator->getErrors(), SORT_REGULAR)); + } + + /** + * Alias to validate(), to maintain backwards-compatibility with the previous API + */ + public function check($value, $schema) + { + return $this->validate($value, $schema); + } + + /** + * Alias to validate(), to maintain backwards-compatibility with the previous API + */ + public function coerce(&$value, $schema) + { + return $this->validate($value, $schema, Constraint::CHECK_MODE_COERCE_TYPES); + } +}