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