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

262 lines
8.0 KiB
PHP
Raw Permalink 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 Option {
/**
* @param $key
* @param $options
* @param bool $false
* @return bool
*/
static function getByKey( $key, $options, $false = false ) {
if ( isset( $options[$key] ) ) {
//Debug::text('Returning Value: '. $options[$key], __FILE__, __LINE__, __METHOD__, 9);
return $options[$key];
}
return $false;
//return FALSE;
}
/**
* @param $value
* @param $options
* @param bool $value_is_translated
* @return bool
*/
static function getByValue( $value, $options, $value_is_translated = true ) {
// I18n: Calling gettext on the value here enables a match with the translated value in the relevant factory.
// BUT... such string comparisons are messy and we really should be using getByKey for most everything.
// Exceptions can be made by passing false for $value_is_translated.
if ( $value_is_translated == true ) {
$value = TTi18n::gettext( $value );
}
if ( is_array( $value ) ) {
return false;
}
if ( !is_array( $options ) ) {
return false;
}
$value = strtolower( $value ); //Use a case insensitive match so things like iButton matches iBUTTON.
$flipped_options = array_flip( array_map( 'strtolower', $options ) );
if ( isset( $flipped_options[$value] ) ) {
//Debug::text('Returning Key: '. $flipped_options[$value], __FILE__, __LINE__, __METHOD__, 9);
return $flipped_options[$value];
}
return false;
}
/**
* Tries to replicate Factory::handleSQLSyntax() or 'text_metaphone' matching in SQL.
* @param $value
* @param $options
* @param bool $value_is_translated
* @return array|bool|mixed
*/
static function getByFuzzyValue( $value, $options, $value_is_translated = true ) {
// I18n: Calling gettext on the value here enables a match with the translated value in the relevant factory.
// BUT... such string comparisons are messy and we really should be using getByKey for most everything.
// Exceptions can be made by passing false for $value_is_translated.
if ( $value_is_translated == true ) {
$value = TTi18n::gettext( $value );
}
if ( is_array( $value ) ) {
return false;
}
if ( !is_array( $options ) ) {
return false;
}
//
//Try to replicate a SQL search from Factory::handleSQLSyntax().
//
$value = str_replace( '*', '%', $value ); //Switch to consistent more SQL like syntax with % wildcards.
if ( $value != '' && strpos( $value, '%' ) === false && ( strpos( $value, '|' ) === false && strpos( $value, '"' ) === false ) ) {
$value .= '%';
}
$flags_exact_match = false;
if ( strpos( $value, '"' ) !== false ) {
$flags_exact_match = true;
}
$flags_exact_end = false;
if ( strpos( $value, '|' ) !== false || strpos( $value, '"' ) !== false ) {
$flags_exact_end = true;
}
//Now that the flags are set above, get rid of special chars to prepare for regex.
$value = str_replace( [ '"', '|' ], '', $value );
//Help prevent regex attack vectors, like backtracking DDOS.
// Don't allow any brackets (ie: (), [] ), as to avoid mismatched brackets causing regex compilation errors.
$value = preg_replace( '/[^A-Za-z0-9-\.\ %\|]/', '', $value );
$regex_retarr = preg_grep( '/^' . str_replace( [ '%' ], [ '.*' ], $value ) . ( ( $flags_exact_end == true ) ? '$' : '' ) . '/i', $options );
if ( !is_array( $regex_retarr ) ) {
$regex_retarr = []; //Empty array.
}
if ( $flags_exact_match === false ) { //Skip metaphone match when using exact match.
//Metaphone match -- Need to strip all special operator characters as they are no good with metaphone anyways.
$metaphone_retarr = preg_grep( '/^' . metaphone( $value ) . ( ( $flags_exact_end == true ) ? '$' : '' ) . '/i', array_map( 'metaphone', $options ) );
if ( !is_array( $metaphone_retarr ) ) {
$metaphone_retarr = []; //Empty array.
}
} else {
$metaphone_retarr = []; //Empty array.
}
$retarr = ( $regex_retarr + $metaphone_retarr ); //Merge while keeping array keys.
if ( empty( $retarr ) == false ) {
arsort( $retarr );
//Debug::Arr( $search_arr, 'Search Str: '. $search_str .' Search Array: ', __FILE__, __LINE__, __METHOD__, 10);
//Debug::Arr( $retarr, 'Matches: ', __FILE__, __LINE__, __METHOD__, 10);
return array_keys( $retarr );
}
return false;
}
/**
* Takes $needles as an array, loops through them returning matching
* keys => value pairs from haystack
* Useful for filtering results to a select box, like status.
* @param $needles
* @param $haystack
* @return array|bool
*/
static function getByArray( $needles, $haystack ) {
if ( !is_array( $needles ) ) {
$needles = [ $needles ];
}
$needles = array_unique( $needles );
$retval = [];
foreach ( $needles as $needle ) {
if ( isset( $haystack[$needle] ) ) {
$retval[$needle] = $haystack[$needle];
}
}
if ( empty( $retval ) == false ) {
return $retval;
}
return false;
}
/**
* @param $bitmask
* @param $options
* @return array|bool
*/
static function getArrayByBitMask( $bitmask, $options ) {
$bitmask = (int)$bitmask;
$retarr = [];
if ( is_numeric( $bitmask ) && is_array( $options ) ) {
foreach ( $options as $key => $value ) {
//Debug::Text('Checking Bitmask: '. $bitmask .' mod '. $key .' != 0', __FILE__, __LINE__, __METHOD__, 10);
if ( ( $bitmask & (int)$key ) !== 0 ) {
//Debug::Text('Found Bit: '. $key, __FILE__, __LINE__, __METHOD__, 10);
$retarr[] = $key;
}
}
unset( $value ); //code standards
}
if ( empty( $retarr ) == false ) {
return $retarr;
}
return false;
}
/**
* @param $keys
* @param $options
* @return int|mixed
*/
static function getBitMaskByArray( $keys, $options ) {
//If an integer is passed in try and convert it to an array automatically.
if ( is_numeric( $keys ) == true ) {
$keys = self::getArrayByBitMask( $keys, $options );
}
$retval = 0;
if ( is_array( $keys ) && is_array( $options ) ) {
foreach ( $keys as $key ) {
if ( isset( $options[$key] ) ) {
$retval |= $key;
} else {
Debug::Text( 'Key is not a valid bitmask int: ' . $key, __FILE__, __LINE__, __METHOD__, 10 );
}
}
}
return $retval;
}
}
?>