TimeTrex/classes/modules/core/Validator.class.php

1359 lines
34 KiB
PHP
Raw Normal View History

2022-12-13 07:10:06 +01:00
<?php
/*********************************************************************************
*
* TimeTrex is a Workforce Management program developed by
* TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
*
* You should have received a copy of the GNU Affero General Public License along
* with this program; if not, see http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
*
* You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
* #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
*
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by TimeTrex" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by TimeTrex".
*
********************************************************************************/
/**
* @package Core
*/
class Validator {
private $num_errors = 0; //Number of errors.
private $num_warnings = 0; //Number of errors.
private $errors = []; //Array of errors.
private $warnings = []; //Array of errors.
private $verbosity = 8;
public $validate_only = false;
/**
* Checks a result set for one or more rows.
* @param $label
* @param $rs
* @param null $msg
* @return bool
*/
function isResultSetWithRows( $label, $rs, $msg = null ) {
//Debug::Arr($rs, 'ResultSet: ', __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( is_object( $rs ) ) {
if ( isset( $rs->rs ) && is_object( $rs->rs ) && isset( $rs->rs->_numOfRows ) && $rs->rs->_numOfRows > 0 ) {
return true;
}
}
$this->Error( $label, $msg );
return false;
}
/**
* @param $label
* @param $rs
* @param null $msg
* @return bool
*/
function isNotResultSetWithRows( $label, $rs, $msg = null ) {
//Debug::Arr($rs, 'ResultSet: ', __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( is_object( $rs ) ) {
if ( isset( $rs->rs ) && is_object( $rs->rs ) && isset( $rs->rs->_numOfRows ) && $rs->rs->_numOfRows > 0 ) {
$this->Error( $label, $msg );
return false;
}
//foreach($rs as $result) {
// $this->Error($label, $msg);
// unset($result); // code standards
// return FALSE;
//}
}
return true;
}
/**
* Function to simple set an error.
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isTrue( $label, $value, $msg = null ) {
if ( $value == true ) {
return true;
}
$this->Error( $label, $msg, (int)$value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isFalse( $label, $value, $msg = null ) {
if ( $value == false ) {
return true;
}
$this->Error( $label, $msg, (int)$value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isNull( $label, $value, $msg = null ) {
//Debug::text('Value: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $value == null ) {
return true;
}
$this->Error( $label, $msg, (int)$value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isNotNull( $label, $value, $msg = null ) {
//Debug::text('Value: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $value != null ) { //== NULL matches on (float)0.0
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param $msg
* @param $array
* @return bool
*/
function inArrayValue( $label, $value, $msg, $array ) {
//Debug::text('Value: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( is_array( $array ) && in_array( $value, array_values( $array ) ) ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $key
* @param $msg
* @param $array
* @return bool
*/
function inArrayKey( $label, $key, $msg, $array ) {
//Debug::text('Key: '. $key, __FILE__, __LINE__, __METHOD__, $this->verbosity);
//Debug::Arr($array, 'isArrayKey Array:', __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( is_array( $array ) && in_array( $key, array_keys( $array ) ) ) {
return true;
}
$this->Error( $label, $msg, $key );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isDigits( $label, $value, $msg = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( ctype_digit( trim( $value ) ) == true ) { //Must be *only* digits.
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isNumeric( $label, $value, $msg = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
//if ( preg_match('/^[-0-9]+$/', $value) ) {
if ( is_numeric( $value ) == true ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isAlphaNumeric( $label, $value, $msg = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( preg_match('/^[A-Za-z0-9]+$/', $value) ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isUUID( $label, $value, $msg = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
//Benchmarking proved that this method is faster than ctype_alnum()
//this regex is duplicated into Factory::setID()
if ( TTUUID::isUUID( $value ) == true ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param null $max
* @return bool
*/
function isLessThan( $label, $value, $msg = null, $max = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $max === null || $max === '' ) {
$max = PHP_INT_MAX;
}
if ( $value <= $max ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param null $min
* @return bool
*/
function isGreaterThan( $label, $value, $msg = null, $min = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $min === null || $min === '' ) {
$min = ( -1 * PHP_INT_MAX );
}
if ( $value >= $min ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isFloat( $label, $value, $msg = null ) {
//Debug::Text('Value:'. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
//Don't use TTi18n::parseFloat() here, as if we are going to be doing that we should do it as early as possible to the user input, like in setObjectFromArray()
// We do need to check if the value passed in is already cast to float/int and just accept it in that case.
// Because in other locales preg_match() casts $value to a string, which means decimal could become a comma, then it won't match.
// Allow commas and decimals to be accepted as floats, as parseFloat() should almost always be called after this function, and it accepts both in all locales.
if ( ( is_float( $value ) == true || is_int( $value ) === true ) || preg_match( '/^(([\.,][0-9]+)|([-0-9]+([\.,\ 0-9]*)?))$/', trim( $value ) ) === 1 ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param $msg
* @param $regex
* @return bool
*/
function isRegEx( $label, $value, $msg, $regex ) {
//Debug::text('Value: '. $value .' RegEx: '. $regex, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( preg_match( $regex, $value ) ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param $msg
* @param $regex
* @return bool
*/
function isNotRegEx( $label, $value, $msg, $regex ) {
//Debug::text('Value: '. $value .' RegEx: '. $regex, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( preg_match( $regex, $value ) == false ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param int $min
* @param int $max
* @return bool
*/
function isLength( $label, $value, $msg = null, $min = 1, $max = 255 ) {
$len = strlen( $value );
//Debug::text('Value: '. $value .' Length: '. $len .' Min: '. $min .' Max: '. $max, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $len < $min || $len > $max ) {
$this->Error( $label, $msg, $value );
return false;
}
return true;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param int $min
* @param int $max
* @return bool
*/
function isLengthBeforeDecimal( $label, $value, $msg = null, $min = 1, $max = 255 ) {
$len = strlen( Misc::getBeforeDecimal( $value ) );
//Debug::text('Value: '. $value .' Length: '. $len .' Min: '. $min .' Max: '. $max, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $len < $min || $len > $max ) {
$this->Error( $label, $msg, $value );
return false;
}
return true;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param int $min
* @param int $max
* @return bool
*/
function isLengthAfterDecimal( $label, $value, $msg = null, $min = 1, $max = 255 ) {
$len = strlen( Misc::getAfterDecimal( $value, false ) );
//Debug::text('Value: '. $value .' Length: '. $len .' Min: '. $min .' Max: '. $max, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $len < $min || $len > $max ) {
$this->Error( $label, $msg, $value );
return false;
}
return true;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isUniqueCharacters( $label, $value, $msg = null ) {
//Check for unique characters and not consecutive characters.
//This will fail on:
// aaaaaaa
// bbbbbbb
// abc
// xyz
if ( strlen( $value ) > 2 ) {
$char_arr = str_split( strtolower( $value ) );
$prev_char_int = ord( $char_arr[0] );
foreach ( $char_arr as $char ) {
$curr_char_int = ord( $char );
if ( abs( $prev_char_int - $curr_char_int ) > 1 ) {
return true;
}
$prev_char_int = $curr_char_int;
}
$this->Error( $label, $msg, $value );
return false;
}
return true;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param bool $max_duplicate_percent
* @param bool $consecutive_only
* @return bool
*/
function isDuplicateCharacters( $label, $value, $msg = null, $max_duplicate_percent = false, $consecutive_only = false ) {
if ( strlen( $value ) > 2 && $max_duplicate_percent != false ) {
$duplicate_chars = 0;
$char_arr = str_split( strtolower( $value ) );
$prev_char_int = ord( $char_arr[0] );
foreach ( $char_arr as $char ) {
$curr_char_int = ord( $char );
if ( abs( $prev_char_int - $curr_char_int ) > 1 ) {
if ( $consecutive_only == true ) {
$duplicate_chars = 0; //Reset duplicate count.
}
} else {
$duplicate_chars++;
}
$prev_char_int = $curr_char_int;
}
$duplicate_percent = ( ( $duplicate_chars / strlen( $value ) ) * 100 );
Debug::text( 'Duplicate Chars: ' . $duplicate_chars . ' Percent: ' . $duplicate_percent . ' Max Percent: ' . $max_duplicate_percent . ' Consec: ' . (int)$consecutive_only, __FILE__, __LINE__, __METHOD__, $this->verbosity );
if ( $duplicate_percent < $max_duplicate_percent ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
return true;
}
/**
* @param $label
* @param $value
* @param $msg
* @param $bad_words
* @return bool
*/
function isAllowedWords( $label, $value, $msg, $bad_words ) {
$words = explode( ' ', $value );
if ( is_array( $words ) ) {
foreach ( $words as $word ) {
foreach ( $bad_words as $bad_word ) {
if ( strtolower( $word ) == strtolower( $bad_word ) ) {
$this->Error( $label, $msg, $value );
return false;
}
}
}
}
return true;
}
/**
* @param $label
* @param $value
* @param $msg
* @param $bad_words
* @return bool
*/
function isAllowedValues( $label, $value, $msg, $bad_words ) {
foreach ( $bad_words as $bad_word ) {
if ( strtolower( $value ) == strtolower( $bad_word ) ) {
$this->Error( $label, $msg, $value );
return false;
}
}
return true;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isPhoneNumber( $label, $value, $msg = null ) {
//Strip out all non-numeric characters.
$phone = $this->stripNonNumeric( $value );
//Debug::text('Raw Phone: '. $value .' Phone: '. $phone, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( strlen( $phone ) >= 6 && strlen( $phone ) <= 20 && preg_match( '/^[0-9\(\)\-\.\+\ ]{6,20}$/i', $value ) ) {
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param null $country
* @param null $province
* @return bool
*/
function isPostalCode( $label, $value, $msg = null, $country = null, $province = null ) {
//Debug::text('Raw Postal Code: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
//Remove any spaces, keep dashes for US extended ZIP.
$value = str_replace( [ ' ' ], '', trim( $value ) );
$province = strtolower( trim( $province ) );
switch ( strtolower( trim( $country ) ) ) {
case 'us':
//US zip code
if ( preg_match( '/^[0-9]{5}$/i', $value ) || preg_match( '/^[0-9]{5}\-[0-9]{4}$/i', $value ) ) {
if ( $province != '' ) {
$province_postal_code_map = [
'ak' => [ '9950099929' ],
'al' => [ '3500036999' ],
'ar' => [ '7160072999', '7550275505' ],
'az' => [ '8500086599' ],
'ca' => [ '9000096199' ],
'co' => [ '8000081699' ],
'ct' => [ '0600006999' ],
'dc' => [ '2000020099', '2020020599' ],
'de' => [ '1970019999' ],
'fl' => [ '3200033999', '3410034999' ],
'ga' => [ '3000031999' ],
'hi' => [ '9670096798', '9680096899' ],
'ia' => [ '5000052999' ],
'id' => [ '8320083899' ],
'il' => [ '6000062999' ],
'in' => [ '4600047999' ],
'ks' => [ '6600067999' ],
'ky' => [ '4000042799', '4527545275' ],
'la' => [ '7000071499', '7174971749' ],
'ma' => [ '0100002799' ],
'md' => [ '2033120331', '2060021999' ],
'me' => [ '0380103801', '0380403804', '0390004999' ],
'mi' => [ '4800049999' ],
'mn' => [ '5500056799' ],
'mo' => [ '6300065899' ],
'ms' => [ '3860039799' ],
'mt' => [ '5900059999' ],
'nc' => [ '2700028999' ],
'nd' => [ '5800058899' ],
'ne' => [ '6800069399' ],
'nh' => [ '0300003803', '0380903899' ],
'nj' => [ '0700008999' ],
'nm' => [ '8700088499' ],
'nv' => [ '8900089899' ],
'ny' => [ '0040000599', '0639006390', '0900014999' ],
'oh' => [ '4300045999' ],
'ok' => [ '7300073199', '7340074999' ],
'or' => [ '9700097999' ],
'pa' => [ '1500019699' ],
'ri' => [ '0280002999', '0637906379' ],
'sc' => [ '2900029999' ],
'sd' => [ '5700057799' ],
'tn' => [ '3700038599', '7239572395' ],
'tx' => [ '7330073399', '7394973949', '7500079999', '8850188599' ],
'ut' => [ '8400084799' ],
'va' => [ '2010520199', '2030120301', '2037020370', '2200024699' ],
'vt' => [ '0500005999' ],
'wa' => [ '9800099499' ],
'wi' => [ '4993649936', '5300054999' ],
'wv' => [ '2470026899' ],
'wy' => [ '8200083199' ],
];
if ( isset( $province_postal_code_map[$province] ) ) {
$zip5 = substr( $value, 0, 5 );
//Debug::text('Checking ZIP code range, short zip: '. $zip5, __FILE__, __LINE__, __METHOD__, $this->verbosity);
foreach ( $province_postal_code_map[$province] as $postal_code_range ) {
//Debug::text('Checking ZIP code range: '. $postal_code_range, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( ( $zip5 >= substr( $postal_code_range, 0, 5 ) ) && ( $zip5 <= substr( $postal_code_range, 5 ) ) ) {
return true;
}
}
} // else { //Debug::text('Postal Code does not match province!', __FILE__, __LINE__, __METHOD__, $this->verbosity);
} else {
return true;
}
}
break;
case 'ca':
//Canada postal code
if ( preg_match( '/^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}[-]?[0-9]{1}[a-zA-Z]{1}[0-9]{1}$/i', $value ) ) {
if ( $province != '' ) {
//Debug::text('Verifying postal code against province!', __FILE__, __LINE__, __METHOD__, $this->verbosity);
$province_postal_code_map = [
'ab' => [ 't' ],
'bc' => [ 'v' ],
'sk' => [ 's' ],
'mb' => [ 'r' ],
'qc' => [ 'g', 'h', 'j' ],
'on' => [ 'k', 'l', 'm', 'n', 'p' ],
'nl' => [ 'a' ],
'nb' => [ 'e' ],
'ns' => [ 'b' ],
'pe' => [ 'c' ],
'nt' => [ 'x' ],
'yt' => [ 'y' ],
'nu' => [ 'x' ],
];
//Debug::Arr($province_postal_code_map[$province], 'Valid Postal Codes for Province', __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( isset( $province_postal_code_map[$province] ) && in_array( substr( strtolower( $value ), 0, 1 ), $province_postal_code_map[$province] ) ) {
return true;
} // else { //Debug::text('Postal Code does not match province!', __FILE__, __LINE__, __METHOD__, $this->verbosity);
} else {
return true;
}
}
break;
default:
//US
if ( preg_match( '/^[0-9]{5}$/i', $value ) || preg_match( '/^[0-9]{5}\-[0-9]{4}$/i', $value ) ) {
return true;
}
//CA
if ( preg_match( '/^[a-zA-Z]{1}[0-9]{1}[a-zA-Z]{1}[-]?[0-9]{1}[a-zA-Z]{1}[0-9]{1}$/i', $value ) ) {
return true;
}
//Other
if ( preg_match( '/^[a-zA-Z0-9]{1,10}$/i', $value ) ) {
return true;
}
break;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isEmail( $label, $value, $msg = null ) {
//Debug::text('Raw Email: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( function_exists( 'filter_var' ) && filter_var( $value, FILTER_VALIDATE_EMAIL ) !== false ) {
return true;
} else if ( preg_match( '/^[\w\.\-\&\+]+\@[\w\.\-]+\.[a-z]{2,5}$/i', $value ) ) { //This Email regex is no where near correct, use PHP filter_var instead. - Allow 5 char suffixes to support .local domains.
return true;
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param bool $error_level
* @return bool
* @noinspection PhpUndefinedConstantInspection
*/
function isEmailAdvanced( $label, $value, $msg = null, $error_level = true, $enable_smtp_verification = false ) {
//Debug::text('Raw Email: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
$retval = Misc::isEmail( $value, true, $error_level, true, $enable_smtp_verification );
if ( $retval === ISEMAIL_VALID ) {
return true;
}
if ( is_array( $msg ) ) {
if ( isset( $msg[$retval] ) ) {
$msg = $msg[$retval];
} else {
$msg = $msg[0];
}
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isIPAddress( $label, $value, $msg = null ) {
//Debug::text('Raw IP: '. $value, __FILE__, __LINE__, __METHOD__, $this->verbosity);
$ip = explode( '.', $value );
if ( count( $ip ) == 4 ) {
$valid = true;
foreach ( $ip as $block ) {
if ( !is_numeric( $block ) || $block >= 255 || $block < 0 ) {
$valid = false;
}
}
if ( $valid == true ) {
return true;
}
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @return bool
*/
function isDate( $label, $value, $msg = null ) {
//Because most epochs are stored as 4-byte integers, make sure we are within range.
if ( TTDate::isValidDate( $value ) ) {
$date = gmdate( 'U', $value );
//Debug::text('Raw Date: '. $value .' Converted Value: '. $date, __FILE__, __LINE__, __METHOD__, $this->verbosity);
if ( $date == $value ) {
return true;
}
}
$this->Error( $label, $msg, $value );
return false;
}
/**
* @param $label
* @param $value
* @param null $msg
* @param null $country
* @return bool
*/
function isSIN( $label, $value, $msg = null, $country = null ) {
$sin = $this->stripNonAlphaNumeric( trim( $value ) ); //UK National Insurance Number (NINO) has letters, so we can only strip spaces.
Debug::text( 'Validating SIN/SSN: ' . $value . ' Country: ' . $country, __FILE__, __LINE__, __METHOD__, $this->verbosity );
//$retval = false;
switch ( strtolower( trim( $country ) ) ) {
case 'ca':
//As of around 2015, SINs starting with 0 can not be valid rather than just reserved for fictitious purposes.
if ( is_numeric( $sin ) && strlen( $sin ) == 9 && $sin >= 10000000 && $sin < 999999999 ) {
$split_sin = str_split( $sin );
if ( ( $split_sin[1] *= 2 ) >= 10 ) {
$split_sin[1] -= 9;
}
if ( ( $split_sin[3] *= 2 ) >= 10 ) {
$split_sin[3] -= 9;
}
if ( ( $split_sin[5] *= 2 ) >= 10 ) {
$split_sin[5] -= 9;
}
if ( ( $split_sin[7] *= 2 ) >= 10 ) {
$split_sin[7] -= 9;
}
if ( ( array_sum( $split_sin ) % 10 ) != 0 ) {
$retval = false;
} else {
$retval = true;
}
} else {
if ( $sin == 999999999 || $sin == '000000000' ) { //Allow all 9/0's for a SIN in case its an out of country employee that doesn't have one.
$retval = true;
} else {
$retval = false;
}
}
break;
case 'us':
if ( strlen( $sin ) == 9 && is_numeric( $sin ) ) {
//Due to highgroup randomization, we can no longer validate SSN's without querying the IRS database.
$retval = true;
} else {
$retval = false;
}
break;
default:
//Allow all foriegn countries to utilize
$retval = self::isLength( $label, $value, $msg, 1, 255 );
break;
}
if ( $retval === true ) {
return true;
}
Debug::text( 'Invalid SIN/SSN: ' . $value . ' Country: ' . $country, __FILE__, __LINE__, __METHOD__, $this->verbosity );
$this->Error( $label, $msg, $value );
return false;
}
/*
* String manipulation functions.
*/
/**
* @param $value
* @return int
*/
function stripNon32bitInteger( $value ) {
if ( (int)$value >= 2147483647 || (int)$value <= -2147483648 ) { //Make sure we cast $value to integer, otherwise its a string comparison which is not as intended.
return 0;
}
return $value;
}
/**
* @param $value
* @return mixed
*/
function stripSpaces( $value ) {
return str_replace( ' ', '', trim( $value ) );
}
/**
* @param $value
* @return mixed
*/
function stripNumeric( $value ) {
$retval = preg_replace( '/[0-9]/', '', $value );
return $retval;
}
/**
* @param $value
* @return mixed
*/
function stripNonNumeric( $value ) {
$retval = preg_replace( '/[^0-9]/', '', $value );
return $retval;
}
/**
* @param $value
* @return mixed
*/
function stripNonAlphaNumeric( $value ) {
$retval = preg_replace( '/[^A-Za-z0-9]/', '', $value );
//Debug::Text('Alpha Numeric String:'. $retval, __FILE__, __LINE__, __METHOD__, $this->verbosity);
return $retval;
}
/**
* @param $value int|float|string
* @return int|float|string
*/
function stripNonFloat( $value ) {
//Don't use TTi18n::parseFloat() here, as if we are going to be doing that we should do it as early as possible to the user input, like in setObjectFromArray().
// Then this function would often be called afterwards, so it should always return a float value or something that can be used in math.
// TTi18n::parseFloat() parses out non-float characters itself.
//We do need to check if the value passed in is already cast to float/int and just accept it in that case.
// Because in other locales preg_match() casts $value to a string, which means decimal could become a comma, then it won't match.
if ( is_float( $value ) === true || is_int( $value ) === true ) {
return $value;
} else {
//Strips repeating "." and "-" characters that might slip in due to typos. Then strips non-float valid characters.
//This is also done in TTi18n::parseFloat() and Validator->stripNonFloat()
//$retval = preg_replace( '/([\-\.,])(?=.*?\1)|[^-0-9\.,]/', '', $value );
//This should return a float value, or at least something that can be used in math functions.
// TTi18n::parseFloat() should be called in setObjectFromArray() well before this would ever be called, which is typically in set*() functions.
// CompanyDeductionFactory requires this, as Advanced Percent calculations have values entered as '124,000' which are run through stripNonFloat() prior to performing math on them.
// ([\D]*$) -- This removes any trailing non-digits or signs. (ie: '123.91-')
$retval = preg_replace( '/([\-\.])(?=.*?\1)|[^-0-9\.]|([\D]*$)/', '', $value );
}
//Debug::Text('Float String:'. $retval, __FILE__, __LINE__, __METHOD__, $this->verbosity);
return $retval;
}
/**
* Suitable for passing to parseTimeUnit() after.
* @param $value
* @return mixed
*/
function stripNonTimeUnit( $value ) {
$retval = preg_replace( '/[^-0-9\.:]/', '', $value );
//Debug::Text('Float String:'. $retval, __FILE__, __LINE__, __METHOD__, $this->verbosity);
return $retval;
}
/**
* @param $value
* @return string
*/
function stripHTML( $value ) {
return strip_tags( $value );
}
/**
* @param $value
* @return string
*/
function escapeHTML( $value ) {
return htmlspecialchars( $value, ENT_QUOTES, 'UTF-8' );
}
/**
* @param $value
* @return string
*/
function purifyHTML( $value ) {
global $config_vars;
//Require inside this function as HTMLPurifier is a huge file.
require_once ( Environment::getBasePath() . 'vendor' . DIRECTORY_SEPARATOR . 'ezyang' . DIRECTORY_SEPARATOR . 'htmlpurifier' . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'HTMLPurifier.auto.php' );
$config = HTMLPurifier_Config::createDefault();
if ( isset( $config_vars['cache']['enable'] ) && $config_vars['cache']['enable'] == true
&& $config_vars['cache']['dir'] != '' && is_writable( $config_vars['cache']['dir'] ) ) {
$config->set( 'Cache.SerializerPath', $config_vars['cache']['dir'] );
//Debug::Text('Caching HTMLPurifier...', __FILE__, __LINE__, __METHOD__, $this->verbosity);
} else {
$config->set( 'Cache.DefinitionImpl', null );
Debug::Text( 'NOT caching HTMLPurifier...', __FILE__, __LINE__, __METHOD__, $this->verbosity );
}
$purifier = new HTMLPurifier( $config );
return $purifier->purify( $value );
}
/**
* @param $value
* @return bool|string
*/
function getPhoneNumberAreaCode( $value ) {
$phone_number = $this->stripNonNumeric( $value );
if ( strlen( $phone_number ) > 7 ) {
$retval = substr( $phone_number, -10, 3 ); //1 555 555 5555
return $retval;
}
return false;
}
/*
* Class standard functions.
*/
/**
* @param $string
* @param $var_array
* @return mixed
*/
function varReplace( $string, $var_array ) {
//var_array = arary('var1' => 'blah1', 'var2' => 'blah2');
$keys = [];
$values = [];
if ( is_array( $var_array ) && count( $var_array ) > 0 ) {
foreach ( $var_array as $key => $value ) {
$keys[] = '#' . $key;
$values[] = $value;
}
}
$retval = str_replace( $keys, $values, $string );
return $retval;
}
/**
* @param int $validate_only EPOCH
*/
function setValidateOnly( $validate_only ) {
$this->validate_only = $validate_only;
}
/**
* @return bool
*/
function getValidateOnly() {
return $this->validate_only;
}
/**
* Returns both Errors and Warnings combined.
* @return array
*/
function getErrorsAndWarningsArray() {
return [ 'errors' => $this->getErrorsArray(), 'warnings' => $this->getWarningsArray() ];
}
/**
* Merges all errors/warnings from the passed $validator object to this one.
* @param object $validator
* @return bool
*/
function merge( $validator ) {
if ( is_object( $validator ) && $validator->isValid() == false ) {
$this->errors = array_merge( $this->errors, $validator->getErrorsArray() );
$this->num_errors += count( $validator->getErrorsArray() );
$this->warnings = array_merge( $this->warnings, $validator->getWarningsArray() );
$this->num_warnings += count( $validator->getWarningsArray() );
}
return true;
}
/**
* @param string $record_label_prefix
* @return array
*/
private function addRecordLabelPrefixToArray( $arr, $record_label_prefix = null ) {
$retarr = [];
if ( $record_label_prefix != '' ) {
$record_label_prefix .= ': '; //Add a colon separator.
}
if ( count( $arr ) > 0 ) {
foreach ( $arr as $label => $msgs ) {
foreach ( $msgs as $msg ) {
$retarr[$label][] = $record_label_prefix . $msg;
}
}
}
return $retarr;
}
/**
* @param string $record_label_prefix
* @return array
*/
function getErrorsArray( $record_label_prefix = null ) {
return $this->addRecordLabelPrefixToArray( $this->errors, $record_label_prefix );
}
/**
* @return bool|string
*/
function getErrors() {
if ( count( $this->errors ) > 0 ) {
$output = "<ol>\n";
foreach ( $this->errors as $label ) {
foreach ( $label as $msg ) {
$output .= '<li>' . $msg . ".</li>";
}
}
$output .= "</ol>\n";
return $output;
}
return false;
}
/**
* @param bool $numbered_list
* @param array $errors_arr Pass in other error array to be converted to text.
* @return bool|string
*/
function getTextErrors( $numbered_list = true, $errors_arr = null ) {
if ( $errors_arr == null ) {
$errors_arr = $this->errors;
}
if ( is_array( $errors_arr ) && count( $errors_arr ) > 0 ) {
$output = '';
$number_prefix = null;
$i = 1;
foreach ( $errors_arr as $label ) {
foreach ( $label as $msgs ) {
if ( $numbered_list == true ) {
$number_prefix = $i . '. ';
}
if ( is_array( $msgs ) ) {
foreach ( $msgs as $msg ) {
$output .= $number_prefix . $msg . "\n";
$i++;
}
} else {
$output .= $number_prefix . $msgs . "\n";
$i++;
}
}
}
return $output;
}
return false;
}
/**
* @param null $label
* @return bool
*/
final function isValid( $label = null ) {
if ( $this->isError( $label ) || $this->isWarning( $label ) ) {
return false;
}
return true;
}
/**
* @param null $label
* @return bool
*/
final function isError( $label = null ) {
if ( is_string( $label ) && $label != '' ) {
return $this->hasError( $label );
} else if ( $this->num_errors > 0 ) {
Debug::Arr( $this->errors, 'Errors', __FILE__, __LINE__, __METHOD__, $this->verbosity );
return true;
}
return false;
}
/**
* @return bool
*/
function resetErrors() {
$this->errors = []; //Set to blank array rather than use unset() as that will cause PHP warnings in hasError().
$this->num_errors = 0;
return true;
}
/**
* @param $label
* @return bool
*/
function hasError( $label ) {
if ( in_array( $label, array_keys( $this->errors ) ) ) {
return true;
}
return false;
}
/**
* @param $label
* @param $msg
* @param string $value
* @return bool
*/
function Error( $label, $msg, $value = '' ) {
Debug::text( 'Validation Error: Label: ' . $label . ' Value: "' . $value . '" Msg: ' . $msg, __FILE__, __LINE__, __METHOD__, $this->verbosity );
//If label is NULL, assume we don't actually want to trigger an error.
//This is good for just using the check functions for other purposes.
if ( (string)$label != '' ) {
$this->errors[$label][] = $msg;
$this->num_errors++;
return true;
}
return false;
}
//
// Warning functions below here
//
/**
* @param string $record_label_prefix
* @return array
*/
function getWarningsArray( $record_label_prefix = null ) {
return $this->addRecordLabelPrefixToArray( $this->warnings, $record_label_prefix );
}
/**
* @param null $label
* @return bool
*/
final function isWarning( $label = null ) {
if ( is_string( $label ) && $label != '' ) {
return $this->hasWarning( $label );
} else if ( $this->num_warnings > 0 ) {
Debug::Arr( $this->warnings, 'Warnings', __FILE__, __LINE__, __METHOD__, $this->verbosity );
return true;
}
return false;
}
/**
* @return bool
*/
function resetWarnings() {
$this->warnings = []; //Set to blank array rather than use unset() as that will cause PHP warnings in hasWarning().
$this->num_warnings = 0;
return true;
}
/**
* @param $label
* @return bool
*/
function hasWarning( $label ) {
if ( in_array( $label, array_keys( $this->warnings ) ) ) {
return true;
}
return false;
}
/**
* @param $label
* @param $msg
* @param string $value
* @return bool
*/
function Warning( $label, $msg, $value = '' ) {
Debug::text( 'Validation Warning: Label: ' . $label . ' Value: "' . $value . '" Msg: ' . $msg, __FILE__, __LINE__, __METHOD__, $this->verbosity );
if ( (string)$label != '' ) {
$this->warnings[$label][] = $msg;
$this->num_warnings++;
return true;
}
return false;
}
}
?>