{"id":15025696,"url":"https://github.com/lezhnev74/pasvl","last_synced_at":"2025-04-09T20:04:28.590Z","repository":{"id":54568923,"uuid":"116412136","full_name":"lezhnev74/pasvl","owner":"lezhnev74","description":"Array Validator (regular expressions for arrays, sort of)","archived":false,"fork":false,"pushed_at":"2022-01-29T10:58:21.000Z","size":2939,"stargazers_count":50,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T20:04:20.501Z","etag":null,"topics":["array","php","php71","php72","php73","php74","validation"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lezhnev74.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-01-05T17:53:45.000Z","updated_at":"2024-04-03T14:57:16.000Z","dependencies_parsed_at":"2022-08-13T20:00:53.705Z","dependency_job_id":null,"html_url":"https://github.com/lezhnev74/pasvl","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lezhnev74%2Fpasvl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lezhnev74%2Fpasvl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lezhnev74%2Fpasvl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lezhnev74%2Fpasvl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lezhnev74","download_url":"https://codeload.github.com/lezhnev74/pasvl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248103865,"owners_count":21048245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","php","php71","php72","php73","php74","validation"],"created_at":"2024-09-24T20:02:51.174Z","updated_at":"2025-04-09T20:04:28.573Z","avatar_url":"https://github.com/lezhnev74.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Latest Stable Version](https://poser.pugx.org/lezhnev74/pasvl/v/stable)](https://packagist.org/packages/lezhnev74/pasvl)\n[![Build Status](https://travis-ci.org/lezhnev74/pasvl.svg?branch=master)](https://travis-ci.org/lezhnev74/pasvl)\n[![Total Downloads](https://poser.pugx.org/lezhnev74/pasvl/downloads)](https://packagist.org/packages/lezhnev74/pasvl)\n[![License](https://poser.pugx.org/lezhnev74/pasvl/license)](https://packagist.org/packages/lezhnev74/pasvl)\n\n# PASVL - PHP Array Structure Validation Library\n\nThink of a regular expression `[ab]+` which matches a string `abab`. Now imaging the same for arrays.\n\nThe purpose of this library is to validate an existing (nested) array against a template and report a mismatch. \nIt has the object-oriented extendable architecture to write and add custom validators.\n\n**Note to current users**: this version is not backwards compatible with the previous 0.5.6. \n\n## Installation\n```\ncomposer require lezhnev74/pasvl\n```\n\n## Example\n\nRefer to files in `Example` folder.\n\n## Usage\n\n### Array Validation\n```php\n// Define the pattern of the data, define keys and values separately\n$pattern = [\n    '*' =\u003e [\n        'type' =\u003e 'book',\n        'title' =\u003e ':string :contains(\"book\")',\n        'chapters' =\u003e [\n            ':string :len(2) {1,3}' =\u003e [\n                'title' =\u003e ':string',\n                ':exact(\"interesting\") ?' =\u003e ':bool',\n            ],\n        ],\n    ],\n];\n\n// Provide the data to match against the above pattern.\n$data = [\n    [\n        'type' =\u003e 'book',\n        'title' =\u003e 'Geography book',\n        'chapters' =\u003e [\n            'eu' =\u003e ['title' =\u003e 'Europe', 'interesting' =\u003e true],\n            'as' =\u003e ['title' =\u003e 'America', 'interesting' =\u003e false],\n        ],\n    ],\n    [\n        'type' =\u003e 'book',\n        'title' =\u003e 'Foreign languages book',\n        'chapters' =\u003e [\n            'de' =\u003e ['title' =\u003e 'Deutsch'],\n        ],\n    ],\n];\n\n$builder = \\PASVL\\Validation\\ValidatorBuilder::forArray($pattern);\n$validator = $builder-\u003ebuild();\ntry {\n    $validator-\u003evalidate($data);\n} catch (ArrayFailedValidation $e) {\n    // If data cannot be matched against the pattern, then exception is thrown.\n    // It is not always easy to detect why the data failed matching, the exception MAY sometimes give you extra hints.\n    echo \"failed: \" . $e-\u003egetMessage() . \"\\n\";\n}\n```\n\n### Optional String Validation\n```php\n$pattern = \":string :regexp('#^[ab]+$#')\";\n$builder = \\PASVL\\Validation\\ValidatorBuilder::forString($pattern);\n$validator = $builder-\u003ebuild();\n$validator-\u003evalidate(\"abab\"); // the string is valid\n$validator-\u003evalidate(\"abc\"); // throws RuleFailed exception with the message: \"string does not match regular expression ^[ab]+$\"\n```\n\n## Validation Language\nThis package supports a special dialect for validation specification.\nIt looks like this:\n\n![](pasvl.jpg)\n\n#### Short language reference:\n- **Rule Name**\n  Specify zero or one Rule Name to apply to the data. Optinal postfix `?` allows data to be `null`.\n  Refer to the set of built-in rules in `src/Validation/Rules/Library`. For custom rules read below under `Custom Rules`.\n  For example, `:string?` describes strings and `null`.  \n- **Sub-Rule Name**\n  Specify zero or more Sub-Rule Names to apply to the data AFTER the Rule is applied. Sub Rules are extra methods of the main Rule.\n  For example, `:number :float` describes floats.\n- **Quantifier**\n  Specify quantity expectations for data keys. If none is set then default is assumed - `!`.\n  Available quantifiers:                       \n  - `!` - one key required (default)\n  - `?` - optional key\n  - `*` - any count of keys\n  - `{2}` - strict keys count\n  - `{2,4}` - range of keys count\n  \n  For example:\n  ```php\n    $pattern = [\":string *\" =\u003e \":number\"];\n    // the above pattern matches data:\n    $data = [\"june\"=\u003e10, \"aug\" =\u003e \"11\"];\n  ```\n\n#### Pattern Definitions\n- as exact value\n  ```php\n  $pattern = [\"name\" =\u003e \":any\"]; // here the key is the exact value\n  $pattern = [\"name?\" =\u003e \":any\"]; // here the key is the exact value, can be absent as well\n  $pattern = [\":exact('name')\" =\u003e \":any\"]; // this is the same\n  ```\n- as nullable rule\n  ```php\n  $pattern = [\"name\" =\u003e \":string?\"]; // the value must be a string or null\n  ```\n- as rule with subrules\n  ```php\n  $pattern = [\"name\" =\u003e \":string :regexp('#\\d*#')\"]; // the value must be a string which contains only digits\n  ```\n- as rule with quantifiers\n  ```php\n  $pattern = [\":string {2}\" =\u003e \":any\"]; // data must have exactly two string keys\n  ```\n\n#### Compound Definitions\nThis package supports combinations of rules, expressed in a natural language.\nExamples:\n- `:string or :number`\n- `:string and :number`\n- `(:string and :number) or :array`\n\nThere are two combination operators: `and`, `or`. \n`and` operator has precedence. \nBoth are left-associative. \n\n## Custom Rules\nBy default, the system uses only the built-in rules. However you can extend them with your own implementations.\nTo add new custom rules, follow these steps:\n- implement your new rule as a class and extend it from `\\PASVL\\Validation\\Rules\\Rule`\n- implement a new rule locator by extending a class `\\PASVL\\Validation\\Rules\\RuleLocator`\n- configure your validator like this:\n  ```php\n  $builder = ValidatorBuilder::forArray($pattern)-\u003ewithLocator(new MyLocator()); // set your new locator\n  $validator = $builder-\u003ebuild();\n  ```\n\n## Built-in Rules\nThis package comes with a few built-in rules and their corresponding sub-rules (see in folder `src/Validation/Rules/Library`): \n- `:string` - the value must be string\n  - `:regexp(\u003cstring\u003e)` - provide a regular expression(the same as for `preg_match()`)\n  - `:url`\n  - `:email`\n  - `:uuid`\n  - `:contains(\u003cstring\u003e)`\n  - `:starts(\u003cstring\u003e)`\n  - `:ends(\u003cstring\u003e)`\n  - `:in(\u003cstring\u003e,\u003cstring\u003e,...)`\n  - `:len(\u003cint\u003e)`\n  - `:max(\u003cint\u003e)`\n  - `:min(\u003cint\u003e)`\n  - `:between(\u003cint\u003e,\u003cint\u003e)`\n- `:number`\n  - `:max(\u003cint\u003e)`\n  - `:min(\u003cint\u003e)`\n  - `:between(\u003cint\u003e, \u003cint\u003e)`\n  - `:int` - the number must be an integer\n  - `:float` - the number must be a float\n  - `:positive`\n  - `:negative`\n  - `:in(\u003ca\u003e,\u003cb\u003e,\u003cc\u003e)` - the number must be within values (type coercion possible)\n  - `:inStrict(\u003ca\u003e,\u003cb\u003e,\u003cc\u003e)` - the number must be within values (type coercion disabled)\n- `:exact(\u003cvalue\u003e)`  \n- `:bool(\u003c?value\u003e)` - the value must be boolean, if optional argument is given the value must be exactly it  \n- `:object`\n  - `:instance(\u003cfqcn\u003e)`\n  - `:propertyExists(\u003cstring\u003e)`\n  - `:methodExists(\u003cstring\u003e)`\n- `:array`\n  - `:count(\u003cint\u003e)`\n  - `:keys(\u003cstring\u003e,\u003cstring\u003e,...)`\n  - `:min(\u003cint\u003e)` - min count\n  - `:max(\u003cint\u003e)` - max count\n  - `:between(\u003cint\u003e, \u003cint\u003e)` - count must be within\n- `:any` - a placeholder, any value will match\n\n## Hints\n- PHP casts \"1\" to 1 for array keys:\n  ```php\n  $data = [\"12\" =\u003e \"\"];\n  $pattern_invalid = [\":string\" =\u003e \"\"];\n  $pattern_valid = [\":number :int\" =\u003e \"\"];\n  ```\n - Technically speaking PASVL is a non-deterministic backtracking parser, and thus it can't always show you what exact key did not match the pattern. That is because, say, a key can match different patterns and there is no way of knowing which one was meant to be correct. In such cases it returns a message like \"no matches found at X level\".\n\n## 🏆 Contributors\n- **[Greg Corrigan](https://github.com/corrigang)**. Greg spotted a problem with nullable values reported as invalid.\n- **Henry Combrinck**. Henry tested the library extensively on real data and found tricky bugs and edge cases. Awesome contribution to make the package valuable to the community.\n- **[@Averor](https://github.com/Averor)**. Found a bug in parentheses parsing.\n- **[Julien Gidel](https://github.com/JuGid)**. Improved `regexp` sub-rule.\n\n## License\nThis project is licensed under the terms of the MIT license.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flezhnev74%2Fpasvl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flezhnev74%2Fpasvl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flezhnev74%2Fpasvl/lists"}