Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1102391
validator.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Aug 18, 10:13 PM
Size
15 KB
Mime Type
text/x-php
Expires
Wed, Aug 20, 10:13 PM (1 d, 10 h)
Engine
blob
Format
Raw Data
Handle
714147
Attached To
rINP In-Portal
validator.php
View Options
<?php
/**
* @version $Id: validator.php 16756 2023-11-13 08:35:51Z alex $
* @package In-Portal
* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined
(
'FULL_PATH'
)
or
die
(
'restricted access!'
);
class
kValidator
extends
kBase
{
/**
* Form name, used for validation
*
* @var string
*/
protected
$lastFormName
=
false
;
/**
* Prefix & special of last data source
*
* @var string
*/
protected
$lastPrefixSpecial
=
''
;
/**
* Object, that holds a data to be validated
*
* @var kDBItem
*/
protected
$dataSource
=
null
;
/**
* Holds field errors
*
* @var Array
* @access protected
*/
protected
$FieldErrors
=
Array
();
/**
* Phrases for each error pseudo
*
* @var Array
* @access protected
*/
protected
$ErrorMsgs
=
Array
(
'required'
=>
'!la_err_required!'
,
// Field is required
'unique'
=>
'!la_err_unique!'
,
// Field value must be unique
'value_out_of_range'
=>
'!la_err_value_out_of_range!'
,
// Field is out of range, possible values from %s to %s
'length_out_of_range'
=>
'!la_err_length_out_of_range!'
,
// Field is out of range
'bad_type'
=>
'!la_err_bad_type!'
,
// Incorrect data format, please use %s
'invalid_format'
=>
'!la_err_invalid_format!'
,
// Incorrect data format, please use %s
'bad_date_format'
=>
'!la_err_bad_date_format!'
,
// Incorrect date format, please use (%s) ex. (%s)
'primary_lang_required'
=>
'!la_err_primary_lang_required!'
,
// Primary Lang. value Required
);
/**
* Sets data source for validation
*
* @param kDBItem $object
*/
public
function
setDataSource
(&
$object
)
{
if
(
$object
->
getFormName
()
===
$this
->
lastFormName
&&
$object
->
getPrefixSpecial
()
===
$this
->
lastPrefixSpecial
)
{
return
;
}
$this
->
reset
();
$this
->
dataSource
=&
$object
;
$this
->
lastFormName
=
$object
->
getFormName
();
$this
->
lastPrefixSpecial
=
$object
->
getPrefixSpecial
();
}
/**
* Validate all item fields based on
* constraints set in each field options
* in config
*
* @return bool
* @access private
*/
public
function
Validate
()
{
// order is critical - should be called BEFORE checking errors
$this
->
dataSource
->
UpdateFormattersMasterFields
();
$global_res
=
true
;
$fields
=
array_keys
(
$this
->
dataSource
->
getFields
()
);
foreach
(
$fields
as
$field
)
{
// call separately, otherwise 2+ validation errors will be ignored
$res
=
$this
->
ValidateField
(
$field
);
$global_res
=
$global_res
&&
$res
;
}
if
(
!
$global_res
&&
$this
->
Application
->
isDebugMode
()
)
{
$title_info
=
$this
->
dataSource
->
GetTitleField
();
$item_info
=
Array
(
$this
->
dataSource
->
IDField
.
': <strong>'
.
$this
->
dataSource
->
GetID
()
.
'</strong>'
,
);
if
(
$title_info
&&
reset
(
$title_info
)
)
{
$item_info
[]
=
key
(
$title_info
)
.
': <strong>'
.
current
(
$title_info
)
.
'</strong>'
;
}
$item_info
[]
=
'Temp Mode: <strong>'
.
(
$this
->
dataSource
->
IsTempTable
()
?
'Yes'
:
'No'
)
.
'</strong>'
;
$raw_errors
=
array_filter
(
$this
->
FieldErrors
,
function
(
$error_params
)
{
return
isset
(
$error_params
[
'pseudo'
]);
});
$error_msg
=
<<<HTML
Validation failed for <strong>%s</strong> item (%s).<br/>
<strong>Validation errors:</strong><br/>
<pre>%s</pre>
You may ignore this notice if submitted data really has a validation error.
HTML;
$error_msg
=
sprintf
(
$error_msg
,
$this
->
dataSource
->
getPrefixSpecial
(),
implode
(
'; '
,
$item_info
),
print_r
(
$raw_errors
,
true
)
);
trigger_error
(
trim
(
$error_msg
),
E_USER_NOTICE
);
}
return
$global_res
;
}
/**
* Validates given field
*
* @param string $field
* @return bool
* @access public
*/
public
function
ValidateField
(
$field
)
{
$options
=
$this
->
dataSource
->
GetFieldOptions
(
$field
);
$error_field
=
isset
(
$options
[
'error_field'
])
?
$options
[
'error_field'
]
:
$field
;
$res
=
$this
->
GetErrorPseudo
(
$error_field
)
==
''
;
$res
=
$res
&&
$this
->
ValidateRequired
(
$field
,
$options
);
$res
=
$res
&&
$this
->
ValidateType
(
$field
,
$options
);
$res
=
$res
&&
$this
->
ValidateRange
(
$field
,
$options
);
$res
=
$res
&&
$this
->
ValidateUnique
(
$field
,
$options
);
$res
=
$res
&&
$this
->
CustomValidation
(
$field
,
$options
);
return
$res
;
}
/**
* Check if value is set for required field
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access public
*/
public
function
ValidateRequired
(
$field
,
$params
)
{
if
(
!
isset
(
$params
[
'required'
])
||
!
$params
[
'required'
]
)
{
return
true
;
}
$value
=
$this
->
dataSource
->
GetDBField
(
$field
);
if
(
$this
->
Application
->
ConfigValue
(
'TrimRequiredFields'
)
)
{
$value
=
trim
(
$value
);
}
if
(
(
string
)
$value
==
''
)
{
$this
->
SetError
(
$field
,
'required'
);
return
false
;
}
return
true
;
}
/**
* Check if value in field matches field type specified in config
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
*/
protected
function
ValidateType
(
$field
,
$params
)
{
$val
=
$this
->
dataSource
->
GetDBField
(
$field
);
$type_regex
=
"#int|integer|double|float|real|numeric|string#"
;
if
(
$val
==
''
||
!
isset
(
$params
[
'type'
])
||
!
preg_match
(
$type_regex
,
$params
[
'type'
])
)
{
return
true
;
}
if
(
$params
[
'type'
]
==
'numeric'
)
{
trigger_error
(
'Invalid field type <strong>'
.
$params
[
'type'
]
.
'</strong> (in ValidateType method), please use <strong>float</strong> instead'
,
E_USER_NOTICE
);
$params
[
'type'
]
=
'float'
;
}
$res
=
is_numeric
(
$val
);
if
(
$params
[
'type'
]
==
'string'
||
$res
)
{
$f
=
'is_'
.
$params
[
'type'
];
settype
(
$val
,
$params
[
'type'
]);
$res
=
$f
(
$val
)
&&
(
$val
==
$this
->
dataSource
->
GetDBField
(
$field
));
}
if
(
!
$res
)
{
$this
->
SetError
(
$field
,
'bad_type'
,
null
,
array
(
'type'
=>
$params
[
'type'
]));
return
false
;
}
return
true
;
}
/**
* Check if field value is in range specified in config
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
protected
function
ValidateRange
(
$field
,
$params
)
{
$res
=
true
;
$val
=
$this
->
dataSource
->
GetDBField
(
$field
);
if
(
isset
(
$params
[
'type'
])
&&
preg_match
(
"#int|integer|double|float|real#"
,
$params
[
'type'
])
&&
strlen
(
$val
)
>
0
)
{
// validate number
if
(
isset
(
$params
[
'max_value_inc'
]))
{
$res
=
$res
&&
$val
<=
$params
[
'max_value_inc'
];
$max_val
=
$params
[
'max_value_inc'
].
' (inclusive)'
;
}
if
(
isset
(
$params
[
'min_value_inc'
]))
{
$res
=
$res
&&
$val
>=
$params
[
'min_value_inc'
];
$min_val
=
$params
[
'min_value_inc'
].
' (inclusive)'
;
}
if
(
isset
(
$params
[
'max_value_exc'
]))
{
$res
=
$res
&&
$val
<
$params
[
'max_value_exc'
];
$max_val
=
$params
[
'max_value_exc'
].
' (exclusive)'
;
}
if
(
isset
(
$params
[
'min_value_exc'
]))
{
$res
=
$res
&&
$val
>
$params
[
'min_value_exc'
];
$min_val
=
$params
[
'min_value_exc'
].
' (exclusive)'
;
}
}
if
(
!
$res
)
{
if
(
!
isset
(
$min_val
)
)
$min_val
=
'-∞'
;
if
(
!
isset
(
$max_val
)
)
$max_val
=
'∞'
;
$this
->
SetError
(
$field
,
'value_out_of_range'
,
null
,
array
(
'min_value'
=>
$min_val
,
'max_value'
=>
$max_val
));
return
false
;
}
if
(
strlen
(
$val
)
>
0
)
{
// Validate string.
if
(
isset
(
$params
[
'max_len'
])
)
{
$res
=
$res
&&
mb_strlen
(
$val
)
<=
$params
[
'max_len'
];
}
if
(
isset
(
$params
[
'min_len'
])
)
{
$res
=
$res
&&
mb_strlen
(
$val
)
>=
$params
[
'min_len'
];
}
}
if
(
!
$res
)
{
$error_params
=
array
(
'min_length'
=>
(
int
)
getArrayValue
(
$params
,
'min_len'
),
'max_length'
=>
(
int
)
getArrayValue
(
$params
,
'max_len'
),
'value'
=>
mb_strlen
(
$val
)
);
$this
->
SetError
(
$field
,
'length_out_of_range'
,
null
,
$error_params
);
return
false
;
}
return
true
;
}
/**
* Validates that current record has unique field combination among other table records
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
protected
function
ValidateUnique
(
$field
,
$params
)
{
$unique_fields
=
getArrayValue
(
$params
,
'unique'
);
if
(
$unique_fields
===
false
)
{
return
true
;
}
$where
=
Array
();
array_push
(
$unique_fields
,
$field
);
foreach
(
$unique_fields
as
$unique_field
)
{
// if field is not empty or if it is required - we add where condition
$field_value
=
$this
->
dataSource
->
GetDBField
(
$unique_field
);
if
(
(
string
)
$field_value
!=
''
||
$this
->
dataSource
->
isRequired
(
$unique_field
)
)
{
$where
[]
=
'`'
.
$unique_field
.
'` = '
.
$this
->
Conn
->
qstr
(
$field_value
);
}
else
{
// not good if we check by less fields than indicated
return
true
;
}
}
// This can ONLY happen if all unique fields are empty and not required.
// In such case we return true, because if unique field is not required there may be numerous empty values
// if (!$where) return true;
$sql
=
'SELECT COUNT(*)
FROM %s
WHERE ('
.
implode
(
') AND ('
,
$where
)
.
') AND ('
.
$this
->
dataSource
->
IDField
.
' <> '
.
(
int
)
$this
->
dataSource
->
GetID
()
.
')'
;
$res_temp
=
$this
->
Conn
->
GetOne
(
str_replace
(
'%s'
,
$this
->
dataSource
->
TableName
,
$sql
)
);
if
(
getArrayValue
(
$params
,
'current_table_only'
)
||
!
$this
->
dataSource
->
IsTempTable
()
)
{
// Check unique record only in current table.
$res_live
=
0
;
}
else
{
$deleted_ids
=
$this
->
getTempTableDeletedIDs
();
$live_sql
=
str_replace
(
'%s'
,
$this
->
Application
->
GetLiveName
(
$this
->
dataSource
->
TableName
),
$sql
);
if
(
$deleted_ids
)
{
$live_sql
.=
' AND ('
.
$this
->
dataSource
->
IDField
.
' NOT IN ('
.
implode
(
','
,
$deleted_ids
)
.
'))'
;
}
$res_live
=
$this
->
Conn
->
GetOne
(
$live_sql
);
}
$res
=
(
$res_temp
==
0
)
&&
(
$res_live
==
0
);
if
(
!
$res
)
{
$this
->
SetError
(
$field
,
'unique'
);
return
false
;
}
return
true
;
}
/**
* Returns IDs deleted in temp table.
*
* @return array
*/
protected
function
getTempTableDeletedIDs
()
{
$parent_prefix
=
$this
->
Application
->
getUnitOption
(
$this
->
dataSource
->
Prefix
,
'ParentPrefix'
);
if
(
!
$parent_prefix
)
{
return
array
();
}
// 1. Get main IDs, that are edited in temp table.
$parent_table_name
=
$this
->
Application
->
GetTempName
(
$this
->
Application
->
getUnitOption
(
$parent_prefix
,
'TableName'
),
'prefix:'
.
$parent_prefix
);
$sql
=
'SELECT '
.
$this
->
Application
->
getUnitOption
(
$parent_prefix
,
'IDField'
)
.
'
FROM '
.
$parent_table_name
;
$parent_temp_ids
=
$this
->
Conn
->
GetCol
(
$sql
);
// Main item not saved to db, but sub-item is validated (only possible via custom code).
if
(
!
$parent_temp_ids
)
{
return
array
();
}
// 2. From above found IDs find sub-item IDs in LIVE table.
$foreign_key
=
$this
->
Application
->
getUnitOption
(
$this
->
dataSource
->
Prefix
,
'ForeignKey'
);
$foreign_key
=
is_array
(
$foreign_key
)
?
$foreign_key
[
$parent_prefix
]
:
$foreign_key
;
$sql
=
'SELECT '
.
$this
->
dataSource
->
IDField
.
'
FROM '
.
$this
->
Application
->
GetLiveName
(
$this
->
dataSource
->
TableName
)
.
'
WHERE '
.
$foreign_key
.
' IN ('
.
implode
(
','
,
$parent_temp_ids
)
.
')'
;
$live_ids
=
$this
->
Conn
->
GetCol
(
$sql
);
// Sub-items were never saved to LIVE table.
if
(
!
$live_ids
)
{
return
array
();
}
// 3. Return IDs of LIVE table, that are no longer present (deleted) in TEMP table.
$sql
=
'SELECT '
.
$this
->
dataSource
->
IDField
.
'
FROM '
.
$this
->
dataSource
->
TableName
;
$temp_ids
=
$this
->
Conn
->
GetCol
(
$sql
);
return
array_diff
(
$live_ids
,
$temp_ids
);
}
/**
* Check field value by user-defined alghoritm
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
*/
protected
function
CustomValidation
(
$field
,
$params
)
{
return
true
;
}
/**
* Set's field error, if pseudo passed not found then create it with message text supplied.
* Don't overwrite existing pseudo translation.
*
* @param string $field
* @param string $pseudo
* @param string $error_label
* @param Array $error_params
*
* @return bool
* @access public
*/
public
function
SetError
(
$field
,
$pseudo
,
$error_label
=
null
,
$error_params
=
null
)
{
$error_field
=
$this
->
dataSource
->
GetFieldOption
(
$field
,
'error_field'
,
false
,
$field
);
if
(
$this
->
GetErrorPseudo
(
$error_field
)
)
{
// don't set more then one error on field
return
false
;
}
$this
->
FieldErrors
[
$error_field
][
'pseudo'
]
=
$pseudo
;
if
(
isset
(
$error_params
)
)
{
if
(
array_key_exists
(
'value'
,
$error_params
)
)
{
$this
->
FieldErrors
[
$error_field
][
'value'
]
=
$error_params
[
'value'
];
unset
(
$error_params
[
'value'
]);
}
// additional params, that helps to determine error sources
$this
->
FieldErrors
[
$error_field
][
'params'
]
=
$error_params
;
}
if
(
isset
(
$error_label
)
&&
!
isset
(
$this
->
ErrorMsgs
[
$pseudo
])
)
{
// label for error (only when not already set)
$this
->
ErrorMsgs
[
$pseudo
]
=
(
substr
(
$error_label
,
0
,
1
)
==
'+'
)
?
substr
(
$error_label
,
1
)
:
'!'
.
$error_label
.
'!'
;
}
return
true
;
}
/**
* Return error message for field
*
* @param string $field
* @param bool $force_escape
* @return string
* @access public
*/
public
function
GetErrorMsg
(
$field
,
$force_escape
=
null
)
{
$error_pseudo
=
$this
->
GetErrorPseudo
(
$field
);
if
(
!
$error_pseudo
)
{
return
''
;
}
// if special error msg defined in config
$error_msgs
=
$this
->
dataSource
->
GetFieldOption
(
$field
,
'error_msgs'
,
false
,
Array
());
if
(
isset
(
$error_msgs
[
$error_pseudo
])
)
{
$msg
=
$error_msgs
[
$error_pseudo
];
}
else
{
// fallback to defaults
if
(
!
isset
(
$this
->
ErrorMsgs
[
$error_pseudo
])
)
{
trigger_error
(
'No user message is defined for pseudo error <strong>'
.
$error_pseudo
.
'</strong>'
,
E_USER_WARNING
);
return
$error_pseudo
;
//return the pseudo itself
}
$msg
=
$this
->
ErrorMsgs
[
$error_pseudo
];
}
$msg
=
$this
->
Application
->
ReplaceLanguageTags
(
$msg
,
$force_escape
);
if
(
isset
(
$this
->
FieldErrors
[
$field
][
'params'
])
)
{
$params
=
$this
->
FieldErrors
[
$field
][
'params'
];
}
else
{
$params
=
array
();
}
if
(
$params
&&
preg_match
(
'/%[^
\s
]/'
,
$msg
)
)
{
$msg
=
vsprintf
(
$msg
,
array_values
(
$params
));
}
else
{
$field_phrase
=
$this
->
Application
->
isAdmin
?
'la_fld_'
.
$field
:
'lu_fld_'
.
$field
;
$params
[
'field'
]
=
$this
->
Application
->
Phrase
(
$field_phrase
);
foreach
(
$params
as
$param_name
=>
$param_value
)
{
$msg
=
str_replace
(
'{'
.
$param_name
.
'}'
,
$param_value
,
$msg
);
}
}
return
$msg
;
}
/**
* Returns error pseudo
*
* @param string $field
* @return string
* @access public
*
*/
public
function
GetErrorPseudo
(
$field
)
{
if
(
!
isset
(
$this
->
FieldErrors
[
$field
])
)
{
return
''
;
}
return
isset
(
$this
->
FieldErrors
[
$field
][
'pseudo'
])
?
$this
->
FieldErrors
[
$field
][
'pseudo'
]
:
''
;
}
/**
* Removes error on field
*
* @param string $field
* @access public
*/
public
function
RemoveError
(
$field
)
{
unset
(
$this
->
FieldErrors
[
$field
]
);
}
/**
* Returns field errors
*
* @return Array
* @access public
*/
public
function
GetFieldErrors
()
{
return
$this
->
FieldErrors
;
}
/**
* Check if item has errors
*
* @param Array $skip_fields fields to skip during error checking
* @return bool
* @access public
*/
public
function
HasErrors
(
$skip_fields
=
Array
()
)
{
$fields
=
array_keys
(
$this
->
dataSource
->
getFields
()
);
$fields
=
array_diff
(
$fields
,
$skip_fields
);
foreach
(
$fields
as
$field
)
{
// if Formatter has set some error messages during values parsing
if
(
$this
->
GetErrorPseudo
(
$field
)
)
{
return
true
;
}
}
return
false
;
}
/**
* Clears all validation errors
*
* @access public
*/
public
function
reset
()
{
$this
->
FieldErrors
=
Array
();
}
}
Event Timeline
Log In to Comment