<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\library;
use seekquarry\yioop\library\CrawlConstants;
/**
* AnalyticsManager holds the timings and counts a process gathers as it
* runs, so that the code that reports them, such as the query statistics
* of a search or the indexer's log, can read what code far below it
* measured. Models and index bundle iterators store timings here by
* name. Steps that read or write index files are counted and timed by
* kind with addTiming, and timingsReport turns those counts into lines
* for a person to read.
*
* @author Chris Pollett
*/
class AnalyticsManager
{
/**
* TIMINGS is the name under which addTiming keeps, for each kind of
* step, how many steps were taken and how many seconds they took
*/
const TIMINGS = "TIMINGS";
/**
* $data holds each stored value under the name it was stored with
* @var array
*/
private static $data = [];
/**
* get gives the value stored under a name, such as the time a search
* spent on another machine
*
* @param string $attribute name the value was stored under
* @return mixed the value, or null if nothing is stored under the name
*/
public static function get($attribute)
{
return isset(self::$data[$attribute]) ? self::$data[$attribute] : null;
}
/**
* set stores a value under a name, replacing what was there
*
* @param string $attribute name to store the value under
* @param mixed $value the timing or count to store
*/
public static function set($attribute, $value)
{
self::$data[$attribute] = $value;
}
/**
* addTiming adds to the count and the time of one kind of step, such
* as reading a dictionary file from disk or compressing a page before
* it is stored. The caller takes the time with microtime(true) before
* the step and passes it in after, and the time since then is added.
* The current time is given back, so a caller timing several steps in
* a row can start the next one from it. The code that reports the
* timings empties them with set(self::TIMINGS, []) when a search or
* an indexed file starts, as one process handles many of them.
*
* @param string $kind words saying what the step does, used as the
* report's label
* @param float $start_time when the step started, from microtime(true)
* @param int $count how many steps to add
* @return float the current time, from microtime(true)
*/
public static function addTiming($kind, $start_time, $count = 1)
{
$now = microtime(true);
$steps = self::$data[self::TIMINGS][$kind] ?? [0, 0];
self::$data[self::TIMINGS][$kind] = [$steps[0] + $count,
$steps[1] + $now - $start_time];
return $now;
}
/**
* timingsReport gives one line for each kind of step addTiming has
* counted since the timings were last emptied: the kind, how many
* steps were taken and the seconds they took. The kinds are listed in
* the order they were first counted.
*
* @return array lines such as "postings reads: 594 in 0.043 s"
*/
public static function timingsReport()
{
$lines = [];
foreach (self::$data[self::TIMINGS] ?? [] as $kind => $steps) {
$lines[] = "$kind: {$steps[0]} in " .
number_format($steps[1], 3) . " s";
}
return $lines;
}
}