Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions eq.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ function qn_report_name($code) {
}
namespace config {
use equal\services\Container;
use equal\orm\Fields;
use equal\orm\Field;

/*
* This section adds some config-utility functions to the 'config' namespace.
Expand Down Expand Up @@ -463,20 +463,20 @@ public static function init() {

// check service container availability
if(!is_callable('equal\services\Container::getInstance')) {
throw new \Exception('eQual::init - Mandatory Container service is missing or cannot be instanciated.', QN_REPORT_FATAL);
throw new \Exception('eQual::init - Mandatory Container service is missing or cannot be instantiated.', QN_REPORT_FATAL);
}
// instanciate service container
// instantiate service container
$container = Container::getInstance();

// register names for common services and assign default classes
// (these can be overriden in the `config.inc.php` of invoked package)
// (these can be overridden in the `config.inc.php` of invoked package)
$container->register([
'report' => 'equal\error\Reporter',
'auth' => 'equal\auth\AuthenticationManager',
'access' => 'equal\access\AccessController',
'context' => 'equal\php\Context',
'validate' => 'equal\data\DataValidator',
'adapt' => 'equal\data\DataAdapter',
'adapt' => 'equal\data\adapt\DataAdapterProvider',
'orm' => 'equal\orm\ObjectManager',
'route' => 'equal\route\Router',
'log' => 'equal\log\Logger',
Expand Down Expand Up @@ -825,7 +825,11 @@ public static function announce(array $announcement) {
// 3) build result array and set default values for optional parameters that are missing and for which a default value is defined

$invalid_params = [];
$adapter = $container->get('adapt');
/** @var \equal\data\adapt\DataAdapterProvider */
$dap = $container->get('adapt');
// #todo - we should use the adapter based on content-type header, if any
/** @var \equal\data\adapt\DataAdapter */
$adapter = $dap->get('json');
foreach($announcement['params'] as $param => $config) {
// #memo - at some point condition had a clause "|| empty($body[$param])", remember not to alter received data!
if(in_array($param, $missing_params) && isset($config['default'])) {
Expand All @@ -835,14 +839,11 @@ public static function announce(array $announcement) {
// ignore optional params without default value (this allows PATCH of objects on specific fields only)
}
else {
// prevent type confusion while converting data from text
// all inputs are handled as text, conversion is made based on expected type
$result[$param] = $adapter->adapt($body[$param], $config['type']);
$f = new Field($config);
$result[$param] = $adapter->adaptIn($body[$param], $f->getUsage());
// #todo - check value validity according to Usage
/*
// convert value from input format + validate type and usage constraints
$f = new Field($config);
// raises an Exception if assignment is not possible
$f->set($body[$param], 'json'); // not explicit type, but Content-Type from HTTP REQUEST
try {
$f->validate();
$result[$param] = $f->get();
Expand Down Expand Up @@ -888,7 +889,7 @@ public static function announce(array $announcement) {
if(!in_array($param, $mandatory_params)) {
// if it has a default value, assign to it
if(isset($config['default'])) {
$reporter->warning("invalid value for non-mandatory parameter '{$param}' reverted to default '{$config['default']}'");
$reporter->warning("invalid value {$value} for non-mandatory parameter '{$param}' reverted to default '{$config['default']}'");
$result[$param] = $config['default'];
}
else {
Expand Down
254 changes: 129 additions & 125 deletions lib/equal/data/DataValidator.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public static function getConstraintFromUsage($usage) {
* Service constructor.
* This method cannot be called directly (should be invoked through Singleton::getInstance)
* the value will be returned unchanged if:
* - a conversion is not explicitely defined
* - a conversion is not explicitly defined
* - a conversion cannot be made
*/
protected function __construct(/* no dependency */) {
Expand All @@ -224,7 +224,7 @@ protected function __construct(/* no dependency */) {
* Tells if given $value comply with related $constraints set.
*
* Accepted elementary types are: 'boolean' (or 'bool'), 'integer' (or 'int'), 'double' (or 'float'), 'string', 'array', 'file'
* 'file' is a pseudo type whic covers PHP file structure from multipart/form-data, base64 encoded binary value
* 'file' is a pseudo type which covers PHP file structure from multipart/form-data, base64 encoded binary value
*
* Constraints is an array holding constraints description specific to the given value
* it is an array of validation rules, each rule consist of a kind
Expand All @@ -243,142 +243,146 @@ protected function __construct(/* no dependency */) {
*
*/
public function validate($value, $constraints) {
if(!is_array($constraints) || empty($constraints)) return true;
if(!is_array($constraints) || empty($constraints)) {
return true;
}
foreach($constraints as $id => $constraint) {
// ignore empty constraints
if(!isset($constraint['kind']) || !isset($constraint['rule'])) {
continue;
}
switch($constraint['kind']) {
case 'type':
// fix alternate names to the expected value
foreach([
'bool' => 'boolean',
'int' => 'integer',
'float' => 'double',
'text' => 'string',
'binary' => 'string',
'many2one' => 'integer',
'one2many' => 'array',
'many2many' => 'array'
] as $key => $type) {
if($constraint['rule'] == $key) {
$constraint['rule'] = $type;
break;
case 'type':
// fix alternate names to the expected value
foreach([
'bool' => 'boolean',
'int' => 'integer',
'float' => 'double',
'text' => 'string',
'binary' => 'string',
'many2one' => 'integer',
'one2many' => 'array',
'many2many' => 'array'
] as $key => $type) {
if($constraint['rule'] == $key) {
$constraint['rule'] = $type;
break;
}
}
}
// #todo - sync definitions from ObjectManager
if(!in_array($constraint['rule'], ['boolean', 'integer', 'double', 'string', 'date', 'datetime', 'array', 'file'])) {
throw new \Exception("Invalid type {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if($constraint['rule'] == 'file') {
if(!in_array(gettype($value), ['string', 'array'])) return false;
}
// dates are handled as Unix timestamps
else if($constraint['rule'] == 'date' || $constraint['rule'] == 'datetime') {
if(!gettype($value) == 'integer') return false;
}
else if(gettype($value) != $constraint['rule']) return false;
break;
case 'pattern':
case 'regex':
if(!preg_match("/^\/.+\/[a-z]*$/i", $constraint['rule'])) {
throw new \Exception("Invalid pattern {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if(!preg_match($constraint['rule'], $value)) return false;
break;
case 'function':
if(!is_callable($constraint['rule'])) {
throw new \Exception("Unknown function {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if(call_user_func($constraint['rule'], $value) !== true) return false;
break;
case 'min':
if(!is_numeric($constraint['rule'])) {
throw new \Exception("Non numeric min constraint {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
switch(gettype($value)) {
case 'string':
if(strlen($value) < $constraint['rule']) return false;
break;
case 'integer':
case 'double':
if($value < $constraint['rule']) return false;
break;
case 'array':
if(count($value) < $constraint['rule']) return false;
break;
default:
// error : unhandled value type for contraint 'min'
break;
}
break;
case 'max':
if(!is_numeric($constraint['rule'])) {
throw new \Exception("Non numeric max constraint {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
switch(gettype($value)) {
case 'string':
if(strlen($value) > $constraint['rule']) return false;
break;
case 'integer':
case 'double':
if($value > $constraint['rule']) return false;
break;
case 'array':
if(count($value) > $constraint['rule']) return false;
break;
default:
// error : unhandled value type for contraint 'max'
break;
}
break;
case 'selection':
$constraint['kind'] = 'in';
case 'in':
case 'not in':
if(!is_array($constraint['rule'])) {
// warning : 'in' and 'not in' constraint has to be array
// try to force conversion to array
$constraint['rule'] = [$constraint['rule']];
}
$type = gettype($value);
if($type == 'string') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_string($accept)) {
// error : while checking a string 'in' constraint has to hold string values
unset($constraint['rule'][$index]);
// #todo - sync definitions from ObjectManager
if(!in_array($constraint['rule'], ['boolean', 'integer', 'double', 'string', 'date', 'datetime', 'array', 'file'])) {
throw new \Exception("Invalid type {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if($constraint['rule'] == 'file') {
if(!in_array(gettype($value), ['string', 'array'])) {
return false;
}
}
}
else if ($type == 'integer') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_integer($accept)) {
// error : while checking an integer 'in' constraint has to hold integer values
unset($constraint['rule'][$index]);
// dates are handled as Unix timestamps
else if($constraint['rule'] == 'date' || $constraint['rule'] == 'datetime') {
if(!gettype($value) == 'integer') return false;
}
else if(gettype($value) != $constraint['rule']) return false;
break;
case 'pattern':
case 'regex':
if(!preg_match("/^\/.+\/[a-z]*$/i", $constraint['rule'])) {
throw new \Exception("Invalid pattern {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if(!preg_match($constraint['rule'], $value)) return false;
break;
case 'function':
if(!is_callable($constraint['rule'])) {
throw new \Exception("Unknown function {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
if(call_user_func($constraint['rule'], $value) !== true) return false;
break;
case 'min':
if(!is_numeric($constraint['rule'])) {
throw new \Exception("Non numeric min constraint {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
switch(gettype($value)) {
case 'string':
if(strlen($value) < $constraint['rule']) return false;
break;
case 'integer':
case 'double':
if($value < $constraint['rule']) return false;
break;
case 'array':
if(count($value) < $constraint['rule']) return false;
break;
default:
// error : unhandled value type for contraint 'min'
break;
}
break;
case 'max':
if(!is_numeric($constraint['rule'])) {
throw new \Exception("Non numeric max constraint {$constraint['rule']}", QN_ERROR_INVALID_CONFIG);
}
switch(gettype($value)) {
case 'string':
if(strlen($value) > $constraint['rule']) return false;
break;
case 'integer':
case 'double':
if($value > $constraint['rule']) return false;
break;
case 'array':
if(count($value) > $constraint['rule']) return false;
break;
default:
// error : unhandled value type for contraint 'max'
break;
}
break;
case 'selection':
$constraint['kind'] = 'in';
case 'in':
case 'not in':
if(!is_array($constraint['rule'])) {
// warning : 'in' and 'not in' constraint has to be array
// try to force conversion to array
$constraint['rule'] = [$constraint['rule']];
}
$type = gettype($value);
if($type == 'string') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_string($accept)) {
// error : while checking a string 'in' constraint has to hold string values
unset($constraint['rule'][$index]);
}
}
}
}
else if ($type == 'double') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_integer($accept) && !is_double($accept)) {
// error : while checking a float/double 'in' constraint has to hold float values
unset($constraint['rule'][$index]);
else if ($type == 'integer') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_integer($accept)) {
// error : while checking an integer 'in' constraint has to hold integer values
unset($constraint['rule'][$index]);
}
}
}
}
else {
// error : unhandled value type for constraint 'max'
continue 2;
}
if(in_array($value, $constraint['rule'])) {
if($constraint['kind'] == 'not in') return false;
}
else if($constraint['kind'] == 'in') return false;
break;
default:
// warning : unhandled constraint type {$constraint['kind']}
break;
else if ($type == 'double') {
foreach($constraint['rule'] as $index => $accept) {
if(!is_integer($accept) && !is_double($accept)) {
// error : while checking a float/double 'in' constraint has to hold float values
unset($constraint['rule'][$index]);
}
}
}
else {
// error : unhandled value type for constraint 'max'
continue 2;
}
if(in_array($value, $constraint['rule'])) {
if($constraint['kind'] == 'not in') return false;
}
else if($constraint['kind'] == 'in') return false;
break;
default:
// warning : unhandled constraint type {$constraint['kind']}
break;
}
}
return true;
Expand Down
Loading