Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema for unified automatic configuration validation #191

Merged
merged 8 commits into from Mar 26, 2019
Merged

Conversation

@dg
Copy link
Member

dg commented Mar 12, 2019

  • BC break? no

CompilerExtension can provide a schema which is automatically used to validate and normalize configuration:

	public function getConfigSchema(): Nette\DI\Config\Schema
	{
		return Expect::structure([
			'user' => Expect::string()
			'password' => Expect::string()
			'debugger' => Expect::bool(true),
		]);
	}

These rules are used:

  • each item is optional unless you change it via Expect::bool()->required()
  • the default value is null (and [] for array, list and struct) unless you change it via Expect::bool()->default($default) or simply Expect::bool($default)
  • items are not nullable by default
  • syntax of Validators from Nette\Utils can be used, for example Expect::type('string|null') or Expect::type('string|stdClass[]') etc.

This schema describes array with exactly two items:

		return Expect::structure([
			'name' => Expect::string()->required(),
			'password' => Expect::string(),
		]);

Extra items can be allowed via otherItems($type), ie:

		return Expect::structure([
			'name' => Expect::string()->required(),
			'password' => Expect::string(),
		])->otherItems('string');

You can use constraints min() and max() to number values, but also size of strings or arrays:

		Expect::listOf('string')->min(1);

You can add additional assertions:

		Expect::string()->assert('is_file')->assert('is_readable');

Before configuration is processed, it can be normalized:

		return Expect::structure([
			'name' => Expect::string()->required(),
			'password' => Expect::string(),
		])->before(function ($input) { return $output; });

Examples of usage:

@dg dg force-pushed the nette:master branch from f3bf533 to f96d97d Mar 12, 2019
@holantomas

This comment has been minimized.

Copy link

holantomas commented Mar 12, 2019

Great! Only one thing. Did you think about something like Expect::string()->nullable(); instead of using enum()? If you look on database schema it's look little bit confusing(for me).

Expect::arrayOf([
			'dsn' => Expect::string()->required(), // Have to be presented, but CAN be null?
			'user' => Expect::enum(Expect::string(), null), // Have not to be presented and CAN be null
			'reflection' => Expect::string(), // Have not to be presented but CANNOT be null?
			//...
		])

Maybe this look better

Expect::arrayOf([
			'dsn' => Expect::string()->required(), // Have to be present and CANNOT be null
			'user' => Expect::string()->nullable(), // Have not be presented and CAN be null
			'reflection' => Expect::string(), // Have not to be presented but CANNOT be null
			'password' => Expect::string()->required()->nullable(), // Have to be presented but CAN be null
			//...
		])

Or am I missing something?

@dg dg force-pushed the nette:master branch from f96d97d to db0bc17 Mar 13, 2019
@dg dg force-pushed the dg:schema branch from 4ff1c3b to 09b457d Mar 13, 2019
@dakorpar

This comment has been minimized.

Copy link

dakorpar commented Mar 13, 2019

@dg why not making class for config, that way we're far more strict and full autocomplete in all ides, and with php7.4 we will have typehints on properties? Wouldn't that be far better then this dynamic validation?
Since it is huge BC it can also be added abstract config class which implements \ArrayAccess with deprecation warning or something like that. Function for receiving array (from neon) is also needed. Another good way would also be with getters and setters and have that typed immidiatelly.

}


public static function type(string $type): self

This comment has been minimized.

Copy link
@milo

milo Mar 13, 2019

Member

What about:

function type(string ...$types): self
{
    return new self(implode('|', $types));
}

To be able write:

Expect::type('string', Nette\DI\Definitions\Statement::class)

or own method for it.

@dg dg force-pushed the nette:master branch from 1e71ae6 to abe132f Mar 13, 2019
@dg

This comment has been minimized.

Copy link
Member Author

dg commented Mar 14, 2019

@dakorpar I also thought about using the class for configuration and it's definitely a good idea.

@dg

This comment has been minimized.

Copy link
Member Author

dg commented Mar 14, 2019

image

@dg dg force-pushed the nette:master branch from abe132f to 805e3c8 Mar 14, 2019
@dg dg force-pushed the dg:schema branch 2 times, most recently from 7e4108f to d9f7d5f Mar 15, 2019
@dg dg force-pushed the nette:master branch 2 times, most recently from c84fbc6 to 3f1cadd Mar 15, 2019
@dg dg force-pushed the dg:schema branch 3 times, most recently from 8d57c37 to d2d68df Mar 15, 2019
@dg dg force-pushed the nette:master branch 2 times, most recently from 02ef205 to 9bd6554 Mar 17, 2019
@dg dg force-pushed the dg:schema branch from d2d68df to 7d37336 Mar 17, 2019
@dg dg force-pushed the nette:master branch 3 times, most recently from 1adcd1c to 18fdc11 Mar 17, 2019
@dg dg force-pushed the dg:schema branch from 7d37336 to 81bf3d3 Mar 25, 2019
@dg dg force-pushed the nette:master branch from 18fdc11 to 0a0d880 Mar 25, 2019
@dg dg force-pushed the dg:schema branch from 81bf3d3 to f259de1 Mar 25, 2019
@dg dg force-pushed the nette:master branch from 0a0d880 to 798c163 Mar 25, 2019
@dg dg force-pushed the dg:schema branch from f259de1 to f0b85e9 Mar 26, 2019
@dg dg merged commit d46798e into nette:master Mar 26, 2019
1 check was pending
1 check was pending
continuous-integration/travis-ci/pr The Travis CI build is in progress
Details
@dg dg deleted the dg:schema branch Mar 26, 2019
@ondrejmirtes

This comment has been minimized.

Copy link
Contributor

ondrejmirtes commented Mar 27, 2019

Cool! Can I somehow use the system to validate what users have in their parameters section?

@dg

This comment has been minimized.

Copy link
Member Author

dg commented Mar 27, 2019

@ondrejmirtes of course!

@dg

This comment has been minimized.

Copy link
Member Author

dg commented Mar 27, 2019

@holantomas I added nullable() and require(), thanks for suggestions

@trejjam

This comment has been minimized.

Copy link
Contributor

trejjam commented Mar 27, 2019

Hi, thanks for this I really like it!
Is there any recomemded way how to define schema for a pre-v3 compatible nette extensions?
My point is that when I define getConfigSchema(): Nette\DI\Config\Schema method in extensions class, php compiler will yell at me that it does not know class Nette\DI\Config\Schema.

@JanTvrdik

This comment has been minimized.

Copy link
Contributor

JanTvrdik commented Mar 28, 2019

php compiler will yell at me

Are you sure? Afaik return types do not trigger autoloading.

trejjam referenced this pull request in contributte/thepay Mar 30, 2019
@trejjam

This comment has been minimized.

Copy link
Contributor

trejjam commented Mar 30, 2019

I am not sure, thanks. It was a habit from compiled languages.

@fmasa fmasa mentioned this pull request Apr 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants
You can’t perform that action at this time.