{"id":14155154,"url":"https://github.com/nette/schema","last_synced_at":"2026-02-08T04:10:30.805Z","repository":{"id":34378452,"uuid":"178221375","full_name":"nette/schema","owner":"nette","description":"📐 Validating data structures against a given Schema.","archived":false,"fork":false,"pushed_at":"2026-01-09T10:32:34.000Z","size":174,"stargazers_count":999,"open_issues_count":9,"forks_count":29,"subscribers_count":23,"default_branch":"master","last_synced_at":"2026-01-26T22:28:59.653Z","etag":null,"topics":["data-structures","json","nette","nette-framework","php","schema","validation"],"latest_commit_sha":null,"homepage":"https://doc.nette.org/schema","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nette.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"dg","custom":"https://nette.org/donate"}},"created_at":"2019-03-28T14:33:39.000Z","updated_at":"2026-01-21T18:54:34.000Z","dependencies_parsed_at":"2023-10-02T00:21:29.338Z","dependency_job_id":"eb663540-8fe2-4358-899e-784f66666f6c","html_url":"https://github.com/nette/schema","commit_stats":{"total_commits":123,"total_committers":9,"mean_commits":"13.666666666666666","dds":0.09756097560975607,"last_synced_commit":"2073a5a4156aa8f2849f2e81e2b743f338ed3f45"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/nette/schema","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fschema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fschema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fschema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fschema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nette","download_url":"https://codeload.github.com/nette/schema/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nette%2Fschema/sbom","scorecard":{"id":681060,"data":{"date":"2025-08-11","repo":{"name":"github.com/nette/schema","commit":"2a035ff9ec0d3b87e981ab4ecd2586ddfceb3b9f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.1,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":10,"reason":"15 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: license.md:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":9,"reason":"security policy file detected","details":["Info: security policy file detected: github.com/nette/.github/.github/security.md:1","Info: Found linked content: github.com/nette/.github/.github/security.md:1","Warn: One or no descriptive hints of disclosure, vulnerability, and/or timelines in security policy","Info: Found text in security policy: github.com/nette/.github/.github/security.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}}]},"last_synced_at":"2025-08-21T23:11:10.339Z","repository_id":34378452,"created_at":"2025-08-21T23:11:10.340Z","updated_at":"2025-08-21T23:11:10.340Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29219508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T03:18:47.732Z","status":"ssl_error","status_checked_at":"2026-02-08T03:15:31.985Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["data-structures","json","nette","nette-framework","php","schema","validation"],"created_at":"2024-08-17T08:02:17.765Z","updated_at":"2026-02-08T04:10:30.800Z","avatar_url":"https://github.com/nette.png","language":"PHP","readme":"# Nette Schema\n\n[![Downloads this Month](https://img.shields.io/packagist/dm/nette/schema.svg)](https://packagist.org/packages/nette/schema)\n[![Tests](https://github.com/nette/schema/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/schema/actions)\n[![Coverage Status](https://coveralls.io/repos/github/nette/schema/badge.svg?branch=master)](https://coveralls.io/github/nette/schema?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/nette/schema/v/stable)](https://github.com/nette/schema/releases)\n[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/schema/blob/master/license.md)\n\n\nIntroduction\n============\n\nA practical library for validation and normalization of data structures against a given schema with a smart \u0026 easy-to-understand API.\n\nDocumentation can be found on the [website](https://doc.nette.org/schema).\n\nInstallation:\n\n```shell\ncomposer require nette/schema\n```\n\nIt requires PHP version 8.1 and supports PHP up to 8.5.\n\n\n[Support Me](https://github.com/sponsors/dg)\n--------------------------------------------\n\nDo you like Nette Schema? Are you looking forward to the new features?\n\n[![Buy me a coffee](https://files.nette.org/icons/donation-3.svg)](https://github.com/sponsors/dg)\n\nThank you!\n\n\nBasic Usage\n-----------\n\nIn variable `$schema` we have a validation schema (what exactly this means and how to create it we will say later) and in variable `$data` we have a data structure that we want to validate and normalize. This can be, for example, data sent by the user through an API, configuration file, etc.\n\nThe task is handled by the [Nette\\Schema\\Processor](https://api.nette.org/schema/master/Nette/Schema/Processor.html) class, which processes the input and either returns normalized data or throws an [Nette\\Schema\\ValidationException](https://api.nette.org/schema/master/Nette/Schema/ValidationException.html) exception on error.\n\n```php\n$processor = new Nette\\Schema\\Processor;\n\ntry {\n\t$normalized = $processor-\u003eprocess($schema, $data);\n} catch (Nette\\Schema\\ValidationException $e) {\n\techo 'Data is invalid: ' . $e-\u003egetMessage();\n}\n```\n\nMethod `$e-\u003egetMessages()` returns array of all message strings and `$e-\u003egetMessageObjects()` return all messages as [Nette\\Schema\\Message](https://api.nette.org/schema/master/Nette/Schema/Message.html) objects.\n\n\nDefining Schema\n---------------\n\nAnd now let's create a schema. The class [Nette\\Schema\\Expect](https://api.nette.org/schema/master/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int.\n\n```php\nuse Nette\\Schema\\Expect;\n\n$schema = Expect::structure([\n\t'processRefund' =\u003e Expect::bool(),\n\t'refundAmount' =\u003e Expect::int(),\n]);\n```\n\nWe believe that the schema definition looks clear, even if you see it for the very first time.\n\nLets send the following data for validation:\n\n```php\n$data = [\n\t'processRefund' =\u003e true,\n\t'refundAmount' =\u003e 17,\n];\n\n$normalized = $processor-\u003eprocess($schema, $data); // OK, it passes\n```\n\nThe output, i.e. the value `$normalized`, is the object `stdClass`. If we want the output to be an array, we add a cast to schema `Expect::structure([...])-\u003ecastTo('array')`.\n\nAll elements of the structure are optional and have a default value `null`. Example:\n\n```php\n$data = [\n\t'refundAmount' =\u003e 17,\n];\n\n$normalized = $processor-\u003eprocess($schema, $data); // OK, it passes\n// $normalized = {'processRefund' =\u003e null, 'refundAmount' =\u003e 17}\n```\n\nThe fact that the default value is `null` does not mean that it would be accepted in the input data `'processRefund' =\u003e null`. No, the input must be boolean, i.e. only `true` or `false`. We would have to explicitly allow `null` via `Expect::bool()-\u003enullable()`.\n\nAn item can be made mandatory using `Expect::bool()-\u003erequired()`. We change the default value to `false` using `Expect::bool()-\u003edefault(false)` or shortly using `Expect::bool(false)`.\n\nAnd what if we wanted to accept `1` and `0` besides booleans? Then we list the allowed values, which we will also normalize to boolean:\n\n```php\n$schema = Expect::structure([\n\t'processRefund' =\u003e Expect::anyOf(true, false, 1, 0)-\u003ecastTo('bool'),\n\t'refundAmount' =\u003e Expect::int(),\n]);\n\n$normalized = $processor-\u003eprocess($schema, $data);\nis_bool($normalized-\u003eprocessRefund); // true\n```\n\nNow you know the basics of how the schema is defined and how the individual elements of the structure behave. We will now show what all the other elements can be used in defining a schema.\n\n\nData Types: type()\n------------------\n\nAll standard PHP data types can be listed in the schema:\n\n```php\nExpect::string($default = null)\nExpect::int($default = null)\nExpect::float($default = null)\nExpect::bool($default = null)\nExpect::null()\nExpect::array($default = [])\n```\n\nAnd then all types [supported by the Validators](https://doc.nette.org/validators#toc-validation-rules) via `Expect::type('scalar')` or abbreviated `Expect::scalar()`. Also class or interface names are accepted, e.g. `Expect::type('AddressEntity')`.\n\nYou can also use union notation:\n\n```php\nExpect::type('bool|string|array')\n```\n\nThe default value is always `null` except for `array` and `list`, where it is an empty array. (A list is an array indexed in ascending order of numeric keys from zero, that is, a non-associative array).\n\n\nArray of Values: arrayOf() listOf()\n-----------------------------------\n\nThe array is too general structure, it is more useful to specify exactly what elements it can contain. For example, an array whose elements can only be strings:\n\n```php\n$schema = Expect::arrayOf('string');\n\n$processor-\u003eprocess($schema, ['hello', 'world']); // OK\n$processor-\u003eprocess($schema, ['a' =\u003e 'hello', 'b' =\u003e 'world']); // OK\n$processor-\u003eprocess($schema, ['key' =\u003e 123]); // ERROR: 123 is not a string\n```\n\nThe second parameter can be used to specify keys (since version 1.2):\n\n```php\n$schema = Expect::arrayOf('string', 'int');\n\n$processor-\u003eprocess($schema, ['hello', 'world']); // OK\n$processor-\u003eprocess($schema, ['a' =\u003e 'hello']); // ERROR: 'a' is not int\n```\n\nThe list is an indexed array:\n\n```php\n$schema = Expect::listOf('string');\n\n$processor-\u003eprocess($schema, ['a', 'b']); // OK\n$processor-\u003eprocess($schema, ['a', 123]); // ERROR: 123 is not a string\n$processor-\u003eprocess($schema, ['key' =\u003e 'a']); // ERROR: is not a list\n$processor-\u003eprocess($schema, [1 =\u003e 'a', 0 =\u003e 'b']); // ERROR: is not a list\n```\n\nThe parameter can also be a schema, so we can write:\n\n```php\nExpect::arrayOf(Expect::bool())\n```\n\nThe default value is an empty array. If you specify a default value and call `mergeDefaults()`, it will be merged with the passed data.\n\n\nEnumeration: anyOf()\n--------------------\n\n`anyOf()` is a set of values ​​or schemas that a value can be. Here's how to write an array of elements that can be either `'a'`, `true`, or `null`:\n\n```php\n$schema = Expect::listOf(\n\tExpect::anyOf('a', true, null),\n);\n\n$processor-\u003eprocess($schema, ['a', true, null, 'a']); // OK\n$processor-\u003eprocess($schema, ['a', false]); // ERROR: false does not belong there\n```\n\nThe enumeration elements can also be schemas:\n\n```php\n$schema = Expect::listOf(\n\tExpect::anyOf(Expect::string(), true, null),\n);\n\n$processor-\u003eprocess($schema, ['foo', true, null, 'bar']); // OK\n$processor-\u003eprocess($schema, [123]); // ERROR\n```\n\nThe `anyOf()` method accepts variants as individual parameters, not as array. To pass it an array of values, use the unpacking operator `anyOf(...$variants)`.\n\nThe default value is `null`. Use the `firstIsDefault()` method to make the first element the default:\n\n```php\n// default is 'hello'\nExpect::anyOf(Expect::string('hello'), true, null)-\u003efirstIsDefault();\n```\n\n\nStructures\n----------\n\nStructures are objects with defined keys. Each of these key =\u003e value pairs is referred to as a \"property\":\n\nStructures accept arrays and objects and return objects `stdClass` (unless you change it with `castTo('array')`, etc.).\n\nBy default, all properties are optional and have a default value of `null`. You can define mandatory properties using `required()`:\n\n```php\n$schema = Expect::structure([\n\t'required' =\u003e Expect::string()-\u003erequired(),\n\t'optional' =\u003e Expect::string(), // the default value is null\n]);\n\n$processor-\u003eprocess($schema, ['optional' =\u003e '']);\n// ERROR: option 'required' is missing\n\n$processor-\u003eprocess($schema, ['required' =\u003e 'foo']);\n// OK, returns {'required' =\u003e 'foo', 'optional' =\u003e null}\n```\n\nIf you do not want to output properties with only a default value, use `skipDefaults()`:\n\n```php\n$schema = Expect::structure([\n\t'required' =\u003e Expect::string()-\u003erequired(),\n\t'optional' =\u003e Expect::string(),\n])-\u003eskipDefaults();\n\n$processor-\u003eprocess($schema, ['required' =\u003e 'foo']);\n// OK, returns {'required' =\u003e 'foo'}\n```\n\nAlthough `null` is the default value of the `optional` property, it is not allowed in the input data (the value must be a string). Properties accepting `null` are defined using `nullable()`:\n\n```php\n$schema = Expect::structure([\n\t'optional' =\u003e Expect::string(),\n\t'nullable' =\u003e Expect::string()-\u003enullable(),\n]);\n\n$processor-\u003eprocess($schema, ['optional' =\u003e null]);\n// ERROR: 'optional' expects to be string, null given.\n\n$processor-\u003eprocess($schema, ['nullable' =\u003e null]);\n// OK, returns {'optional' =\u003e null, 'nullable' =\u003e null}\n```\n\nBy default, there can be no extra items in the input data:\n\n```php\n$schema = Expect::structure([\n\t'key' =\u003e Expect::string(),\n]);\n\n$processor-\u003eprocess($schema, ['additional' =\u003e 1]);\n// ERROR: Unexpected item 'additional'\n```\n\nWhich we can change with `otherItems()`. As a parameter, we will specify the schema for each extra element:\n\n```php\n$schema = Expect::structure([\n\t'key' =\u003e Expect::string(),\n])-\u003eotherItems(Expect::int());\n\n$processor-\u003eprocess($schema, ['additional' =\u003e 1]); // OK\n$processor-\u003eprocess($schema, ['additional' =\u003e true]); // ERROR\n```\n\n\nDeprecations\n------------\n\nYou can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor-\u003egetWarnings()`:\n\n```php\n$schema = Expect::structure([\n\t'old' =\u003e Expect::int()-\u003edeprecated('The item %path% is deprecated'),\n]);\n\n$processor-\u003eprocess($schema, ['old' =\u003e 1]); // OK\n$processor-\u003egetWarnings(); // [\"The item 'old' is deprecated\"]\n```\n\n\nRanges: min() max()\n-------------------\n\nUse `min()` and `max()` to limit the number of elements for arrays:\n\n```php\n// array, at least 10 items, maximum 20 items\nExpect::array()-\u003emin(10)-\u003emax(20);\n```\n\nFor strings, limit their length:\n\n```php\n// string, at least 10 characters long, maximum 20 characters\nExpect::string()-\u003emin(10)-\u003emax(20);\n```\n\nFor numbers, limit their value:\n\n```php\n// integer, between 10 and 20 inclusive\nExpect::int()-\u003emin(10)-\u003emax(20);\n```\n\nOf course, it is possible to mention only `min()`, or only `max()`:\n\n```php\n// string, maximum 20 characters\nExpect::string()-\u003emax(20);\n```\n\n\nRegular Expressions: pattern()\n------------------------------\n\nUsing `pattern()`, you can specify a regular expression which the **whole** input string must match (i.e. as if it were wrapped in characters `^` a `$`):\n\n```php\n// just 9 digits\nExpect::string()-\u003epattern('\\d{9}');\n```\n\n\nCustom Assertions: assert()\n---------------------------\n\nYou can add any other restrictions using `assert(callable $fn)`.\n\n```php\n$countIsEven = fn($v) =\u003e count($v) % 2 === 0;\n\n$schema = Expect::arrayOf('string')\n\t-\u003eassert($countIsEven); // the count must be even\n\n$processor-\u003eprocess($schema, ['a', 'b']); // OK\n$processor-\u003eprocess($schema, ['a', 'b', 'c']); // ERROR: 3 is not even\n```\n\nOr\n\n```php\nExpect::string()-\u003eassert('is_file'); // the file must exist\n```\n\nYou can add your own description for each assertion. It will be part of the error message.\n\n```php\n$schema = Expect::arrayOf('string')\n\t-\u003eassert($countIsEven, 'Even items in array');\n\n$processor-\u003eprocess($schema, ['a', 'b', 'c']);\n// Failed assertion \"Even items in array\" for item with value array.\n```\n\nThe method can be called repeatedly to add multiple constraints. It can be intermixed with calls to `transform()` and `castTo()`.\n\n\nTransformation: transform()\n---------------------------\n\nSuccessfully validated data can be modified using a custom function:\n\n```php\n// conversion to uppercase:\nExpect::string()-\u003etransform(fn(string $s) =\u003e strtoupper($s));\n```\n\nThe method can be called repeatedly to add multiple transformations. It can be intermixed with calls to `assert()` and `castTo()`. The operations will be executed in the order in which they are declared:\n\n```php\nExpect::type('string|int')\n\t-\u003ecastTo('string')\n\t-\u003eassert('ctype_lower', 'All characters must be lowercased')\n\t-\u003etransform(fn(string $s) =\u003e strtoupper($s)); // conversion to uppercase\n```\n\nThe `transform()` method can both transform and validate the value simultaneously. This is often simpler and less redundant than chaining `transform()` and `assert()`. For this purpose, the function receives a [Nette\\Schema\\Context](https://api.nette.org/schema/master/Nette/Schema/Context.html) object with an `addError()` method, which can be used to add information about validation issues:\n\n```php\nExpect::string()\n\t-\u003etransform(function (string $s, Nette\\Schema\\Context $context) {\n\t\tif (!ctype_lower($s)) {\n\t\t\t$context-\u003eaddError('All characters must be lowercased', 'my.case.error');\n\t\t\treturn null;\n\t\t}\n\n\t\treturn strtoupper($s);\n\t});\n```\n\n\nCasting: castTo()\n-----------------\n\nSuccessfully validated data can be cast:\n\n```php\nExpect::scalar()-\u003ecastTo('string');\n```\n\nIn addition to native PHP types, you can also cast to classes. It distinguishes whether it is a simple class without a constructor or a class with a constructor. If the class has no constructor, an instance of it is created and all elements of the structure are written to its properties:\n\n```php\nclass Info\n{\n\tpublic bool $processRefund;\n\tpublic int $refundAmount;\n}\n\nExpect::structure([\n\t'processRefund' =\u003e Expect::bool(),\n\t'refundAmount' =\u003e Expect::int(),\n])-\u003ecastTo(Info::class);\n\n// creates '$obj = new Info' and writes to $obj-\u003eprocessRefund and $obj-\u003erefundAmount\n```\n\nIf the class has a constructor, the elements of the structure are passed as named parameters to the constructor:\n\n```php\nclass Info\n{\n\tpublic function __construct(\n\t\tpublic bool $processRefund,\n\t\tpublic int $refundAmount,\n\t) {\n\t}\n}\n\n// creates $obj = new Info(processRefund: ..., refundAmount: ...)\n```\n\nCasting combined with a scalar parameter creates an object and passes the value as the sole parameter to the constructor:\n\n```php\nExpect::string()-\u003ecastTo(DateTime::class);\n// creates new DateTime(...)\n```\n\n\nNormalization: before()\n-----------------------\n\nPrior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`:\n\n```php\n$explode = fn($v) =\u003e explode(' ', $v);\n\n$schema = Expect::arrayOf('string')\n\t-\u003ebefore($explode);\n\n$normalized = $processor-\u003eprocess($schema, 'a b c');\n// OK, returns ['a', 'b', 'c']\n```\n\n\nMapping to Objects: from()\n--------------------------\n\nYou can generate structure schema from the class. Example:\n\n```php\nclass Config\n{\n\tpublic string $name;\n\tpublic ?string $password;\n\tpublic bool $admin = false;\n}\n\n$schema = Expect::from(new Config);\n\n$data = [\n\t'name' =\u003e 'jeff',\n];\n\n$normalized = $processor-\u003eprocess($schema, $data);\n// $normalized instanceof Config\n// $normalized = {'name' =\u003e 'jeff', 'password' =\u003e null, 'admin' =\u003e false}\n```\n\nAnonymous classes are also supported:\n\n```php\n$schema = Expect::from(new class {\n\tpublic string $name;\n\tpublic ?string $password;\n\tpublic bool $admin = false;\n});\n```\n\nBecause the information obtained from the class definition may not be sufficient, you can add a custom schema for the elements with the second parameter:\n\n```php\n$schema = Expect::from(new Config, [\n\t'name' =\u003e Expect::string()-\u003epattern('\\w:.*'),\n]);\n```\n","funding_links":["https://github.com/sponsors/dg","https://nette.org/donate"],"categories":["php"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnette%2Fschema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnette%2Fschema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnette%2Fschema/lists"}