From 4826ad3cbac64534ad1f592ede684a6a4d570ffe Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Thu, 6 Jun 2019 18:14:06 +0100 Subject: [PATCH 1/7] Modernised and tests passing --- php/.gitignore | 1 + php/RxTest.php | 163 ++ php/composer.json | 21 + php/composer.lock | 1491 +++++++++++++++++ php/src/Rx/Core/CheckSchemaTrait.php | 19 + php/src/Rx/Core/Type/All.php | 51 + php/src/Rx/Core/Type/Any.php | 55 + php/src/Rx/Core/Type/Arr.php | 66 + php/src/Rx/Core/Type/Boolean.php | 37 + php/src/Rx/Core/Type/Def.php | 34 + php/src/Rx/Core/Type/Fail.php | 34 + php/src/Rx/Core/Type/Integer.php | 17 + php/src/Rx/Core/Type/Map.php | 53 + php/src/Rx/Core/Type/Nil.php | 34 + php/src/Rx/Core/Type/Num.php | 71 + php/src/Rx/Core/Type/One.php | 34 + php/src/Rx/Core/Type/Rec.php | 109 ++ php/src/Rx/Core/Type/Seq.php | 87 + php/src/Rx/Core/Type/Str.php | 65 + php/src/Rx/Core/TypeInterface.php | 8 + .../Exception/InvalidParamTypeException.php | 6 + .../Rx/Exception/MissingParamException.php | 6 + .../NoAlternativesGivenException.php | 6 + .../RequiredAndOptionalException.php | 6 + php/src/Rx/Exception/RxException.php | 6 + php/src/Rx/RangeChecker.php | 41 + php/src/Rx/Rx.php | 114 ++ php/src/Rx/Util.php | 24 + 28 files changed, 2659 insertions(+) create mode 100644 php/.gitignore create mode 100644 php/RxTest.php create mode 100644 php/composer.json create mode 100644 php/composer.lock create mode 100644 php/src/Rx/Core/CheckSchemaTrait.php create mode 100644 php/src/Rx/Core/Type/All.php create mode 100644 php/src/Rx/Core/Type/Any.php create mode 100644 php/src/Rx/Core/Type/Arr.php create mode 100644 php/src/Rx/Core/Type/Boolean.php create mode 100644 php/src/Rx/Core/Type/Def.php create mode 100644 php/src/Rx/Core/Type/Fail.php create mode 100644 php/src/Rx/Core/Type/Integer.php create mode 100644 php/src/Rx/Core/Type/Map.php create mode 100644 php/src/Rx/Core/Type/Nil.php create mode 100644 php/src/Rx/Core/Type/Num.php create mode 100644 php/src/Rx/Core/Type/One.php create mode 100644 php/src/Rx/Core/Type/Rec.php create mode 100644 php/src/Rx/Core/Type/Seq.php create mode 100644 php/src/Rx/Core/Type/Str.php create mode 100644 php/src/Rx/Core/TypeInterface.php create mode 100644 php/src/Rx/Exception/InvalidParamTypeException.php create mode 100644 php/src/Rx/Exception/MissingParamException.php create mode 100644 php/src/Rx/Exception/NoAlternativesGivenException.php create mode 100644 php/src/Rx/Exception/RequiredAndOptionalException.php create mode 100644 php/src/Rx/Exception/RxException.php create mode 100644 php/src/Rx/RangeChecker.php create mode 100644 php/src/Rx/Rx.php create mode 100644 php/src/Rx/Util.php 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/RxTest.php b/php/RxTest.php new file mode 100644 index 0000000..0441ff3 --- /dev/null +++ b/php/RxTest.php @@ -0,0 +1,163 @@ +#!/usr/bin/php + $v) { + if (is_array($v)) { + $new_v = new stdClass(); + + foreach ($v as $entry) { + $new_v->$entry = $entry; + } + + $test_data[$k] = $v = $new_v; + } + + foreach ($test_data[$k] as $entry => $json) { + $j_arr = json_decode("[ $json ]"); + $test_data[$k]->$entry = $j_arr[0]; + # diag("loaded $json ($k/$entry) as " . var_export($j_arr[0], true)); + } +} + +function normalize($entries, $test_data) { + if ($entries == '*') { + $entries = new stdClass(); + $key = "*"; + $entries->$key = null; + } + + if (is_array($entries)) { + $new_entries = new stdClass(); + foreach ($entries as $entry) { + $new_entries->$entry = $entry; + } + + $entries = $new_entries; + } + + if (count((array) $entries) == 1 and property_exists($entries, "*")) { + $key = "*"; + $value = $entries->$key; + $new_entries = new stdClass(); + foreach ($test_data as $name => $entry) { + $new_entries->$name = $entry; + } + $entries = $new_entries; + } + + return $entries; +} + +function test_json($x, $y) { + return false; +} + +asort($test_schemata); +foreach ($test_schemata as $schema_name => $test) { + if (isset($_ENV["RX_TEST_SCHEMA"]) and $_ENV["RX_TEST_SCHEMA"] != $schema_name) + continue; + + $Rx = new Rx\Rx(); + + $schema = null; + + if (isset($test->composedtype)) { + try { + $Rx->learnType($test->composedtype->uri, $test->composedtype->schema); + } catch (Exception $e) { + if (isset($test->composedtype->invalid)) { + pass("BAD COMPOSED TYPE: $schema_name"); + continue; + } else { + throw $e; + } + } + + if (isset($test->composedtype->invalid)) { + fail("BAD COMPOSED TYPE: $schema_name"); + continue; + } + + if (isset($test->composedtype->prefix)) + $Rx->addPrefix($test->composedtype->prefix[0], + $test->composedtype->prefix[1]); + } + + try { + $schema = $Rx->makeSchema($test->schema); + } catch (Exception $e) { + if (isset($test->invalid)) { + pass("BAD SCHEMA: $schema_name"); + continue; + } else { + throw $e; + } + } + + if (isset($test->invalid)) { + fail("BAD SCHEMA: $schema_name"); + continue; + } + + if (! $schema) die("did not get schema for valid input"); + + foreach (array('pass', 'fail') as $pf) { + $expect = ($pf == 'pass') ? 'VALID ' : 'INVALID'; + if (!isset($test->$pf)) continue; + + foreach ($test->$pf as $source => $entries) { + $entries = normalize($entries, $test_data[$source]); + + foreach ($entries as $name => $want) { + $value = $test_data[$source]->$name; + + $result = $schema->check($value); + if ($pf == 'fail') $result = ! $result; + + if ("$source/$entry" == "num/0e0") + todo_start("PHP's json_decode can't handle 0e0 as number"); + + ok($result, "$expect: $source/$name against $schema_name"); + + if ("$source/$entry" == "num/0e0") + todo_end(); + } + } + } +} + +?> diff --git a/php/composer.json b/php/composer.json new file mode 100644 index 0000000..4487bba --- /dev/null +++ b/php/composer.json @@ -0,0 +1,21 @@ +{ + "name": "shaggy8871/rx", + "description": "The Rx schema and validation system", + "type": "library", + "license": "GPL", + "minimum-stability": "stable", + "require": {}, + "require-dev": { + "phpunit/phpunit": "^8" + }, + "autoload": { + "psr-4": { + "Rx\\": "src/Rx/" + } + }, + "autoload-dev": { + "psr-4": { + "Test\\": "test/" + } + } +} diff --git a/php/composer.lock b/php/composer.lock new file mode 100644 index 0000000..1a67804 --- /dev/null +++ b/php/composer.lock @@ -0,0 +1,1491 @@ +{ + "_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": "cee5af0bbd85177084613b80c0e03c23", + "packages": [], + "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": "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": "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/src/Rx/Core/CheckSchemaTrait.php b/php/src/Rx/Core/CheckSchemaTrait.php new file mode 100644 index 0000000..c4906d2 --- /dev/null +++ b/php/src/Rx/Core/CheckSchemaTrait.php @@ -0,0 +1,19 @@ + $entry) { + if (!in_array($key, static::VALID_PARAMS)) { + throw new \Exception("Unknown parameter $key for $type schema"); + } + } + + } + +} \ 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..01c2390 --- /dev/null +++ b/php/src/Rx/Core/Type/All.php @@ -0,0 +1,51 @@ +checkSchema($schema, static::TYPE); + + if (empty($schema->of)) { + throw new NoAlternativesGivenException("No alternatives given for //all `of`"); + } + + foreach ($schema->of as $alt) { + $this->alts[] = $rx->makeSchema($alt); + } + + } + + public function check($value): bool + { + + foreach ($this->alts as $alt) { + if (! $alt->check($value)) { + return false; + } + } + 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..fe4c3da --- /dev/null +++ b/php/src/Rx/Core/Type/Any.php @@ -0,0 +1,55 @@ +checkSchema($schema, static::TYPE); + + if (property_exists($schema, 'of')) { + if (empty($schema->of)) { + throw new NoAlternativesGivenException("No alternatives given for //any `of`"); + } + foreach ($schema->of as $alt) { + $this->alts[] = $rx->makeSchema($alt); + } + } + } + + public function check($value): bool + { + + if (empty($this->alts)) { + return true; + } + foreach ($this->alts as $alt) { + if ($alt->check($value)) { + return true; + } + } + + return false; + + } + +} \ 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..b83d952 --- /dev/null +++ b/php/src/Rx/Core/Type/Arr.php @@ -0,0 +1,66 @@ +checkSchema($schema, static::TYPE); + + if (empty($schema->contents)) { + throw new MissingParamException('No `contents` param for //arr schema'); + } + + $this->contentSchema = $rx->makeSchema($schema->contents); + + if (isset($schema->length)) { + $this->lengthChecker = new RangeChecker($schema->length); + } + + } + + public function check($value): bool + { + + if (! Util::isSeqIntArray($value)) { + return false; + } + + if ($this->lengthChecker) { + if (! $this->lengthChecker->check(count($value))) { + return false; + } + } + + foreach ($value as $i => $entry) { + if (! $this->contentSchema->check($entry)) { + return false; + } + } + + 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..d612769 --- /dev/null +++ b/php/src/Rx/Core/Type/Boolean.php @@ -0,0 +1,37 @@ +checkSchema($schema, static::TYPE); + + } + + function check($value): bool + { + + return is_bool($value); + + } + +} \ 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..e72ca0e --- /dev/null +++ b/php/src/Rx/Core/Type/Def.php @@ -0,0 +1,34 @@ +checkSchema($schema, static::TYPE); + + } + + public function check($value): bool + { + + return ! is_null($value); + + } + +} \ 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..1850527 --- /dev/null +++ b/php/src/Rx/Core/Type/Fail.php @@ -0,0 +1,34 @@ +checkSchema($schema, static::TYPE); + + } + + public function check($value): bool + { + + return false; + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/Type/Integer.php b/php/src/Rx/Core/Type/Integer.php new file mode 100644 index 0000000..387320f --- /dev/null +++ b/php/src/Rx/Core/Type/Integer.php @@ -0,0 +1,17 @@ +checkSchema($schema, static::TYPE); + + if ($schema->values) { + $this->valuesSchema = $rx->makeSchema($schema->values); + } + + } + + public function check($value): bool + { + + if (!is_object($value) || get_class($value) != 'stdClass') { + return false; + } + + if ($this->valuesSchema) { + foreach ($value as $key => $entry) { + if (! $this->valuesSchema->check($entry)) { + return false; + } + } + } + + 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..4a8722a --- /dev/null +++ b/php/src/Rx/Core/Type/Nil.php @@ -0,0 +1,34 @@ +checkSchema($schema, static::TYPE); + + } + + public function check($value): bool + { + + return is_null($value); + + } + +} \ 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..b4a9bf9 --- /dev/null +++ b/php/src/Rx/Core/Type/Num.php @@ -0,0 +1,71 @@ +checkSchema($schema, static::TYPE); + + if (isset($schema->value)) { + if (! (is_int($schema->value) || is_float($schema->value))) { + throw new InvalidParamTypeException('The `value` param for ' . static::TYPE . ' schema is not an int or float'); + } + if (static::TYPE == '//int' && is_float($schema->value) && $schema->value != floor($schema->value)) { + throw new InvalidParamTypeException('The `value` param for ' . static::TYPE . ' schema is not an int'); + } + $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))) { + return false; + } + if (static::TYPE == '//int' && is_float($value) && $value != floor($value)) { + return false; + } + + if ($this->fixedValue !== null) { + if ($value != $this->fixedValue) { + return false; + } + } + + if ($this->rangeChecker && ! $this->rangeChecker->check($value)) { + return false; + } + + 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..ca48fee --- /dev/null +++ b/php/src/Rx/Core/Type/One.php @@ -0,0 +1,34 @@ +checkSchema($schema, static::TYPE); + + } + + public function check($value): bool + { + + return is_scalar($value); + + } + +} \ 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..9ae7043 --- /dev/null +++ b/php/src/Rx/Core/Type/Rec.php @@ -0,0 +1,109 @@ +checkSchema($schema, static::TYPE); + + $this->required = new \stdClass(); + $this->optional = new \stdClass(); + $this->known = new \stdClass(); + + if (isset($schema->rest)) { + $this->restSchema = $rx->makeSchema($schema->rest); + } + + if (isset($schema->required)) { + foreach ($schema->required as $key => $entry) { + $this->known->$key = true; + $this->required->$key = $rx->makeSchema($entry); + } + } + + if (isset($schema->optional)) { + foreach ($schema->optional as $key => $entry) { + if (isset($this->known->$key)) { + throw new RequiredAndOptionalException("`$key` is both required and optional in //rec"); + } + + $this->known->$key = true; + $this->optional->$key = $rx->makeSchema($entry); + } + } + + } + + public function check($value): bool + { + + if (!is_object($value) || get_class($value) != 'stdClass') { + return false; + } + + $rest = new \stdClass(); + $haveRest = false; + + foreach ($value as $key => $entry) { + if (! isset($this->known->$key)) { + $haveRest = true; + $rest->$key = $entry; + } + } + + if ($haveRest && ! $this->restSchema) { + 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 ($haveRest && ! $this->restSchema->check($rest)) { + return false; + } + + 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..c84d485 --- /dev/null +++ b/php/src/Rx/Core/Type/Seq.php @@ -0,0 +1,87 @@ +checkSchema($schema, static::TYPE); + + if (! isset($schema->contents)) { + throw new MissingParamException('No `contents` param for //seq schema'); + } + + if (! is_array($schema->contents)) { + throw new InvalidParamTypeException('The `contents` param for //seq schema is not an array'); + } + + $this->contentSchemata = []; + + foreach ($schema->contents as $i => $entry) { + $this->contentSchemata[] = $rx->makeSchema($entry); + } + + if (isset($schema->tail)) { + $this->tailSchema = $rx->makeSchema($schema->tail); + } + + } + + public function check($value): bool + { + + if (! Util::isSeqIntArray($value)) { + return false; + } + + foreach ($this->contentSchemata as $i => $schema) { + if (! array_key_exists($i, $value)) { + return false; + } + if (! $schema->check($value[$i])) { + return false; + } + } + + if (count($value) > count($this->contentSchemata)) { + if (! $this->tailSchema) { + return false; + } + + $tail = array_slice( + $value, + count($this->contentSchemata), + count($value) - count($this->contentSchemata) + ); + + if (! $this->tailSchema->check($tail)) { + return false; + } + } + + 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..be7f7f3 --- /dev/null +++ b/php/src/Rx/Core/Type/Str.php @@ -0,0 +1,65 @@ +checkSchema($schema, static::TYPE); + + if (isset($schema->value)) { + if (! is_string($schema->value)) { + throw new InvalidParamTypeException('The `value` param for //str schema is not a string'); + } + + $this->fixedValue = $schema->value; + } + + if (isset($schema->length)) { + $this->lengthChecker = new RangeChecker($schema->length); + } + + } + + public function check($value): bool + { + + if (! is_string($value)) { + return false; + } + if ($this->fixedValue !== null && $value != $this->fixedValue) { + return false; + } + + if ($this->lengthChecker) { + if (! $this->lengthChecker->check(strlen($value))) { + return false; + } + } + + return true; + + } + +} \ 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..254536c --- /dev/null +++ b/php/src/Rx/Core/TypeInterface.php @@ -0,0 +1,8 @@ + '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..d3ae9ab --- /dev/null +++ b/php/src/Rx/Rx.php @@ -0,0 +1,114 @@ + '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("Tried 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) + { + + if (! is_object($schema)) { + $schemaName = $schema; + $schema = new \stdClass(); + $schema->type = $schemaName; + } + + if (empty($schema->type)) { + throw new RxException("Can't make a schema with no type"); + } + + $uri = $this->expandUri($schema->type); + + if (! isset($this->typeRegistry->$uri)) { + throw new RxException("Unknown type: $uri"); + } + + $typeClass = $this->typeRegistry->$uri; + + if (is_array($typeClass) && isset($typeClass['schema'])) { + // @todo: debug this! + foreach ($schema as $key => $entry) { + if ($key != 'type') { + throw new RxException('Composed type does not take check arguments'); + } + } + return $this->makeSchema($typeClass['schema']); + } elseif ($typeClass) { + return new $typeClass($schema, $this); + } + + 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..6f0f98e --- /dev/null +++ b/php/src/Rx/Util.php @@ -0,0 +1,24 @@ + Date: Sat, 8 Jun 2019 00:28:47 +0100 Subject: [PATCH 2/7] Cleanups and improvements, and basic console example --- php/Rx.php | 599 ------------------ php/composer.json | 4 +- php/composer.lock | 180 ++++-- php/console.php | 59 ++ php/rx-test.php | 162 ----- php/src/Rx/Core/Type/All.php | 27 +- php/src/Rx/Core/Type/Any.php | 33 +- php/src/Rx/Core/Type/Arr.php | 36 +- php/src/Rx/Core/Type/Boolean.php | 26 +- php/src/Rx/Core/Type/Def.php | 23 +- php/src/Rx/Core/Type/Fail.php | 19 +- php/src/Rx/Core/Type/Map.php | 24 +- php/src/Rx/Core/Type/Nil.php | 23 +- php/src/Rx/Core/Type/Num.php | 37 +- php/src/Rx/Core/Type/One.php | 23 +- php/src/Rx/Core/Type/Rec.php | 47 +- php/src/Rx/Core/Type/Seq.php | 44 +- php/src/Rx/Core/Type/Str.php | 39 +- php/src/Rx/Core/TypeAbstract.php | 24 + php/src/Rx/Core/TypeInterface.php | 3 + php/src/Rx/Exception/CheckFailedException.php | 6 + php/src/Rx/Rx.php | 11 +- php/{ => tests}/RxTest.php | 16 +- php/{ext => tests}/Test.php | 0 php/{util-test.php => tests/UtilTest.php} | 13 +- 25 files changed, 448 insertions(+), 1030 deletions(-) delete mode 100644 php/Rx.php create mode 100644 php/console.php delete mode 100644 php/rx-test.php create mode 100644 php/src/Rx/Core/TypeAbstract.php create mode 100644 php/src/Rx/Exception/CheckFailedException.php rename php/{ => tests}/RxTest.php (90%) rename php/{ext => tests}/Test.php (100%) rename php/{util-test.php => tests/UtilTest.php} (65%) 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 index 4487bba..806022b 100644 --- a/php/composer.json +++ b/php/composer.json @@ -4,7 +4,9 @@ "type": "library", "license": "GPL", "minimum-stability": "stable", - "require": {}, + "require": { + "symfony/yaml": "^4.3" + }, "require-dev": { "phpunit/phpunit": "^8" }, diff --git a/php/composer.lock b/php/composer.lock index 1a67804..e98f488 100644 --- a/php/composer.lock +++ b/php/composer.lock @@ -4,8 +4,126 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cee5af0bbd85177084613b80c0e03c23", - "packages": [], + "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", @@ -1331,64 +1449,6 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2016-10-03T07:35:21+00:00" }, - { - "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": "theseer/tokenizer", "version": "1.1.2", diff --git a/php/console.php b/php/console.php new file mode 100644 index 0000000..05b8438 --- /dev/null +++ b/php/console.php @@ -0,0 +1,59 @@ +getMessage()}\n"; + die(); +} +try { + $credentialsFile = Yaml::parse(file_get_contents($argv[1]), Yaml::PARSE_OBJECT_FOR_MAP); +} catch (Exception $e) { + echo " ❌ Unable to parse credentials file.\n"; + echo " {$e->getMessage()}\n"; + die(); +} + +$rx = new Rx\Rx(); + +// Add custom types +if ($argv[3]) { + $types = explode(';', $argv[3]); + foreach($types as $type) { + try { + $typeFile = Yaml::parse(file_get_contents($type), Yaml::PARSE_OBJECT_FOR_MAP); + $rx->learnType($typeFile->uri, $typeFile->schema); + } catch (Exception $e) { + echo " ❌ Unable to add custom types.\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 according to schema.\n"; +} catch (Exception $e) { + echo " ❌ An error occurred validating the file against the schema.\n"; + echo " {$e->getMessage()}\n"; +} diff --git a/php/rx-test.php b/php/rx-test.php deleted file mode 100644 index a182338..0000000 --- a/php/rx-test.php +++ /dev/null @@ -1,162 +0,0 @@ -#!/usr/bin/php - $v) { - if (is_array($v)) { - $new_v = new stdClass(); - - foreach ($v as $entry) { - $new_v->$entry = $entry; - } - - $test_data[$k] = $v = $new_v; - } - - foreach ($test_data[$k] as $entry => $json) { - $j_arr = json_decode("[ $json ]"); - $test_data[$k]->$entry = $j_arr[0]; - # diag("loaded $json ($k/$entry) as " . var_export($j_arr[0], true)); - } -} - -function normalize($entries, $test_data) { - if ($entries == '*') { - $entries = new stdClass(); - $key = "*"; - $entries->$key = null; - } - - if (is_array($entries)) { - $new_entries = new stdClass(); - foreach ($entries as $entry) { - $new_entries->$entry = $entry; - } - - $entries = $new_entries; - } - - if (count((array) $entries) == 1 and property_exists($entries, "*")) { - $key = "*"; - $value = $entries->$key; - $new_entries = new stdClass(); - foreach ($test_data as $name => $entry) { - $new_entries->$name = $entry; - } - $entries = $new_entries; - } - - return $entries; -} - -function test_json($x, $y) { - return false; -} - -asort($test_schemata); -foreach ($test_schemata as $schema_name => $test) { - if (isset($_ENV["RX_TEST_SCHEMA"]) and $_ENV["RX_TEST_SCHEMA"] != $schema_name) - continue; - - $Rx = new Rx(); - - $schema = null; - - if (isset($test->composedtype)) { - try { - $Rx->learn_type($test->composedtype->uri, $test->composedtype->schema); - } catch (Exception $e) { - if (isset($test->composedtype->invalid)) { - pass("BAD COMPOSED TYPE: $schema_name"); - continue; - } else { - throw $e; - } - } - - if (isset($test->composedtype->invalid)) { - fail("BAD COMPOSED TYPE: $schema_name"); - continue; - } - - if (isset($test->composedtype->prefix)) - $Rx->add_prefix($test->composedtype->prefix[0], - $test->composedtype->prefix[1]); - } - - try { - $schema = $Rx->make_schema($test->schema); - } catch (Exception $e) { - if (isset($test->invalid)) { - pass("BAD SCHEMA: $schema_name"); - continue; - } else { - throw $e; - } - } - - if (isset($test->invalid)) { - fail("BAD SCHEMA: $schema_name"); - continue; - } - - if (! $schema) die("did not get schema for valid input"); - - foreach (array('pass', 'fail') as $pf) { - $expect = ($pf == 'pass') ? 'VALID ' : 'INVALID'; - if (!isset($test->$pf)) continue; - - foreach ($test->$pf as $source => $entries) { - $entries = normalize($entries, $test_data[$source]); - - foreach ($entries as $name => $want) { - $value = $test_data[$source]->$name; - - $result = $schema->check($value); - if ($pf == 'fail') $result = ! $result; - - if ("$source/$entry" == "num/0e0") - todo_start("PHP's json_decode can't handle 0e0 as number"); - - ok($result, "$expect: $source/$name against $schema_name"); - - if ("$source/$entry" == "num/0e0") - todo_end(); - } - } - } -} - -?> diff --git a/php/src/Rx/Core/Type/All.php b/php/src/Rx/Core/Type/All.php index 01c2390..cf384a8 100644 --- a/php/src/Rx/Core/Type/All.php +++ b/php/src/Rx/Core/Type/All.php @@ -3,15 +3,19 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface, +}; use Rx\Rx; -use Rx\Exception\NoAlternativesGivenException; +use Rx\Exception\{ + NoAlternativesGivenException, + CheckFailedException +}; -class All implements TypeInterface +class All extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/all'; const TYPE = '//all'; const VALID_PARAMS = [ @@ -21,17 +25,17 @@ class All implements TypeInterface private $alts = []; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (empty($schema->of)) { - throw new NoAlternativesGivenException("No alternatives given for //all `of`"); + throw new NoAlternativesGivenException(sprintf("No `of` given in `%s` %s schema.", $this->propName, static::TYPE)); } foreach ($schema->of as $alt) { - $this->alts[] = $rx->makeSchema($alt); + $this->alts[] = $rx->makeSchema($alt, $propName); } } @@ -40,10 +44,9 @@ public function check($value): bool { foreach ($this->alts as $alt) { - if (! $alt->check($value)) { - return false; - } + $alt->check($value); } + return true; } diff --git a/php/src/Rx/Core/Type/Any.php b/php/src/Rx/Core/Type/Any.php index fe4c3da..71766a5 100644 --- a/php/src/Rx/Core/Type/Any.php +++ b/php/src/Rx/Core/Type/Any.php @@ -3,15 +3,19 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; -use Rx\Exception\NoAlternativesGivenException; +use Rx\Exception\{ + NoAlternativesGivenException, + CheckFailedException +}; -class Any implements TypeInterface +class Any extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/any'; const TYPE = '//any'; const VALID_PARAMS = [ @@ -21,17 +25,17 @@ class Any implements TypeInterface private $alts = []; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (property_exists($schema, 'of')) { if (empty($schema->of)) { - throw new NoAlternativesGivenException("No alternatives given for //any `of`"); + throw new NoAlternativesGivenException(sprintf("No `of` given in `%s` %s schema.", $this->propName, static::TYPE)); } foreach ($schema->of as $alt) { - $this->alts[] = $rx->makeSchema($alt); + $this->alts[] = $rx->makeSchema($alt, $propName); } } } @@ -42,13 +46,18 @@ public function check($value): bool if (empty($this->alts)) { return true; } + foreach ($this->alts as $alt) { - if ($alt->check($value)) { - return true; + try { + if ($alt->check($value)) { + return true; + } + } catch (CheckFailedException $e) { + // ignore } } - return false; + throw new CheckFailedException(sprintf('Values in %s %s do not match any `of`.', $this->propName, static::TYPE)); } diff --git a/php/src/Rx/Core/Type/Arr.php b/php/src/Rx/Core/Type/Arr.php index b83d952..93488c3 100644 --- a/php/src/Rx/Core/Type/Arr.php +++ b/php/src/Rx/Core/Type/Arr.php @@ -3,15 +3,23 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; -use Rx\{Rx, RangeChecker, Util}; -use Rx\Exception\MissingParamException; - -class Arr implements TypeInterface +use Rx\Core\{ + TypeAbstract, + TypeInterface, +}; +use Rx\{ + Rx, + RangeChecker, + Util +}; +use Rx\Exception\{ + MissingParamException, + CheckFailedException +}; + +class Arr extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/arr'; const TYPE = '//arr'; const VALID_PARAMS = [ @@ -23,13 +31,13 @@ class Arr implements TypeInterface private $contentSchema; private $lengthChecker; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (empty($schema->contents)) { - throw new MissingParamException('No `contents` param for //arr schema'); + throw new MissingParamException(sprintf('No `contents` param found for %s %s schema.', $this->propName, static::TYPE)); } $this->contentSchema = $rx->makeSchema($schema->contents); @@ -44,19 +52,17 @@ public function check($value): bool { if (! Util::isSeqIntArray($value)) { - return false; + throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', $this->propName, static::TYPE)); } if ($this->lengthChecker) { if (! $this->lengthChecker->check(count($value))) { - return false; + throw new CheckFailedException(sprintf('Array length check fails in %s %s.', $this->propName, static::TYPE)); } } foreach ($value as $i => $entry) { - if (! $this->contentSchema->check($entry)) { - return false; - } + $this->contentSchema->check($entry); } return true; diff --git a/php/src/Rx/Core/Type/Boolean.php b/php/src/Rx/Core/Type/Boolean.php index d612769..e29adbf 100644 --- a/php/src/Rx/Core/Type/Boolean.php +++ b/php/src/Rx/Core/Type/Boolean.php @@ -3,34 +3,30 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -/** - * Can't use Bool as class name - */ -class Boolean implements TypeInterface +class Boolean extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/bool'; const TYPE = '//bool'; const VALID_PARAMS = [ 'type', ]; - public function __construct(\stdClass $schema, Rx $rx) - { - - $this->checkSchema($schema, static::TYPE); - - } - function check($value): bool { - return is_bool($value); + if (! is_bool($value)) { + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + } + + return true; } diff --git a/php/src/Rx/Core/Type/Def.php b/php/src/Rx/Core/Type/Def.php index e72ca0e..c9db2db 100644 --- a/php/src/Rx/Core/Type/Def.php +++ b/php/src/Rx/Core/Type/Def.php @@ -3,31 +3,30 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface, +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -class Def implements TypeInterface +class Def extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/def'; const TYPE = '//def'; const VALID_PARAMS = [ 'type', ]; - public function __construct(\stdClass $schema, Rx $rx) - { - - $this->checkSchema($schema, static::TYPE); - - } - public function check($value): bool { - return ! is_null($value); + if (is_null($value)) { + throw new CheckFailedException('Value missing or not set in //def.'); + } + + return true; } diff --git a/php/src/Rx/Core/Type/Fail.php b/php/src/Rx/Core/Type/Fail.php index 1850527..a2bc444 100644 --- a/php/src/Rx/Core/Type/Fail.php +++ b/php/src/Rx/Core/Type/Fail.php @@ -3,31 +3,26 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -class Fail implements TypeInterface +class Fail extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/fail'; const TYPE = '//fail'; const VALID_PARAMS = [ 'type', ]; - public function __construct(\stdClass $schema, Rx $rx) - { - - $this->checkSchema($schema, static::TYPE); - - } - public function check($value): bool { - return false; + throw new CheckFailedException('Failed as per //fail.'); } diff --git a/php/src/Rx/Core/Type/Map.php b/php/src/Rx/Core/Type/Map.php index 8529b64..18ffd5d 100644 --- a/php/src/Rx/Core/Type/Map.php +++ b/php/src/Rx/Core/Type/Map.php @@ -3,14 +3,16 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -class Map implements TypeInterface +class Map extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/map'; const TYPE = '//map'; const VALID_PARAMS = [ @@ -20,13 +22,13 @@ class Map implements TypeInterface private $valuesSchema; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); - if ($schema->values) { - $this->valuesSchema = $rx->makeSchema($schema->values); + if (isset($schema->values)) { + $this->valuesSchema = $rx->makeSchema($schema->values, $propName); } } @@ -35,14 +37,12 @@ public function check($value): bool { if (!is_object($value) || get_class($value) != 'stdClass') { - return false; + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), $this->propName, static::TYPE)); } if ($this->valuesSchema) { foreach ($value as $key => $entry) { - if (! $this->valuesSchema->check($entry)) { - return false; - } + $this->valuesSchema->check($entry); } } diff --git a/php/src/Rx/Core/Type/Nil.php b/php/src/Rx/Core/Type/Nil.php index 4a8722a..d7e3f4c 100644 --- a/php/src/Rx/Core/Type/Nil.php +++ b/php/src/Rx/Core/Type/Nil.php @@ -3,31 +3,30 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -class Nil implements TypeInterface +class Nil extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/nil'; const TYPE = '//nil'; const VALID_PARAMS = [ 'type', ]; - public function __construct(\stdClass $schema, Rx $rx) - { - - $this->checkSchema($schema, static::TYPE); - - } - public function check($value): bool { - return is_null($value); + if (! is_null($value)) { + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + } + + return true; } diff --git a/php/src/Rx/Core/Type/Num.php b/php/src/Rx/Core/Type/Num.php index b4a9bf9..cd92ac7 100644 --- a/php/src/Rx/Core/Type/Num.php +++ b/php/src/Rx/Core/Type/Num.php @@ -3,15 +3,22 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; -use Rx\{Rx, RangeChecker}; -use Rx\Exception\InvalidParamTypeException; - -class Num implements TypeInterface +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; +use Rx\{ + Rx, + RangeChecker +}; +use Rx\Exception\{ + InvalidParamTypeException, + CheckFailedException +}; + +class Num extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/num'; const TYPE = '//num'; const VALID_PARAMS = [ @@ -23,17 +30,17 @@ class Num implements TypeInterface private $rangeChecker; private $fixedValue; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (isset($schema->value)) { if (! (is_int($schema->value) || is_float($schema->value))) { - throw new InvalidParamTypeException('The `value` param for ' . static::TYPE . ' schema is not an int or float'); + 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('The `value` param for ' . static::TYPE . ' schema is not an int'); + throw new InvalidParamTypeException(sprintf('The `value` param for %s schema is not an int', static::TYPE)); } $this->fixedValue = $schema->value; } @@ -48,20 +55,20 @@ public function check($value): bool { if (! (is_int($value) || is_float($value))) { - return false; + throw new CheckFailedException(sprintf('Expected int/float, got %s in %s.', gettype($value), static::TYPE)); } if (static::TYPE == '//int' && is_float($value) && $value != floor($value)) { - return false; + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); } if ($this->fixedValue !== null) { if ($value != $this->fixedValue) { - return false; + throw new CheckFailedException(sprintf('Value does not equal value \'%s\' in %s.', strval($this->fixedValue), static::TYPE)); } } if ($this->rangeChecker && ! $this->rangeChecker->check($value)) { - return false; + throw new CheckFailedException(sprintf('Range check fails in %s.', static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/One.php b/php/src/Rx/Core/Type/One.php index ca48fee..52233b5 100644 --- a/php/src/Rx/Core/Type/One.php +++ b/php/src/Rx/Core/Type/One.php @@ -3,31 +3,30 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; +use Rx\Exception\CheckFailedException; -class One implements TypeInterface +class One extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/one'; const TYPE = '//one'; const VALID_PARAMS = [ 'type', ]; - public function __construct(\stdClass $schema, Rx $rx) - { - - $this->checkSchema($schema, static::TYPE); - - } - public function check($value): bool { - return is_scalar($value); + if (! is_scalar($value)) { + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + } + + return true; } diff --git a/php/src/Rx/Core/Type/Rec.php b/php/src/Rx/Core/Type/Rec.php index 9ae7043..2ebae80 100644 --- a/php/src/Rx/Core/Type/Rec.php +++ b/php/src/Rx/Core/Type/Rec.php @@ -3,15 +3,19 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; use Rx\Rx; -use Rx\Exception\RequiredAndOptionalException; +use Rx\Exception\{ + RequiredAndOptionalException, + CheckFailedException +}; -class Rec implements TypeInterface +class Rec extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/rec'; const TYPE = '//rec'; const VALID_PARAMS = [ @@ -26,34 +30,33 @@ class Rec implements TypeInterface private $known; private $restSchema; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); $this->required = new \stdClass(); $this->optional = new \stdClass(); $this->known = new \stdClass(); if (isset($schema->rest)) { - $this->restSchema = $rx->makeSchema($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); + $this->required->$key = $rx->makeSchema($entry, $key); } } if (isset($schema->optional)) { foreach ($schema->optional as $key => $entry) { if (isset($this->known->$key)) { - throw new RequiredAndOptionalException("`$key` is both required and optional in //rec"); + throw new RequiredAndOptionalException(sprintf('`%s` is both required and optional in %s %s', $key, $this->propName ?? 'top', static::TYPE)); } - $this->known->$key = true; - $this->optional->$key = $rx->makeSchema($entry); + $this->optional->$key = $rx->makeSchema($entry, $key); } } @@ -63,12 +66,12 @@ public function check($value): bool { if (!is_object($value) || get_class($value) != 'stdClass') { - return false; + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), $this->propName ?? 'top', static::TYPE)); } $rest = new \stdClass(); $haveRest = false; - + foreach ($value as $key => $entry) { if (! isset($this->known->$key)) { $haveRest = true; @@ -77,29 +80,25 @@ public function check($value): bool } if ($haveRest && ! $this->restSchema) { - return false; + throw new CheckFailedException(sprintf('Invalid keys [%s] found in %s %s.', implode(', ', array_keys(get_object_vars($rest))), $this->propName ?? 'top', static::TYPE)); } foreach ($this->required as $key => $schema) { if (! property_exists($value, $key)) { - return false; - } - if (! $schema->check($value->$key)) { - return false; + throw new CheckFailedException(sprintf('Key `%s` not found in `required` %s %s.', strval($key), $this->propName ?? 'top', static::TYPE)); } + $schema->check($value->$key); } foreach ($this->optional as $key => $schema) { if (! property_exists($value, $key)) { continue; } - if (! $schema->check($value->$key)) { - return false; - } + $schema->check($value->$key); } - if ($haveRest && ! $this->restSchema->check($rest)) { - return false; + if ($haveRest) { + $this->restSchema->check($rest); } return true; diff --git a/php/src/Rx/Core/Type/Seq.php b/php/src/Rx/Core/Type/Seq.php index c84d485..a53fa54 100644 --- a/php/src/Rx/Core/Type/Seq.php +++ b/php/src/Rx/Core/Type/Seq.php @@ -3,15 +3,23 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; -use Rx\{Rx, Util}; -use Rx\Exception\{MissingParamException, InvalidParamTypeException}; +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; +use Rx\{ + Rx, + Util +}; +use Rx\Exception\{ + MissingParamException, + InvalidParamTypeException, + CheckFailedException +}; -class Seq implements TypeInterface +class Seq extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/seq'; const TYPE = '//seq'; const VALID_PARAMS = [ @@ -23,23 +31,23 @@ class Seq implements TypeInterface private $contentSchemata; private $tailSchema; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (! isset($schema->contents)) { - throw new MissingParamException('No `contents` param for //seq schema'); + throw new MissingParamException(sprintf('No `contents` param for %s %s schema', $propName, static::TYPE)); } if (! is_array($schema->contents)) { - throw new InvalidParamTypeException('The `contents` param for //seq schema is not an array'); + throw new InvalidParamTypeException(sprintf('The `contents` param for %s %s schema is not an array', $propName, static::TYPE)); } $this->contentSchemata = []; foreach ($schema->contents as $i => $entry) { - $this->contentSchemata[] = $rx->makeSchema($entry); + $this->contentSchemata[] = $rx->makeSchema($entry, 'seq#' . $i); } if (isset($schema->tail)) { @@ -52,21 +60,19 @@ public function check($value): bool { if (! Util::isSeqIntArray($value)) { - return false; + throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', $this->propName, static::TYPE)); } foreach ($this->contentSchemata as $i => $schema) { if (! array_key_exists($i, $value)) { - return false; - } - if (! $schema->check($value[$i])) { - return false; + throw new CheckFailedException(sprintf('Key `%s` not found in `contents` of %s %s', strval($i), $this->propName, static::TYPE)); } + $schema->check($value[$i]); } if (count($value) > count($this->contentSchemata)) { if (! $this->tailSchema) { - return false; + throw new CheckFailedException(sprintf('`tail` missing from, or invalid length of %s', static::TYPE)); } $tail = array_slice( @@ -75,9 +81,7 @@ public function check($value): bool count($value) - count($this->contentSchemata) ); - if (! $this->tailSchema->check($tail)) { - return false; - } + $this->tailSchema->check($tail); } return true; diff --git a/php/src/Rx/Core/Type/Str.php b/php/src/Rx/Core/Type/Str.php index be7f7f3..a08e58f 100644 --- a/php/src/Rx/Core/Type/Str.php +++ b/php/src/Rx/Core/Type/Str.php @@ -3,15 +3,22 @@ namespace Rx\Core\Type; -use Rx\Core\{TypeInterface, CheckSchemaTrait}; -use Rx\{Rx, RangeChecker}; -use Rx\Exception\InvalidParamTypeException; - -class Str implements TypeInterface +use Rx\Core\{ + TypeAbstract, + TypeInterface +}; +use Rx\{ + Rx, + RangeChecker +}; +use Rx\Exception\{ + InvalidParamTypeException, + CheckFailedException +}; + +class Str extends TypeAbstract implements TypeInterface { - use CheckSchemaTrait; - const URI = 'tag:codesimply.com,2008:rx/core/str'; const TYPE = '//str'; const VALID_PARAMS = [ @@ -21,16 +28,16 @@ class Str implements TypeInterface ]; private $fixedValue; - private $lengthChecker; + private $lengthChecker; - public function __construct(\stdClass $schema, Rx $rx) + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) { - $this->checkSchema($schema, static::TYPE); + parent::__construct($schema, $rx, $propName); if (isset($schema->value)) { if (! is_string($schema->value)) { - throw new InvalidParamTypeException('The `value` param for //str schema is not a string'); + throw new InvalidParamTypeException(sprintf('The `value` param for %s %s schema is not a string', $this->propName, static::TYPE)); } $this->fixedValue = $schema->value; @@ -46,16 +53,14 @@ public function check($value): bool { if (! is_string($value)) { - return false; + throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); } if ($this->fixedValue !== null && $value != $this->fixedValue) { - return false; + 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) { - if (! $this->lengthChecker->check(strlen($value))) { - return false; - } + 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; diff --git a/php/src/Rx/Core/TypeAbstract.php b/php/src/Rx/Core/TypeAbstract.php new file mode 100644 index 0000000..e52a550 --- /dev/null +++ b/php/src/Rx/Core/TypeAbstract.php @@ -0,0 +1,24 @@ +propName = $propName; + + $this->checkSchema($schema, static::TYPE); + + } + +} \ No newline at end of file diff --git a/php/src/Rx/Core/TypeInterface.php b/php/src/Rx/Core/TypeInterface.php index 254536c..ac3781f 100644 --- a/php/src/Rx/Core/TypeInterface.php +++ b/php/src/Rx/Core/TypeInterface.php @@ -2,7 +2,10 @@ namespace Rx\Core; +use Rx\Rx; + interface TypeInterface { + public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null); public function check($value): bool; } \ No newline at end of file diff --git a/php/src/Rx/Exception/CheckFailedException.php b/php/src/Rx/Exception/CheckFailedException.php new file mode 100644 index 0000000..011a1fa --- /dev/null +++ b/php/src/Rx/Exception/CheckFailedException.php @@ -0,0 +1,6 @@ +makeSchema($schema); $this->typeRegistry->$uri = ['schema' => $schema]; } - public function makeSchema($schema) + public function makeSchema($schema, ?string $propName = null) { if (! is_object($schema)) { @@ -84,7 +84,7 @@ public function makeSchema($schema) } if (empty($schema->type)) { - throw new RxException("Can't make a schema with no type"); + throw new RxException("Can't make a schema with no `type` key."); } $uri = $this->expandUri($schema->type); @@ -96,15 +96,14 @@ public function makeSchema($schema) $typeClass = $this->typeRegistry->$uri; if (is_array($typeClass) && isset($typeClass['schema'])) { - // @todo: debug this! foreach ($schema as $key => $entry) { if ($key != 'type') { - throw new RxException('Composed type does not take check arguments'); + throw new RxException('Composed type does not take check arguments.'); } } return $this->makeSchema($typeClass['schema']); } elseif ($typeClass) { - return new $typeClass($schema, $this); + return new $typeClass($schema, $this, $propName); } return false; diff --git a/php/RxTest.php b/php/tests/RxTest.php similarity index 90% rename from php/RxTest.php rename to php/tests/RxTest.php index 0441ff3..3f8c0a1 100644 --- a/php/RxTest.php +++ b/php/tests/RxTest.php @@ -2,9 +2,9 @@ $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" ); } From 821fb28a5cfaa94f21c0a3679177dba58f11f64e Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Sat, 8 Jun 2019 12:38:51 +0100 Subject: [PATCH 3/7] More consistent error messages --- php/console.php | 2 +- php/src/Rx/Core/CheckSchemaTrait.php | 7 +++++-- php/src/Rx/Core/Type/All.php | 7 +++++-- php/src/Rx/Core/Type/Any.php | 9 ++++++--- php/src/Rx/Core/Type/Arr.php | 8 ++++---- php/src/Rx/Core/Type/Boolean.php | 7 +++++-- php/src/Rx/Core/Type/Def.php | 7 +++++-- php/src/Rx/Core/Type/Map.php | 7 +++++-- php/src/Rx/Core/Type/Nil.php | 7 +++++-- php/src/Rx/Core/Type/Num.php | 11 ++++++----- php/src/Rx/Core/Type/One.php | 7 +++++-- php/src/Rx/Core/Type/Rec.php | 17 ++++++++++------- php/src/Rx/Core/Type/Seq.php | 14 +++++++------- php/src/Rx/Core/Type/Str.php | 7 ++++--- php/src/Rx/Core/TypeAbstract.php | 2 +- php/src/Rx/Rx.php | 14 +++++++------- php/src/Rx/Util.php | 9 +++++++++ 17 files changed, 90 insertions(+), 52 deletions(-) diff --git a/php/console.php b/php/console.php index 05b8438..b100839 100644 --- a/php/console.php +++ b/php/console.php @@ -52,7 +52,7 @@ try { $schema->check($credentialsFile); - echo " ✅ File is according to schema.\n"; + 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 index c4906d2..9512799 100644 --- a/php/src/Rx/Core/CheckSchemaTrait.php +++ b/php/src/Rx/Core/CheckSchemaTrait.php @@ -2,15 +2,18 @@ namespace Rx\Core; +use Rx\Exception\RxException; +use Rx\Util; + trait CheckSchemaTrait { - private function checkSchema(\stdClass $schema, string $type): void + private function checkSchema(\stdClass $schema): void { foreach ($schema as $key => $entry) { if (!in_array($key, static::VALID_PARAMS)) { - throw new \Exception("Unknown parameter $key for $type schema"); + throw new RxException(sprintf('Unknown key `%s` in %s %s.', $key, Util::formatPropName($this->propName), static::TYPE)); } } diff --git a/php/src/Rx/Core/Type/All.php b/php/src/Rx/Core/Type/All.php index cf384a8..7a4bdec 100644 --- a/php/src/Rx/Core/Type/All.php +++ b/php/src/Rx/Core/Type/All.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface, }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\{ NoAlternativesGivenException, CheckFailedException @@ -31,7 +34,7 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) parent::__construct($schema, $rx, $propName); if (empty($schema->of)) { - throw new NoAlternativesGivenException(sprintf("No `of` given in `%s` %s schema.", $this->propName, static::TYPE)); + throw new NoAlternativesGivenException(sprintf("No `of` given in %s %s.", Util::formatPropName($this->propName), static::TYPE)); } foreach ($schema->of as $alt) { diff --git a/php/src/Rx/Core/Type/Any.php b/php/src/Rx/Core/Type/Any.php index 71766a5..2486229 100644 --- a/php/src/Rx/Core/Type/Any.php +++ b/php/src/Rx/Core/Type/Any.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\{ NoAlternativesGivenException, CheckFailedException @@ -32,7 +35,7 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) if (property_exists($schema, 'of')) { if (empty($schema->of)) { - throw new NoAlternativesGivenException(sprintf("No `of` given in `%s` %s schema.", $this->propName, static::TYPE)); + 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); @@ -57,7 +60,7 @@ public function check($value): bool } } - throw new CheckFailedException(sprintf('Values in %s %s do not match any `of`.', $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Values in %s %s do not match any `of`.', Util::formatPropName($this->propName), static::TYPE)); } diff --git a/php/src/Rx/Core/Type/Arr.php b/php/src/Rx/Core/Type/Arr.php index 93488c3..0844cc3 100644 --- a/php/src/Rx/Core/Type/Arr.php +++ b/php/src/Rx/Core/Type/Arr.php @@ -37,10 +37,10 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) parent::__construct($schema, $rx, $propName); if (empty($schema->contents)) { - throw new MissingParamException(sprintf('No `contents` param found for %s %s schema.', $this->propName, static::TYPE)); + throw new MissingParamException(sprintf('No `contents` key found for %s %s.', Util::formatPropName($this->propName), static::TYPE)); } - $this->contentSchema = $rx->makeSchema($schema->contents); + $this->contentSchema = $rx->makeSchema($schema->contents, $propName); if (isset($schema->length)) { $this->lengthChecker = new RangeChecker($schema->length); @@ -52,12 +52,12 @@ public function check($value): bool { if (! Util::isSeqIntArray($value)) { - throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', $this->propName, static::TYPE)); + 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.', $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Array length check fails in %s %s.', Util::formatPropName($this->propName), static::TYPE)); } } diff --git a/php/src/Rx/Core/Type/Boolean.php b/php/src/Rx/Core/Type/Boolean.php index e29adbf..17ec191 100644 --- a/php/src/Rx/Core/Type/Boolean.php +++ b/php/src/Rx/Core/Type/Boolean.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\CheckFailedException; class Boolean extends TypeAbstract implements TypeInterface @@ -23,7 +26,7 @@ function check($value): bool { if (! is_bool($value)) { - throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Key %s is not of type %s.', Util::formatPropName($this->propName), static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/Def.php b/php/src/Rx/Core/Type/Def.php index c9db2db..2e4170b 100644 --- a/php/src/Rx/Core/Type/Def.php +++ b/php/src/Rx/Core/Type/Def.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface, }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\CheckFailedException; class Def extends TypeAbstract implements TypeInterface @@ -23,7 +26,7 @@ public function check($value): bool { if (is_null($value)) { - throw new CheckFailedException('Value missing or not set in //def.'); + throw new CheckFailedException(sprintf('Value missing or not set in %s %s.', Util::formatPropName($this->propName), static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/Map.php b/php/src/Rx/Core/Type/Map.php index 18ffd5d..76c547d 100644 --- a/php/src/Rx/Core/Type/Map.php +++ b/php/src/Rx/Core/Type/Map.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\CheckFailedException; class Map extends TypeAbstract implements TypeInterface @@ -37,7 +40,7 @@ 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), $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), Util::formatPropName($this->propName), static::TYPE)); } if ($this->valuesSchema) { diff --git a/php/src/Rx/Core/Type/Nil.php b/php/src/Rx/Core/Type/Nil.php index d7e3f4c..42994f7 100644 --- a/php/src/Rx/Core/Type/Nil.php +++ b/php/src/Rx/Core/Type/Nil.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\CheckFailedException; class Nil extends TypeAbstract implements TypeInterface @@ -23,7 +26,7 @@ public function check($value): bool { if (! is_null($value)) { - throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Key %s is not of type %s.', Util::formatPropName($this->propName), static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/Num.php b/php/src/Rx/Core/Type/Num.php index cd92ac7..33a049e 100644 --- a/php/src/Rx/Core/Type/Num.php +++ b/php/src/Rx/Core/Type/Num.php @@ -9,7 +9,8 @@ }; use Rx\{ Rx, - RangeChecker + RangeChecker, + Util }; use Rx\Exception\{ InvalidParamTypeException, @@ -55,20 +56,20 @@ public function check($value): bool { if (! (is_int($value) || is_float($value))) { - throw new CheckFailedException(sprintf('Expected int/float, got %s in %s.', gettype($value), static::TYPE)); + 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.', $this->propName, static::TYPE)); + 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 does not equal value \'%s\' in %s.', strval($this->fixedValue), static::TYPE)); + 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.', static::TYPE)); + throw new CheckFailedException(sprintf('Range check fails in %s %s.', Util::formatPropName($this->propName), static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/One.php b/php/src/Rx/Core/Type/One.php index 52233b5..682cdea 100644 --- a/php/src/Rx/Core/Type/One.php +++ b/php/src/Rx/Core/Type/One.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\CheckFailedException; class One extends TypeAbstract implements TypeInterface @@ -23,7 +26,7 @@ public function check($value): bool { if (! is_scalar($value)) { - throw new CheckFailedException(sprintf('Key `%s` is not of type %s.', $this->propName, static::TYPE)); + throw new CheckFailedException(sprintf('Key %s is not of type %s.', Util::formatPropName($this->propName), static::TYPE)); } return true; diff --git a/php/src/Rx/Core/Type/Rec.php b/php/src/Rx/Core/Type/Rec.php index 2ebae80..a8675a4 100644 --- a/php/src/Rx/Core/Type/Rec.php +++ b/php/src/Rx/Core/Type/Rec.php @@ -7,7 +7,10 @@ TypeAbstract, TypeInterface }; -use Rx\Rx; +use Rx\{ + Rx, + Util +}; use Rx\Exception\{ RequiredAndOptionalException, CheckFailedException @@ -46,17 +49,17 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) if (isset($schema->required)) { foreach ($schema->required as $key => $entry) { $this->known->$key = true; - $this->required->$key = $rx->makeSchema($entry, $key); + $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, $this->propName ?? 'top', static::TYPE)); + 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, $key); + $this->optional->$key = $rx->makeSchema($entry, ($propName ? $propName . '->' : '') . $key); } } @@ -66,7 +69,7 @@ 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), $this->propName ?? 'top', static::TYPE)); + throw new CheckFailedException(sprintf('Expected object, got %s in %s %s.', gettype($value), Util::formatPropName($this->propName), static::TYPE)); } $rest = new \stdClass(); @@ -80,12 +83,12 @@ public function check($value): bool } if ($haveRest && ! $this->restSchema) { - throw new CheckFailedException(sprintf('Invalid keys [%s] found in %s %s.', implode(', ', array_keys(get_object_vars($rest))), $this->propName ?? 'top', static::TYPE)); + 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('Key `%s` not found in `required` %s %s.', strval($key), $this->propName ?? 'top', static::TYPE)); + 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); } diff --git a/php/src/Rx/Core/Type/Seq.php b/php/src/Rx/Core/Type/Seq.php index a53fa54..d97024e 100644 --- a/php/src/Rx/Core/Type/Seq.php +++ b/php/src/Rx/Core/Type/Seq.php @@ -37,21 +37,21 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) parent::__construct($schema, $rx, $propName); if (! isset($schema->contents)) { - throw new MissingParamException(sprintf('No `contents` param for %s %s schema', $propName, static::TYPE)); + 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` param for %s %s schema is not an array', $propName, static::TYPE)); + 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, 'seq#' . $i); + $this->contentSchemata[] = $rx->makeSchema($entry, $propName . '->seq#' . $i); } if (isset($schema->tail)) { - $this->tailSchema = $rx->makeSchema($schema->tail); + $this->tailSchema = $rx->makeSchema($schema->tail, $propName . '->tail'); } } @@ -60,19 +60,19 @@ public function check($value): bool { if (! Util::isSeqIntArray($value)) { - throw new CheckFailedException(sprintf('Numeric keys not found in %s %s.', $this->propName, static::TYPE)); + 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('Key `%s` not found in `contents` of %s %s', strval($i), $this->propName, static::TYPE)); + 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('`tail` missing from, or invalid length of %s', static::TYPE)); + throw new CheckFailedException(sprintf('Key `tail` missing, or invalid length of %s %s.', Util::formatPropName($this->propName), static::TYPE)); } $tail = array_slice( diff --git a/php/src/Rx/Core/Type/Str.php b/php/src/Rx/Core/Type/Str.php index a08e58f..f4dfa15 100644 --- a/php/src/Rx/Core/Type/Str.php +++ b/php/src/Rx/Core/Type/Str.php @@ -9,7 +9,8 @@ }; use Rx\{ Rx, - RangeChecker + RangeChecker, + Util }; use Rx\Exception\{ InvalidParamTypeException, @@ -37,7 +38,7 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) if (isset($schema->value)) { if (! is_string($schema->value)) { - throw new InvalidParamTypeException(sprintf('The `value` param for %s %s schema is not a string', $this->propName, static::TYPE)); + throw new InvalidParamTypeException(sprintf('The `value` for %s %s is not a string.', Util::formatPropName($this->propName), static::TYPE)); } $this->fixedValue = $schema->value; @@ -56,7 +57,7 @@ public function check($value): bool 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)); + 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))) { diff --git a/php/src/Rx/Core/TypeAbstract.php b/php/src/Rx/Core/TypeAbstract.php index e52a550..1a585a7 100644 --- a/php/src/Rx/Core/TypeAbstract.php +++ b/php/src/Rx/Core/TypeAbstract.php @@ -17,7 +17,7 @@ public function __construct(\stdClass $schema, Rx $rx, ?string $propName = null) $this->propName = $propName; - $this->checkSchema($schema, static::TYPE); + $this->checkSchema($schema); } diff --git a/php/src/Rx/Rx.php b/php/src/Rx/Rx.php index 0696719..c773ca9 100644 --- a/php/src/Rx/Rx.php +++ b/php/src/Rx/Rx.php @@ -39,13 +39,13 @@ private function expandUri(string $name): string if (preg_match('/^\\/(.*?)\\/(.+)$/', $name, $matches)) { if (! array_key_exists($matches[1], $this->prefixRegistry)) { - throw new RxException("Unknown type prefix '$matches[1]' in '$name'"); + 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"); + throw new RxException("Couldn't understand type name '$name'."); } @@ -53,7 +53,7 @@ public function addPrefix(string $name, string $base): void { if (isset($this->prefixRegistry[$name])) { - throw new RxException("The prefix '$name' is already registered"); + throw new RxException("The prefix '$name' is already registered."); } $this->prefixRegistry[$name] = $base; @@ -64,7 +64,7 @@ public function learnType(string $uri, \stdClass $schema): void { if (isset($this->typeRegistry->$uri)) { - throw new RxException("Tried to learn type for already-registered uri $uri"); + throw new RxException("Failed to learn type for already-registered uri $uri."); } // Make sure schema is valid @@ -84,13 +84,13 @@ public function makeSchema($schema, ?string $propName = null) } if (empty($schema->type)) { - throw new RxException("Can't make a schema with no `type` key."); + 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("Unknown type: $uri"); + throw new RxException(sprintf('Unknown type \'%s\' in %s.', $uri, Util::formatPropName($propName))); } $typeClass = $this->typeRegistry->$uri; @@ -98,7 +98,7 @@ public function makeSchema($schema, ?string $propName = null) if (is_array($typeClass) && isset($typeClass['schema'])) { foreach ($schema as $key => $entry) { if ($key != 'type') { - throw new RxException('Composed type does not take check arguments.'); + throw new RxException(sprintf('Composed type does not take additional arguments in %s.', Util::formatPropName($propName))); } } return $this->makeSchema($typeClass['schema']); diff --git a/php/src/Rx/Util.php b/php/src/Rx/Util.php index 6f0f98e..78a67df 100644 --- a/php/src/Rx/Util.php +++ b/php/src/Rx/Util.php @@ -8,6 +8,7 @@ final class Util public static function isSeqIntArray($value): bool { + if (! is_array($value)) { return false; } @@ -19,6 +20,14 @@ public static function isSeqIntArray($value): bool } return true; + + } + + public static function formatPropName(?string $propName = null): string + { + + return $propName ? sprintf('`%s`', $propName) : '_TOP_'; + } } \ No newline at end of file From a419ca7d5a82c5d31e5c7f0516533dbae2390792 Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Sat, 8 Jun 2019 15:03:32 +0100 Subject: [PATCH 4/7] License and composer changes --- LICENSE | 5 ++++- php/README.md | 42 +++++++++++++++++++++++++++++++++++++++++ php/composer.json | 6 +++++- php/{console.php => rx} | 16 +++++++++------- 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 php/README.md rename php/{console.php => rx} (83%) mode change 100644 => 100755 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/README.md b/php/README.md new file mode 100644 index 0000000..ae61905 --- /dev/null +++ b/php/README.md @@ -0,0 +1,42 @@ +# 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 + +## 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/rx +``` + +## Documentation + +[Check out the documentation](http://rx.codesimply.com/) + +## Usage + +* Create a schema file +* Run `./vendor/bin/rx ` + +For example, `./vendor/bin/rx test.yml schema.yml types/*.yml` \ No newline at end of file diff --git a/php/composer.json b/php/composer.json index 806022b..bf8d2da 100644 --- a/php/composer.json +++ b/php/composer.json @@ -5,6 +5,7 @@ "license": "GPL", "minimum-stability": "stable", "require": { + "php": ">=7.1.0", "symfony/yaml": "^4.3" }, "require-dev": { @@ -19,5 +20,8 @@ "psr-4": { "Test\\": "test/" } - } + }, + "bin": [ + "rx" + ] } diff --git a/php/console.php b/php/rx old mode 100644 new mode 100755 similarity index 83% rename from php/console.php rename to php/rx index b100839..b3ed5ef --- a/php/console.php +++ b/php/rx @@ -1,3 +1,4 @@ +#!/usr/bin/env php \n"; die(); } try { - $schemaFile = Yaml::parse(file_get_contents($argv[2]), Yaml::PARSE_OBJECT_FOR_MAP); + $credentialsFile = Yaml::parse(file_get_contents($argv[1]), Yaml::PARSE_OBJECT_FOR_MAP); } catch (Exception $e) { - echo " ❌ Unable to parse schema file.\n"; + echo " ❌ Unable to parse yaml/json file.\n"; echo " {$e->getMessage()}\n"; die(); } try { - $credentialsFile = Yaml::parse(file_get_contents($argv[1]), Yaml::PARSE_OBJECT_FOR_MAP); + $schemaFile = Yaml::parse(file_get_contents($argv[2]), Yaml::PARSE_OBJECT_FOR_MAP); } catch (Exception $e) { - echo " ❌ Unable to parse credentials file.\n"; + echo " ❌ Unable to parse schema file.\n"; echo " {$e->getMessage()}\n"; die(); } @@ -28,13 +30,13 @@ // Add custom types if ($argv[3]) { - $types = explode(';', $argv[3]); - foreach($types as $type) { + 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.\n"; + echo " ❌ Unable to add custom types via glob.\n"; echo " {$e->getMessage()}\n"; die(); } From be22a4cf7d056f855934be7598826eec42f7cc75 Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Sat, 8 Jun 2019 15:21:27 +0100 Subject: [PATCH 5/7] Clarifying glob instructions --- php/README.md | 5 +++-- php/rx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/php/README.md b/php/README.md index ae61905..d17bf16 100644 --- a/php/README.md +++ b/php/README.md @@ -37,6 +37,7 @@ composer require shaggy8871/rx ## Usage * Create a schema file -* Run `./vendor/bin/rx ` +* Run `./vendor/bin/rx [""]` +* Be sure to quote custom type glob! -For example, `./vendor/bin/rx test.yml schema.yml types/*.yml` \ No newline at end of file +For example, `./vendor/bin/rx test.yml schema.yml "types/*.yml"` \ No newline at end of file diff --git a/php/rx b/php/rx index b3ed5ef..0095c6f 100755 --- a/php/rx +++ b/php/rx @@ -8,7 +8,7 @@ use Symfony\Component\Yaml\Yaml; if (! isset($argv[1]) || ! isset($argv[2]) || ! file_exists($argv[1]) || ! file_exists($argv[2])) { echo " ❌ Unable to find file(s) in argument(s).\n"; - echo " Usage: ./rx \n"; + echo " Usage: ./rx [\"\"]\n"; die(); } try { From 4bcfec9435819cf18b6afcade0ffe877e6ef4c80 Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Sat, 8 Jun 2019 15:57:13 +0100 Subject: [PATCH 6/7] Updating license --- php/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/composer.json b/php/composer.json index bf8d2da..ee54005 100644 --- a/php/composer.json +++ b/php/composer.json @@ -2,7 +2,7 @@ "name": "shaggy8871/rx", "description": "The Rx schema and validation system", "type": "library", - "license": "GPL", + "license": "GPL-3.0-or-later", "minimum-stability": "stable", "require": { "php": ">=7.1.0", From 2839c9cefe4dafbe01ca331ae8d283e09cde0cba Mon Sep 17 00:00:00 2001 From: John Ginsberg Date: Sat, 8 Jun 2019 15:59:11 +0100 Subject: [PATCH 7/7] Updating README to point to standalone repo --- php/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/php/README.md b/php/README.md index d17bf16..d4502b0 100644 --- a/php/README.md +++ b/php/README.md @@ -5,6 +5,8 @@ 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 @@ -27,7 +29,7 @@ JSON-style data structures and is as easy to work with as JSON itself. ## Installation ``` -composer require shaggy8871/rx +composer require shaggy8871/php-rx ``` ## Documentation @@ -40,4 +42,4 @@ composer require shaggy8871/rx * Run `./vendor/bin/rx [""]` * Be sure to quote custom type glob! -For example, `./vendor/bin/rx test.yml schema.yml "types/*.yml"` \ No newline at end of file +For example, `./vendor/bin/rx test.yml schema.yml "types/*.yml"`