1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * Class calculates graph data and makes SVG graph. 24 */ 25class CSvgGraphHelper { 26 27 /** 28 * Calculate graph data and draw SVG graph based on given graph configuration. 29 * 30 * @param array $options Options for graph. 31 * @param array $options['data_sets'] Graph data set options. 32 * @param int $options['data_source'] Data source of graph. 33 * @param bool $options['dashboard_time'] True if dashboard time is used. 34 * @param array $options['time_period'] Graph time period used. 35 * @param array $options['left_y_axis'] Options for graph left Y axis. 36 * @param array $options['right_y_axis'] Options for graph right Y axis. 37 * @param array $options['x_axis'] Options for graph X axis. 38 * @param array $options['legend'] Options for graph legend. 39 * @param int $options['legend_lines'] Number of lines in the legend. 40 * @param array $options['problems'] Graph problems options. 41 * @param array $options['overrides'] Graph override options. 42 * 43 * @return array 44 */ 45 public static function get(array $options = [], $width, $height) { 46 $metrics = []; 47 $errors = []; 48 49 // Find which metrics will be shown in graph and calculate time periods and display options. 50 self::getMetrics($metrics, $options['data_sets']); 51 // Apply overrides for previously selected $metrics. 52 53 $metrics = CMacrosResolverHelper::resolveItemNames($metrics); 54 $metrics = CArrayHelper::renameObjectsKeys($metrics, ['name_expanded' => 'name']); 55 56 self::applyOverrides($metrics, $options['overrides']); 57 // Apply time periods for each $metric, based on graph/dashboard time as well as metric level timeshifts. 58 self::getTimePeriods($metrics, $options['time_period']); 59 // Find what data source (history or trends) will be used for each metric. 60 self::getGraphDataSource($metrics, $errors, $options['data_source'], $width); 61 // Load Data for each metric. 62 self::getMetricsData($metrics, $width); 63 // Load aggregated Data for each dataset. 64 self::getMetricsAggregatedData($metrics); 65 66 // Legend single line height is 18. Value should be synchronized with $svg-legend-line-height in 'screen.scss'. 67 $legend_height = ($options['legend'] == SVG_GRAPH_LEGEND_TYPE_SHORT) ? $options['legend_lines'] * 18 : 0; 68 69 // Draw SVG graph. 70 $graph = (new CSvgGraph([ 71 'time_period' => $options['time_period'], 72 'x_axis' => $options['x_axis'], 73 'left_y_axis' => $options['left_y_axis'], 74 'right_y_axis' => $options['right_y_axis'] 75 ])) 76 ->setSize($width, $height - $legend_height) 77 ->addMetrics($metrics); 78 79 // Get problems to display in graph. 80 if ($options['problems']['show_problems'] == SVG_GRAPH_PROBLEMS_SHOW) { 81 $options['problems']['itemids'] = 82 ($options['problems']['graph_item_problems'] == SVG_GRAPH_SELECTED_ITEM_PROBLEMS) 83 ? array_unique(zbx_objectValues($metrics, 'itemid')) 84 : null; 85 86 $problems = self::getProblems($options['problems'], $options['time_period']); 87 $graph->addProblems($problems); 88 } 89 90 if ($legend_height > 0) { 91 $labels = []; 92 93 foreach ($metrics as $metric) { 94 $labels[] = [ 95 'name' => $metric['name'], 96 'color' => $metric['options']['color'] 97 ]; 98 } 99 100 $legend = (new CSvgGraphLegend($labels)) 101 ->setAttribute('style', 'height: '.$legend_height.'px') 102 ->toString(); 103 } 104 else { 105 $legend = ''; 106 } 107 108 // Draw graph. 109 $graph->draw(); 110 111 // SBox available only for graphs without overriten relative time. 112 if ($options['dashboard_time']) { 113 $graph->addSBox(); 114 } 115 116 // Add mouse following helper line. 117 $graph->addHelper(); 118 119 return [ 120 'svg' => $graph, 121 'legend' => $legend, 122 'data' => [ 123 'dims' => [ 124 'x' => $graph->getCanvasX(), 125 'y' => $graph->getCanvasY(), 126 'w' => $graph->getCanvasWidth(), 127 'h' => $graph->getCanvasHeight() 128 ], 129 'spp' => (int) ($options['time_period']['time_to'] - $options['time_period']['time_from']) / $graph->getCanvasWidth() 130 ], 131 'errors' => $errors 132 ]; 133 } 134 135 /** 136 * Select aggregated data to show in graph for each metric. 137 */ 138 protected static function getMetricsAggregatedData(array &$metrics = []) { 139 $dataset_metrics = []; 140 141 foreach ($metrics as $metric_num => &$metric) { 142 if ($metric['options']['aggregate_function'] == GRAPH_AGGREGATE_NONE) { 143 continue; 144 } 145 146 $dataset_num = $metric['data_set']; 147 148 if ($metric['options']['aggregate_grouping'] == GRAPH_AGGREGATE_BY_ITEM) { 149 $name = $metric['hosts'][0]['name'].NAME_DELIMITER.$metric['name']; 150 } 151 else { 152 $name = 'Dataset #'.($dataset_num + 1); 153 } 154 155 $item = [ 156 'itemid' => $metric['itemid'], 157 'value_type' => $metric['value_type'], 158 'source' => ($metric['source'] == SVG_GRAPH_DATA_SOURCE_HISTORY) ? 'history' : 'trends' 159 ]; 160 161 if (!array_key_exists($dataset_num, $dataset_metrics)) { 162 $metric = array_merge($metric, [ 163 'name' => graph_item_aggr_fnc2str($metric['options']['aggregate_function']).'('.$name.')', 164 'items' => [], 165 'points' => [] 166 ]); 167 $metric['options']['aggregate_interval'] = 168 (int) timeUnitToSeconds($metric['options']['aggregate_interval'], true); 169 170 if ($metric['options']['aggregate_grouping'] == GRAPH_AGGREGATE_BY_DATASET) { 171 $dataset_metrics[$dataset_num] = $metric_num; 172 } 173 174 $metric['items'][] = $item; 175 } 176 else { 177 $metrics[$dataset_metrics[$dataset_num]]['items'][] = $item; 178 unset($metrics[$metric_num]); 179 } 180 } 181 unset($metric); 182 183 foreach ($metrics as &$metric) { 184 if ($metric['options']['aggregate_function'] == GRAPH_AGGREGATE_NONE) { 185 continue; 186 } 187 188 $result = Manager::History()->getGraphAggregationByInterval( 189 $metric['items'], $metric['time_period']['time_from'], $metric['time_period']['time_to'], 190 $metric['options']['aggregate_function'], $metric['options']['aggregate_interval'] 191 ); 192 193 $metric_points = []; 194 195 if ($result) { 196 foreach ($result as $itemid => $points) { 197 foreach ($points['data'] as $point) { 198 $metric_points[$point['tick']]['itemid'][] = $point['itemid']; 199 $metric_points[$point['tick']]['clock'][] = $point['clock']; 200 201 if (array_key_exists('count', $point)) { 202 $metric_points[$point['tick']]['count'][] = $point['count']; 203 } 204 if (array_key_exists('value', $point)) { 205 $metric_points[$point['tick']]['value'][] = $point['value']; 206 } 207 } 208 } 209 ksort($metric_points, SORT_NUMERIC); 210 211 switch ($metric['options']['aggregate_function']) { 212 case GRAPH_AGGREGATE_MIN: 213 foreach ($metric_points as $tick => $point) { 214 $metric['points'][] = ['clock' => $tick, 'value' => min($point['value'])]; 215 } 216 break; 217 case GRAPH_AGGREGATE_MAX: 218 foreach ($metric_points as $tick => $point) { 219 $metric['points'][] = ['clock' => $tick, 'value' => max($point['value'])]; 220 } 221 break; 222 case GRAPH_AGGREGATE_AVG: 223 foreach ($metric_points as $tick => $point) { 224 $metric['points'][] = [ 225 'clock' => $tick, 226 'value' => CMathHelper::safeAvg($point['value']) 227 ]; 228 } 229 break; 230 case GRAPH_AGGREGATE_COUNT: 231 foreach ($metric_points as $tick => $point) { 232 $metric['points'][] = ['clock' => $tick, 'value' => array_sum($point['count'])]; 233 } 234 break; 235 case GRAPH_AGGREGATE_SUM: 236 foreach ($metric_points as $tick => $point) { 237 $metric['points'][] = ['clock' => $tick, 'value' => array_sum($point['value'])]; 238 } 239 break; 240 case GRAPH_AGGREGATE_FIRST: 241 foreach ($metric_points as $tick => $point) { 242 if ($point['clock']) { 243 $metric['points'][] = [ 244 'clock' => $tick, 245 'value' => $point['value'][array_search(min($point['clock']), $point['clock'])] 246 ]; 247 } 248 } 249 break; 250 case GRAPH_AGGREGATE_LAST: 251 foreach ($metric_points as $tick => $point) { 252 if ($point['clock']) { 253 $metric['points'][] = [ 254 'clock' => $tick, 255 'value' => $point['value'][array_search(max($point['clock']), $point['clock'])] 256 ]; 257 } 258 } 259 break; 260 } 261 } 262 } 263 } 264 265 /** 266 * Select data to show in graph for each metric. 267 */ 268 protected static function getMetricsData(array &$metrics = [], $width) { 269 // To reduce number of requests, group metrics by time range. 270 $tr_groups = []; 271 foreach ($metrics as $metric_num => &$metric) { 272 if ($metric['options']['aggregate_function'] != GRAPH_AGGREGATE_NONE) { 273 continue; 274 } 275 276 $metric['name'] = $metric['hosts'][0]['name'].NAME_DELIMITER.$metric['name']; 277 $metric['points'] = []; 278 279 $key = $metric['time_period']['time_from'].$metric['time_period']['time_to']; 280 if (!array_key_exists($key, $tr_groups)) { 281 $tr_groups[$key] = [ 282 'time' => [ 283 'from' => $metric['time_period']['time_from'], 284 'to' => $metric['time_period']['time_to'] 285 ] 286 ]; 287 } 288 289 $tr_groups[$key]['items'][$metric_num] = [ 290 'itemid' => $metric['itemid'], 291 'value_type' => $metric['value_type'], 292 'source' => ($metric['source'] == SVG_GRAPH_DATA_SOURCE_HISTORY) ? 'history' : 'trends' 293 ]; 294 } 295 unset($metric); 296 297 // Request data. 298 foreach ($tr_groups as $tr_group) { 299 $results = Manager::History()->getGraphAggregationByWidth($tr_group['items'], $tr_group['time']['from'], 300 $tr_group['time']['to'], $width 301 ); 302 303 if ($results) { 304 foreach ($tr_group['items'] as $metric_num => $item) { 305 $metric = &$metrics[$metric_num]; 306 307 // Collect and sort data points. 308 if (array_key_exists($item['itemid'], $results)) { 309 $points = []; 310 foreach ($results[$item['itemid']]['data'] as $point) { 311 $points[] = ['clock' => $point['clock'], 'value' => $point['avg']]; 312 } 313 usort($points, [__CLASS__, 'sortByClock']); 314 $metric['points'] = $points; 315 316 unset($metric['source'], $metric['history'], $metric['trends']); 317 } 318 } 319 unset($metric); 320 } 321 } 322 } 323 324 /** 325 * Calculate what data source must be used for each metric. 326 */ 327 protected static function getGraphDataSource(array &$metrics = [], array &$errors = [], $data_source, $width) { 328 /** 329 * If data source is not specified, calculate it automatically. Otherwise, set given $data_source to each 330 * $metric. 331 */ 332 if ($data_source == SVG_GRAPH_DATA_SOURCE_AUTO) { 333 /** 334 * First, if global configuration setting "Override item history period" is enabled, override globally 335 * specified "Data storage period" value to each metric's custom history storage duration, converting it 336 * to seconds. If "Override item history period" is disabled, item level field 'history' will be used 337 * later but now we are just storing the field name 'history' in array $to_resolve. 338 * 339 * Do the same with trends. 340 */ 341 $to_resolve = []; 342 343 if (CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL)) { 344 foreach ($metrics as &$metric) { 345 $metric['history'] = timeUnitToSeconds(CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY)); 346 } 347 unset($metric); 348 } 349 else { 350 $to_resolve[] = 'history'; 351 } 352 353 if (CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL)) { 354 foreach ($metrics as &$metric) { 355 $metric['trends'] = timeUnitToSeconds(CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS)); 356 } 357 unset($metric); 358 } 359 else { 360 $to_resolve[] = 'trends'; 361 } 362 363 // If no global history and trend override enabled, resolve 'history' and/or 'trends' values for given $metric. 364 if ($to_resolve) { 365 $metrics = CMacrosResolverHelper::resolveTimeUnitMacros($metrics, $to_resolve); 366 $simple_interval_parser = new CSimpleIntervalParser(); 367 368 foreach ($metrics as $num => &$metric) { 369 // Convert its values to seconds. 370 if (!CHousekeepingHelper::get(CHousekeepingHelper::HK_HISTORY_GLOBAL)) { 371 if ($simple_interval_parser->parse($metric['history']) != CParser::PARSE_SUCCESS) { 372 $errors[] = _s('Incorrect value for field "%1$s": %2$s.', 'history', 373 _('invalid history storage period') 374 ); 375 unset($metrics[$num]); 376 } 377 else { 378 $metric['history'] = timeUnitToSeconds($metric['history']); 379 } 380 } 381 382 if (!CHousekeepingHelper::get(CHousekeepingHelper::HK_TRENDS_GLOBAL)) { 383 if ($simple_interval_parser->parse($metric['trends']) != CParser::PARSE_SUCCESS) { 384 $errors[] = _s('Incorrect value for field "%1$s": %2$s.', 'trends', 385 _('invalid trend storage period') 386 ); 387 unset($metrics[$num]); 388 } 389 else { 390 $metric['trends'] = timeUnitToSeconds($metric['trends']); 391 } 392 } 393 } 394 unset($metric); 395 } 396 397 foreach ($metrics as &$metric) { 398 /** 399 * History as a data source is used in 2 cases: 400 * 1) if trends are disabled (set to 0) either for particular $metric item or globally; 401 * 2) if period for requested data is newer than the period of keeping history for particular $metric 402 * item. 403 * 404 * Use trends otherwise. 405 */ 406 $history = $metric['history']; 407 $trends = $metric['trends']; 408 $time_from = $metric['time_period']['time_from']; 409 $period = $metric['time_period']['time_to'] - $time_from; 410 411 $metric['source'] = ($trends == 0 || (time() - $history < $time_from 412 && $period / $width <= ZBX_MAX_TREND_DIFF / ZBX_GRAPH_MAX_SKIP_CELL)) 413 ? SVG_GRAPH_DATA_SOURCE_HISTORY 414 : SVG_GRAPH_DATA_SOURCE_TRENDS; 415 } 416 unset($metric); 417 } 418 else { 419 foreach ($metrics as &$metric) { 420 $metric['source'] = $data_source; 421 } 422 unset($metric); 423 } 424 } 425 426 /** 427 * Find problems at given time period that matches specified problem options. 428 */ 429 protected static function getProblems(array $problem_options, array $time_period) { 430 $options = [ 431 'output' => ['objectid', 'name', 'severity', 'clock', 'r_eventid'], 432 'select_acknowledges' => ['action'], 433 'problem_time_from' => $time_period['time_from'], 434 'problem_time_till' => $time_period['time_to'], 435 'preservekeys' => true 436 ]; 437 438 // Find triggers involved. 439 if ($problem_options['problemhosts'] !== '') { 440 $options['hostids'] = array_keys(API::Host()->get([ 441 'output' => [], 442 'searchWildcardsEnabled' => true, 443 'searchByAny' => true, 444 'search' => [ 445 'name' => self::processPattern($problem_options['problemhosts']) 446 ], 447 'preservekeys' => true 448 ])); 449 450 // Return if no hosts found. 451 if (!$options['hostids']) { 452 return []; 453 } 454 } 455 456 $options['objectids'] = array_keys(API::Trigger()->get([ 457 'output' => [], 458 'hostids' => array_key_exists('hostids', $options) ? $options['hostids'] : null, 459 'itemids' => $problem_options['itemids'], 460 'monitored' => true, 461 'preservekeys' => true 462 ])); 463 464 unset($options['hostids']); 465 466 // Return if no triggers found. 467 if (!$options['objectids']) { 468 return []; 469 } 470 471 // Add severity filter. 472 $filter_severities = implode(',', $problem_options['severities']); 473 $all_severities = implode(',', range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1)); 474 475 if ($filter_severities !== '' && $filter_severities !== $all_severities) { 476 $options['severities'] = $problem_options['severities']; 477 } 478 479 // Add problem name filter. 480 if ($problem_options['problem_name'] !== '') { 481 $options['searchWildcardsEnabled'] = true; 482 $options['search']['name'] = $problem_options['problem_name']; 483 } 484 485 // Add tags filter. 486 if ($problem_options['tags']) { 487 $options['evaltype'] = $problem_options['evaltype']; 488 $options['tags'] = $problem_options['tags']; 489 } 490 491 $events = API::Event()->get($options); 492 493 // Find end time for each problem. 494 if ($events) { 495 $r_events = API::Event()->get([ 496 'output' => ['clock'], 497 'eventids' => zbx_objectValues($events, 'r_eventid'), 498 'preservekeys' => true 499 ]); 500 } 501 502 // Add recovery times for each problem event. 503 foreach ($events as &$event) { 504 $event['r_clock'] = array_key_exists($event['r_eventid'], $r_events) 505 ? $r_events[$event['r_eventid']]['clock'] 506 : 0; 507 } 508 unset($event); 509 510 CArrayHelper::sort($events, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]); 511 512 return $events; 513 } 514 515 /** 516 * Select metrics from given data set options. Apply data set options to each selected metric. 517 */ 518 protected static function getMetrics(array &$metrics, array $data_sets) { 519 $max_metrics = SVG_GRAPH_MAX_NUMBER_OF_METRICS; 520 521 foreach ($data_sets as $index => $data_set) { 522 if (!$data_set['hosts'] || !$data_set['items']) { 523 continue; 524 } 525 526 if ($max_metrics == 0) { 527 break; 528 } 529 530 // Find hosts. 531 $hosts = API::Host()->get([ 532 'output' => [], 533 'search' => [ 534 'name' => self::processPattern($data_set['hosts']) 535 ], 536 'searchWildcardsEnabled' => true, 537 'searchByAny' => true, 538 'preservekeys' => true 539 ]); 540 541 if ($hosts) { 542 $items = API::Item()->get([ 543 'output' => [ 544 'itemid', 'hostid', 'name', 'history', 'trends', 'units', 'value_type', 'valuemapid', 'key_' 545 ], 546 'selectHosts' => ['hostid', 'name'], 547 'hostids' => array_keys($hosts), 548 'webitems' => true, 549 'filter' => [ 550 'value_type' => [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT] 551 ], 552 'search' => [ 553 'name' => self::processPattern($data_set['items']) 554 ], 555 'searchWildcardsEnabled' => true, 556 'searchByAny' => true, 557 'sortfield' => 'name', 558 'sortorder' => ZBX_SORT_UP, 559 'limit' => $max_metrics 560 ]); 561 562 if (!$items) { 563 continue; 564 } 565 566 unset($data_set['hosts'], $data_set['items']); 567 568 // The bigger transparency level, the less visible the metric is. 569 $data_set['transparency'] = 10 - (int) $data_set['transparency']; 570 571 $data_set['timeshift'] = ($data_set['timeshift'] !== '') 572 ? (int) timeUnitToSeconds($data_set['timeshift']) 573 : 0; 574 575 $colors = getColorVariations('#'.$data_set['color'], count($items)); 576 577 foreach ($items as $item) { 578 $data_set['color'] = array_shift($colors); 579 $metrics[] = $item + ['data_set' => $index, 'options' => $data_set]; 580 $max_metrics--; 581 } 582 } 583 } 584 } 585 586 /** 587 * Apply overrides for each pattern matchig metric. 588 */ 589 protected static function applyOverrides(array &$metrics = [], array $overrides = []) { 590 foreach ($overrides as $override) { 591 if ($override['hosts'] === '' || $override['items'] === '') { 592 continue; 593 } 594 595 // Convert timeshift to seconds. 596 if (array_key_exists('timeshift', $override)) { 597 $override['timeshift'] = ($override['timeshift'] !== '') 598 ? (int) timeUnitToSeconds($override['timeshift']) 599 : 0; 600 } 601 602 $hosts_patterns = self::processPattern($override['hosts']); 603 $items_patterns = self::processPattern($override['items']); 604 605 unset($override['hosts'], $override['items']); 606 607 $metrics_matched = []; 608 609 foreach ($metrics as $metric_num => $metric) { 610 // If '*' used, apply options to all metrics. 611 $host_matches = ($hosts_patterns === null); 612 $item_matches = ($items_patterns === null); 613 614 /** 615 * Find if host and item names matches one of given patterns. 616 * 617 * It currently checks if at least one of host pattern and at least one of item pattern matches, 618 * without checking relation between matching host and item. 619 */ 620 if ($hosts_patterns !== null) { 621 for ($hosts_pattern = reset($hosts_patterns); !$host_matches && $hosts_pattern !== false; 622 $hosts_pattern = next($hosts_patterns)) { 623 $pattern = '/^'.str_replace('\*', '.*', preg_quote($hosts_pattern, '/')).'$/i'; 624 $host_matches = (bool) preg_match($pattern, $metric['hosts'][0]['name']); 625 } 626 } 627 628 if ($items_patterns !== null && $host_matches) { 629 for ($items_pattern = reset($items_patterns); !$item_matches && $items_pattern !== false; 630 $items_pattern = next($items_patterns)) { 631 $pattern = '/^'.str_replace('\*', '.*', preg_quote($items_pattern, '/')).'$/i'; 632 $item_matches = (bool) preg_match($pattern, $metric['name']); 633 } 634 } 635 636 /** 637 * We need to know total amount of matched metrics to calculate variations of colors. That's why we 638 * first collect matching metrics and than override existing metric options. 639 */ 640 if ($host_matches && $item_matches) { 641 $metrics_matched[] = $metric_num; 642 } 643 } 644 645 // Apply override options to matching metrics. 646 if ($metrics_matched) { 647 $colors = (array_key_exists('color', $override) && $override['color'] !== '') 648 ? getColorVariations('#'.$override['color'], count($metrics_matched)) 649 : null; 650 651 if (array_key_exists('transparency', $override)) { 652 // The bigger transparency level, the less visible the metric is. 653 $override['transparency'] = 10 - (int) $override['transparency']; 654 } 655 656 foreach ($metrics_matched as $metric_num) { 657 $metric = &$metrics[$metric_num]; 658 659 if ($colors !== null) { 660 $override['color'] = array_shift($colors); 661 } 662 $metric['options'] = $override + $metric['options']; 663 664 // Fix missing options if draw type has changed. 665 switch ($metric['options']['type']) { 666 case SVG_GRAPH_TYPE_POINTS: 667 if (!array_key_exists('pointsize', $metric['options'])) { 668 $metric['options']['pointsize'] = SVG_GRAPH_DEFAULT_POINTSIZE; 669 } 670 break; 671 672 case SVG_GRAPH_TYPE_LINE: 673 case SVG_GRAPH_TYPE_STAIRCASE: 674 if (!array_key_exists('fill', $metric['options'])) { 675 $metric['options']['fill'] = SVG_GRAPH_DEFAULT_FILL; 676 } 677 if (!array_key_exists('missingdatafunc', $metric['options'])) { 678 $metric['options']['missingdatafunc'] = SVG_GRAPH_MISSING_DATA_NONE; 679 } 680 if (!array_key_exists('width', $metric['options'])) { 681 $metric['options']['width'] = SVG_GRAPH_DEFAULT_WIDTH; 682 } 683 break; 684 } 685 } 686 unset($metric); 687 } 688 } 689 } 690 691 /** 692 * Apply time period for each metric. 693 */ 694 protected static function getTimePeriods(array &$metrics = [], array $options) { 695 foreach ($metrics as &$metric) { 696 $metric['time_period'] = $options; 697 698 if ($metric['options']['timeshift'] != 0) { 699 $metric['time_period']['time_from'] += $metric['options']['timeshift']; 700 $metric['time_period']['time_to'] += $metric['options']['timeshift']; 701 } 702 } 703 unset($metric); 704 } 705 706 /** 707 * Prepare an array to be used for hosts/items filtering. 708 * 709 * @param array $patterns Array of strings containing hosts/items patterns. 710 * 711 * @return array|mixed Returns array of patterns. 712 * Returns NULL if array contains '*' (so any possible host/item search matches). 713 */ 714 protected static function processPattern(array $patterns) { 715 return in_array('*', $patterns) ? null : $patterns; 716 } 717 718 /* 719 * Sort data points by clock field. 720 * Do not use this function directly. It serves as value_compare_func function for usort. 721 */ 722 protected static function sortByClock($a, $b) { 723 if ($a['clock'] == $b['clock']) { 724 return 0; 725 } 726 727 return ($a['clock'] < $b['clock']) ? -1 : 1; 728 } 729} 730