📜 ⬆️ ⬇️

PHP user types

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:
')
image

alexeymaximov / fulltype
alexeymaximov / containum

UPD 02.12.2018 - The publication has been updated to match the documentation provided with the current library versions.

Fulltype


GitHub: alexeymaximov / fulltype

This 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:


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.

image

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!

Source: https://habr.com/ru/post/331160/


All Articles