mirror of
https://github.com/moodle/moodle.git
synced 2025-06-08 00:55:06 +02:00
This has been generated running the following Sniff, part of the Moodle's CodeSniffer standard: - PSR2.Methods.MethodDeclaration It just ensures all the function declarations have the correct order for: - abstract and final. - visibility (public, protected, private). - static. So, all the lines modified by this commit are function declarations and the only changes are in the positions of those keywords.
348 lines
13 KiB
PHP
348 lines
13 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle 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.
|
|
//
|
|
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace core\progress;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Base class for handling progress information.
|
|
*
|
|
* Subclasses should generally override the {@link current_progress} function which
|
|
* summarises all progress information.
|
|
*
|
|
* @package core_progress
|
|
* @copyright 2013 The Open University
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
abstract class base {
|
|
/**
|
|
* @var int Constant indicating that the number of progress calls is unknown.
|
|
*/
|
|
const INDETERMINATE = -1;
|
|
|
|
/**
|
|
* This value is set rather high to ensure there are no regressions from
|
|
* previous behaviour. For testing, it may be useful to set the
|
|
* frontendservertimeout config option to a lower value, such as 180
|
|
* seconds (default for some commercial products).
|
|
*
|
|
* @var int The number of seconds that can pass without {@link progress()} calls.
|
|
*/
|
|
const TIME_LIMIT_WITHOUT_PROGRESS = 3600;
|
|
|
|
/**
|
|
* @var int Time of last progress call.
|
|
*/
|
|
protected $lastprogresstime;
|
|
|
|
/**
|
|
* @var int Number of progress calls (restricted to ~ 1/second).
|
|
*/
|
|
protected $count;
|
|
|
|
/**
|
|
* @var array Array of progress descriptions for each stack level.
|
|
*/
|
|
protected $descriptions = array();
|
|
|
|
/**
|
|
* @var array Array of maximum progress values for each stack level.
|
|
*/
|
|
protected $maxes = array();
|
|
|
|
/**
|
|
* @var array Array of current progress values.
|
|
*/
|
|
protected $currents = array();
|
|
|
|
/**
|
|
* @var int[] Array of counts within parent progress entry (ignored for first)
|
|
*/
|
|
protected $parentcounts = array();
|
|
|
|
/**
|
|
* Marks the start of an operation that will display progress.
|
|
*
|
|
* This can be called multiple times for nested progress sections. It must
|
|
* be paired with calls to end_progress.
|
|
*
|
|
* The progress maximum may be {@link self::INDETERMINATE} if the current operation has
|
|
* an unknown number of steps. (This is default.)
|
|
*
|
|
* Calling this function will always result in a new display, so this
|
|
* should not be called exceedingly frequently.
|
|
*
|
|
* When it is complete by calling {@link end_progress()}, each {@link start_progress} section
|
|
* automatically adds progress to its parent, as defined by $parentcount.
|
|
*
|
|
* @param string $description Description to display
|
|
* @param int $max Maximum value of progress for this section
|
|
* @param int $parentcount How many progress points this section counts for
|
|
* @throws \coding_exception If max is invalid
|
|
*/
|
|
public function start_progress($description, $max = self::INDETERMINATE,
|
|
$parentcount = 1) {
|
|
if ($max != self::INDETERMINATE && $max < 0) {
|
|
throw new \coding_exception(
|
|
'start_progress() max value cannot be negative');
|
|
}
|
|
if ($parentcount < 1) {
|
|
throw new \coding_exception(
|
|
'start_progress() parent progress count must be at least 1');
|
|
}
|
|
if (!empty($this->descriptions)) {
|
|
$prevmax = end($this->maxes);
|
|
if ($prevmax !== self::INDETERMINATE) {
|
|
$prevcurrent = end($this->currents);
|
|
if ($prevcurrent + $parentcount > $prevmax) {
|
|
throw new \coding_exception(
|
|
'start_progress() parent progress would exceed max');
|
|
}
|
|
}
|
|
} else {
|
|
if ($parentcount != 1) {
|
|
throw new \coding_exception(
|
|
'start_progress() progress count must be 1 when no parent');
|
|
}
|
|
}
|
|
$this->descriptions[] = $description;
|
|
$this->maxes[] = $max;
|
|
$this->currents[] = 0;
|
|
$this->parentcounts[] = $parentcount;
|
|
$this->update_progress();
|
|
}
|
|
|
|
/**
|
|
* Marks the end of an operation that will display progress.
|
|
*
|
|
* This must be paired with each {@link start_progress} call.
|
|
*
|
|
* If there is a parent progress section, its progress will be increased
|
|
* automatically to reflect the end of the child section.
|
|
*
|
|
* @throws \coding_exception If progress hasn't been started
|
|
*/
|
|
public function end_progress() {
|
|
if (!count($this->descriptions)) {
|
|
throw new \coding_exception('end_progress() without start_progress()');
|
|
}
|
|
array_pop($this->descriptions);
|
|
array_pop($this->maxes);
|
|
array_pop($this->currents);
|
|
$parentcount = array_pop($this->parentcounts);
|
|
if (!empty($this->descriptions)) {
|
|
$lastmax = end($this->maxes);
|
|
if ($lastmax != self::INDETERMINATE) {
|
|
$lastvalue = end($this->currents);
|
|
$this->currents[key($this->currents)] = $lastvalue + $parentcount;
|
|
}
|
|
}
|
|
$this->update_progress();
|
|
}
|
|
|
|
/**
|
|
* Indicates that progress has occurred.
|
|
*
|
|
* The progress value should indicate the total progress so far, from 0
|
|
* to the value supplied for $max (inclusive) in {@link start_progress}.
|
|
*
|
|
* You do not need to call this function for every value. It is OK to skip
|
|
* values. It is also OK to call this function as often as desired; it
|
|
* doesn't update the display if called more than once per second.
|
|
*
|
|
* It must be INDETERMINATE if {@link start_progress} was called with $max set to
|
|
* INDETERMINATE. Otherwise it must not be indeterminate.
|
|
*
|
|
* @param int $progress Progress so far
|
|
* @throws \coding_exception If progress value is invalid
|
|
*/
|
|
public function progress($progress = self::INDETERMINATE) {
|
|
// Check we are inside a progress section.
|
|
$max = end($this->maxes);
|
|
if ($max === false) {
|
|
throw new \coding_exception(
|
|
'progress() without start_progress');
|
|
}
|
|
|
|
// Check and apply new progress.
|
|
if ($progress === self::INDETERMINATE) {
|
|
// Indeterminate progress.
|
|
if ($max !== self::INDETERMINATE) {
|
|
throw new \coding_exception(
|
|
'progress() INDETERMINATE, expecting value');
|
|
}
|
|
} else {
|
|
// Determinate progress.
|
|
$current = end($this->currents);
|
|
if ($max === self::INDETERMINATE) {
|
|
throw new \coding_exception(
|
|
'progress() with value, expecting INDETERMINATE');
|
|
} else if ($progress < 0 || $progress > $max) {
|
|
throw new \coding_exception(
|
|
'progress() value out of range');
|
|
} else if ($progress < $current) {
|
|
throw new \coding_exception(
|
|
'progress() value may not go backwards');
|
|
}
|
|
$this->currents[key($this->currents)] = $progress;
|
|
}
|
|
|
|
// Don't update progress bar too frequently (more than once per second).
|
|
$now = $this->get_time();
|
|
if ($now === $this->lastprogresstime) {
|
|
return;
|
|
}
|
|
|
|
// Update progress.
|
|
$this->count++;
|
|
$this->lastprogresstime = $now;
|
|
|
|
// Update time limit before next progress display.
|
|
\core_php_time_limit::raise(self::TIME_LIMIT_WITHOUT_PROGRESS);
|
|
$this->update_progress();
|
|
}
|
|
|
|
/**
|
|
* An alternative to calling progress. This keeps track of the number of items done internally. Call this method
|
|
* with no parameters to increment the internal counter by one or you can use the $incby parameter to specify a positive
|
|
* change in progress. The internal progress counter should not exceed $max as passed to {@link start_progress} for this
|
|
* section.
|
|
*
|
|
* If you called {@link start_progress} with parameter INDETERMINATE then you cannot call this method.
|
|
*
|
|
* @var int $incby The positive change to apply to the internal progress counter. Defaults to 1.
|
|
*/
|
|
public function increment_progress($incby = 1) {
|
|
$current = end($this->currents);
|
|
$this->progress($current + $incby);
|
|
}
|
|
|
|
/**
|
|
* Gets time (this is provided so that unit tests can override it).
|
|
*
|
|
* @return int Current system time
|
|
*/
|
|
protected function get_time() {
|
|
return time();
|
|
}
|
|
|
|
/**
|
|
* Called whenever new progress should be displayed.
|
|
*/
|
|
abstract protected function update_progress();
|
|
|
|
/**
|
|
* @return bool True if currently inside a progress section
|
|
*/
|
|
public function is_in_progress_section() {
|
|
return !empty($this->descriptions);
|
|
}
|
|
|
|
/**
|
|
* Checks max value of current progress section.
|
|
*
|
|
* @return int Current max value - may be {@link \core\progress\base::INDETERMINATE}.
|
|
* @throws \coding_exception If not in a progress section
|
|
*/
|
|
public function get_current_max() {
|
|
$max = end($this->maxes);
|
|
if ($max === false) {
|
|
throw new \coding_exception('Not inside progress section');
|
|
}
|
|
return $max;
|
|
}
|
|
|
|
/**
|
|
* @throws \coding_exception
|
|
* @return string Current progress section description
|
|
*/
|
|
public function get_current_description() {
|
|
$description = end($this->descriptions);
|
|
if ($description === false) {
|
|
throw new \coding_exception('Not inside progress section');
|
|
}
|
|
return $description;
|
|
}
|
|
|
|
/**
|
|
* Obtains current progress in a way suitable for drawing a progress bar.
|
|
*
|
|
* Progress is returned as a minimum and maximum value. If there is no
|
|
* indeterminate progress, these values will be identical. If there is
|
|
* intermediate progress, these values can be different. (For example, if
|
|
* the top level progress sections is indeterminate, then the values will
|
|
* always be 0.0 and 1.0.)
|
|
*
|
|
* @return array Minimum and maximum possible progress proportions
|
|
*/
|
|
public function get_progress_proportion_range() {
|
|
// If there is no progress underway, we must have finished.
|
|
if (empty($this->currents)) {
|
|
return array(1.0, 1.0);
|
|
}
|
|
$count = count($this->currents);
|
|
$min = 0.0;
|
|
$max = 1.0;
|
|
for ($i = 0; $i < $count; $i++) {
|
|
// Get max value at that section - if it's indeterminate we can tell
|
|
// no more.
|
|
$sectionmax = $this->maxes[$i];
|
|
if ($sectionmax === self::INDETERMINATE) {
|
|
return array($min, $max);
|
|
}
|
|
|
|
// Special case if current value is max (this should only happen
|
|
// just before ending a section).
|
|
$sectioncurrent = $this->currents[$i];
|
|
if ($sectioncurrent === $sectionmax) {
|
|
return array($max, $max);
|
|
}
|
|
|
|
// Using the current value at that section, we know we are somewhere
|
|
// between 'current' and the next 'current' value which depends on
|
|
// the parentcount of the nested section (if any).
|
|
$newmin = ($sectioncurrent / $sectionmax) * ($max - $min) + $min;
|
|
$nextcurrent = $sectioncurrent + 1;
|
|
if ($i + 1 < $count) {
|
|
$weight = $this->parentcounts[$i + 1];
|
|
$nextcurrent = $sectioncurrent + $weight;
|
|
}
|
|
$newmax = ($nextcurrent / $sectionmax) * ($max - $min) + $min;
|
|
$min = $newmin;
|
|
$max = $newmax;
|
|
}
|
|
|
|
// If there was nothing indeterminate, we use the min value as current.
|
|
return array($min, $min);
|
|
}
|
|
|
|
/**
|
|
* Obtains current indeterminate progress in a way suitable for adding to
|
|
* the progress display.
|
|
*
|
|
* This returns the number of indeterminate calls (at any level) during the
|
|
* lifetime of this progress reporter, whether or not there is a current
|
|
* indeterminate step. (The number will not be ridiculously high because
|
|
* progress calls are limited to one per second.)
|
|
*
|
|
* @return int Number of indeterminate progress calls
|
|
*/
|
|
public function get_progress_count() {
|
|
return $this->count;
|
|
}
|
|
}
|