For data that the program receives from the outside, it is customary to follow the rule of trustno1. This is true not only for data received directly from the user, but also for data that the client code transmits to the routines.
PHP 7 is equipped with an advanced type control system for arguments, including not only classes, but also scalars. However, with regard to complex data structures, nothing has changed - for them there is a single type of
array , which in PHP can contain anything.
I hope that new versions of PHP will fix the situation. And at the moment I want to share with the community some of my best practices in this area:
')
alexeymaximov / fulltypealexeymaximov / containumUPD 02.12.2018 - The publication has been updated to match the documentation provided with the current library versions.Fulltype
GitHub: alexeymaximov / fulltypeThis library is designed directly for working with types. You can define your own data type using the function
ArsMnemonica \ Fulltype \ fulltype :: define :
function define(string $aName, TypeInterface $aType): TypeInterface;
You can both create and instantiate your own class that implements the
ArsMnemonica \ Fulltype \ TypeInterface interface , or use the built-in class.
The function
ArsMnemonica \ Fulltype \ fulltype :: type is used to refer to the type :
function type(string $aName): TypeInterface;
It takes the name of the type as an argument (the aName argument to the define function), and returns the corresponding object.
To check the value of the corresponding type, use the function
ArsMnemonica \ Fulltype \ fulltype :: is :
function is($aValue, TypeInterface $aType): bool;
or by the
validate method of the type object itself:
function TypeInterface::validate($aValue): bool;
To check the value against the type, you can also use the function
ArsMnemonica \ Fulltype \ fulltype :: assert :
function assert($aValue, TypeInterface $aType): mixed;
This function returns the $ aValue value passed to it in case of a successful check, or throws an exception inherited from TypeError, the message of which contains the description of the transferred value and the place where the assert function is called.
The following built-in types are defined (
ArsMnemonica \ Fulltype namespace and
fulltype class):
function bool(): BooleanType;
Boolean true / false.
function number(float $aMin = null, float $aMax = null): NumericType; function int(int $aMin = null, int $aMax = null): IntegerType; function cardinal(int $aMax = null): CardinalType;
Numeric types
The type NumericType corresponds to the php types int and float.
Its successor type IntegerType corresponds only to the PHP type int.
Both types can be limited to minimum and maximum values.
The CardinalType type, which is an inheritor of IntType, corresponds to quantitative numbers (unsigned integers) - its minimum value is 0, and the maximum can be determined.
function string(int $aLength = null): StringType;
String type, may be limited by maximum length.
function regexp(string $aRegularExpression): RegularExpressionType;
Regular expression.
function enum(...$aValues): EnumerableType;
Enum type
Limits the set of valid values ​​to a given set.
function object(string $aBase = null): ObjectType;
Object type
The value can only be an object of a given class (interfaces are also valid).
function null(TypeInterface $aType): NullableType;
Nullable-type.
Supplements the set of valid values ​​of the child type with a value of null.
function array(int $aLength = null): ArrayType;
A massive type (the array keyword is not allowed as a function name) may be limited by the maximum length.
The value can be:
- array;
- an object that implements the interfaces ArrayAccess, Countable and Traversable (
objective method should be called).
To set a valid type of array values, use the
of (TypeInterface) method, and for keys, use the
by (TypeInterface) method. If you specify a key type other than the PHP types int and string, the massive type will only make sense in relation to objects, since PHP arrays cannot have keys of other types.
function any(TypeInterface ...$aTypes): MultipleType;
A structured type that requires the value passed for validation to match at least one of its child types.
function union(TypeInterface ...$aTypes): ExclusiveType;
Structural type that requires matching the value passed for validation to strictly one of the child types.
function struct(TypeInterface ...$aTypes): ComplexType;
Structural type that requires matching the value passed for validation to all child types.
For this type it makes sense to set the following child types:
function key(string $aName, TypeInterface $aType = null): PropertyType
A key type that requires the value passed for validation to be an array containing a string key of the specified type.
function offset(string $aName, TypeInterface $aType = null): PropertyType
The type offset that requires the value passed for validation to be an array or an object that implements the ArrayAccess interface containing a string offset of the specified type.
function property(string $aName, TypeInterface $aType = null): PropertyType
A type is a property that requires a value passed for validation to be an object containing a property of the specified type.
If the
optional method is called for the above types, then the corresponding test assumes the absence of such a key / offset / property.
To illustrate the work of the library, consider the following example:
use ArsMnemonica\Fulltype\fulltype as t; const input_t = 'input'; t::define(input_t, t::struct( t::key('name', t::string()), t::key('authors', t::any( t::string(), t::array()->of(t::string()) )), t::union( t::key('text', t::string()), t::key('content', t::struct( t::key('title', t::string(255)), t::key✱('annotation', t::string(65535)), t::key('text', t::string()), t::key✱('pages', t::cardinal?(5000)) )) ), t::key('status', t::enum('WILL_READ', 'READ', 'FAVORITE_BOOK')) )); echo "Processing input...\n"; if (PHP_SAPI === 'cli') { $input = []; parse_str(implode('&', array_slice($argv, 1)), $input); } else { $input = $_GET; } foreach ($input as $key => $value) { echo "$key: " . json_encode($value) . "\n"; } echo "Validation: " . (t::is($input, t::type(input_t)) ? 'success' : 'failed') . PHP_EOL;
This code checks the correctness of the transferred description of the element of the book series:
- The required parameter name must be a string of arbitrary length.
- The required parameter authors must be a string of arbitrary length or an array of such strings.
- The text parameter, which is a string of arbitrary length, or the composite content parameter can be passed.
- The required status parameter must be one of the specified values.
Such a set of parameters will be valid:
name="The Lord of the Rings" authors[]="JRR Tolkien" content[title]="The Return of the King" content[text]=... status=READ
And this will not pass the test:
name="The Lord of the Rings" authors[]="JRR Tolkien" text=... content[title]="The Return of the King" content[text]=... status=READ
Instead of conclusion
Although all of this is to some extent a set of bicycles, but I hope that he can come in handy for someone at work. typedef can be useful for checking script parameters along with their conversion using json_decode. And containers can be useful for restricting the types of arrays in arguments using ready-made tools.
It would be possible to add typed properties of objects, arrange libraries in the form of extensions to improve performance, or do something else thoughtless, but so far I do not see an urgent need for this.

Also, I will be glad to hear constructive criticism and to improve something in these simple tools or to learn about some silver-bullet whistled past me.
Thank you for your attention!