amsify42 / php-typestruct
PHP package for validating data against the structure defined
Requires
- php: >=7.0.0
- amsify42/php-vars-data: dev-master
Requires (Dev)
- phpunit/phpunit: ^7.0
This package is not auto-updated.
Last update: 2024-11-20 01:38:51 UTC
README
PHP package for validating data against the structure defined.
Installation
$ composer require amsify42/php-typestruct
Table of Contents
1. Introduction
The purpose of this php package is to make validation easy and the structure defined for validation should be readable. The data passed can be validated against the structure defined.
2. Validation
Let's say we have data in array format.
$data = [ 'id' => 42, 'name' => 'amsify', 'price' => 4.2 ];
and we want this to be strictly validated. Now we can define the structure against which this data to be validated.
namespace App\TypeStruct; export typestruct Simple { id: int, name: string, price: float }
Notice that the structure we defined does not completely look like a PHP syntax but it will work as a structure against data.
$data = [ 'id' => 42, 'name' => 'amsify', 'price' => 4.2 ]; $typeStruct = new Amsify42\TypeStruct\TypeStruct(); $typeStruct->setClass(App\TypeStruct\Simple::class); $result = $typeStruct->validate($data);
Note we are creating new instance of Amsify42\TypeStruct\TypeStruct
, passing the full class name of typestruct App\TypeStruct\Simple
and pasing data to validate() method.
The validate method will return with the info whether the data passed is validated against the structure and it will return
array(2) { ["is_validated"]=> bool(true) ["messages"]=> array(0) { } }
The is_validated
will have true
or false
based on whether data is validated or not and messages
will have error messages in hierarchy based on elements which are not validated.
Helper method
We can also use helper method to get the Amsify42\TypeStruct\TypeStruct
new instance.
/** * If we have direct of the typestruct file */ $typeStruct = get_typestruct('/path/to/Simple.php'); $result = $typeStruct->validate($data);
/** * For class, we need to pass full class name and 2nd param as 'class' */ $typeStruct = get_typestruct(App\TypeStruct\Simple::class, 'class'); $result = $typeStruct->validate($data);
Autoloading
Autoloading of the typestruct file will be done automatically if its name and path is based on psr-4 standards else you need to use setPath()
method with typestruct instance which expects direct path of the typestruct file.
Options
With Typestruct instance we can set these options before calling validate()
method
/** * To tell the typestruct that data we are passing is of type object(stdClass) * default is false */ $typeStruct->isDataObject(true); /** * If true, it will validate and collect all error messages else it will get the first error and exit * Default is true */ $typeStruct->validateFull(false); /** * Default is empty string, you can either pass 'json' or 'xml' based on the type of data you are passing for validation. */ $typeStruct->contentType('json'); /** * Absolute path to the typestruct file */ $typeStruct->setPath('/path/to/Sample.php'); /** * Full class name of typestruct file */ $typeStruct->setClass(App\TypeStruct\Simple::class);
3. Data
The data you can pass for validation are
Array Object(stdClass) Json XML
As we have already seen the array example, lets see the examples for the rest
Object(stdClass)
$data = new \stdClass(); $data->id = 42; $data->name = 'amsify'; $data->price = 4.2; $typeStruct = new Amsify42\TypeStruct\TypeStruct(); $typeStruct->isDataObject(true)->setClass(App\TypeStruct\Simple::class); $result = $typeStruct->validate($data);
Note: We are passing true
to method isDataObject()
to tell TypeStruct that the data we are passing is of type Object(stdClass).
Json
$jsonData = '{"id":42,"name":"amsify","price":4.2}'; $typeStruct = new TypeStruct(); $typeStruct->contentType('json')->setClass(App\TypeStruct\Simple::class); $result = $typeStruct->validate($jsonData);
XML
$xmlData = '<?xml version="1.0" encoding="UTF-8" ?> <root> <id>42</id> <name>amsify</name> <price>4.2</price> </root>'; $typeStruct = new TypeStruct(); $typeStruct->contentType('xml')->setClass(App\TypeStruct\Simple::class); $result = $typeStruct->validate($xmlData);
Note: We are calling contentType() method to set its type for both Json
and XML
.
4. Class Validation
We can do the validation by creating class and extending it to the Amsify42\TypeStruct\Validator
<?php namespace App\Validators; use Amsify42\TypeStruct\Validator; class Sample extends Validator { protected $tsClass = \App\TypeStruct\Simple::class; protected $data = [ 'id' => 42, 'name' => 'amsify', 'price' => 4.2 ]; }
Since we already set TypeStruct
class name and data
in protected properties. We can create instance of this class directly and validate
$sample = new \Amsify42\Validators\Sample(); $result = $sample->validate();
You can also set the data before validating against the typestruct like this
$sample = new \Amsify42\Validators\Sample(); $sample->setData(['id' => 42, 'name' => 'amsify']); $result = $sample->validate();
and we can also use these protected properties which extends Amsify42\TypeStruct\Validator
/** * Instead of setting typestruct class name, we can also set direct path of that typestruct file */ protected $tsPath; /** * This will decide whether validation will stop at first error itself or when completing all validation errors. Default is true */ protected $validateFull; /** * You can set to json or xml, default is empty string */ protected $contentType; /** * Tells the typestruct whether the data we setting/passing is of type Object(stdClass) */ protected $isDataObject;
5. Rules
Basic
These are the basic types we can use for elements. It will check whether key exist and its type.
export typestruct Sample {
id: int,
name: string,
price: float,
points: numeric,
is_active: boolean,
is_public: tinyInt,
items: array
some: any
}
numeric
work just like php is_numeric()
method which allows numbers even in quotes. tinyInt
expects the value to be either 0
or 1
and type any
means that element value could be of any type.
Optional
To make the element optional, we simply prefix it with question mark ?
export typestruct Sample {
id: int,
name: string,
email: ?string
}
Optional can also be applied to child dictionary
export typestruct Sample {
id: int,
name: string,
email: ?string,
details : ?{
address: string,
pincode: ?int
}
}
Length
We can also set the limits to the length of these types like this
export typestruct Sample { id: int(5), name: string(20), price: float(5.2), is_active: boolean, items: [5] }
Array
These are the array types we can use
items: int[] items: string[] items: float[] items: numeric[] items: boolean[] items: tinyInt[]
External as Child
We can also use the other external TypeStruct file as a element
namespace App\TypeStruct; export typestruct Category { id: int, name: string }
Now we can use Category
as type like this
namespace App\TypeStruct; use App\TypeStruct\Category; export typestruct Product { id: int, name: string, price: float, active: boolean, category: Category }
or as array of this type
namespace App\TypeStruct; use App\TypeStruct\Category; export typestruct Product { id: int, name: string, price: float, active: boolean, categories: Category[] }
More Rules
You can also attach more rules to the input like this
namespace App\TypeStruct; export typestruct User { id: int, name: string, email: string<email> }
As you can see, we have added rule email
to the email element which will check for valid email address. You can add more rules to the element separated by dot .
like this
namespace App\TypeStruct; export typestruct User { id: int, url: string<url.checkHost> }
These are the pre defined rules you can use
nonEmpty - Check for non empty value just like php method empty() checks email - Check for valid email url - Check if string is a valid url date - Check if string is a valid date
6. Custom Rules
We can also write method to perform cutom validation but this can only be achieved when we create class and extends it to Amsify42\TypeStruct\Validator
namespace App\TypeStruct; export typestruct Simple { id: int, name: string<checkName>, price: float }
Now we can write method checkName
in our validator class like this
<?php namespace App\Validators; use Amsify42\TypeStruct\Validator; use App\TypeStruct\Simple; class Sample extends Validator { protected $tsClass = Simple::class; protected $data = [ 'id' => 42, 'name' => 'amsify', 'price' => 4.2 ]; public function checkName() { if($this->value() !== 'amsify') { return 'Name should be amsify'; } return true; } }
We can use $this->name()
to get the name of current element and $this->value()
to get the value of the current element which is applicable to the rule. To get the other element value, we already have $this->data
accessible from these custom rule methods.
If you want to access data from custom method more easily, you can also use the method $this->path()
which will directly get the element from multi level path.
class Sample extends Validator { ... protected $data = [ 'id' => 42, 'detail' => [ 'more' => [ 'location' => 'City' ] ] ]; public function checkCustom() { echo $this->path('detail.more.location'); /* It will print `City` */ } }
Note: $this->path
expects parameters to be key name separated by dot(if multiple keys) and will either return NULL
(if key does not exist) or the target key value.
7. Complex Example
namespace App\TypeStruct; use App\TypeStruct\User; export typestruct Sample { name: string, email: string, is_test: tinyInt, id: int, address: { door: string, zip: int }, items: [], user : User, someEl: { key1: string, key2: int, key12: array, records: \App\TypeStruct\Record[], someChild: { key3: boolean, key4: float, someAgainChild: { key5: string, key6: float, key56: boolean[] } } } }
<?php namespace App\TypeStruct; export typestruct User { id: int, name: string, email: string<email> }
<?php namespace App\TypeStruct; export typestruct Record { id: int, name: string }
The above complex and multi level typestruct example file will be validated with the data:
[ 'name' => 'amsify', 'is_test' => '1', 'user' => [ 'id' => 1, 'name' => 'some', 'email' => 'some@site.com' ], 'address' => [ 'door' => '12-3-534', 'zip' => 600035 ], 'url' => 'https://www.site.com/page.html', 'items' => [1,2,3,4,5,6,7], 'someEl' => [ 'key1' => 'val1', 'key2' => 2, 'key12' => [1,2,12], 'records' => [ [ 'id' => 1, 'name' => 'r1' ], [ 'id' => 2, 'name' => 'r2' ] ], 'someChild' => [ 'key3' => true, 'key4' => 4.01, 'someAgainChild' => [ 'key5' => 'val5', 'key6' => 6.4, 'key56' => [true,false,true] ] ] ] ]