You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

398 lines
9.5 KiB

1 year ago
<?php
/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** 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 General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
class CProfile {
private static $userDetails = [];
private static $profiles = null;
private static $update = [];
private static $insert = [];
private static $stringProfileMaxLength;
private static $is_initialized = false;
public static function init() {
self::$userDetails = CWebUser::$data;
self::$profiles = [];
self::$stringProfileMaxLength = DB::getFieldLength('profiles', 'value_str');
DBselect('SELECT NULL FROM users u WHERE '.dbConditionId('u.userid', (array) self::$userDetails['userid']).
' FOR UPDATE'
);
if (!self::$is_initialized) {
register_shutdown_function(function() {
DBstart();
$result = self::flush();
DBend($result);
});
}
self::$is_initialized = true;
}
/**
* Check if data needs to be inserted or updated.
*
* @return bool
*/
public static function isModified() {
return (self::$insert || self::$update);
}
public static function flush() {
$result = false;
if (self::$profiles !== null && self::$userDetails['userid'] > 0 && self::isModified()) {
$result = true;
foreach (self::$insert as $idx => $profile) {
foreach ($profile as $idx2 => $data) {
if (!self::insertDB($idx, $data['value'], $data['type'], $idx2)) {
$result = false;
}
}
}
ksort(self::$update);
foreach (self::$update as $idx => $profile) {
ksort($profile);
foreach ($profile as $idx2 => $data) {
if (!self::updateDB($idx, $data['value'], $data['type'], $idx2)) {
$result = false;
}
}
}
self::clear();
}
return $result;
}
public static function clear() {
self::$insert = [];
self::$update = [];
}
/**
* Return array of matched idx keys for current user.
*
* @param string $idx_pattern Search pattern, SQL like wildcards can be used.
* @param int $idx2 Numerical index will be matched against idx2 index.
*
* @return array
*/
public static function findByIdxPattern($idx_pattern, $idx2) {
if (!CWebUser::$data) {
return null;
}
if (self::$profiles === null) {
self::init();
}
// Convert SQL _ and % wildcard characters to regexp.
$regexp = str_replace(['_', '%'], ['.', '.*'], preg_quote($idx_pattern, '/'));
$regexp = '/^'.$regexp.'/';
$results = [];
foreach (self::$profiles as $k => $v) {
if (preg_match($regexp, $k, $match) && array_key_exists($idx2, $v)) {
$results[] = $k;
}
}
if ($results) {
return $results;
}
// Aggressive caching, cache all items matched $idx key.
$query = DBselect(
'SELECT type,value_id,value_int,value_str,idx,idx2'.
' FROM profiles'.
' WHERE userid='.self::$userDetails['userid'].
' AND idx LIKE '.zbx_dbstr($idx_pattern)
);
while ($row = DBfetch($query)) {
$value_type = self::getFieldByType($row['type']);
$idx = $row['idx'];
if (!array_key_exists($idx, self::$profiles)) {
self::$profiles[$idx] = [];
}
self::$profiles[$idx][$row['idx2']] = $row[$value_type];
if ($row['idx2'] == $idx2) {
$results[] = $idx;
}
}
return $results;
}
/**
* Return matched idx value for current user.
*
* @param string $idx Search pattern.
* @param mixed $default_value Default value if no rows was found.
* @param int|null $idx2 Numerical index will be matched against idx2 index.
*
* @return mixed
*/
public static function get($idx, $default_value = null, $idx2 = 0) {
// no user data available, just return the default value
if (!CWebUser::$data || $idx2 === null) {
return $default_value;
}
if (self::$profiles === null) {
self::init();
}
if (array_key_exists($idx, self::$profiles)) {
// When there is cached data for $idx but $idx2 was not found we should return default value.
return array_key_exists($idx2, self::$profiles[$idx]) ? self::$profiles[$idx][$idx2] : $default_value;
}
self::$profiles[$idx] = [];
// Aggressive caching, cache all items matched $idx key.
$query = DBselect(
'SELECT type,value_id,value_int,value_str,idx2'.
' FROM profiles'.
' WHERE userid='.self::$userDetails['userid'].
' AND idx='.zbx_dbstr($idx)
);
while ($row = DBfetch($query)) {
$value_type = self::getFieldByType($row['type']);
self::$profiles[$idx][$row['idx2']] = $row[$value_type];
}
ksort(self::$profiles[$idx], SORT_NUMERIC);
return array_key_exists($idx2, self::$profiles[$idx]) ? self::$profiles[$idx][$idx2] : $default_value;
}
/**
* Returns the values stored under the given $idx as an array.
*
* @param string $idx
* @param mixed $defaultValue
*
* @return mixed
*/
public static function getArray($idx, $defaultValue = null) {
if (self::get($idx, null, 0) === null) {
return $defaultValue;
}
return self::$profiles[$idx];
}
/**
* Removes profile values from DB and profiles cache.
*
* @param string $idx first identifier
* @param int|array $idx2 second identifier, which can be list of identifiers as well
*/
public static function delete($idx, $idx2 = 0) {
if (self::$profiles === null) {
self::init();
}
$idx2 = (array) $idx2;
self::deleteValues($idx, $idx2);
if (array_key_exists($idx, self::$profiles)) {
foreach ($idx2 as $index) {
unset(self::$profiles[$idx][$index]);
}
}
}
/**
* Removes all values stored under the given idx.
*
* @param string $idx
*/
public static function deleteIdx($idx) {
if (self::$profiles === null) {
self::init();
}
DB::delete('profiles', ['idx' => $idx, 'userid' => self::$userDetails['userid']]);
unset(self::$profiles[$idx]);
}
/**
* Deletes the given values from the DB.
*
* @param string $idx
* @param array $idx2
*/
protected static function deleteValues($idx, array $idx2) {
// remove from DB
DB::delete('profiles', ['idx' => $idx, 'idx2' => $idx2, 'userid' => self::$userDetails['userid']]);
}
/**
* Update favorite values in DB profiles table.
*
* @param string $idx max length is 96
* @param mixed $value max length 255 for string
* @param int $type
* @param int $idx2
*/
public static function update($idx, $value, $type, $idx2 = 0) {
if (self::$profiles === null) {
self::init();
}
if (is_array($value)) {
return;
}
if (!self::checkValueType($value, $type)) {
return;
}
$profile = [
'idx' => $idx,
'value' => $value,
'type' => $type,
'idx2' => $idx2
];
$current = self::get($idx, null, $idx2);
if (is_null($current)) {
if (!isset(self::$insert[$idx])) {
self::$insert[$idx] = [];
}
self::$insert[$idx][$idx2] = $profile;
}
else {
if ($current != $value) {
if (!isset(self::$update[$idx])) {
self::$update[$idx] = [];
}
self::$update[$idx][$idx2] = $profile;
}
}
if (!isset(self::$profiles[$idx])) {
self::$profiles[$idx] = [];
}
self::$profiles[$idx][$idx2] = $value;
}
/**
* Stores an array in the profiles.
*
* Each value is stored under the given idx and a sequentially generated idx2.
*
* @param string $idx
* @param array $values
* @param int $type
*/
public static function updateArray($idx, array $values, $type) {
// save new values
$i = 0;
foreach ($values as $value) {
self::update($idx, $value, $type, $i);
$i++;
}
// delete remaining old values
$idx2 = [];
while (self::get($idx, null, $i) !== null) {
$idx2[] = $i;
$i++;
}
self::delete($idx, $idx2);
}
private static function insertDB($idx, $value, $type, $idx2) {
$value_type = self::getFieldByType($type);
$values = [
'profileid' => get_dbid('profiles', 'profileid'),
'userid' => self::$userDetails['userid'],
'idx' => zbx_dbstr($idx),
$value_type => zbx_dbstr($value),
'type' => $type,
'idx2' => zbx_dbstr($idx2)
] + [
'value_str' => zbx_dbstr('')
];
return DBexecute('INSERT INTO profiles ('.implode(', ', array_keys($values)).') VALUES ('.implode(', ', $values).')');
}
private static function updateDB($idx, $value, $type, $idx2) {
$valueType = self::getFieldByType($type);
return DBexecute(
'UPDATE profiles SET '.
$valueType.'='.zbx_dbstr($value).','.
' type='.$type.
' WHERE userid='.self::$userDetails['userid'].
' AND idx='.zbx_dbstr($idx).
' AND idx2='.zbx_dbstr($idx2)
);
}
private static function getFieldByType($type) {
switch ($type) {
case PROFILE_TYPE_INT:
$field = 'value_int';
break;
case PROFILE_TYPE_STR:
$field = 'value_str';
break;
case PROFILE_TYPE_ID:
default:
$field = 'value_id';
}
return $field;
}
private static function checkValueType($value, $type) {
switch ($type) {
case PROFILE_TYPE_ID:
return zbx_ctype_digit($value);
case PROFILE_TYPE_INT:
return zbx_is_int($value);
case PROFILE_TYPE_STR:
return mb_strlen($value) <= self::$stringProfileMaxLength;
default:
return true;
}
}
}