summaryrefslogtreecommitdiffstats
path: root/modules-available/statistics/inc/statisticsfilter.inc.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules-available/statistics/inc/statisticsfilter.inc.php')
-rw-r--r--modules-available/statistics/inc/statisticsfilter.inc.php382
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();