1359 lines
34 KiB
PHP
1359 lines
34 KiB
PHP
<?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;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
?>
|