diff options
Diffstat (limited to 'modules-available/statistics/inc/statisticsfilter.inc.php')
-rw-r--r-- | modules-available/statistics/inc/statisticsfilter.inc.php | 382 |
1 files changed, 310 insertions, 72 deletions
diff --git a/modules-available/statistics/inc/statisticsfilter.inc.php b/modules-available/statistics/inc/statisticsfilter.inc.php index 4a4899e2..5e6448c7 100644 --- a/modules-available/statistics/inc/statisticsfilter.inc.php +++ b/modules-available/statistics/inc/statisticsfilter.inc.php @@ -10,8 +10,10 @@ abstract class StatisticsFilter */ const LEGACY_DELIMITER = '~,~'; - const SIZE_ID44 = array(0, 8, 16, 24, 30, 40, 50, 60, 80, 100, 120, 150, 180, 250, 300, 400, 500, 1000, 2000, 4000); - const SIZE_RAM = array(1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 320, 480, 512, 768, 1024); + const SIZE_PARTITION = [0, 8, 16, 24, 30, 40, 50, 60, 80, 100, 120, 150, 180, 250, 300, 400, 500, 1000, 1500, 2000, 3000, + 4000, 6000, 8000, 10000]; + const SIZE_RAM = [1, 2, 3, 4, 6, 8, 10, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 320, 480, 512, 768, 1024, 1536, + 2048]; private static $keyCounter = 0; @@ -56,22 +58,57 @@ abstract class StatisticsFilter $this->placeholder = $placeholder; } - public function type() + public function type(): string { return ($this->ops === self::OP_ORDINAL || $this->ops === self::OP_FUZZY_ORDINAL) ? 'int' : 'string'; } - /* returns a where clause and adds needed operators to the passed arrays */ - public abstract function whereClause(string $operator, $argument, array &$args, array &$joins); + /** + * Needed for joins with the hardware tables, to use the HardwareQueryColumn afterwards. + * The HardwareQuery class should probably be extended/rewritten to be more versatile in + * this regard. + */ + public static function addHardwareJoin(array &$args, array &$joins, string $hwtype = null): string + { + $joins['mxhw'] = ' INNER JOIN machine_x_hw mxhw ON (mxhw.disconnecttime = 0 AND mxhw.machineuuid = m.machineuuid)'; + $key = self::getNewKey('foo'); + $shw = self::getNewKey('shw'); + if ($hwtype === null) { + $joins[] = " INNER JOIN statistic_hw $shw ON (mxhw.hwid = {$shw}.hwid)"; + } else { + $joins[] = " INNER JOIN statistic_hw $shw ON (mxhw.hwid = {$shw}.hwid AND {$shw}.hwtype = :$key)"; + $args[$key] = $hwtype; + } + return $shw; + } - public function bind(string $op, $argument) { return new DatabaseFilter($this, $op, $argument); } + /** + * To be called by DatabaseFilter::whereClause() when building actual query. + * @param string $operator operator to use + * @param string[]|string $argument argument to compare against + * @param string[] $args assoc array to add parametrized version of $argument to + * @param string[] $joins any optional joins can be added to this array + * @return string where clause + */ + public abstract function whereClause(string $operator, $argument, array &$args, array &$joins): string; + /** + * Called to get an instance of DatabaseFilter that binds the given $op and $argument to this filter. + * @param string[]|string $argument + */ + public function bind(string $op, $argument): DatabaseFilter { return new DatabaseFilter($this, $op, $argument); } + + /** + * Check if given $operator is valid for this filter. Throws error and halts if not. + * @return void + */ public final function validateOperator(string $operator) { if (empty($this->ops)) return; if (!in_array($operator, $this->ops)) { - Util::traceError("Invalid op '$operator' for " . get_class($this) . '::' . $this->column); + // Yes keep $this in this call, get_class() !== get_class($this) + ErrorHandler::traceError("Invalid op '$operator' for " . get_class($this) . '::' . $this->column); } } @@ -100,7 +137,7 @@ abstract class StatisticsFilter return ($array[$best] + $array[$best - 1]) / 2; } - public static function getNewKey($colname) + public static function getNewKey($colname): string { return $colname . '_' . (self::$keyCounter++); } @@ -108,7 +145,7 @@ abstract class StatisticsFilter /** * @return DatabaseFilter[] */ - public static function parseQuery() + public static function parseQuery(): array { // Get current settings from GET $ops = Request::get('op', [], 'array'); @@ -141,10 +178,7 @@ abstract class StatisticsFilter return $filters; } - /** - * @param \StatisticsFilterSet $filterSet - */ - public static function renderFilterBox($show, $filterSet) + public static function renderFilterBox(string $show, StatisticsFilterSet $filterSet): void { // Build location list, with permissions if (Module::isAvailable('locations')) { @@ -156,7 +190,7 @@ abstract class StatisticsFilter foreach (self::$columns as $key => $filter) { $col = [ 'key' => $key, - 'name' => Dictionary::translateFile('filters', $key, true), + 'name' => Dictionary::translateFile('filters', $key), 'placeholder' => $filter->placeholder, ]; $bind = $filterSet->hasFilterKey($key); @@ -169,8 +203,9 @@ abstract class StatisticsFilter $col['inputclass'] = 'is-date'; } elseif ($filter->type() === 'enum') { $col['enum'] = true; + /** @var EnumStatisticsFilter $filter */ $col['values'] = $filter->values; - if ($bind !== false) { + if ($bind !== null) { // Current value from GET foreach ($col['values'] as &$value) { if ($value['key'] == $bind->argument) { @@ -180,7 +215,7 @@ abstract class StatisticsFilter } } // current value from GET - if ($bind !== false) { + if ($bind !== null) { $col['currentvalue'] = $bind->argument; $col['checked'] = 'checked'; $showCount++; @@ -190,7 +225,7 @@ abstract class StatisticsFilter $col['op'] = $filter->ops; foreach ($col['op'] as &$value) { $value = ['op' => $value]; - if ($bind !== false && $bind->op === $value['op']) { + if ($bind !== null && $bind->op === $value['op']) { $value['selected'] = 'selected'; } } @@ -218,16 +253,16 @@ abstract class StatisticsFilter 'clientip' => new IpStatisticsFilter(), 'hostname' => new SimpleStatisticsFilter('hostname', self::OP_STRCMP, 'pc.fqdn.example.com'), 'machineuuid' => new SimpleStatisticsFilter('machineuuid', self::OP_STRCMP, '88888888-4444-4444-121212121212'), - 'macaddr' => new SimpleStatisticsFilter('macaddr', self::OP_STRCMP, '11-22-33-44-55-66'), + 'macaddr' => new MacAddressStatisticsFilter(), 'firstseen' => new DateStatisticsFilter('firstseen', '2020-10-15 14:00'), 'lastseen' => new DateStatisticsFilter('lastseen', '2020-10-15 14:00'), - 'logintime' => new DateStatisticsFilter('logintime', '2020-10-15 14:00'), 'lastboot' => new DateStatisticsFilter('lastboot', '2020-10-15 14:00'), 'runtime' => new RuntimeStatisticsFilter(), 'realcores' => new SimpleStatisticsFilter('realcores', self::OP_ORDINAL, ''), - 'systemmodel' => new SimpleStatisticsFilter('systemmodel', self::OP_STRCMP, 'PC-365 (IBM)'), + 'systemmodel' => new SystemModelStatisticsFilter(), 'cpumodel' => new SimpleStatisticsFilter('cpumodel', self::OP_STRCMP, 'Pentium Pro 200 MHz'), - 'hddgb' => new Id44GbStatisticsFilter(), + 'hddgb' => new PartitionGbStatisticsFilter('id44mb'), + 'persistentgb' => new PartitionGbStatisticsFilter('id45mb'), 'gbram' => new RamGbStatisticsFilter(), 'kvmstate' => new EnumStatisticsFilter('kvmstate', ['ENABLED', 'DISABLED', 'UNSUPPORTED']), 'badsectors' => new SimpleStatisticsFilter('badsectors', self::OP_ORDINAL, ''), @@ -236,6 +271,12 @@ abstract class StatisticsFilter 'live_swapfree' => new SimpleStatisticsFilter('live_swapfree', self::OP_ORDINAL, 'MiB'), 'live_memfree' => new SimpleStatisticsFilter('live_memfree', self::OP_ORDINAL, 'MiB'), 'live_tmpfree' => new SimpleStatisticsFilter('live_tmpfree', self::OP_ORDINAL, 'MiB'), + 'live_id45free' => new SimpleNotZeroStatisticsFilter('live_id45free', self::OP_ORDINAL, 'MiB'), + 'standbycrash' => new StandbyCrashStatisticsFilter(), + 'pcidev' => new PciDeviceStatisticsFilter(), + 'nicspeed' => new NicSpeedStatisticsFilter(), + 'hddrpm' => new HddRpmStatisticsFilter(), + //'anydev' => new AnyHardwarePropStatisticsFilter(), ]; if (Module::isAvailable('locations')) { self::$columns['location'] = new LocationStatisticsFilter(); @@ -247,14 +288,14 @@ abstract class StatisticsFilter class SimpleStatisticsFilter extends StatisticsFilter { - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { $addendum = ''; $key = self::getNewKey($this->column); $args[$key] = $argument; if (is_array($argument)) { - if ($operator{0} === '!') { + if ($operator[0] === '!') { $op = 'NOT IN'; } else { $op = 'IN'; @@ -277,6 +318,20 @@ class SimpleStatisticsFilter extends StatisticsFilter } +class SimpleNotZeroStatisticsFilter extends SimpleStatisticsFilter +{ + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + $str = parent::whereClause($operator, $argument, $args, $joins); + if ((int)$argument !== 0 || $operator !== '=') { + $str = "($str AND {$this->column} != 0)"; + } + return $str; + } + +} + class EnumStatisticsFilter extends SimpleStatisticsFilter { @@ -301,28 +356,55 @@ class EnumStatisticsFilter extends SimpleStatisticsFilter $this->values = $values; } - public function type() { return 'enum'; } + public function type(): string { return 'enum'; } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { - $keys = ArrayUtil::flattenByKey($this->values, 'key'); - if (is_array($argument)) { - $ok = true; - foreach ($argument as $e) { - if (!in_array($e, $keys)) { - $ok = false; + if ($this->validateArgument()) { + $keys = ArrayUtil::flattenByKey($this->values, 'key'); + if (is_array($argument)) { + $ok = true; + foreach ($argument as $e) { + if (!in_array($e, $keys)) { + $ok = false; + } } + } else { + $ok = in_array($argument, $keys); + } + if (!$ok) { + Message::addError('invalid-enum-item', $this->column, $argument); + return '0'; } - } else { - $ok = in_array($argument, $keys); } - if (!$ok) { - Message::addError('invalid-enum-item', $this->column, $argument); - return '0'; + return parent::whereClause($operator, $argument, $args, $joins); + } + + protected function validateArgument(): bool { return true; } + +} + +class StandbyCrashStatisticsFilter extends EnumStatisticsFilter +{ + + public function __construct() + { + parent::__construct('standbysem', ['NONE', 'MANY']); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + if ($argument === 'NONE') { + $argument = 0; + } else { // MANY + $argument = 3; + $operator = $operator === '=' ? '>' : '<='; } return parent::whereClause($operator, $argument, $args, $joins); } + protected function validateArgument(): bool { return false; } + } class DateStatisticsFilter extends StatisticsFilter @@ -333,12 +415,11 @@ class DateStatisticsFilter extends StatisticsFilter parent::__construct($column, self::OP_ORDINAL, $placeholder); } - public function type() { return 'date'; } + public function type(): string { return 'date'; } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { $key = self::getNewKey($this->column); - $addendum = ''; if (!preg_match('/^(?<date>\d{4}-\d{2}-\d{2})(\s+(?<h>\d{1,2})(:(?<m>\d{2})(:\d+)?)?)?$/', $argument, $out)) { Message::addError('invalid-date-format', $argument); @@ -364,7 +445,7 @@ class DateStatisticsFilter extends StatisticsFilter $args[$key] = strtotime('+1 ' . $span . ' -1 second', $args[$key]); } - return 'm.' . $this->column . ' ' . $operator . ' :' . $key . $addendum; + return 'm.' . $this->column . ' ' . $operator . ' :' . $key; } } @@ -377,7 +458,7 @@ class RuntimeStatisticsFilter extends StatisticsFilter parent::__construct('lastboot', self::OP_ORDINAL); } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { $upper = time() - (int)$argument * 3600; $lower = $upper - 3600; @@ -401,7 +482,7 @@ class RuntimeStatisticsFilter extends StatisticsFilter abstract class GbToMbRangeStatisticsFilter extends StatisticsFilter { - protected function rangeClause(string $operator, $argument, array $fuzzyVals) + protected function rangeClause(string $operator, $argument, array $fuzzyVals): string { if ($operator === '~' || $operator === '!~') { $lower = (int)floor(StatisticsFilter::findBestValue($fuzzyVals, (int)$argument, false) * 1024 - 500); @@ -434,24 +515,24 @@ class RamGbStatisticsFilter extends GbToMbRangeStatisticsFilter parent::__construct('mbram', self::OP_FUZZY_ORDINAL, 'GiB'); } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { return parent::rangeClause($operator, $argument, self::SIZE_RAM); } } -class Id44GbStatisticsFilter extends GbToMbRangeStatisticsFilter +class PartitionGbStatisticsFilter extends GbToMbRangeStatisticsFilter { - public function __construct() + public function __construct(string $column) { - parent::__construct('id44mb', self::OP_FUZZY_ORDINAL,'GiB'); + parent::__construct($column, self::OP_FUZZY_ORDINAL, 'GiB'); } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { - return parent::rangeClause($operator, $argument, self::SIZE_ID44); + return parent::rangeClause($operator, $argument, self::SIZE_PARTITION); } } @@ -463,18 +544,17 @@ class StateStatisticsFilter extends EnumStatisticsFilter parent::__construct('state', ['on', 'off', 'idle', 'occupied', 'standby']); } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { $map = [ 'on' => ['IDLE', 'OCCUPIED'], 'off' => ['OFFLINE'], 'idle' => ['IDLE'], 'occupied' => ['OCCUPIED'], 'standby' => ['STANDBY'] ]; - $neg = $operator == '!=' ? 'NOT ' : ''; + $neg = $operator === '!=' ? 'NOT ' : ''; if (array_key_exists($argument, $map)) { $key = StatisticsFilter::getNewKey($this->column); $args[$key] = $map[$argument]; return " m.state $neg IN ( :$key ) "; - } else { - Message::addError('invalid-filter-argument', 'state', $argument); - return ' 1'; } + Message::addError('invalid-filter-argument', 'state', $argument); + return ' 1'; } } @@ -493,15 +573,15 @@ class LocationStatisticsFilter extends EnumStatisticsFilter parent::__construct('locationid', $locs, self::OP_LOCATIONS); } - public function type() { return 'enum'; } + public function type(): string { return 'enum'; } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { $recursive = (substr($operator, -1) === '~'); $operator = str_replace('~', '=', $operator); if ($recursive && is_array($argument)) { - Util::traceError('Cannot use ~ operator for location with array'); + ErrorHandler::traceError('Cannot use ~ operator for location with array'); } if ($recursive) { $argument = array_keys(Location::getRecursiveFlat($argument)); @@ -539,21 +619,13 @@ class IpStatisticsFilter extends StatisticsFilter } elseif (strpos($argument, '/') !== false) { // TODO: IPv6 CIDR $range = IpUtil::parseCidr($argument); - if ($range === false) { + if ($range === null) { Message::addError('invalid-cidr-notion', $argument); return '0'; } return 'INET_ATON(clientip) BETWEEN ' . $range['start'] . ' AND ' . $range['end']; } elseif (($num = substr_count($argument, ':')) !== 0 && $num <= 7) { - // IPv6, not yet in DB but let's prepare - if ($num > 7 || strpos($argument, '::') !== false) { // Too many :, or invalid compressed format - Message::addError('invalid-ip-address', $argument); - return '0'; - } elseif ($num <= 7 && substr($argument, -1) === ':') { - $argument .= '*'; - } elseif ($num < 7) { - $argument .= ':*'; - } + // TODO: Probably valid IPv6, not yet in DB } elseif (($num = substr_count($argument, '.')) !== 0 && $num <= 3) { if (substr($argument, -1) === '.') { $argument .= '*'; @@ -564,7 +636,8 @@ class IpStatisticsFilter extends StatisticsFilter Message::addError('invalid-ip-address', $argument); return '0'; } - return "clientip LIKE '" . str_replace('*', '%', $argument) . "'"; + $operator = $operator[0] === '!' ? 'NOT LIKE' : 'LIKE'; + return "clientip $operator '" . str_replace('*', '%', $argument) . "'"; } } @@ -576,18 +649,170 @@ class IsClientStatisticsFilter extends StatisticsFilter parent::__construct(null, []); } - public function whereClause(string $operator, $argument, array &$args, array &$joins) + public function whereClause(string $operator, $argument, array &$args, array &$joins): string { if ($argument) { - $joins[] = ' LEFT JOIN runmode USING (machineuuid)'; + $joins[] = ' LEFT JOIN runmode ON (m.machineuuid = runmode.machineuuid)'; return "(runmode.isclient <> 0 OR runmode.isclient IS NULL)"; } - $joins[] = ' INNER JOIN runmode USING (machineuuid)'; + $joins[] = ' INNER JOIN runmode ON (m.machineuuid = runmode.machineuuid)'; return "runmode.isclient = 0"; } } +class PciDeviceStatisticsFilter extends StatisticsFilter +{ + + public function __construct() + { + parent::__construct(null, ['='], 'vvvv[:dddd][,cccc]'); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + // vendor[:device][,class] + if (!preg_match('/^(?<v>[0-9a-f]{4})(?::(?<d>[0-9a-f]{4}))?(?:,(?<c>[0-9a-f]{4}))?$/i', $argument, $out)) { + Message::addError('invalid-pciid', $argument); + return '0'; + } + $vendor = $out['v']; + $device = $out['d'] ?? ''; + $class = $out['c'] ?? ''; + // basic join for hw_x_machine + $shw = StatisticsFilter::addHardwareJoin($args, $joins, HardwareInfo::PCI_DEVICE); + $_ = []; + $c = new HardwareQueryColumn(true, 'vendor'); + $c->addCondition($operator, $vendor); + $c->generate($joins, $_, $args, [], $shw); + if (!empty($device)) { + $c = new HardwareQueryColumn(true, 'device'); + $c->addCondition($operator, $device); + $c->generate($joins, $_, $args, [], $shw); + } + if (!empty($class)) { + $c = new HardwareQueryColumn(true, 'class'); + $c->addCondition($operator, $class); + $c->generate($joins, $_, $args, [], $shw); + } + return '1'; + } + +} + +class NicSpeedStatisticsFilter extends StatisticsFilter +{ + + public function __construct() + { + parent::__construct(null, StatisticsFilter::OP_ORDINAL, 'MBit/s'); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + $shw = StatisticsFilter::addHardwareJoin($args, $joins, HardwareInfo::MAINBOARD); + $_ = []; + $c = new HardwareQueryColumn(false, 'nic-speed'); + $c->addCondition($operator, $argument); + $c->generate($joins, $_, $args, [], $shw); + return '1'; + } + +} + +class HddRpmStatisticsFilter extends StatisticsFilter +{ + + public function __construct() + { + parent::__construct(null, StatisticsFilter::OP_ORDINAL, '7200'); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + $shw = StatisticsFilter::addHardwareJoin($args, $joins, HardwareInfo::HDD); + $_ = []; + $c = new HardwareQueryColumn(true, 'rotation_rate'); + $c->addCondition($operator, $argument); + $c->generate($joins, $_, $args, [], $shw); + return '1'; + } + +} + +class SystemModelStatisticsFilter extends StatisticsFilter +{ + + public function __construct() + { + parent::__construct(null, StatisticsFilter::OP_STRCMP, 'PC-365 (IBM)'); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + $shw = StatisticsFilter::addHardwareJoin($args, $joins, HardwareInfo::DMI_SYSTEM); + $_ = []; + $manufacturer = null; + $model = $argument; + if (preg_match('/^(.*)\((.*)\)\s*$/', $model, $out)) { + $manufacturer = trim($out[2]); + $model = trim($out[1]); + } + $c = new HardwareQueryColumn(true, 'Product Name'); + $c->addCondition($operator, $model); + $c->generate($joins, $_, $args, [], $shw); + if ($manufacturer !== null) { + $c = new HardwareQueryColumn(true, 'Manufacturer'); + $c->addCondition($operator, $manufacturer); + $c->generate($joins, $_, $args, [], $shw); + } + return '1'; + } + +} + +class MacAddressStatisticsFilter extends SimpleStatisticsFilter +{ + public function __construct() + { + parent::__construct('macaddr', self::OP_STRCMP, '11-22-33-44-55-66'); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + // Allow just 12 hex digits, and convert ':' to '-', which we unfortunately settled on for the DB format + if (preg_match('/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', + $argument, $out)) { + $argument = $out[1] . '-' . $out[2] . '-' . $out[3] . '-' . $out[4] . '-' . $out[5] . '-' . $out[6]; + } elseif (strpos($argument, ':') !== false) { + $argument = str_replace(':', '-', $argument); + } + return parent::whereClause($operator, $argument, $args, $joins); + } +} + +class AnyHardwarePropStatisticsFilter extends StatisticsFilter +{ + + public function __construct() + { + parent::__construct(null, ['~']); + } + + public function whereClause(string $operator, $argument, array &$args, array &$joins): string + { + $shw = StatisticsFilter::addHardwareJoin($args, $joins); + $val = self::getNewKey('val'); + $key1 = self::getNewKey('hw'); + $joins[] = "LEFT JOIN statistic_hw_prop $key1 ON (`$key1`.`value` LIKE :$val AND `$key1`.hwid = `$shw`.hwid)"; + $key2 = self::getNewKey('hw'); + $joins[] = "LEFT JOIN machine_x_hw_prop $key2 ON (`$key2`.`value` LIKE :$val AND `$key2`.machinehwid = mxhw.machinehwid)"; + $args[$val] = '%' . str_replace(['%', '*'], ['_', '%'], $argument) . '%'; + return "((`$key1`.`value` IS NOT NULL) OR (`$key2`.`value` IS NOT NULL))"; + } + +} + class DatabaseFilter { /** @var StatisticsFilter @@ -595,6 +820,10 @@ class DatabaseFilter private $inst; public $op; public $argument; + + /** + * Called by StatisticsFilter::bind(). + */ public function __construct(StatisticsFilter $inst, string $op, $argument) { $inst->validateOperator($op); @@ -602,16 +831,25 @@ class DatabaseFilter $this->op = $op; $this->argument = $argument; } - public function whereClause(array &$args, array &$joins) + + /** + * Called from StatisticsFilterSet::makeFragments() to build the final query. + */ + public function whereClause(array &$args, array &$joins): string { return $this->inst->whereClause($this->op, $this->argument, $args, $joins); } - public function isClass($what) + public function isClass(string $what): bool { return get_class($this->inst) === $what; } + public function getClass(): string + { + return get_class($this->inst); + } + } StatisticsFilter::initConstants(); |