<?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; } } ?>