diff --git a/LICENSE b/LICENSE index bf3bc9c..b98d9b9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,8 @@ -The contents of the Rx repository are copyright (C) 2008, Ricardo SIGNES. +The contents of the Rx repository are released under the GNU General Public License + +* Copyright (C) 2019, J Ginsberg (./php) +* Copyright (C) 2008, Ricardo SIGNES They may be distributed under the terms of the GNU General Public License, below. The Perl implementation, Data::Rx, may also be distributed under the diff --git a/php/.gitignore b/php/.gitignore new file mode 100644 index 0000000..c1376ba --- /dev/null +++ b/php/.gitignore @@ -0,0 +1 @@ +vendor/** \ No newline at end of file diff --git a/php/README.md b/php/README.md new file mode 100644 index 0000000..d4502b0 --- /dev/null +++ b/php/README.md @@ -0,0 +1,45 @@ +# Shaggy8871\Rx + +[![Author](https://img.shields.io/badge/author-@shaggy8871-blue.svg?style=flat-square)](https://twitter.com/johnginsberg) +[![Software License](https://img.shields.io/badge/license-GPL-brightgreen.svg?style=flat-square)](https://github.com/shaggy8871/Rx/blob/master/LICENSE) + +Based on https://github.com/rjbs/Rx with ideas from https://blog.picnic.nl/how-to-use-yaml-schema-to-validate-your-yaml-files-c82c049c2097 + +Standalone PHP version is available at https://github.com/shaggy8871/php-rx + +## What is Rx? + +When adding an API to your web service, you have to choose how to encode the +data you send across the line. XML is one common choice for this, but it can +grow arcane and cumbersome pretty quickly. Lots of webservice authors want to +avoid thinking about XML, and instead choose formats that provide a few simple +data types that correspond to common data structures in modern programming +languages. In other words, JSON and YAML. + +Unfortunately, while these formats make it easy to pass around complex data +structures, they lack a system for validation. XML has XML Schemas and RELAX +NG, but these are complicated and sometimes confusing standards. They're not +very portable to the kind of data structure provided by JSON, and if you wanted +to avoid XML as a data encoding, writing more XML to validate the first XML is +probably even less appealing. + +Rx is meant to provide a system for data validation that matches up with +JSON-style data structures and is as easy to work with as JSON itself. + +## Installation + +``` +composer require shaggy8871/php-rx +``` + +## Documentation + +[Check out the documentation](http://rx.codesimply.com/) + +## Usage + +* Create a schema file +* Run `./vendor/bin/rx [""]` +* Be sure to quote custom type glob! + +For example, `./vendor/bin/rx test.yml schema.yml "types/*.yml"` diff --git a/php/Rx.php b/php/Rx.php deleted file mode 100644 index 6231030..0000000 --- a/php/Rx.php +++ /dev/null @@ -1,599 +0,0 @@ -type_registry = new stdClass(); - $this->prefix_registry = array( - '' => 'tag:codesimply.com,2008:rx/core/', - '.meta' => 'tag:codesimply.com,2008:rx/meta/', - ); - - $core_types = Rx::core_types(); - - foreach ($core_types as $class_name) { - $uri = eval("return $class_name::uri;"); - $this->type_registry->$uri = $class_name; - } - } - - function expand_uri($name) { - if (preg_match('/^\w+:/', $name)) return $name; - - if (preg_match('/^\\/(.*?)\\/(.+)$/', $name, $matches)) { - if (! array_key_exists($matches[1], $this->prefix_registry)) { - throw new Exception("unknown type prefix '$matches[1]' in '$name'"); - } - - $uri = $this->prefix_registry[ $matches[1] ] . $matches[2]; - return $uri; - } - - throw new Exception("couldn't understand type name $name"); - } - - function core_types () { - static $core_types = array();; - - if (count($core_types)) return $core_types; - - $core_types = array( - 'RxCoretypeAll', - 'RxCoretypeAny', - 'RxCoretypeArr', - 'RxCoretypeBool', - 'RxCoretypeDef', - 'RxCoretypeFail', - 'RxCoretypeInt', - 'RxCoretypeMap', - 'RxCoretypeNil', - 'RxCoretypeNum', - 'RxCoretypeOne', - 'RxCoretypeRec', - 'RxCoretypeSeq', - 'RxCoretypeStr', - ); - - return $core_types; - } - - function add_prefix($name, $base) { - if (isset($this->prefix_registry[$name])) - throw new Exception("the prefix '$name' is already registered"); - - $this->prefix_registry[$name] = $base; - } - - function learn_type($uri, $schema) { - if (isset($this->type_registry->$uri)) - throw new Exception("tried to learn type for already-registered uri $uri"); - - # make sure schema is valid - $this->make_schema($schema); - - $this->type_registry->$uri = array( 'schema' => $schema ); - } - - function make_schema($schema) { - if (! is_object($schema)) { - $schema_name = $schema; - $schema = new stdClass(); - $schema->type = $schema_name; - } - - if (empty($schema->type)) - throw new Exception("can't make a schema with no type"); - - $uri = $this->expand_uri($schema->type); - - if (! isset($this->type_registry->$uri)) - throw new Exception("unknown type: $uri"); - - $type_class = $this->type_registry->$uri; - - if (is_array($type_class)) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception('composed type does not take check arguments'); - return $this->make_schema($type_class['schema']); - } elseif ($type_class) { - return new $type_class($schema, $this); - } - - return false; - } -} - -class RxCoretypeAll { - const uri = 'tag:codesimply.com,2008:rx/core/all'; - - var $alts; - - function check($value) { - foreach ($this->alts as $alt) if (! $alt->check($value)) return false; - return true; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'of' and $key != 'type') - throw new Exception("unknown parameter $key for //all schema"); - } - - function __construct($schema, $rx) { - RxCoretypeAll::_check_schema($schema); - - if (empty($schema->of)) - throw new Exception("no alternatives given for //all of"); - - $this->alts = Array(); - foreach ($schema->of as $alt) - array_push($this->alts, $rx->make_schema($alt)); - } -} - -class RxCoretypeAny { - const uri = 'tag:codesimply.com,2008:rx/core/any'; - - var $alts; - - function check($value) { - if ($this->alts == null) return true; - foreach ($this->alts as $alt) if ($alt->check($value)) return true; - return false; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'of' and $key != 'type') - throw new Exception("unknown parameter $key for //any schema"); - } - - function __construct($schema, $rx) { - RxCoretypeAny::_check_schema($schema); - - if (property_exists($schema, 'of')) { - if (count($schema->of) == 0) - throw new Exception("no alternatives given for //any of"); - - $this->alts = Array(); - foreach ($schema->of as $alt) - array_push($this->alts, $rx->make_schema($alt)); - } - } -} - -class RxCoretypeBool { - const uri = 'tag:codesimply.com,2008:rx/core/bool'; - function check($value) { return is_bool($value); } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception("unknown parameter $key for //bool schema"); - } - - function __construct($schema, $rx) { - RxCoretypeBool::_check_schema($schema); - } -} - -class RxCoreTypeArr { - const uri = 'tag:codesimply.com,2008:rx/core/arr'; - - var $content_schema; - var $length_checker; - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'contents' and $key != 'length' and $key != 'type') - throw new Exception("unknown parameter $key for //arr schema"); - } - - function __construct($schema, $rx) { - RxCoretypeArr::_check_schema($schema); - - if (empty($schema->contents)) - throw new Exception('no contents entry for //arr schema'); - - $this->content_schema = $rx->make_schema($schema->contents); - - if (isset($schema->length)) { - $this->length_checker = new RxRangeChecker( $schema->length ); - } - } - - function check($value) { - if (! RxUtil::is_seq_int_array($value)) return false; - - if ($this->length_checker) - if (! $this->length_checker->check(count($value))) return false; - - foreach ($value as $i => $entry) { - if (! $this->content_schema->check($entry)) return false; - } - - return true; - } -} - -class RxCoreTypeNum { - const uri = 'tag:codesimply.com,2008:rx/core/num'; - - var $range_checker; - var $fixed_value; - - function check($value) { - if (! (is_int($value) or is_float($value))) return false; - - if ($this->fixed_value !== null) { - if ($value != $this->fixed_value) - return false; - } - - if ($this->range_checker and ! $this->range_checker->check($value)) - return false; - - return true; - } - - function _check_schema($schema, $type) { - foreach ($schema as $key => $entry) - if ($key != 'range' and $key != 'type' and $key != 'value') - throw new Exception("unknown parameter $key for $type schema"); - } - - function __construct ($schema) { - RxCoretypeNum::_check_schema($schema, '//num'); - - if (isset($schema->value)) { - if (! (is_int($schema->value) || is_float($schema->value))) - throw new Exception('invalid value for //num schema'); - - $this->fixed_value = $schema->value; - } - - if (isset($schema->range)) { - $this->range_checker = new RxRangeChecker( $schema->range ); - } - } -} - -class RxCoreTypeInt { - const uri = 'tag:codesimply.com,2008:rx/core/int'; - - var $range_checker; - var $fixed_value; - - function check($value) { - if (! (is_int($value) || is_float($value))) return false; - if (is_float($value) and $value != floor($value)) return false; - - if ($this->fixed_value !== null) { - if ($value != $this->fixed_value) - return false; - } - - if ($this->range_checker and ! $this->range_checker->check($value)) - return false; - - return true; - } - - function __construct ($schema) { - RxCoretypeNum::_check_schema($schema, '//int'); - - if (isset($schema->value)) { - if (! (is_int($schema->value) || is_float($schema->value))) - throw new Exception('invalid value for //int schema'); - - if (is_float($schema->value) and $schema->value != floor($schema->value)) - throw new Exception('invalid value for //int schema'); - - $this->fixed_value = $schema->value; - } - - if (isset($schema->range)) { - $this->range_checker = new RxRangeChecker( $schema->range ); - } - } -} - -class RxCoretypeDef { - const uri = 'tag:codesimply.com,2008:rx/core/def'; - function check($value) { return ! is_null($value); } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception("unknown parameter $key for //def schema"); - } - - function __construct($schema, $rx) { - RxCoretypeDef::_check_schema($schema); - } -} - -class RxCoretypeFail { - const uri = 'tag:codesimply.com,2008:rx/core/fail'; - function check($value) { return false; } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception("unknown parameter $key for //fail schema"); - } - - function __construct($schema, $rx) { - RxCoretypeFail::_check_schema($schema); - } -} - -class RxCoretypeNil { - const uri = 'tag:codesimply.com,2008:rx/core/nil'; - function check($value) { return is_null($value); } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception("unknown parameter $key for //nil schema"); - } - - function __construct($schema, $rx) { - RxCoretypeNil::_check_schema($schema); - } -} - -class RxCoretypeOne { - const uri = 'tag:codesimply.com,2008:rx/core/one'; - function check($value) { return is_scalar($value); } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'type') - throw new Exception("unknown parameter $key for //one schema"); - } - - function __construct($schema, $rx) { - RxCoretypeOne::_check_schema($schema); - } -} - -class RxCoretypeStr { - const uri = 'tag:codesimply.com,2008:rx/core/str'; - var $fixed_value; - var $length_checker; - - function check($value) { - if (! is_string($value)) return false; - if ($this->fixed_value !== null and $value != $this->fixed_value) - return false; - - if ($this->length_checker) - if (! $this->length_checker->check(strlen($value))) return false; - - return true; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'value' and $key != 'type' and $key != 'length') - throw new Exception("unknown parameter $key for //str schema"); - } - - function __construct($schema, $rx) { - RxCoretypeStr::_check_schema($schema); - - if (isset($schema->value)) { - if (! is_string($schema->value)) - throw new Exception('invalid value for //str schema'); - - $this->fixed_value = $schema->value; - } - - if (isset($schema->length)) { - $this->length_checker = new RxRangeChecker( $schema->length ); - } - } -} - -class RxCoretypeSeq { - const uri = 'tag:codesimply.com,2008:rx/core/seq'; - - var $content_schemata; - var $tail_schema; - - function check($value) { - if (! RxUtil::is_seq_int_array($value)) return false; - - foreach ($this->content_schemata as $i => $schema) { - if (! array_key_exists($i, $value)) return false; - if (! $schema->check($value[$i])) return false; - } - - if (count($value) > count($this->content_schemata)) { - if (! $this->tail_schema) return false; - - $tail = array_slice( - $value, - count($this->content_schemata), - count($value) - count($this->content_schemata) - ); - - if (! $this->tail_schema->check($tail)) return false; - } - - return true; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'contents' and $key != 'tail' and $key != 'type') - throw new Exception("unknown parameter $key for //arr schema"); - } - - function __construct($schema, $rx) { - RxCoretypeSeq::_check_schema($schema); - - if (! isset($schema->contents)) - throw new Exception('no contents entry for //seq schema'); - - if (! is_array($schema->contents)) - throw new Exception('contents entry for //seq schema is not an array'); - - $this->content_schemata = array(); - - foreach ($schema->contents as $i => $entry) { - array_push($this->content_schemata, $rx->make_schema($entry)); - } - - if (isset($schema->tail)) - $this->tail_schema = $rx->make_schema($schema->tail); - } -} - -class RxCoretypeMap { - const uri = 'tag:codesimply.com,2008:rx/core/map'; - - var $values_schema; - - function check($value) { - if (!is_object($value) || get_class($value) != 'stdClass') return false; - - if ($this->values_schema) { - foreach ($value as $key => $entry) { - if (! $this->values_schema->check($entry)) return false; - } - } - - return true; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'values' and $key != 'type') - throw new Exception("unknown parameter $key for //map schema"); - } - - function __construct($schema, $rx) { - RxCoretypeMap::_check_schema($schema); - - if ($schema->values) - $this->values_schema = $rx->make_schema($schema->values); - } -} - -class RxCoretypeRec { - const uri = 'tag:codesimply.com,2008:rx/core/rec'; - - var $required; - var $optional; - var $known; - var $rest_schema; - - function check($value) { - if (!is_object($value) || get_class($value) != 'stdClass') return false; - - $rest = new stdClass(); - $have_rest = false; - - foreach ($value as $key => $entry) { - if (! isset($this->known->$key)) { - $have_rest = true; - $rest->$key = $entry; - } - } - - if ($have_rest and ! $this->rest_schema) return false; - - foreach ($this->required as $key => $schema) { - if (! property_exists($value, $key)) return false; - if (! $schema->check($value->$key)) return false; - } - - foreach ($this->optional as $key => $schema) { - if (! property_exists($value, $key)) continue; - if (! $schema->check($value->$key)) return false; - } - - if ($have_rest and ! $this->rest_schema->check($rest)) return false; - - return true; - } - - function _check_schema($schema) { - foreach ($schema as $key => $entry) - if ($key != 'optional' and $key != 'required' and $key != 'rest' and $key != 'type') - throw new Exception("unknown parameter $key for //rec schema"); - } - - function __construct($schema, $rx) { - RxCoretypeRec::_check_schema($schema); - - $this->known = new stdClass(); - $this->required = new stdClass(); - $this->optional = new stdClass(); - - if (isset($schema->rest)) - $this->rest_schema = $rx->make_schema($schema->rest); - - if (isset($schema->required)) { - foreach ($schema->required as $key => $entry) { - $this->known->$key = true; - $this->required->$key = $rx->make_schema($entry); - } - } - - if (isset($schema->optional)) { - foreach ($schema->optional as $key => $entry) { - if (isset($this->known->$key)) - throw new Exception("$key is both required and optional in //map"); - - $this->known->$key = true; - $this->optional->$key = $rx->make_schema($entry); - } - } - } -} - -class RxUtil { - function is_seq_int_array($value) { - if (! is_array($value)) return false; - - for ($i = 0; $i < count($value); $i++) - if (! array_key_exists($i, $value)) return false; - - return true; - } -} - -class RxRangeChecker { - var $min; - var $min_ex; - var $max_ex; - var $max; - - function __construct ($arg) { - $valid_names = array('min', 'max', 'min-ex', 'max-ex'); - - foreach ($valid_names as $name) { - if (! property_exists($arg, $name)) continue; - - $prop_name = preg_replace('/-/', '_', $name); - $this->$prop_name = $arg->$name; - } - } - - function check($value) { - if (! is_null($this->min) and $value < $this->min ) return false; - if (! is_null($this->min_ex) and $value <= $this->min_ex) return false; - if (! is_null($this->max_ex) and $value >= $this->max_ex) return false; - if (! is_null($this->max) and $value > $this->max ) return false; - - return true; - } -} - -?> diff --git a/php/composer.json b/php/composer.json new file mode 100644 index 0000000..ee54005 --- /dev/null +++ b/php/composer.json @@ -0,0 +1,27 @@ +{ + "name": "shaggy8871/rx", + "description": "The Rx schema and validation system", + "type": "library", + "license": "GPL-3.0-or-later", + "minimum-stability": "stable", + "require": { + "php": ">=7.1.0", + "symfony/yaml": "^4.3" + }, + "require-dev": { + "phpunit/phpunit": "^8" + }, + "autoload": { + "psr-4": { + "Rx\\": "src/Rx/" + } + }, + "autoload-dev": { + "psr-4": { + "Test\\": "test/" + } + }, + "bin": [ + "rx" + ] +} diff --git a/php/composer.lock b/php/composer.lock new file mode 100644 index 0000000..e98f488 --- /dev/null +++ b/php/composer.lock @@ -0,0 +1,1551 @@ +{ + "_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#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "8bad5224a9f18bacd7b1b77b3fc4d162", + "packages": [ + { + "name": "symfony/polyfill-ctype", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "symfony/yaml", + "version": "v4.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "c60ecf5ba842324433b46f58dc7afc4487dbab99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c60ecf5ba842324433b46f58dc7afc4487dbab99", + "reference": "c60ecf5ba842324433b46f58dc7afc4487dbab99", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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": "2019-04-06T14:04:46+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "a2c590166b2133a4633738648b6b064edae0814a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", + "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.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://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2019-03-17T17:37:11+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2019-04-07T13:18:21+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "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": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08T19:23:20+00:00" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08T19:19:57+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "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": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.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" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2019-04-30T17:48:53+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "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": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.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": "2018-08-05T17:53:17+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "7.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aed67b57d459dcab93e84a5c9703d3deb5025dff", + "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.2", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^4.1", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-xdebug": "^2.6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.0-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": "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": "2019-06-06T12:28:18+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "050bedf145a257b1ff02746c31894800e5122946" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.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": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2018-09-13T20:33:42+00:00" + }, + { + "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-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-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": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-02-20T10:12:59+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-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": "2018-10-30T05:52:18+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "8.1.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e3c9da6e645492c461e0a11eca117f83f4f4c840", + "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.2", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^7.0", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^3.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/version": "^2.0.1" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.1-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": "2019-05-28T11:53:42+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "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": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-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": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2018-07-12T15:12:46+00:00" + }, + { + "name": "sebastian/diff", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-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", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-02-04T06:01:07+00:00" + }, + { + "name": "sebastian/environment", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-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": "2019-05-05T09:05:15+00:00" + }, + { + "name": "sebastian/exporter", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.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": "2017-04-03T13:19:02+00:00" + }, + { + "name": "sebastian/global-state", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "shasum": "" + }, + "require": { + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.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": "2019-02-01T05:30:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-08-03T12:35:26+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.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": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.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": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-04-04T09:56:43+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "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": "2018-12-25T11:19:39+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/php/rx b/php/rx new file mode 100755 index 0000000..0095c6f --- /dev/null +++ b/php/rx @@ -0,0 +1,61 @@ +#!/usr/bin/env php + [\"\"]\n"; + die(); +} +try { + $credentialsFile = Yaml::parse(file_get_contents($argv[1]), Yaml::PARSE_OBJECT_FOR_MAP); +} catch (Exception $e) { + echo " ❌ Unable to parse yaml/json file.\n"; + echo " {$e->getMessage()}\n"; + die(); +} +try { + $schemaFile = Yaml::parse(file_get_contents($argv[2]), Yaml::PARSE_OBJECT_FOR_MAP); +} catch (Exception $e) { + echo " ❌ Unable to parse schema file.\n"; + echo " {$e->getMessage()}\n"; + die(); +} + +$rx = new Rx\Rx(); + +// Add custom types +if ($argv[3]) { + foreach (glob($argv[3]) as $type) { + try { + $typeFile = Yaml::parse(file_get_contents($type), Yaml::PARSE_OBJECT_FOR_MAP); + $rx->learnType($typeFile->uri, $typeFile->schema); + echo " ✅ Added {$typeFile->uri}.\n"; + } catch (Exception $e) { + echo " ❌ Unable to add custom types via glob.\n"; + echo " {$e->getMessage()}\n"; + die(); + } + } +} + +try { + $schema = $rx->makeSchema($schemaFile); + echo " ✅ Schema loaded successfully.\n"; +} catch (Exception $e) { + echo " ❌ An error occurred loading the schema.\n"; + echo " {$e->getMessage()}\n"; + die(); +} + +try { + $schema->check($credentialsFile); + echo " ✅ File is correct according to the schema.\n"; +} catch (Exception $e) { + echo " ❌ An error occurred validating the file against the schema.\n"; + echo " {$e->getMessage()}\n"; +} diff --git a/php/src/Rx/Core/CheckSchemaTrait.php b/php/src/Rx/Core/CheckSchemaTrait.php new file mode 100644 index 0000000..9512799 --- /dev/null +++ b/php/src/Rx/Core/CheckSchemaTrait.php @@ -0,0 +1,22 @@ + $entry) { + if (!in_array($key, static::VALID_PARAMS)) { + throw new RxException(sprintf('Unknown key `%s` in %s %s.', $key, Util::formatPropName($this->propName), static::TYPE)); + } + } + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/All.php b/php/src/Rx/Core/Type/All.php new file mode 100644 index 0000000..7a4bdec --- /dev/null +++ b/php/src/Rx/Core/Type/All.php @@ -0,0 +1,57 @@ +of)) { + throw new NoAlternativesGivenException(sprintf("No `of` given in %s %s.", Util::formatPropName($this->propName), static::TYPE)); + } + + foreach ($schema->of as $alt) { + $this->alts[] = $rx->makeSchema($alt, $propName); + } + + } + + public function check($value): bool + { + + foreach ($this->alts as $alt) { + $alt->check($value); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Any.php b/php/src/Rx/Core/Type/Any.php new file mode 100644 index 0000000..2486229 --- /dev/null +++ b/php/src/Rx/Core/Type/Any.php @@ -0,0 +1,67 @@ +of)) { + throw new NoAlternativesGivenException(sprintf("No `of` given in %s %s.", Util::formatPropName($this->propName), static::TYPE)); + } + foreach ($schema->of as $alt) { + $this->alts[] = $rx->makeSchema($alt, $propName); + } + } + } + + public function check($value): bool + { + + if (empty($this->alts)) { + return true; + } + + foreach ($this->alts as $alt) { + try { + if ($alt->check($value)) { + return true; + } + } catch (CheckFailedException $e) { + // ignore + } + } + + throw new CheckFailedException(sprintf('Values in %s %s do not match any `of`.', Util::formatPropName($this->propName), static::TYPE)); + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Arr.php b/php/src/Rx/Core/Type/Arr.php new file mode 100644 index 0000000..0844cc3 --- /dev/null +++ b/php/src/Rx/Core/Type/Arr.php @@ -0,0 +1,72 @@ +contents)) { + throw new MissingParamException(sprintf('No `contents` key found for %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + $this->contentSchema = $rx->makeSchema($schema->contents, $propName); + + if (isset($schema->length)) { + $this->lengthChecker = new RangeChecker($schema->length); + } + + } + + public function check($value): bool + { + + if (! Util::isSeqIntArray($value)) { + throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + if ($this->lengthChecker) { + if (! $this->lengthChecker->check(count($value))) { + throw new CheckFailedException(sprintf('Array length check fails in %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + } + + foreach ($value as $i => $entry) { + $this->contentSchema->check($entry); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Boolean.php b/php/src/Rx/Core/Type/Boolean.php new file mode 100644 index 0000000..17ec191 --- /dev/null +++ b/php/src/Rx/Core/Type/Boolean.php @@ -0,0 +1,36 @@ +propName), static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Def.php b/php/src/Rx/Core/Type/Def.php new file mode 100644 index 0000000..2e4170b --- /dev/null +++ b/php/src/Rx/Core/Type/Def.php @@ -0,0 +1,36 @@ +propName), static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Fail.php b/php/src/Rx/Core/Type/Fail.php new file mode 100644 index 0000000..a2bc444 --- /dev/null +++ b/php/src/Rx/Core/Type/Fail.php @@ -0,0 +1,29 @@ +values)) { + $this->valuesSchema = $rx->makeSchema($schema->values, $propName); + } + + } + + public function check($value): bool + { + + if (!is_object($value) || get_class($value) != 'stdClass') { + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), Util::formatPropName($this->propName), static::TYPE)); + } + + if ($this->valuesSchema) { + foreach ($value as $key => $entry) { + $this->valuesSchema->check($entry); + } + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Nil.php b/php/src/Rx/Core/Type/Nil.php new file mode 100644 index 0000000..42994f7 --- /dev/null +++ b/php/src/Rx/Core/Type/Nil.php @@ -0,0 +1,36 @@ +propName), static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Num.php b/php/src/Rx/Core/Type/Num.php new file mode 100644 index 0000000..33a049e --- /dev/null +++ b/php/src/Rx/Core/Type/Num.php @@ -0,0 +1,79 @@ +value)) { + if (! (is_int($schema->value) || is_float($schema->value))) { + throw new InvalidParamTypeException(sprintf('The `value` param for %s schema is not an int or float.', static::TYPE)); + } + if (static::TYPE == '//int' && is_float($schema->value) && $schema->value != floor($schema->value)) { + throw new InvalidParamTypeException(sprintf('The `value` param for %s schema is not an int', static::TYPE)); + } + $this->fixedValue = $schema->value; + } + + if (isset($schema->range)) { + $this->rangeChecker = new RangeChecker($schema->range); + } + + } + + public function check($value): bool + { + + if (! (is_int($value) || is_float($value))) { + throw new CheckFailedException(sprintf('Expected int/float, got %s in %s %s.', gettype($value), Util::formatPropName($this->propName), static::TYPE)); + } + if (static::TYPE == '//int' && is_float($value) && $value != floor($value)) { + throw new CheckFailedException(sprintf('Key %s is not of type %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + if ($this->fixedValue !== null) { + if ($value != $this->fixedValue) { + throw new CheckFailedException(sprintf('Value \'%s\' does not equal \'%s\' in %s %s.', strval($value), strval($this->fixedValue), Util::formatPropName($this->propName), static::TYPE)); + } + } + + if ($this->rangeChecker && ! $this->rangeChecker->check($value)) { + throw new CheckFailedException(sprintf('Range check fails in %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/One.php b/php/src/Rx/Core/Type/One.php new file mode 100644 index 0000000..682cdea --- /dev/null +++ b/php/src/Rx/Core/Type/One.php @@ -0,0 +1,36 @@ +propName), static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Rec.php b/php/src/Rx/Core/Type/Rec.php new file mode 100644 index 0000000..a8675a4 --- /dev/null +++ b/php/src/Rx/Core/Type/Rec.php @@ -0,0 +1,111 @@ +required = new \stdClass(); + $this->optional = new \stdClass(); + $this->known = new \stdClass(); + + if (isset($schema->rest)) { + $this->restSchema = $rx->makeSchema($schema->rest, $propName); + } + + if (isset($schema->required)) { + foreach ($schema->required as $key => $entry) { + $this->known->$key = true; + $this->required->$key = $rx->makeSchema($entry, ($propName ? $propName . '->' : '') . $key); + } + } + + if (isset($schema->optional)) { + foreach ($schema->optional as $key => $entry) { + if (isset($this->known->$key)) { + throw new RequiredAndOptionalException(sprintf('`%s` is both required and optional in %s %s', $key, Util::formatPropName($this->propName), static::TYPE)); + } + $this->known->$key = true; + $this->optional->$key = $rx->makeSchema($entry, ($propName ? $propName . '->' : '') . $key); + } + } + + } + + public function check($value): bool + { + + if (!is_object($value) || get_class($value) != 'stdClass') { + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), Util::formatPropName($this->propName), static::TYPE)); + } + + $rest = new \stdClass(); + $haveRest = false; + + foreach ($value as $key => $entry) { + if (! isset($this->known->$key)) { + $haveRest = true; + $rest->$key = $entry; + } + } + + if ($haveRest && ! $this->restSchema) { + throw new CheckFailedException(sprintf('Invalid keys [%s] found in %s %s.', implode(', ', array_keys(get_object_vars($rest))), Util::formatPropName($this->propName), static::TYPE)); + } + + foreach ($this->required as $key => $schema) { + if (! property_exists($value, $key)) { + throw new CheckFailedException(sprintf('Value for `%s` not found in `required` of %s %s.', strval($key), Util::formatPropName($this->propName), static::TYPE)); + } + $schema->check($value->$key); + } + + foreach ($this->optional as $key => $schema) { + if (! property_exists($value, $key)) { + continue; + } + $schema->check($value->$key); + } + + if ($haveRest) { + $this->restSchema->check($rest); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Seq.php b/php/src/Rx/Core/Type/Seq.php new file mode 100644 index 0000000..d97024e --- /dev/null +++ b/php/src/Rx/Core/Type/Seq.php @@ -0,0 +1,91 @@ +contents)) { + throw new MissingParamException(sprintf('No `contents` key for %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + if (! is_array($schema->contents)) { + throw new InvalidParamTypeException(sprintf('The `contents` for %s %s is not an array.', Util::formatPropName($this->propName), static::TYPE)); + } + + $this->contentSchemata = []; + + foreach ($schema->contents as $i => $entry) { + $this->contentSchemata[] = $rx->makeSchema($entry, $propName . '->seq#' . $i); + } + + if (isset($schema->tail)) { + $this->tailSchema = $rx->makeSchema($schema->tail, $propName . '->tail'); + } + + } + + public function check($value): bool + { + + if (! Util::isSeqIntArray($value)) { + throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + foreach ($this->contentSchemata as $i => $schema) { + if (! array_key_exists($i, $value)) { + throw new CheckFailedException(sprintf('Value for `%s` not found in `contents` of %s %s.', strval($i), Util::formatPropName($this->propName), static::TYPE)); + } + $schema->check($value[$i]); + } + + if (count($value) > count($this->contentSchemata)) { + if (! $this->tailSchema) { + throw new CheckFailedException(sprintf('Key `tail` missing, or invalid length of %s %s.', Util::formatPropName($this->propName), static::TYPE)); + } + + $tail = array_slice( + $value, + count($this->contentSchemata), + count($value) - count($this->contentSchemata) + ); + + $this->tailSchema->check($tail); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Str.php b/php/src/Rx/Core/Type/Str.php new file mode 100644 index 0000000..f4dfa15 --- /dev/null +++ b/php/src/Rx/Core/Type/Str.php @@ -0,0 +1,71 @@ +value)) { + if (! is_string($schema->value)) { + throw new InvalidParamTypeException(sprintf('The `value` for %s %s is not a string.', Util::formatPropName($this->propName), static::TYPE)); + } + + $this->fixedValue = $schema->value; + } + + if (isset($schema->length)) { + $this->lengthChecker = new RangeChecker($schema->length); + } + + } + + public function check($value): bool + { + + if (! is_string($value)) { + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + } + if ($this->fixedValue !== null && $value != $this->fixedValue) { + throw new CheckFailedException(sprintf('\'%s\' does not equal value \'%s\' in `%s` %s.', strval($value), strval($this->fixedValue), $this->propName, static::TYPE)); + } + + if ($this->lengthChecker && ! $this->lengthChecker->check(strlen($value))) { + throw new CheckFailedException(sprintf('\'%s\' length check fails in `%s` %s.', strval($value), $this->propName, static::TYPE)); + } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/TypeAbstract.php b/php/src/Rx/Core/TypeAbstract.php new file mode 100644 index 0000000..1a585a7 --- /dev/null +++ b/php/src/Rx/Core/TypeAbstract.php @@ -0,0 +1,24 @@ +propName = $propName; + + $this->checkSchema($schema); + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/TypeInterface.php b/php/src/Rx/Core/TypeInterface.php new file mode 100644 index 0000000..ac3781f --- /dev/null +++ b/php/src/Rx/Core/TypeInterface.php @@ -0,0 +1,11 @@ + 'min', 'max' => 'max', 'min-ex' => 'minEx', 'max-ex' => 'maxEx']; + + protected $min; + protected $minEx; + protected $maxEx; + protected $max; + + public function __construct(\stdClass $arg) + { + + foreach (self::VALID_ARGS as $name) { + if (! property_exists($arg, $name)) { + continue; + } + $this->{self::ARGS_PROPS[$name]} = $arg->$name; + } + + } + + public function check($value): bool + { + + if (! is_null($this->min) && $value < $this->min ) { return false; } + if (! is_null($this->minEx) && $value <= $this->minEx) { return false; } + if (! is_null($this->maxEx) && $value >= $this->maxEx) { return false; } + if (! is_null($this->max) && $value > $this->max ) { return false; } + + return true; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Rx.php b/php/src/Rx/Rx.php new file mode 100644 index 0000000..c773ca9 --- /dev/null +++ b/php/src/Rx/Rx.php @@ -0,0 +1,113 @@ + 'tag:codesimply.com,2008:rx/core/', + '.meta' => 'tag:codesimply.com,2008:rx/meta/', + ]; + + function __construct() + { + + $this->typeRegistry = new \stdClass(); + + foreach (self::CORE_TYPES as $className) { + $fullClassName = 'Rx\\Core\\Type\\' . $className; + $this->typeRegistry->{$fullClassName::URI} = $fullClassName; + } + + } + + private function expandUri(string $name): string + { + + if (preg_match('/^\w+:/', $name)) { + return $name; + } + + if (preg_match('/^\\/(.*?)\\/(.+)$/', $name, $matches)) { + if (! array_key_exists($matches[1], $this->prefixRegistry)) { + throw new RxException("Unknown type prefix '$matches[1]' in '$name'."); + } + $uri = $this->prefixRegistry[ $matches[1] ] . $matches[2]; + return $uri; + } + + throw new RxException("Couldn't understand type name '$name'."); + + } + + public function addPrefix(string $name, string $base): void + { + + if (isset($this->prefixRegistry[$name])) { + throw new RxException("The prefix '$name' is already registered."); + } + + $this->prefixRegistry[$name] = $base; + + } + + public function learnType(string $uri, \stdClass $schema): void + { + + if (isset($this->typeRegistry->$uri)) { + throw new RxException("Failed to learn type for already-registered uri $uri."); + } + + // Make sure schema is valid + $this->makeSchema($schema); + + $this->typeRegistry->$uri = ['schema' => $schema]; + + } + + public function makeSchema($schema, ?string $propName = null) + { + + if (! is_object($schema)) { + $schemaName = $schema; + $schema = new \stdClass(); + $schema->type = $schemaName; + } + + if (empty($schema->type)) { + throw new RxException(sprintf('Can\'t make a schema without a `type` key in %s.', Util::formatPropName($propName))); + } + + $uri = $this->expandUri($schema->type); + + if (! isset($this->typeRegistry->$uri)) { + throw new RxException(sprintf('Unknown type \'%s\' in %s.', $uri, Util::formatPropName($propName))); + } + + $typeClass = $this->typeRegistry->$uri; + + if (is_array($typeClass) && isset($typeClass['schema'])) { + foreach ($schema as $key => $entry) { + if ($key != 'type') { + throw new RxException(sprintf('Composed type does not take additional arguments in %s.', Util::formatPropName($propName))); + } + } + return $this->makeSchema($typeClass['schema']); + } elseif ($typeClass) { + return new $typeClass($schema, $this, $propName); + } + + return false; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Util.php b/php/src/Rx/Util.php new file mode 100644 index 0000000..78a67df --- /dev/null +++ b/php/src/Rx/Util.php @@ -0,0 +1,33 @@ +composedtype)) { try { - $Rx->learn_type($test->composedtype->uri, $test->composedtype->schema); + $Rx->learnType($test->composedtype->uri, $test->composedtype->schema); } catch (Exception $e) { if (isset($test->composedtype->invalid)) { pass("BAD COMPOSED TYPE: $schema_name"); @@ -112,12 +113,12 @@ function test_json($x, $y) { } if (isset($test->composedtype->prefix)) - $Rx->add_prefix($test->composedtype->prefix[0], + $Rx->addPrefix($test->composedtype->prefix[0], $test->composedtype->prefix[1]); } try { - $schema = $Rx->make_schema($test->schema); + $schema = $Rx->makeSchema($test->schema); } catch (Exception $e) { if (isset($test->invalid)) { pass("BAD SCHEMA: $schema_name"); @@ -144,8 +145,12 @@ function test_json($x, $y) { foreach ($entries as $name => $want) { $value = $test_data[$source]->$name; - $result = $schema->check($value); - if ($pf == 'fail') $result = ! $result; + try { + $result = $schema->check($value); + } catch (Exception $e) { + $result = false; + if ($pf == 'fail') $result = ! $result; + } if ("$source/$entry" == "num/0e0") todo_start("PHP's json_decode can't handle 0e0 as number"); diff --git a/php/ext/Test.php b/php/tests/Test.php similarity index 100% rename from php/ext/Test.php rename to php/tests/Test.php diff --git a/php/util-test.php b/php/tests/UtilTest.php similarity index 65% rename from php/util-test.php rename to php/tests/UtilTest.php index e3ede2f..ac9b076 100644 --- a/php/util-test.php +++ b/php/tests/UtilTest.php @@ -1,16 +1,17 @@ #!/usr/bin/php array('foo', 'bar', 'baz'), 'noelems' => array() ); -foreach ($test_arrays as $name => $value) { +foreach ($testArrays as $name => $value) { ok( - RxUtil::is_seq_int_array($value), + Rx\Util::isSeqIntArray($value), "test array $name is a seq_int_array" ); } @@ -22,7 +23,7 @@ foreach ($test_nonarrays as $name => $value) { ok( - ! RxUtil::is_seq_int_array($value), + ! Rx\Util::isSeqIntArray($value), "test array $name is not a seq_int_array" ); }