From f0cd6f14affeede88a2c742477ec309b3c9aca4a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 17 May 2024 23:10:39 +0800 Subject: [PATCH 1/8] MDL-81960 core: Migrate \moodle_url to autoloaded file --- lib/classes/url.php | 848 +++++++++++++++++++++++++++++++++++++++ lib/db/legacyclasses.php | 1 + lib/weblib.php | 835 +------------------------------------- 3 files changed, 852 insertions(+), 832 deletions(-) create mode 100644 lib/classes/url.php diff --git a/lib/classes/url.php b/lib/classes/url.php new file mode 100644 index 00000000000..5e8a6da1022 --- /dev/null +++ b/lib/classes/url.php @@ -0,0 +1,848 @@ +. + +use Psr\Http\Message\UriInterface; + +/** + * Class for creating and manipulating urls. + * + * It can be used in moodle pages where config.php has been included without any further includes. + * + * It is useful for manipulating urls with long lists of params. + * One situation where it will be useful is a page which links to itself to perform various actions + * and / or to process form data. A url object: + * can be created for a page to refer to itself with all the proper get params being passed from page call to + * page call and methods can be used to output a url including all the params, optionally adding and overriding + * params and can also be used to + * - output the url without any get params + * - and output the params as hidden fields to be output within a form + * + * @copyright 2007 jamiesensei + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class url { + /** + * Scheme, ex.: http, https + * @var string + */ + protected $scheme = ''; + + /** + * Hostname. + * @var string + */ + protected $host = ''; + + /** + * Port number, empty means default 80 or 443 in case of http. + * @var int + */ + protected $port = ''; + + /** + * Username for http auth. + * @var string + */ + protected $user = ''; + + /** + * Password for http auth. + * @var string + */ + protected $pass = ''; + + /** + * Script path. + * @var string + */ + protected $path = ''; + + /** + * Optional slash argument value. + * @var string + */ + protected $slashargument = ''; + + /** + * Anchor, may be also empty, null means none. + * @var string + */ + protected $anchor = null; + + /** + * Url parameters as associative array. + * @var array + */ + protected $params = array(); + + /** + * Create new instance of url. + * + * @param url|string $url - url means make a copy of another + * url and change parameters, string means full url or shortened + * form (ex.: '/course/view.php'). It is strongly encouraged to not include + * query string because it may result in double encoded values. Use the + * $params instead. For admin URLs, just use /admin/script.php, this + * class takes care of the $CFG->admin issue. + * @param array $params these params override current params or add new + * @param string $anchor The anchor to use as part of the URL if there is one. + * @throws moodle_exception + */ + public function __construct($url, array $params = null, $anchor = null) { + global $CFG; + + if ($url instanceof url) { + $this->scheme = $url->scheme; + $this->host = $url->host; + $this->port = $url->port; + $this->user = $url->user; + $this->pass = $url->pass; + $this->path = $url->path; + $this->slashargument = $url->slashargument; + $this->params = $url->params; + $this->anchor = $url->anchor; + + } else { + $url = $url ?? ''; + // Detect if anchor used. + $apos = strpos($url, '#'); + if ($apos !== false) { + $anchor = substr($url, $apos); + $anchor = ltrim($anchor, '#'); + $this->set_anchor($anchor); + $url = substr($url, 0, $apos); + } + + // Normalise shortened form of our url ex.: '/course/view.php'. + if (strpos($url, '/') === 0) { + $url = $CFG->wwwroot.$url; + } + + if ($CFG->admin !== 'admin') { + if (strpos($url, "$CFG->wwwroot/admin/") === 0) { + $url = str_replace("$CFG->wwwroot/admin/", "$CFG->wwwroot/$CFG->admin/", $url); + } + } + + // Parse the $url. + $parts = parse_url($url); + if ($parts === false) { + throw new moodle_exception('invalidurl'); + } + if (isset($parts['query'])) { + // Note: the values may not be correctly decoded, url parameters should be always passed as array. + parse_str(str_replace('&', '&', $parts['query']), $this->params); + } + unset($parts['query']); + foreach ($parts as $key => $value) { + $this->$key = $value; + } + + // Detect slashargument value from path - we do not support directory names ending with .php. + $pos = strpos($this->path, '.php/'); + if ($pos !== false) { + $this->slashargument = substr($this->path, $pos + 4); + $this->path = substr($this->path, 0, $pos + 4); + } + } + + $this->params($params); + if ($anchor !== null) { + $this->anchor = (string)$anchor; + } + } + + /** + * Add an array of params to the params for this url. + * + * The added params override existing ones if they have the same name. + * + * @param array $params Defaults to null. If null then returns all params. + * @return array Array of Params for url. + * @throws coding_exception + */ + public function params(array $params = null) { + $params = (array)$params; + + foreach ($params as $key => $value) { + if (is_int($key)) { + throw new coding_exception('Url parameters can not have numeric keys!'); + } + if (!is_string($value)) { + if (is_array($value)) { + throw new coding_exception('Url parameters values can not be arrays!'); + } + if (is_object($value) and !method_exists($value, '__toString')) { + throw new coding_exception('Url parameters values can not be objects, unless __toString() is defined!'); + } + } + $this->params[$key] = (string)$value; + } + return $this->params; + } + + /** + * Remove all params if no arguments passed. + * Remove selected params if arguments are passed. + * + * Can be called as either remove_params('param1', 'param2') + * or remove_params(array('param1', 'param2')). + * + * @param string[]|string $params,... either an array of param names, or 1..n string params to remove as args. + * @return array url parameters + */ + public function remove_params($params = null) { + if (!is_array($params)) { + $params = func_get_args(); + } + foreach ($params as $param) { + unset($this->params[$param]); + } + return $this->params; + } + + /** + * Remove all url parameters. + * + * @todo remove the unused param. + * @param array $params Unused param + * @return void + */ + public function remove_all_params($params = null) { + $this->params = array(); + $this->slashargument = ''; + } + + /** + * Add a param to the params for this url. + * + * The added param overrides existing one if they have the same name. + * + * @param string $paramname name + * @param string $newvalue Param value. If new value specified current value is overriden or parameter is added + * @return mixed string parameter value, null if parameter does not exist + */ + public function param($paramname, $newvalue = '') { + if (func_num_args() > 1) { + // Set new value. + $this->params(array($paramname => $newvalue)); + } + if (isset($this->params[$paramname])) { + return $this->params[$paramname]; + } else { + return null; + } + } + + /** + * Merges parameters and validates them + * + * @param array $overrideparams + * @return array merged parameters + * @throws coding_exception + */ + protected function merge_overrideparams(array $overrideparams = null) { + $overrideparams = (array)$overrideparams; + $params = $this->params; + foreach ($overrideparams as $key => $value) { + if (is_int($key)) { + throw new coding_exception('Overridden parameters can not have numeric keys!'); + } + if (is_array($value)) { + throw new coding_exception('Overridden parameters values can not be arrays!'); + } + if (is_object($value) and !method_exists($value, '__toString')) { + throw new coding_exception('Overridden parameters values can not be objects, unless __toString() is defined!'); + } + $params[$key] = (string)$value; + } + return $params; + } + + /** + * Get the params as as a query string. + * + * This method should not be used outside of this method. + * + * @param bool $escaped Use & as params separator instead of plain & + * @param array $overrideparams params to add to the output params, these + * override existing ones with the same name. + * @return string query string that can be added to a url. + */ + public function get_query_string($escaped = true, array $overrideparams = null) { + $arr = array(); + if ($overrideparams !== null) { + $params = $this->merge_overrideparams($overrideparams); + } else { + $params = $this->params; + } + foreach ($params as $key => $val) { + if (is_array($val)) { + foreach ($val as $index => $value) { + $arr[] = rawurlencode($key.'['.$index.']')."=".rawurlencode($value); + } + } else { + if (isset($val) && $val !== '') { + $arr[] = rawurlencode($key)."=".rawurlencode($val); + } else { + $arr[] = rawurlencode($key); + } + } + } + if ($escaped) { + return implode('&', $arr); + } else { + return implode('&', $arr); + } + } + + /** + * Get the url params as an array of key => value pairs. + * + * This helps in handling cases where url params contain arrays. + * + * @return array params array for templates. + */ + public function export_params_for_template(): array { + $data = []; + foreach ($this->params as $key => $val) { + if (is_array($val)) { + foreach ($val as $index => $value) { + $data[] = ['name' => $key.'['.$index.']', 'value' => $value]; + } + } else { + $data[] = ['name' => $key, 'value' => $val]; + } + } + return $data; + } + + /** + * Shortcut for printing of encoded URL. + * + * @return string + */ + public function __toString() { + return $this->out(true); + } + + /** + * Output url. + * + * If you use the returned URL in HTML code, you want the escaped ampersands. If you use + * the returned URL in HTTP headers, you want $escaped=false. + * + * @param bool $escaped Use & as params separator instead of plain & + * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @return string Resulting URL + */ + public function out($escaped = true, array $overrideparams = null) { + + global $CFG; + + if (!is_bool($escaped)) { + debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); + } + + $url = $this; + + // Allow url's to be rewritten by a plugin. + if (isset($CFG->urlrewriteclass) && !isset($CFG->upgraderunning)) { + $class = $CFG->urlrewriteclass; + $pluginurl = $class::url_rewrite($url); + if ($pluginurl instanceof url) { + $url = $pluginurl; + } + } + + return $url->raw_out($escaped, $overrideparams); + + } + + /** + * Output url without any rewrites + * + * This is identical in signature and use to out() but doesn't call the rewrite handler. + * + * @param bool $escaped Use & as params separator instead of plain & + * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @return string Resulting URL + */ + public function raw_out($escaped = true, array $overrideparams = null) { + if (!is_bool($escaped)) { + debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); + } + + $uri = $this->out_omit_querystring().$this->slashargument; + + $querystring = $this->get_query_string($escaped, $overrideparams); + if ($querystring !== '') { + $uri .= '?' . $querystring; + } + + $uri .= $this->get_encoded_anchor(); + + return $uri; + } + + /** + * Encode the anchor according to RFC 3986. + * + * @return string The encoded anchor + */ + public function get_encoded_anchor(): string { + if (is_null($this->anchor)) { + return ''; + } + + // RFC 3986 allows the following characters in a fragment without them being encoded: + // pct-encoded: "%" HEXDIG HEXDIG + // unreserved: ALPHA / DIGIT / "-" / "." / "_" / "~" / + // sub-delims: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" / ":" / "@" + // fragment: "/" / "?" + // + // All other characters should be encoded. + // These should not be encoded in the fragment unless they were already encoded. + + // The following characters are allowed in the fragment without encoding. + // In addition to this list is pct-encoded, but we can't easily handle this with a regular expression. + $allowed = 'a-zA-Z0-9\\-._~!$&\'()*+,;=:@\/?'; + $anchor = '#'; + + $remainder = $this->anchor; + do { + // Split the string on any %. + $parts = explode('%', $remainder, 2); + $anchorparts = array_shift($parts); + + // The first part can go through our preg_replace_callback to quote any relevant characters. + $anchor .= preg_replace_callback( + '/[^' . $allowed . ']/', + fn ($matches) => rawurlencode($matches[0]), + $anchorparts, + ); + + // The second part _might_ be a valid pct-encoded character. + if (count($parts) === 0) { + break; + } + + // If the second part is a valid pct-encoded character, append it to the anchor. + $remainder = array_shift($parts); + if (preg_match('/^[a-fA-F0-9]{2}/', $remainder, $matches)) { + $anchor .= "%{$matches[0]}"; + $remainder = substr($remainder, 2); + } else { + // This was not a valid pct-encoded character. Encode the % and continue with the next part. + $anchor .= rawurlencode('%'); + } + } while (strlen($remainder) > 0); + + return $anchor; + } + + /** + * Returns url without parameters, everything before '?'. + * + * @param bool $includeanchor if {@link self::anchor} is defined, should it be returned? + * @return string + */ + public function out_omit_querystring($includeanchor = false) { + + $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): ''; + $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':''; + $uri .= $this->host ? $this->host : ''; + $uri .= $this->port ? ':'.$this->port : ''; + $uri .= $this->path ? $this->path : ''; + if ($includeanchor) { + $uri .= $this->get_encoded_anchor(); + } + + return $uri; + } + + /** + * Compares this url with another. + * + * See documentation of constants for an explanation of the comparison flags. + * + * @param url $url The url object to compare + * @param int $matchtype The type of comparison (URL_MATCH_BASE, URL_MATCH_PARAMS, URL_MATCH_EXACT) + * @return bool + */ + public function compare(url $url, $matchtype = URL_MATCH_EXACT) { + + $baseself = $this->out_omit_querystring(); + $baseother = $url->out_omit_querystring(); + + // Append index.php if there is no specific file. + if (substr($baseself, -1) == '/') { + $baseself .= 'index.php'; + } + if (substr($baseother, -1) == '/') { + $baseother .= 'index.php'; + } + + // Compare the two base URLs. + if ($baseself != $baseother) { + return false; + } + + if ($matchtype == URL_MATCH_BASE) { + return true; + } + + $urlparams = $url->params(); + foreach ($this->params() as $param => $value) { + if ($param == 'sesskey') { + continue; + } + if (!array_key_exists($param, $urlparams) || $urlparams[$param] != $value) { + return false; + } + } + + if ($matchtype == URL_MATCH_PARAMS) { + return true; + } + + foreach ($urlparams as $param => $value) { + if ($param == 'sesskey') { + continue; + } + if (!array_key_exists($param, $this->params()) || $this->param($param) != $value) { + return false; + } + } + + if ($url->anchor !== $this->anchor) { + return false; + } + + return true; + } + + /** + * Sets the anchor for the URI (the bit after the hash) + * + * @param string $anchor null means remove previous + */ + public function set_anchor($anchor) { + if (is_null($anchor)) { + // Remove. + $this->anchor = null; + } else { + $this->anchor = $anchor; + } + } + + /** + * Sets the scheme for the URI (the bit before ://) + * + * @param string $scheme + */ + public function set_scheme($scheme) { + // See http://www.ietf.org/rfc/rfc3986.txt part 3.1. + if (preg_match('/^[a-zA-Z][a-zA-Z0-9+.-]*$/', $scheme)) { + $this->scheme = $scheme; + } else { + throw new coding_exception('Bad URL scheme.'); + } + } + + /** + * Sets the url slashargument value. + * + * @param string $path usually file path + * @param string $parameter name of page parameter if slasharguments not supported + * @param bool $supported usually null, then it depends on $CFG->slasharguments, use true or false for other servers + * @return void + */ + public function set_slashargument($path, $parameter = 'file', $supported = null) { + global $CFG; + if (is_null($supported)) { + $supported = !empty($CFG->slasharguments); + } + + if ($supported) { + $parts = explode('/', $path); + $parts = array_map('rawurlencode', $parts); + $path = implode('/', $parts); + $this->slashargument = $path; + unset($this->params[$parameter]); + + } else { + $this->slashargument = ''; + $this->params[$parameter] = $path; + } + } + + // Static factory methods. + + /** + * Create a new url instance from a UriInterface. + * + * @param UriInterface $uri + * @return self + */ + public static function from_uri(UriInterface $uri): self { + $url = new self( + url: $uri->getScheme() . '://' . $uri->getAuthority() . $uri->getPath(), + anchor: $uri->getFragment() ?: null, + ); + + $params = $uri->getQuery(); + foreach (explode('&', $params) as $param) { + $url->param(...explode('=', $param, 2)); + } + + return $url; + } + + /** + * General moodle file url. + * + * @param string $urlbase the script serving the file + * @param string $path + * @param bool $forcedownload + * @return url + */ + public static function make_file_url($urlbase, $path, $forcedownload = false) { + $params = array(); + if ($forcedownload) { + $params['forcedownload'] = 1; + } + $url = new url($urlbase, $params); + $url->set_slashargument($path); + return $url; + } + + /** + * Factory method for creation of url pointing to plugin file. + * + * Please note this method can be used only from the plugins to + * create urls of own files, it must not be used outside of plugins! + * + * @param int $contextid + * @param string $component + * @param string $area + * @param ?int $itemid + * @param string $pathname + * @param string $filename + * @param bool $forcedownload + * @param mixed $includetoken Whether to use a user token when displaying this group image. + * True indicates to generate a token for current user, and integer value indicates to generate a token for the + * user whose id is the value indicated. + * If the group picture is included in an e-mail or some other location where the audience is a specific + * user who will not be logged in when viewing, then we use a token to authenticate the user. + * @return url + */ + public static function make_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, + $forcedownload = false, $includetoken = false) { + global $CFG, $USER; + + $path = []; + + if ($includetoken) { + $urlbase = "$CFG->wwwroot/tokenpluginfile.php"; + $userid = $includetoken === true ? $USER->id : $includetoken; + $token = get_user_key('core_files', $userid); + if ($CFG->slasharguments) { + $path[] = $token; + } + } else { + $urlbase = "$CFG->wwwroot/pluginfile.php"; + } + $path[] = $contextid; + $path[] = $component; + $path[] = $area; + + if ($itemid !== null) { + $path[] = $itemid; + } + + $path = "/" . implode('/', $path) . "{$pathname}{$filename}"; + + $url = self::make_file_url($urlbase, $path, $forcedownload, $includetoken); + if ($includetoken && empty($CFG->slasharguments)) { + $url->param('token', $token); + } + return $url; + } + + /** + * Factory method for creation of url pointing to plugin file. + * This method is the same that make_pluginfile_url but pointing to the webservice pluginfile.php script. + * It should be used only in external functions. + * + * @since 2.8 + * @param int $contextid + * @param string $component + * @param string $area + * @param int $itemid + * @param string $pathname + * @param string $filename + * @param bool $forcedownload + * @return url + */ + public static function make_webservice_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, + $forcedownload = false) { + global $CFG; + $urlbase = "$CFG->wwwroot/webservice/pluginfile.php"; + if ($itemid === null) { + return self::make_file_url($urlbase, "/$contextid/$component/$area".$pathname.$filename, $forcedownload); + } else { + return self::make_file_url($urlbase, "/$contextid/$component/$area/$itemid".$pathname.$filename, $forcedownload); + } + } + + /** + * Factory method for creation of url pointing to draft file of current user. + * + * @param int $draftid draft item id + * @param string $pathname + * @param string $filename + * @param bool $forcedownload + * @return url + */ + public static function make_draftfile_url($draftid, $pathname, $filename, $forcedownload = false) { + global $CFG, $USER; + $urlbase = "$CFG->wwwroot/draftfile.php"; + $context = context_user::instance($USER->id); + + return self::make_file_url($urlbase, "/$context->id/user/draft/$draftid".$pathname.$filename, $forcedownload); + } + + /** + * Factory method for creating of links to legacy course files. + * + * @param int $courseid + * @param string $filepath + * @param bool $forcedownload + * @return url + */ + public static function make_legacyfile_url($courseid, $filepath, $forcedownload = false) { + global $CFG; + + $urlbase = "$CFG->wwwroot/file.php"; + return self::make_file_url($urlbase, '/'.$courseid.'/'.$filepath, $forcedownload); + } + + /** + * Checks if URL is relative to $CFG->wwwroot. + * + * @return bool True if URL is relative to $CFG->wwwroot; otherwise, false. + */ + public function is_local_url(): bool { + global $CFG; + + $url = $this->out(); + // Does URL start with wwwroot? Otherwise, URL isn't relative to wwwroot. + return ( ($url === $CFG->wwwroot) || (strpos($url, $CFG->wwwroot.'/') === 0) ); + } + + /** + * Returns URL as relative path from $CFG->wwwroot + * + * Can be used for passing around urls with the wwwroot stripped + * + * @param boolean $escaped Use & as params separator instead of plain & + * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @return string Resulting URL + * @throws coding_exception if called on a non-local url + */ + public function out_as_local_url($escaped = true, array $overrideparams = null) { + global $CFG; + + // URL should be relative to wwwroot. If not then throw exception. + if ($this->is_local_url()) { + $url = $this->out($escaped, $overrideparams); + $localurl = substr($url, strlen($CFG->wwwroot)); + return !empty($localurl) ? $localurl : ''; + } else { + throw new coding_exception('out_as_local_url called on a non-local URL'); + } + } + + /** + * Returns the 'path' portion of a URL. For example, if the URL is + * http://www.example.org:447/my/file/is/here.txt?really=1 then this will + * return '/my/file/is/here.txt'. + * + * By default the path includes slash-arguments (for example, + * '/myfile.php/extra/arguments') so it is what you would expect from a + * URL path. If you don't want this behaviour, you can opt to exclude the + * slash arguments. (Be careful: if the $CFG variable slasharguments is + * disabled, these URLs will have a different format and you may need to + * look at the 'file' parameter too.) + * + * @param bool $includeslashargument If true, includes slash arguments + * @return string Path of URL + */ + public function get_path($includeslashargument = true) { + return $this->path . ($includeslashargument ? $this->slashargument : ''); + } + + /** + * Returns a given parameter value from the URL. + * + * @param string $name Name of parameter + * @return string Value of parameter or null if not set + */ + public function get_param($name) { + if (array_key_exists($name, $this->params)) { + return $this->params[$name]; + } else { + return null; + } + } + + /** + * Returns the 'scheme' portion of a URL. For example, if the URL is + * http://www.example.org:447/my/file/is/here.txt?really=1 then this will + * return 'http' (without the colon). + * + * @return string Scheme of the URL. + */ + public function get_scheme() { + return $this->scheme; + } + + /** + * Returns the 'host' portion of a URL. For example, if the URL is + * http://www.example.org:447/my/file/is/here.txt?really=1 then this will + * return 'www.example.org'. + * + * @return string Host of the URL. + */ + public function get_host() { + return $this->host; + } + + /** + * Returns the 'port' portion of a URL. For example, if the URL is + * http://www.example.org:447/my/file/is/here.txt?really=1 then this will + * return '447'. + * + * @return string Port of the URL. + */ + public function get_port() { + return $this->port; + } +} + +class_alias(url::class, \moodle_url::class); diff --git a/lib/db/legacyclasses.php b/lib/db/legacyclasses.php index 75076d95c32..0cc09322714 100644 --- a/lib/db/legacyclasses.php +++ b/lib/db/legacyclasses.php @@ -38,6 +38,7 @@ $legacyclasses = [ \invalid_response_exception::class => 'exception/invalid_response_exception.php', \invalid_state_exception::class => 'exception/invalid_state_exception.php', \moodle_exception::class => 'exception/moodle_exception.php', + \moodle_url::class => 'url.php', \require_login_exception::class => 'exception/require_login_exception.php', \require_login_session_timeout_exception::class => 'exception/require_login_session_timeout_exception.php', \required_capability_exception::class => 'exception/required_capability_exception.php', diff --git a/lib/weblib.php b/lib/weblib.php index d6351ea2fb2..7e9634c890c 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -30,8 +30,6 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -use Psr\Http\Message\UriInterface; - defined('MOODLE_INTERNAL') || die(); // Constants. @@ -81,6 +79,9 @@ define('URL_MATCH_PARAMS', 1); */ define('URL_MATCH_EXACT', 2); +// TODO MDL-81933 Remove after Moodle 4.5 release. +require_once($CFG->libdir . '/classes/url.php'); + // Functions. /** @@ -242,836 +243,6 @@ function get_local_referer($stripquery = true) { } } -/** - * Class for creating and manipulating urls. - * - * It can be used in moodle pages where config.php has been included without any further includes. - * - * It is useful for manipulating urls with long lists of params. - * One situation where it will be useful is a page which links to itself to perform various actions - * and / or to process form data. A moodle_url object : - * can be created for a page to refer to itself with all the proper get params being passed from page call to - * page call and methods can be used to output a url including all the params, optionally adding and overriding - * params and can also be used to - * - output the url without any get params - * - and output the params as hidden fields to be output within a form - * - * @copyright 2007 jamiesensei - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class moodle_url { - - /** - * Scheme, ex.: http, https - * @var string - */ - protected $scheme = ''; - - /** - * Hostname. - * @var string - */ - protected $host = ''; - - /** - * Port number, empty means default 80 or 443 in case of http. - * @var int - */ - protected $port = ''; - - /** - * Username for http auth. - * @var string - */ - protected $user = ''; - - /** - * Password for http auth. - * @var string - */ - protected $pass = ''; - - /** - * Script path. - * @var string - */ - protected $path = ''; - - /** - * Optional slash argument value. - * @var string - */ - protected $slashargument = ''; - - /** - * Anchor, may be also empty, null means none. - * @var string - */ - protected $anchor = null; - - /** - * Url parameters as associative array. - * @var array - */ - protected $params = array(); - - /** - * Create new instance of moodle_url. - * - * @param moodle_url|string $url - moodle_url means make a copy of another - * moodle_url and change parameters, string means full url or shortened - * form (ex.: '/course/view.php'). It is strongly encouraged to not include - * query string because it may result in double encoded values. Use the - * $params instead. For admin URLs, just use /admin/script.php, this - * class takes care of the $CFG->admin issue. - * @param array $params these params override current params or add new - * @param string $anchor The anchor to use as part of the URL if there is one. - * @throws moodle_exception - */ - public function __construct($url, array $params = null, $anchor = null) { - global $CFG; - - if ($url instanceof moodle_url) { - $this->scheme = $url->scheme; - $this->host = $url->host; - $this->port = $url->port; - $this->user = $url->user; - $this->pass = $url->pass; - $this->path = $url->path; - $this->slashargument = $url->slashargument; - $this->params = $url->params; - $this->anchor = $url->anchor; - - } else { - $url = $url ?? ''; - // Detect if anchor used. - $apos = strpos($url, '#'); - if ($apos !== false) { - $anchor = substr($url, $apos); - $anchor = ltrim($anchor, '#'); - $this->set_anchor($anchor); - $url = substr($url, 0, $apos); - } - - // Normalise shortened form of our url ex.: '/course/view.php'. - if (strpos($url, '/') === 0) { - $url = $CFG->wwwroot.$url; - } - - if ($CFG->admin !== 'admin') { - if (strpos($url, "$CFG->wwwroot/admin/") === 0) { - $url = str_replace("$CFG->wwwroot/admin/", "$CFG->wwwroot/$CFG->admin/", $url); - } - } - - // Parse the $url. - $parts = parse_url($url); - if ($parts === false) { - throw new moodle_exception('invalidurl'); - } - if (isset($parts['query'])) { - // Note: the values may not be correctly decoded, url parameters should be always passed as array. - parse_str(str_replace('&', '&', $parts['query']), $this->params); - } - unset($parts['query']); - foreach ($parts as $key => $value) { - $this->$key = $value; - } - - // Detect slashargument value from path - we do not support directory names ending with .php. - $pos = strpos($this->path, '.php/'); - if ($pos !== false) { - $this->slashargument = substr($this->path, $pos + 4); - $this->path = substr($this->path, 0, $pos + 4); - } - } - - $this->params($params); - if ($anchor !== null) { - $this->anchor = (string)$anchor; - } - } - - /** - * Add an array of params to the params for this url. - * - * The added params override existing ones if they have the same name. - * - * @param array $params Defaults to null. If null then returns all params. - * @return array Array of Params for url. - * @throws coding_exception - */ - public function params(array $params = null) { - $params = (array)$params; - - foreach ($params as $key => $value) { - if (is_int($key)) { - throw new coding_exception('Url parameters can not have numeric keys!'); - } - if (!is_string($value)) { - if (is_array($value)) { - throw new coding_exception('Url parameters values can not be arrays!'); - } - if (is_object($value) and !method_exists($value, '__toString')) { - throw new coding_exception('Url parameters values can not be objects, unless __toString() is defined!'); - } - } - $this->params[$key] = (string)$value; - } - return $this->params; - } - - /** - * Remove all params if no arguments passed. - * Remove selected params if arguments are passed. - * - * Can be called as either remove_params('param1', 'param2') - * or remove_params(array('param1', 'param2')). - * - * @param string[]|string $params,... either an array of param names, or 1..n string params to remove as args. - * @return array url parameters - */ - public function remove_params($params = null) { - if (!is_array($params)) { - $params = func_get_args(); - } - foreach ($params as $param) { - unset($this->params[$param]); - } - return $this->params; - } - - /** - * Remove all url parameters. - * - * @todo remove the unused param. - * @param array $params Unused param - * @return void - */ - public function remove_all_params($params = null) { - $this->params = array(); - $this->slashargument = ''; - } - - /** - * Add a param to the params for this url. - * - * The added param overrides existing one if they have the same name. - * - * @param string $paramname name - * @param string $newvalue Param value. If new value specified current value is overriden or parameter is added - * @return mixed string parameter value, null if parameter does not exist - */ - public function param($paramname, $newvalue = '') { - if (func_num_args() > 1) { - // Set new value. - $this->params(array($paramname => $newvalue)); - } - if (isset($this->params[$paramname])) { - return $this->params[$paramname]; - } else { - return null; - } - } - - /** - * Merges parameters and validates them - * - * @param array $overrideparams - * @return array merged parameters - * @throws coding_exception - */ - protected function merge_overrideparams(array $overrideparams = null) { - $overrideparams = (array)$overrideparams; - $params = $this->params; - foreach ($overrideparams as $key => $value) { - if (is_int($key)) { - throw new coding_exception('Overridden parameters can not have numeric keys!'); - } - if (is_array($value)) { - throw new coding_exception('Overridden parameters values can not be arrays!'); - } - if (is_object($value) and !method_exists($value, '__toString')) { - throw new coding_exception('Overridden parameters values can not be objects, unless __toString() is defined!'); - } - $params[$key] = (string)$value; - } - return $params; - } - - /** - * Get the params as as a query string. - * - * This method should not be used outside of this method. - * - * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output params, these - * override existing ones with the same name. - * @return string query string that can be added to a url. - */ - public function get_query_string($escaped = true, array $overrideparams = null) { - $arr = array(); - if ($overrideparams !== null) { - $params = $this->merge_overrideparams($overrideparams); - } else { - $params = $this->params; - } - foreach ($params as $key => $val) { - if (is_array($val)) { - foreach ($val as $index => $value) { - $arr[] = rawurlencode($key.'['.$index.']')."=".rawurlencode($value); - } - } else { - if (isset($val) && $val !== '') { - $arr[] = rawurlencode($key)."=".rawurlencode($val); - } else { - $arr[] = rawurlencode($key); - } - } - } - if ($escaped) { - return implode('&', $arr); - } else { - return implode('&', $arr); - } - } - - /** - * Get the url params as an array of key => value pairs. - * - * This helps in handling cases where url params contain arrays. - * - * @return array params array for templates. - */ - public function export_params_for_template(): array { - $data = []; - foreach ($this->params as $key => $val) { - if (is_array($val)) { - foreach ($val as $index => $value) { - $data[] = ['name' => $key.'['.$index.']', 'value' => $value]; - } - } else { - $data[] = ['name' => $key, 'value' => $val]; - } - } - return $data; - } - - /** - * Shortcut for printing of encoded URL. - * - * @return string - */ - public function __toString() { - return $this->out(true); - } - - /** - * Output url. - * - * If you use the returned URL in HTML code, you want the escaped ampersands. If you use - * the returned URL in HTTP headers, you want $escaped=false. - * - * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. - * @return string Resulting URL - */ - public function out($escaped = true, array $overrideparams = null) { - - global $CFG; - - if (!is_bool($escaped)) { - debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); - } - - $url = $this; - - // Allow url's to be rewritten by a plugin. - if (isset($CFG->urlrewriteclass) && !isset($CFG->upgraderunning)) { - $class = $CFG->urlrewriteclass; - $pluginurl = $class::url_rewrite($url); - if ($pluginurl instanceof moodle_url) { - $url = $pluginurl; - } - } - - return $url->raw_out($escaped, $overrideparams); - - } - - /** - * Output url without any rewrites - * - * This is identical in signature and use to out() but doesn't call the rewrite handler. - * - * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. - * @return string Resulting URL - */ - public function raw_out($escaped = true, array $overrideparams = null) { - if (!is_bool($escaped)) { - debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); - } - - $uri = $this->out_omit_querystring().$this->slashargument; - - $querystring = $this->get_query_string($escaped, $overrideparams); - if ($querystring !== '') { - $uri .= '?' . $querystring; - } - - $uri .= $this->get_encoded_anchor(); - - return $uri; - } - - /** - * Encode the anchor according to RFC 3986. - * - * @return string The encoded anchor - */ - public function get_encoded_anchor(): string { - if (is_null($this->anchor)) { - return ''; - } - - // RFC 3986 allows the following characters in a fragment without them being encoded: - // pct-encoded: "%" HEXDIG HEXDIG - // unreserved: ALPHA / DIGIT / "-" / "." / "_" / "~" / - // sub-delims: "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" / ":" / "@" - // fragment: "/" / "?" - // - // All other characters should be encoded. - // These should not be encoded in the fragment unless they were already encoded. - - // The following characters are allowed in the fragment without encoding. - // In addition to this list is pct-encoded, but we can't easily handle this with a regular expression. - $allowed = 'a-zA-Z0-9\\-._~!$&\'()*+,;=:@\/?'; - $anchor = '#'; - - $remainder = $this->anchor; - do { - // Split the string on any %. - $parts = explode('%', $remainder, 2); - $anchorparts = array_shift($parts); - - // The first part can go through our preg_replace_callback to quote any relevant characters. - $anchor .= preg_replace_callback( - '/[^' . $allowed . ']/', - fn ($matches) => rawurlencode($matches[0]), - $anchorparts, - ); - - // The second part _might_ be a valid pct-encoded character. - if (count($parts) === 0) { - break; - } - - // If the second part is a valid pct-encoded character, append it to the anchor. - $remainder = array_shift($parts); - if (preg_match('/^[a-fA-F0-9]{2}/', $remainder, $matches)) { - $anchor .= "%{$matches[0]}"; - $remainder = substr($remainder, 2); - } else { - // This was not a valid pct-encoded character. Encode the % and continue with the next part. - $anchor .= rawurlencode('%'); - } - } while (strlen($remainder) > 0); - - return $anchor; - } - - /** - * Returns url without parameters, everything before '?'. - * - * @param bool $includeanchor if {@link self::anchor} is defined, should it be returned? - * @return string - */ - public function out_omit_querystring($includeanchor = false) { - - $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): ''; - $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':''; - $uri .= $this->host ? $this->host : ''; - $uri .= $this->port ? ':'.$this->port : ''; - $uri .= $this->path ? $this->path : ''; - if ($includeanchor) { - $uri .= $this->get_encoded_anchor(); - } - - return $uri; - } - - /** - * Compares this moodle_url with another. - * - * See documentation of constants for an explanation of the comparison flags. - * - * @param moodle_url $url The moodle_url object to compare - * @param int $matchtype The type of comparison (URL_MATCH_BASE, URL_MATCH_PARAMS, URL_MATCH_EXACT) - * @return bool - */ - public function compare(moodle_url $url, $matchtype = URL_MATCH_EXACT) { - - $baseself = $this->out_omit_querystring(); - $baseother = $url->out_omit_querystring(); - - // Append index.php if there is no specific file. - if (substr($baseself, -1) == '/') { - $baseself .= 'index.php'; - } - if (substr($baseother, -1) == '/') { - $baseother .= 'index.php'; - } - - // Compare the two base URLs. - if ($baseself != $baseother) { - return false; - } - - if ($matchtype == URL_MATCH_BASE) { - return true; - } - - $urlparams = $url->params(); - foreach ($this->params() as $param => $value) { - if ($param == 'sesskey') { - continue; - } - if (!array_key_exists($param, $urlparams) || $urlparams[$param] != $value) { - return false; - } - } - - if ($matchtype == URL_MATCH_PARAMS) { - return true; - } - - foreach ($urlparams as $param => $value) { - if ($param == 'sesskey') { - continue; - } - if (!array_key_exists($param, $this->params()) || $this->param($param) != $value) { - return false; - } - } - - if ($url->anchor !== $this->anchor) { - return false; - } - - return true; - } - - /** - * Sets the anchor for the URI (the bit after the hash) - * - * @param string $anchor null means remove previous - */ - public function set_anchor($anchor) { - if (is_null($anchor)) { - // Remove. - $this->anchor = null; - } else { - $this->anchor = $anchor; - } - } - - /** - * Sets the scheme for the URI (the bit before ://) - * - * @param string $scheme - */ - public function set_scheme($scheme) { - // See http://www.ietf.org/rfc/rfc3986.txt part 3.1. - if (preg_match('/^[a-zA-Z][a-zA-Z0-9+.-]*$/', $scheme)) { - $this->scheme = $scheme; - } else { - throw new coding_exception('Bad URL scheme.'); - } - } - - /** - * Sets the url slashargument value. - * - * @param string $path usually file path - * @param string $parameter name of page parameter if slasharguments not supported - * @param bool $supported usually null, then it depends on $CFG->slasharguments, use true or false for other servers - * @return void - */ - public function set_slashargument($path, $parameter = 'file', $supported = null) { - global $CFG; - if (is_null($supported)) { - $supported = !empty($CFG->slasharguments); - } - - if ($supported) { - $parts = explode('/', $path); - $parts = array_map('rawurlencode', $parts); - $path = implode('/', $parts); - $this->slashargument = $path; - unset($this->params[$parameter]); - - } else { - $this->slashargument = ''; - $this->params[$parameter] = $path; - } - } - - // Static factory methods. - - /** - * Create a new moodle_url instance from a UriInterface. - * - * @param UriInterface $uri - * @return self - */ - public static function from_uri(UriInterface $uri): self { - $url = new self( - url: $uri->getScheme() . '://' . $uri->getAuthority() . $uri->getPath(), - anchor: $uri->getFragment() ?: null, - ); - - $params = $uri->getQuery(); - foreach (explode('&', $params) as $param) { - $url->param(...explode('=', $param, 2)); - } - - return $url; - } - - /** - * General moodle file url. - * - * @param string $urlbase the script serving the file - * @param string $path - * @param bool $forcedownload - * @return moodle_url - */ - public static function make_file_url($urlbase, $path, $forcedownload = false) { - $params = array(); - if ($forcedownload) { - $params['forcedownload'] = 1; - } - $url = new moodle_url($urlbase, $params); - $url->set_slashargument($path); - return $url; - } - - /** - * Factory method for creation of url pointing to plugin file. - * - * Please note this method can be used only from the plugins to - * create urls of own files, it must not be used outside of plugins! - * - * @param int $contextid - * @param string $component - * @param string $area - * @param ?int $itemid - * @param string $pathname - * @param string $filename - * @param bool $forcedownload - * @param mixed $includetoken Whether to use a user token when displaying this group image. - * True indicates to generate a token for current user, and integer value indicates to generate a token for the - * user whose id is the value indicated. - * If the group picture is included in an e-mail or some other location where the audience is a specific - * user who will not be logged in when viewing, then we use a token to authenticate the user. - * @return moodle_url - */ - public static function make_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, - $forcedownload = false, $includetoken = false) { - global $CFG, $USER; - - $path = []; - - if ($includetoken) { - $urlbase = "$CFG->wwwroot/tokenpluginfile.php"; - $userid = $includetoken === true ? $USER->id : $includetoken; - $token = get_user_key('core_files', $userid); - if ($CFG->slasharguments) { - $path[] = $token; - } - } else { - $urlbase = "$CFG->wwwroot/pluginfile.php"; - } - $path[] = $contextid; - $path[] = $component; - $path[] = $area; - - if ($itemid !== null) { - $path[] = $itemid; - } - - $path = "/" . implode('/', $path) . "{$pathname}{$filename}"; - - $url = self::make_file_url($urlbase, $path, $forcedownload, $includetoken); - if ($includetoken && empty($CFG->slasharguments)) { - $url->param('token', $token); - } - return $url; - } - - /** - * Factory method for creation of url pointing to plugin file. - * This method is the same that make_pluginfile_url but pointing to the webservice pluginfile.php script. - * It should be used only in external functions. - * - * @since 2.8 - * @param int $contextid - * @param string $component - * @param string $area - * @param int $itemid - * @param string $pathname - * @param string $filename - * @param bool $forcedownload - * @return moodle_url - */ - public static function make_webservice_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, - $forcedownload = false) { - global $CFG; - $urlbase = "$CFG->wwwroot/webservice/pluginfile.php"; - if ($itemid === null) { - return self::make_file_url($urlbase, "/$contextid/$component/$area".$pathname.$filename, $forcedownload); - } else { - return self::make_file_url($urlbase, "/$contextid/$component/$area/$itemid".$pathname.$filename, $forcedownload); - } - } - - /** - * Factory method for creation of url pointing to draft file of current user. - * - * @param int $draftid draft item id - * @param string $pathname - * @param string $filename - * @param bool $forcedownload - * @return moodle_url - */ - public static function make_draftfile_url($draftid, $pathname, $filename, $forcedownload = false) { - global $CFG, $USER; - $urlbase = "$CFG->wwwroot/draftfile.php"; - $context = context_user::instance($USER->id); - - return self::make_file_url($urlbase, "/$context->id/user/draft/$draftid".$pathname.$filename, $forcedownload); - } - - /** - * Factory method for creating of links to legacy course files. - * - * @param int $courseid - * @param string $filepath - * @param bool $forcedownload - * @return moodle_url - */ - public static function make_legacyfile_url($courseid, $filepath, $forcedownload = false) { - global $CFG; - - $urlbase = "$CFG->wwwroot/file.php"; - return self::make_file_url($urlbase, '/'.$courseid.'/'.$filepath, $forcedownload); - } - - /** - * Checks if URL is relative to $CFG->wwwroot. - * - * @return bool True if URL is relative to $CFG->wwwroot; otherwise, false. - */ - public function is_local_url(): bool { - global $CFG; - - $url = $this->out(); - // Does URL start with wwwroot? Otherwise, URL isn't relative to wwwroot. - return ( ($url === $CFG->wwwroot) || (strpos($url, $CFG->wwwroot.'/') === 0) ); - } - - /** - * Returns URL as relative path from $CFG->wwwroot - * - * Can be used for passing around urls with the wwwroot stripped - * - * @param boolean $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. - * @return string Resulting URL - * @throws coding_exception if called on a non-local url - */ - public function out_as_local_url($escaped = true, array $overrideparams = null) { - global $CFG; - - // URL should be relative to wwwroot. If not then throw exception. - if ($this->is_local_url()) { - $url = $this->out($escaped, $overrideparams); - $localurl = substr($url, strlen($CFG->wwwroot)); - return !empty($localurl) ? $localurl : ''; - } else { - throw new coding_exception('out_as_local_url called on a non-local URL'); - } - } - - /** - * Returns the 'path' portion of a URL. For example, if the URL is - * http://www.example.org:447/my/file/is/here.txt?really=1 then this will - * return '/my/file/is/here.txt'. - * - * By default the path includes slash-arguments (for example, - * '/myfile.php/extra/arguments') so it is what you would expect from a - * URL path. If you don't want this behaviour, you can opt to exclude the - * slash arguments. (Be careful: if the $CFG variable slasharguments is - * disabled, these URLs will have a different format and you may need to - * look at the 'file' parameter too.) - * - * @param bool $includeslashargument If true, includes slash arguments - * @return string Path of URL - */ - public function get_path($includeslashargument = true) { - return $this->path . ($includeslashargument ? $this->slashargument : ''); - } - - /** - * Returns a given parameter value from the URL. - * - * @param string $name Name of parameter - * @return string Value of parameter or null if not set - */ - public function get_param($name) { - if (array_key_exists($name, $this->params)) { - return $this->params[$name]; - } else { - return null; - } - } - - /** - * Returns the 'scheme' portion of a URL. For example, if the URL is - * http://www.example.org:447/my/file/is/here.txt?really=1 then this will - * return 'http' (without the colon). - * - * @return string Scheme of the URL. - */ - public function get_scheme() { - return $this->scheme; - } - - /** - * Returns the 'host' portion of a URL. For example, if the URL is - * http://www.example.org:447/my/file/is/here.txt?really=1 then this will - * return 'www.example.org'. - * - * @return string Host of the URL. - */ - public function get_host() { - return $this->host; - } - - /** - * Returns the 'port' portion of a URL. For example, if the URL is - * http://www.example.org:447/my/file/is/here.txt?really=1 then this will - * return '447'. - * - * @return string Port of the URL. - */ - public function get_port() { - return $this->port; - } -} - /** * Determine if there is data waiting to be processed from a form * From e138e03b2f531cc84cf547b67b6794686634bfd5 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 10 Jun 2024 10:14:10 +0800 Subject: [PATCH 2/8] MDL-81960 core: Move moodle_url to \core\url --- .upgradenotes/MDL-81960-2024061004043644.yml | 7 + lib/classes/url.php | 22 ++- .../{moodle_url_test.php => url_test.php} | 175 +++++++++--------- 3 files changed, 111 insertions(+), 93 deletions(-) create mode 100644 .upgradenotes/MDL-81960-2024061004043644.yml rename lib/tests/{moodle_url_test.php => url_test.php} (74%) diff --git a/.upgradenotes/MDL-81960-2024061004043644.yml b/.upgradenotes/MDL-81960-2024061004043644.yml new file mode 100644 index 00000000000..964e907be25 --- /dev/null +++ b/.upgradenotes/MDL-81960-2024061004043644.yml @@ -0,0 +1,7 @@ +issueNumber: MDL-81960 +notes: + core: + - message: >- + The `\moodle_url` class has been renamed to `\core\url` and now supports + autoloading. Existing uses are currently unaffected. + type: improved diff --git a/lib/classes/url.php b/lib/classes/url.php index 5e8a6da1022..a0b99718280 100644 --- a/lib/classes/url.php +++ b/lib/classes/url.php @@ -14,6 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core; + +use core\context\user as context_user; +use core\exception\coding_exception; +use core\exception\moodle_exception; use Psr\Http\Message\UriInterface; /** @@ -92,8 +97,8 @@ class url { /** * Create new instance of url. * - * @param url|string $url - url means make a copy of another - * url and change parameters, string means full url or shortened + * @param self|string $url - moodle_url means make a copy of another + * moodle_url and change parameters, string means full url or shortened * form (ex.: '/course/view.php'). It is strongly encouraged to not include * query string because it may result in double encoded values. Use the * $params instead. For admin URLs, just use /admin/script.php, this @@ -105,7 +110,7 @@ class url { public function __construct($url, array $params = null, $anchor = null) { global $CFG; - if ($url instanceof url) { + if ($url instanceof self) { $this->scheme = $url->scheme; $this->host = $url->host; $this->port = $url->port; @@ -480,11 +485,11 @@ class url { * * See documentation of constants for an explanation of the comparison flags. * - * @param url $url The url object to compare + * @param self $url The moodle_url object to compare * @param int $matchtype The type of comparison (URL_MATCH_BASE, URL_MATCH_PARAMS, URL_MATCH_EXACT) * @return bool */ - public function compare(url $url, $matchtype = URL_MATCH_EXACT) { + public function compare(self $url, $matchtype = URL_MATCH_EXACT) { $baseself = $this->out_omit_querystring(); $baseother = $url->out_omit_querystring(); @@ -619,14 +624,14 @@ class url { * @param string $urlbase the script serving the file * @param string $path * @param bool $forcedownload - * @return url + * @return self */ public static function make_file_url($urlbase, $path, $forcedownload = false) { $params = array(); if ($forcedownload) { $params['forcedownload'] = 1; } - $url = new url($urlbase, $params); + $url = new self($urlbase, $params); $url->set_slashargument($path); return $url; } @@ -845,4 +850,7 @@ class url { } } +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. class_alias(url::class, \moodle_url::class); diff --git a/lib/tests/moodle_url_test.php b/lib/tests/url_test.php similarity index 74% rename from lib/tests/moodle_url_test.php rename to lib/tests/url_test.php index 9f2869428c5..3dfceb7929e 100644 --- a/lib/tests/moodle_url_test.php +++ b/lib/tests/url_test.php @@ -19,98 +19,98 @@ namespace core; use GuzzleHttp\Psr7\Uri; /** - * Tests for moodle_url. + * Tests for \core\url. * * @package core * @copyright 2018 Andrew Nicols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @covers \moodle_url + * @covers \core\url */ -final class moodle_url_test extends \advanced_testcase { +final class url_test extends \advanced_testcase { /** - * Test basic moodle_url construction. + * Test basic url construction. */ - public function test_moodle_url_constructor(): void { + public function test_constructor(): void { global $CFG; - $url = new \moodle_url('/index.php'); - $this->assertSame($CFG->wwwroot.'/index.php', $url->out()); + $url = new url('/index.php'); + $this->assertSame($CFG->wwwroot . '/index.php', $url->out()); - $url = new \moodle_url('/index.php', array()); - $this->assertSame($CFG->wwwroot.'/index.php', $url->out()); + $url = new url('/index.php', []); + $this->assertSame($CFG->wwwroot . '/index.php', $url->out()); - $url = new \moodle_url('/index.php', array('id' => 2)); - $this->assertSame($CFG->wwwroot.'/index.php?id=2', $url->out()); + $url = new url('/index.php', ['id' => 2]); + $this->assertSame($CFG->wwwroot . '/index.php?id=2', $url->out()); - $url = new \moodle_url('/index.php', array('id' => 'two')); - $this->assertSame($CFG->wwwroot.'/index.php?id=two', $url->out()); + $url = new url('/index.php', ['id' => 'two']); + $this->assertSame($CFG->wwwroot . '/index.php?id=two', $url->out()); - $url = new \moodle_url('/index.php', array('id' => 1, 'cid' => '2')); - $this->assertSame($CFG->wwwroot.'/index.php?id=1&cid=2', $url->out()); - $this->assertSame($CFG->wwwroot.'/index.php?id=1&cid=2', $url->out(false)); + $url = new url('/index.php', ['id' => 1, 'cid' => '2']); + $this->assertSame($CFG->wwwroot . '/index.php?id=1&cid=2', $url->out()); + $this->assertSame($CFG->wwwroot . '/index.php?id=1&cid=2', $url->out(false)); - $url = new \moodle_url('/index.php', null, 'test'); - $this->assertSame($CFG->wwwroot.'/index.php#test', $url->out()); + $url = new url('/index.php', null, 'test'); + $this->assertSame($CFG->wwwroot . '/index.php#test', $url->out()); - $url = new \moodle_url('/index.php', array('id' => 2), 'test'); - $this->assertSame($CFG->wwwroot.'/index.php?id=2#test', $url->out()); + $url = new url('/index.php', ['id' => 2], 'test'); + $this->assertSame($CFG->wwwroot . '/index.php?id=2#test', $url->out()); } /** - * Tests \moodle_url::get_path(). + * Tests url::get_path(). */ - public function test_moodle_url_get_path(): void { - $url = new \moodle_url('http://www.example.org:447/my/file/is/here.txt?really=1'); + public function test_get_path(): void { + $url = new url('http://www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame('/my/file/is/here.txt', $url->get_path()); - $url = new \moodle_url('http://www.example.org/'); + $url = new url('http://www.example.org/'); $this->assertSame('/', $url->get_path()); - $url = new \moodle_url('http://www.example.org/pluginfile.php/slash/arguments'); + $url = new url('http://www.example.org/pluginfile.php/slash/arguments'); $this->assertSame('/pluginfile.php/slash/arguments', $url->get_path()); $this->assertSame('/pluginfile.php', $url->get_path(false)); } - public function test_moodle_url_round_trip(): void { + public function test_round_trip(): void { $strurl = 'http://moodle.org/course/view.php?id=5'; - $url = new \moodle_url($strurl); + $url = new url($strurl); $this->assertSame($strurl, $url->out(false)); $strurl = 'http://moodle.org/user/index.php?contextid=53&sifirst=M&silast=D'; - $url = new \moodle_url($strurl); + $url = new url($strurl); $this->assertSame($strurl, $url->out(false)); } /** * Test Moodle URL objects created with a param with empty value. */ - public function test_moodle_url_empty_param_values(): void { + public function test_empty_param_values(): void { $strurl = 'http://moodle.org/course/view.php?id=0'; - $url = new \moodle_url($strurl, array('id' => 0)); + $url = new url($strurl, ['id' => 0]); $this->assertSame($strurl, $url->out(false)); $strurl = 'http://moodle.org/course/view.php?id'; - $url = new \moodle_url($strurl, array('id' => false)); + $url = new url($strurl, ['id' => false]); $this->assertSame($strurl, $url->out(false)); $strurl = 'http://moodle.org/course/view.php?id'; - $url = new \moodle_url($strurl, array('id' => null)); + $url = new url($strurl, ['id' => null]); $this->assertSame($strurl, $url->out(false)); $strurl = 'http://moodle.org/course/view.php?id'; - $url = new \moodle_url($strurl, array('id' => '')); + $url = new url($strurl, ['id' => '']); $this->assertSame($strurl, $url->out(false)); $strurl = 'http://moodle.org/course/view.php?id'; - $url = new \moodle_url($strurl); + $url = new url($strurl); $this->assertSame($strurl, $url->out(false)); } /** * Test set good scheme on Moodle URL objects. */ - public function test_moodle_url_set_good_scheme(): void { - $url = new \moodle_url('http://moodle.org/foo/bar'); + public function test_set_good_scheme(): void { + $url = new url('http://moodle.org/foo/bar'); $url->set_scheme('myscheme'); $this->assertSame('myscheme://moodle.org/foo/bar', $url->out()); } @@ -118,47 +118,47 @@ final class moodle_url_test extends \advanced_testcase { /** * Test set bad scheme on Moodle URL objects. */ - public function test_moodle_url_set_bad_scheme(): void { - $url = new \moodle_url('http://moodle.org/foo/bar'); + public function test_set_bad_scheme(): void { + $url = new url('http://moodle.org/foo/bar'); $this->expectException(\coding_exception::class); $url->set_scheme('not a valid $ scheme'); } - public function test_moodle_url_round_trip_array_params(): void { + public function test_round_trip_array_params(): void { $strurl = 'http://example.com/?a%5B1%5D=1&a%5B2%5D=2'; - $url = new \moodle_url($strurl); + $url = new url($strurl); $this->assertSame($strurl, $url->out(false)); - $url = new \moodle_url('http://example.com/?a[1]=1&a[2]=2'); + $url = new url('http://example.com/?a[1]=1&a[2]=2'); $this->assertSame($strurl, $url->out(false)); // For un-keyed array params, we expect 0..n keys to be returned. $strurl = 'http://example.com/?a%5B0%5D=0&a%5B1%5D=1'; - $url = new \moodle_url('http://example.com/?a[]=0&a[]=1'); + $url = new url('http://example.com/?a[]=0&a[]=1'); $this->assertSame($strurl, $url->out(false)); } public function test_compare_url(): void { - $url1 = new \moodle_url('index.php', array('var1' => 1, 'var2' => 2)); - $url2 = new \moodle_url('index2.php', array('var1' => 1, 'var2' => 2, 'var3' => 3)); + $url1 = new url('index.php', ['var1' => 1, 'var2' => 2]); + $url2 = new url('index2.php', ['var1' => 1, 'var2' => 2, 'var3' => 3]); $this->assertFalse($url1->compare($url2, URL_MATCH_BASE)); $this->assertFalse($url1->compare($url2, URL_MATCH_PARAMS)); $this->assertFalse($url1->compare($url2, URL_MATCH_EXACT)); - $url2 = new \moodle_url('index.php', array('var1' => 1, 'var3' => 3)); + $url2 = new url('index.php', ['var1' => 1, 'var3' => 3]); $this->assertTrue($url1->compare($url2, URL_MATCH_BASE)); $this->assertFalse($url1->compare($url2, URL_MATCH_PARAMS)); $this->assertFalse($url1->compare($url2, URL_MATCH_EXACT)); - $url2 = new \moodle_url('index.php', array('var1' => 1, 'var2' => 2, 'var3' => 3)); + $url2 = new url('index.php', ['var1' => 1, 'var2' => 2, 'var3' => 3]); $this->assertTrue($url1->compare($url2, URL_MATCH_BASE)); $this->assertTrue($url1->compare($url2, URL_MATCH_PARAMS)); $this->assertFalse($url1->compare($url2, URL_MATCH_EXACT)); - $url2 = new \moodle_url('index.php', array('var2' => 2, 'var1' => 1)); + $url2 = new url('index.php', ['var2' => 2, 'var1' => 1]); $this->assertTrue($url1->compare($url2, URL_MATCH_BASE)); $this->assertTrue($url1->compare($url2, URL_MATCH_PARAMS)); @@ -178,25 +178,25 @@ final class moodle_url_test extends \advanced_testcase { public function test_out_as_local_url(): void { global $CFG; // Test http url. - $url1 = new \moodle_url('/lib/tests/weblib_test.php'); + $url1 = new url('/lib/tests/weblib_test.php'); $this->assertSame('/lib/tests/weblib_test.php', $url1->out_as_local_url()); // Test https url. $httpswwwroot = str_replace("http://", "https://", $CFG->wwwroot); - $url2 = new \moodle_url($httpswwwroot.'/login/profile.php'); + $url2 = new url($httpswwwroot . '/login/profile.php'); $this->assertSame('/login/profile.php', $url2->out_as_local_url()); // Test http url matching wwwroot. - $url3 = new \moodle_url($CFG->wwwroot); + $url3 = new url($CFG->wwwroot); $this->assertSame('', $url3->out_as_local_url()); // Test http url matching wwwroot ending with slash (/). - $url3 = new \moodle_url($CFG->wwwroot.'/'); + $url3 = new url($CFG->wwwroot . '/'); $this->assertSame('/', $url3->out_as_local_url()); } public function test_out_as_local_url_error(): void { - $url2 = new \moodle_url('http://www.google.com/lib/tests/weblib_test.php'); + $url2 = new url('http://www.google.com/lib/tests/weblib_test.php'); $this->expectException(\coding_exception::class); $url2->out_as_local_url(); } @@ -207,8 +207,8 @@ final class moodle_url_test extends \advanced_testcase { public function test_modified_url_out_as_local_url_error(): void { global $CFG; - $modifiedurl = $CFG->wwwroot.'1'; - $url3 = new \moodle_url($modifiedurl.'/login/profile.php'); + $modifiedurl = $CFG->wwwroot . '1'; + $url3 = new url($modifiedurl . '/login/profile.php'); $this->expectException(\coding_exception::class); $url3->out_as_local_url(); } @@ -217,79 +217,79 @@ final class moodle_url_test extends \advanced_testcase { * Try get local url from external https url and you should get error */ public function test_https_out_as_local_url_error(): void { - $url4 = new \moodle_url('https://www.google.com/lib/tests/weblib_test.php'); + $url4 = new url('https://www.google.com/lib/tests/weblib_test.php'); $this->expectException(\coding_exception::class); $url4->out_as_local_url(); } - public function test_moodle_url_get_scheme(): void { + public function test_get_scheme(): void { // Should return the scheme only. - $url = new \moodle_url('http://www.example.org:447/my/file/is/here.txt?really=1'); + $url = new url('http://www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame('http', $url->get_scheme()); // Should work for secure URLs. - $url = new \moodle_url('https://www.example.org:447/my/file/is/here.txt?really=1'); + $url = new url('https://www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame('https', $url->get_scheme()); // Should return an empty string if no scheme is specified. - $url = new \moodle_url('www.example.org:447/my/file/is/here.txt?really=1'); + $url = new url('www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame('', $url->get_scheme()); } - public function test_moodle_url_get_host(): void { + public function test_get_host(): void { // Should return the host part only. - $url = new \moodle_url('http://www.example.org:447/my/file/is/here.txt?really=1'); + $url = new url('http://www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame('www.example.org', $url->get_host()); } - public function test_moodle_url_get_port(): void { + public function test_get_port(): void { // Should return the port if one provided. - $url = new \moodle_url('http://www.example.org:447/my/file/is/here.txt?really=1'); + $url = new url('http://www.example.org:447/my/file/is/here.txt?really=1'); $this->assertSame(447, $url->get_port()); // Should return an empty string if port not specified. - $url = new \moodle_url('http://www.example.org/some/path/here.php'); + $url = new url('http://www.example.org/some/path/here.php'); $this->assertSame('', $url->get_port()); } /** * Test exporting params for templates. * - * @dataProvider moodle_url_export_params_for_template_provider + * @dataProvider export_params_for_template_provider * @param string $url URL with params to test. * @param array $expected The expected result. */ - public function test_moodle_url_export_params_for_template(string $url, array $expected): void { + public function test_export_params_for_template(string $url, array $expected): void { // Should return params in the URL. - $moodleurl = new \moodle_url($url); + $moodleurl = new url($url); $this->assertSame($expected, $moodleurl->export_params_for_template()); } /** - * Data provider for moodle_url_export_params_for_template tests. + * Data provider for export_params_for_template tests. * * @return array[] the array of test data. */ - public function moodle_url_export_params_for_template_provider(): array { + public static function export_params_for_template_provider(): array { $baseurl = "http://example.com"; return [ 'With indexed array params' => [ 'url' => "@{$baseurl}/?tags[0]=123&tags[1]=456", 'expected' => [ 0 => ['name' => 'tags[0]', 'value' => '123'], - 1 => ['name' => 'tags[1]', 'value' => '456'] - ] + 1 => ['name' => 'tags[1]', 'value' => '456'], + ], ], 'Without indexed array params' => [ 'url' => "@{$baseurl}/?tags[]=123&tags[]=456", 'expected' => [ 0 => ['name' => 'tags[0]', 'value' => '123'], - 1 => ['name' => 'tags[1]', 'value' => '456'] - ] + 1 => ['name' => 'tags[1]', 'value' => '456'], + ], ], 'with no params' => [ 'url' => "@{$baseurl}/", - 'expected' => [] + 'expected' => [], ], 'with no array params' => [ 'url' => "@{$baseurl}/?param1=1¶m2=2¶m3=3", @@ -297,7 +297,7 @@ final class moodle_url_test extends \advanced_testcase { 0 => ['name' => 'param1', 'value' => '1'], 1 => ['name' => 'param2', 'value' => '2'], 2 => ['name' => 'param3', 'value' => '3'], - ] + ], ], 'array embedded with other params' => [ 'url' => "@{$baseurl}/?param1=1&tags[0]=123&tags[1]=456¶m2=2¶m3=3", @@ -307,7 +307,7 @@ final class moodle_url_test extends \advanced_testcase { 2 => ['name' => 'tags[1]', 'value' => '456'], 3 => ['name' => 'param2', 'value' => '2'], 4 => ['name' => 'param3', 'value' => '3'], - ] + ], ], 'params with array at the end' => [ 'url' => "@{$baseurl}/?param1=1&tags[]=123&tags[]=456", @@ -315,7 +315,7 @@ final class moodle_url_test extends \advanced_testcase { 0 => ['name' => 'param1', 'value' => '1'], 1 => ['name' => 'tags[0]', 'value' => '123'], 2 => ['name' => 'tags[1]', 'value' => '456'], - ] + ], ], ]; } @@ -334,7 +334,7 @@ final class moodle_url_test extends \advanced_testcase { $this->resetAfterTest(); $CFG->slasharguments = $slashargs; - $url = call_user_func_array('\moodle_url::make_pluginfile_url', $args); + $url = call_user_func_array([url::class, 'make_pluginfile_url'], $args); $this->assertMatchesRegularExpression($expected, $url->out(true)); } @@ -343,7 +343,7 @@ final class moodle_url_test extends \advanced_testcase { * * @return array[] */ - public function make_pluginfile_url_provider() { + public static function make_pluginfile_url_provider(): array { $baseurl = "https://www.example.com/moodle/pluginfile.php"; $tokenbaseurl = "https://www.example.com/moodle/tokenpluginfile.php"; return [ @@ -397,7 +397,8 @@ final class moodle_url_test extends \advanced_testcase { false, true, ], - 'expected' => "@{$tokenbaseurl}\?file=%2F1%2Fmod_forum%2Fposts%2F422%2Fmy%2Flocation%2Ffile.png&token=[a-z0-9]*@", + 'expected' => + "@{$tokenbaseurl}\?file=%2F1%2Fmod_forum%2Fposts%2F422%2Fmy%2Flocation%2Ffile.png&token=[a-z0-9]*@", ], ]; } @@ -406,18 +407,18 @@ final class moodle_url_test extends \advanced_testcase { global $CFG; $uri = new Uri('http://www.example.org:447/my/file/is/here.txt?really=1'); - $url = \moodle_url::from_uri($uri); + $url = url::from_uri($uri); $this->assertSame('http://www.example.org:447/my/file/is/here.txt?really=1', $url->out(false)); $this->assertEquals(1, $url->param('really')); $uri = new Uri('https://www.example.org/my/file/is/here.txt?really=1'); - $url = \moodle_url::from_uri($uri); + $url = url::from_uri($uri); $this->assertSame('https://www.example.org/my/file/is/here.txt?really=1', $url->out(false)); $this->assertEquals(1, $url->param('really')); // Multiple params. $uri = new Uri('https://www.example.org/my/file/is/here.txt?really=1&another=2&&more=3&moar=4'); - $url = \moodle_url::from_uri($uri); + $url = url::from_uri($uri); $this->assertSame('https://www.example.org/my/file/is/here.txt?really=1&another=2&more=3&moar=4', $url->out(false)); $this->assertEquals(1, $url->param('really')); $this->assertEquals(2, $url->param('another')); @@ -426,16 +427,18 @@ final class moodle_url_test extends \advanced_testcase { // Anchors. $uri = new Uri("{$CFG->wwwroot}/course/view/#section-1"); - $url = \moodle_url::from_uri($uri); + $url = url::from_uri($uri); $this->assertSame("{$CFG->wwwroot}/course/view/#section-1", $url->out(false)); $this->assertEmpty($url->params()); } /** + * Test url fragment parsing. + * * @dataProvider url_fragment_parsing_provider */ public function test_url_fragment_parsing(string $fragment, string $expected): void { - $url = new \moodle_url('/index.php', null, $fragment); + $url = new url('/index.php', null, $fragment); // Test the encoded fragment. $this->assertEquals( @@ -484,7 +487,7 @@ final class moodle_url_test extends \advanced_testcase { '%25Percent', ], 'Contains multiple %' => [ - // % followed by a valid pct-encoded followed by two more %%. + // A % followed by a valid pct-encoded followed by two more %%. '%%23%%', '%25%23%25%25', ], From b637f8cc4eab28b3a6a9d3fa5af97f7f8b7218bd Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 10 Jun 2024 10:16:24 +0800 Subject: [PATCH 3/8] MDL-81960 core: Coding style fixes --- lib/classes/url.php | 125 +++++++++++++++------------ lib/tests/url_test.php | 186 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+), 53 deletions(-) diff --git a/lib/classes/url.php b/lib/classes/url.php index a0b99718280..af29e06d832 100644 --- a/lib/classes/url.php +++ b/lib/classes/url.php @@ -92,7 +92,7 @@ class url { * Url parameters as associative array. * @var array */ - protected $params = array(); + protected $params = []; /** * Create new instance of url. @@ -103,11 +103,15 @@ class url { * query string because it may result in double encoded values. Use the * $params instead. For admin URLs, just use /admin/script.php, this * class takes care of the $CFG->admin issue. - * @param array $params these params override current params or add new + * @param null|array $params these params override current params or add new * @param string $anchor The anchor to use as part of the URL if there is one. * @throws moodle_exception */ - public function __construct($url, array $params = null, $anchor = null) { + public function __construct( + $url, + ?array $params = null, + $anchor = null, + ) { global $CFG; if ($url instanceof self) { @@ -120,7 +124,6 @@ class url { $this->slashargument = $url->slashargument; $this->params = $url->params; $this->anchor = $url->anchor; - } else { $url = $url ?? ''; // Detect if anchor used. @@ -134,7 +137,7 @@ class url { // Normalise shortened form of our url ex.: '/course/view.php'. if (strpos($url, '/') === 0) { - $url = $CFG->wwwroot.$url; + $url = $CFG->wwwroot . $url; } if ($CFG->admin !== 'admin') { @@ -176,11 +179,11 @@ class url { * * The added params override existing ones if they have the same name. * - * @param array $params Defaults to null. If null then returns all params. + * @param null|array $params Defaults to null. If null then returns all params. * @return array Array of Params for url. * @throws coding_exception */ - public function params(array $params = null) { + public function params(?array $params = null) { $params = (array)$params; foreach ($params as $key => $value) { @@ -191,7 +194,7 @@ class url { if (is_array($value)) { throw new coding_exception('Url parameters values can not be arrays!'); } - if (is_object($value) and !method_exists($value, '__toString')) { + if (is_object($value) && !method_exists($value, '__toString')) { throw new coding_exception('Url parameters values can not be objects, unless __toString() is defined!'); } } @@ -207,13 +210,19 @@ class url { * Can be called as either remove_params('param1', 'param2') * or remove_params(array('param1', 'param2')). * - * @param string[]|string $params,... either an array of param names, or 1..n string params to remove as args. + * @param string[]|string ...$params either an array of param names, or 1..n string params to remove as args. * @return array url parameters */ - public function remove_params($params = null) { - if (!is_array($params)) { - $params = func_get_args(); + public function remove_params(...$params) { + if (empty($params)) { + return $this->params; } + + $firstparam = reset($params); + if (is_array($firstparam)) { + $params = $firstparam; + } + foreach ($params as $param) { unset($this->params[$param]); } @@ -223,12 +232,10 @@ class url { /** * Remove all url parameters. * - * @todo remove the unused param. - * @param array $params Unused param - * @return void + * @param array $unused Unused param */ - public function remove_all_params($params = null) { - $this->params = array(); + public function remove_all_params($unused = null) { + $this->params = []; $this->slashargument = ''; } @@ -244,7 +251,7 @@ class url { public function param($paramname, $newvalue = '') { if (func_num_args() > 1) { // Set new value. - $this->params(array($paramname => $newvalue)); + $this->params([$paramname => $newvalue]); } if (isset($this->params[$paramname])) { return $this->params[$paramname]; @@ -256,11 +263,11 @@ class url { /** * Merges parameters and validates them * - * @param array $overrideparams + * @param null|array $overrideparams * @return array merged parameters * @throws coding_exception */ - protected function merge_overrideparams(array $overrideparams = null) { + protected function merge_overrideparams(?array $overrideparams = null) { $overrideparams = (array)$overrideparams; $params = $this->params; foreach ($overrideparams as $key => $value) { @@ -270,7 +277,7 @@ class url { if (is_array($value)) { throw new coding_exception('Overridden parameters values can not be arrays!'); } - if (is_object($value) and !method_exists($value, '__toString')) { + if (is_object($value) && !method_exists($value, '__toString')) { throw new coding_exception('Overridden parameters values can not be objects, unless __toString() is defined!'); } $params[$key] = (string)$value; @@ -284,12 +291,12 @@ class url { * This method should not be used outside of this method. * * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output params, these + * @param null|array $overrideparams params to add to the output params, these * override existing ones with the same name. * @return string query string that can be added to a url. */ - public function get_query_string($escaped = true, array $overrideparams = null) { - $arr = array(); + public function get_query_string($escaped = true, ?array $overrideparams = null) { + $arr = []; if ($overrideparams !== null) { $params = $this->merge_overrideparams($overrideparams); } else { @@ -298,11 +305,11 @@ class url { foreach ($params as $key => $val) { if (is_array($val)) { foreach ($val as $index => $value) { - $arr[] = rawurlencode($key.'['.$index.']')."=".rawurlencode($value); + $arr[] = rawurlencode($key . '[' . $index . ']') . "=" . rawurlencode($value); } } else { if (isset($val) && $val !== '') { - $arr[] = rawurlencode($key)."=".rawurlencode($val); + $arr[] = rawurlencode($key) . "=" . rawurlencode($val); } else { $arr[] = rawurlencode($key); } @@ -327,7 +334,7 @@ class url { foreach ($this->params as $key => $val) { if (is_array($val)) { foreach ($val as $index => $value) { - $data[] = ['name' => $key.'['.$index.']', 'value' => $value]; + $data[] = ['name' => $key . '[' . $index . ']', 'value' => $value]; } } else { $data[] = ['name' => $key, 'value' => $val]; @@ -352,15 +359,15 @@ class url { * the returned URL in HTTP headers, you want $escaped=false. * * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @param null|array $overrideparams params to add to the output url, these override existing ones with the same name. * @return string Resulting URL */ - public function out($escaped = true, array $overrideparams = null) { + public function out($escaped = true, ?array $overrideparams = null) { global $CFG; if (!is_bool($escaped)) { - debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); + debugging('Escape parameter must be of type boolean, ' . gettype($escaped) . ' given instead.'); } $url = $this; @@ -375,7 +382,6 @@ class url { } return $url->raw_out($escaped, $overrideparams); - } /** @@ -384,15 +390,15 @@ class url { * This is identical in signature and use to out() but doesn't call the rewrite handler. * * @param bool $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @param null|array $overrideparams params to add to the output url, these override existing ones with the same name. * @return string Resulting URL */ - public function raw_out($escaped = true, array $overrideparams = null) { + public function raw_out($escaped = true, ?array $overrideparams = null) { if (!is_bool($escaped)) { - debugging('Escape parameter must be of type boolean, '.gettype($escaped).' given instead.'); + debugging('Escape parameter must be of type boolean, ' . gettype($escaped) . ' given instead.'); } - $uri = $this->out_omit_querystring().$this->slashargument; + $uri = $this->out_omit_querystring() . $this->slashargument; $querystring = $this->get_query_string($escaped, $overrideparams); if ($querystring !== '') { @@ -463,15 +469,15 @@ class url { /** * Returns url without parameters, everything before '?'. * - * @param bool $includeanchor if {@link self::anchor} is defined, should it be returned? + * @param bool $includeanchor if {@see self::anchor} is defined, should it be returned? * @return string */ public function out_omit_querystring($includeanchor = false) { - $uri = $this->scheme ? $this->scheme.':'.((strtolower($this->scheme) == 'mailto') ? '':'//'): ''; - $uri .= $this->user ? $this->user.($this->pass? ':'.$this->pass:'').'@':''; + $uri = $this->scheme ? $this->scheme . ':' . ((strtolower($this->scheme) == 'mailto') ? '' : '//') : ''; + $uri .= $this->user ? $this->user . ($this->pass ? ':' . $this->pass : '') . '@' : ''; $uri .= $this->host ? $this->host : ''; - $uri .= $this->port ? ':'.$this->port : ''; + $uri .= $this->port ? ':' . $this->port : ''; $uri .= $this->path ? $this->path : ''; if ($includeanchor) { $uri .= $this->get_encoded_anchor(); @@ -575,7 +581,6 @@ class url { * @param string $path usually file path * @param string $parameter name of page parameter if slasharguments not supported * @param bool $supported usually null, then it depends on $CFG->slasharguments, use true or false for other servers - * @return void */ public function set_slashargument($path, $parameter = 'file', $supported = null) { global $CFG; @@ -589,7 +594,6 @@ class url { $path = implode('/', $parts); $this->slashargument = $path; unset($this->params[$parameter]); - } else { $this->slashargument = ''; $this->params[$parameter] = $path; @@ -627,7 +631,7 @@ class url { * @return self */ public static function make_file_url($urlbase, $path, $forcedownload = false) { - $params = array(); + $params = []; if ($forcedownload) { $params['forcedownload'] = 1; } @@ -656,8 +660,16 @@ class url { * user who will not be logged in when viewing, then we use a token to authenticate the user. * @return url */ - public static function make_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, - $forcedownload = false, $includetoken = false) { + public static function make_pluginfile_url( + $contextid, + $component, + $area, + $itemid, + $pathname, + $filename, + $forcedownload = false, + $includetoken = false + ) { global $CFG, $USER; $path = []; @@ -704,14 +716,21 @@ class url { * @param bool $forcedownload * @return url */ - public static function make_webservice_pluginfile_url($contextid, $component, $area, $itemid, $pathname, $filename, - $forcedownload = false) { + public static function make_webservice_pluginfile_url( + $contextid, + $component, + $area, + $itemid, + $pathname, + $filename, + $forcedownload = false + ) { global $CFG; $urlbase = "$CFG->wwwroot/webservice/pluginfile.php"; if ($itemid === null) { - return self::make_file_url($urlbase, "/$contextid/$component/$area".$pathname.$filename, $forcedownload); + return self::make_file_url($urlbase, "/$contextid/$component/$area" . $pathname . $filename, $forcedownload); } else { - return self::make_file_url($urlbase, "/$contextid/$component/$area/$itemid".$pathname.$filename, $forcedownload); + return self::make_file_url($urlbase, "/$contextid/$component/$area/$itemid" . $pathname . $filename, $forcedownload); } } @@ -729,7 +748,7 @@ class url { $urlbase = "$CFG->wwwroot/draftfile.php"; $context = context_user::instance($USER->id); - return self::make_file_url($urlbase, "/$context->id/user/draft/$draftid".$pathname.$filename, $forcedownload); + return self::make_file_url($urlbase, "/$context->id/user/draft/$draftid" . $pathname . $filename, $forcedownload); } /** @@ -744,7 +763,7 @@ class url { global $CFG; $urlbase = "$CFG->wwwroot/file.php"; - return self::make_file_url($urlbase, '/'.$courseid.'/'.$filepath, $forcedownload); + return self::make_file_url($urlbase, '/' . $courseid . '/' . $filepath, $forcedownload); } /** @@ -757,7 +776,7 @@ class url { $url = $this->out(); // Does URL start with wwwroot? Otherwise, URL isn't relative to wwwroot. - return ( ($url === $CFG->wwwroot) || (strpos($url, $CFG->wwwroot.'/') === 0) ); + return ( ($url === $CFG->wwwroot) || (strpos($url, $CFG->wwwroot . '/') === 0) ); } /** @@ -766,11 +785,11 @@ class url { * Can be used for passing around urls with the wwwroot stripped * * @param boolean $escaped Use & as params separator instead of plain & - * @param array $overrideparams params to add to the output url, these override existing ones with the same name. + * @param ?array $overrideparams params to add to the output url, these override existing ones with the same name. * @return string Resulting URL * @throws coding_exception if called on a non-local url */ - public function out_as_local_url($escaped = true, array $overrideparams = null) { + public function out_as_local_url($escaped = true, ?array $overrideparams = null) { global $CFG; // URL should be relative to wwwroot. If not then throw exception. diff --git a/lib/tests/url_test.php b/lib/tests/url_test.php index 3dfceb7929e..fd3cc8a5dbb 100644 --- a/lib/tests/url_test.php +++ b/lib/tests/url_test.php @@ -503,4 +503,190 @@ final class url_test extends \advanced_testcase { 'Quotes become encoded' => ['test with "quotes"', 'test%20with%20%22quotes%22'], ]; } + + /** + * Test the coding exceptions when returning URL as relative path from $CFG->wwwroot. + * + * @param url $url The URL pointing to a web resource. + * @param string $exmessage The expected output URL. + * @dataProvider out_as_local_url_coding_exception_provider + */ + public function test_out_as_local_url_coding_exception(url $url, string $exmessage): void { + $this->expectException(\coding_exception::class); + $this->expectExceptionMessage($exmessage); + $localurl = $url->out_as_local_url(); + } + + /** + * Data provider for throwing coding exceptions in url::out_as_local_url(). + * + * @return array + */ + public static function out_as_local_url_coding_exception_provider(): array { + return [ + 'Google Maps CDN (HTTPS)' => [ + new url('https://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), + 'Coding error detected, it must be fixed by a programmer: out_as_local_url called on a non-local URL', + ], + 'Google Maps CDN (HTTP)' => [ + new url('http://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), + 'Coding error detected, it must be fixed by a programmer: out_as_local_url called on a non-local URL', + ], + ]; + } + + /** + * Test URL as relative path from $CFG->wwwroot. + * + * @param url $url The URL pointing to a web resource. + * @param string $expected The expected local URL. + * @param string|null $wwwroot + * @dataProvider out_as_local_url_provider + */ + public function test_out_as_local_url( + url $url, + string $expected, + ?string $wwwroot = null, + ): void { + global $CFG; + + if ($wwwroot !== null) { + $CFG->wwwroot = $wwwroot; + $this->resetAfterTest(true); + } + $this->assertEquals($expected, $url->out_as_local_url(false)); + } + + /** + * Data provider for returning local paths via url::out_as_local_url(). + * + * @return array + */ + public static function out_as_local_url_provider(): array { + global $CFG; + $wwwroot = rtrim($CFG->wwwroot, '/'); + $httpswwwroot = str_replace('https://', 'http://', $CFG->wwwroot); + + return [ + 'HTTP URL' => [ + new url("{$wwwroot}/lib/tests/weblib_test.php"), + '/lib/tests/weblib_test.php', + $wwwroot, + ], + 'HTTPS URL' => [ + new url("{$httpswwwroot}/lib/tests/weblib_test.php"), + '/lib/tests/weblib_test.php', + $httpswwwroot, + ], + 'Plain wwwroot' => [ + new url($CFG->wwwroot), + '', + ], + 'wwwroot With trailing /' => [ + new url($CFG->wwwroot . '/'), + '/', + ], + 'Environment XML file' => [ + new url('/admin/environment.xml'), + '/admin/environment.xml', + ], + 'H5P JS internal resource' => [ + new url('/h5p/js/embed.js'), + '/h5p/js/embed.js', + ], + 'A Moodle JS resource using the full path including the proper JS Handler' => [ + new url($wwwroot . '/lib/javascript.php/1/lib/editor/tiny/js/tinymce/tinymce.js'), + '/lib/javascript.php/1/lib/editor/tiny/js/tinymce/tinymce.js', + ], + ]; + } + + /** + * Test URL as relative path from $CFG->wwwroot. + * + * @param url $url The URL pointing to a web resource. + * @param bool $expected The expected result. + * @dataProvider is_local_url_provider + */ + public function test_is_local_url(url $url, bool $expected): void { + $this->assertEquals($expected, $url->is_local_url(), "'{$url}' is not a local URL!"); + } + + /** + * Data provider for testing url::is_local_url(). + * + * @return array + */ + public static function is_local_url_provider(): array { + global $CFG; + $wwwroot = rtrim($CFG->wwwroot, '/'); + + return [ + 'Google Maps CDN (HTTPS)' => [ + new url('https://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), + false, + ], + 'Google Maps CDN (HTTP)' => [ + new url('http://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), + false, + ], + 'wwwroot' => [ + new url($wwwroot), + true, + ], + 'wwwroot/' => [ + new url($wwwroot . '/'), + true, + ], + 'Environment XML file' => [ + new url('/admin/environment.xml'), + true, + ], + 'H5P JS internal resource' => [ + new url('/h5p/js/embed.js'), + true, + ], + ]; + } + + /** + * @dataProvider remove_params_provider + */ + public function test_remove_params($params, $remove, $expected): void { + $url = new url('/index.php', $params); + if ($remove !== null) { + $url->remove_params(...$remove); + } + $this->assertSame($expected, $url->params()); + } + + public static function remove_params_provider(): array { + return [ + [ + ['id' => 1, 'cid' => 2, 'sid' => 3], + null, + ['id' => '1', 'cid' => '2', 'sid' => '3'], + ], + [ + ['id' => 1, 'cid' => 2, 'sid' => 3], + [], + ['id' => '1', 'cid' => '2', 'sid' => '3'], + ], + [ + ['id' => 1, 'cid' => 2, 'sid' => 3], + ['other'], + ['id' => '1', 'cid' => '2', 'sid' => '3'], + ], + [ + ['id' => 1, 'cid' => 2, 'sid' => 3], + ['id', 'sid'], + ['cid' => '2'], + ], + [ + ['id' => 1, 'cid' => 2, 'sid' => 3], + [['id', 'sid']], + ['cid' => '2'], + ], + ]; + } } From 097b3fbee31797f636434985d6fccdcde14aa619 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 10 Jun 2024 11:56:02 +0800 Subject: [PATCH 4/8] MDL-81960 core: Move moodle_url tests to correct location --- lib/classes/component.php | 1 + lib/tests/url_test.php | 20 ------ lib/tests/weblib_test.php | 130 -------------------------------------- 3 files changed, 1 insertion(+), 150 deletions(-) diff --git a/lib/classes/component.php b/lib/classes/component.php index e7f3eb4b324..0169c7e7f77 100644 --- a/lib/classes/component.php +++ b/lib/classes/component.php @@ -390,6 +390,7 @@ class core_component { $keyclasses = [ \core\exception\moodle_exception::class, \core\output\bootstrap_renderer::class, + \core\url::class, ]; foreach ($keyclasses as $classname) { if (!array_key_exists($classname, $cache['classmap'])) { diff --git a/lib/tests/url_test.php b/lib/tests/url_test.php index fd3cc8a5dbb..069dcc4be41 100644 --- a/lib/tests/url_test.php +++ b/lib/tests/url_test.php @@ -175,26 +175,6 @@ final class url_test extends \advanced_testcase { $this->assertTrue($url1->compare($url2, URL_MATCH_EXACT)); } - public function test_out_as_local_url(): void { - global $CFG; - // Test http url. - $url1 = new url('/lib/tests/weblib_test.php'); - $this->assertSame('/lib/tests/weblib_test.php', $url1->out_as_local_url()); - - // Test https url. - $httpswwwroot = str_replace("http://", "https://", $CFG->wwwroot); - $url2 = new url($httpswwwroot . '/login/profile.php'); - $this->assertSame('/login/profile.php', $url2->out_as_local_url()); - - // Test http url matching wwwroot. - $url3 = new url($CFG->wwwroot); - $this->assertSame('', $url3->out_as_local_url()); - - // Test http url matching wwwroot ending with slash (/). - $url3 = new url($CFG->wwwroot . '/'); - $this->assertSame('/', $url3->out_as_local_url()); - } - public function test_out_as_local_url_error(): void { $url2 = new url('http://www.google.com/lib/tests/weblib_test.php'); $this->expectException(\coding_exception::class); diff --git a/lib/tests/weblib_test.php b/lib/tests/weblib_test.php index 705f962a6d4..4de970c12d0 100644 --- a/lib/tests/weblib_test.php +++ b/lib/tests/weblib_test.php @@ -1199,136 +1199,6 @@ EXPECTED; $this->assertEquals($expected, get_html_lang_attribute_value($langcode)); } - /** - * Test the coding exceptions when returning URL as relative path from $CFG->wwwroot. - * - * @param moodle_url $url The URL pointing to a web resource. - * @param string $exmessage The expected output URL. - * @throws coding_exception If called on a non-local URL. - * @see \moodle_url::out_as_local_url() - * @covers \moodle_url::out_as_local_url - * @dataProvider out_as_local_url_coding_exception_provider - */ - public function test_out_as_local_url_coding_exception(\moodle_url $url, string $exmessage): void { - $this->expectException(\coding_exception::class); - $this->expectExceptionMessage($exmessage); - $localurl = $url->out_as_local_url(); - } - - /** - * Data provider for throwing coding exceptions in \moodle_url::out_as_local_url(). - * - * @return array - * @throws moodle_exception On seriously malformed URLs (parse_url). - * @see \moodle_url::out_as_local_url() - * @see parse_url() - */ - public function out_as_local_url_coding_exception_provider() { - return [ - 'Google Maps CDN (HTTPS)' => [ - new \moodle_url('https://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), - 'Coding error detected, it must be fixed by a programmer: out_as_local_url called on a non-local URL' - ], - 'Google Maps CDN (HTTP)' => [ - new \moodle_url('http://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), - 'Coding error detected, it must be fixed by a programmer: out_as_local_url called on a non-local URL' - ], - ]; - } - - /** - * Test URL as relative path from $CFG->wwwroot. - * - * @param moodle_url $url The URL pointing to a web resource. - * @param string $expected The expected local URL. - * @throws coding_exception If called on a non-local URL. - * @see \moodle_url::out_as_local_url() - * @covers \moodle_url::out_as_local_url - * @dataProvider out_as_local_url_provider - */ - public function test_out_as_local_url(\moodle_url $url, string $expected): void { - $this->assertEquals($expected, $url->out_as_local_url(false)); - } - - /** - * Data provider for returning local paths via \moodle_url::out_as_local_url(). - * - * @return array - * @throws moodle_exception On seriously malformed URLs (parse_url). - * @see \moodle_url::out_as_local_url() - * @see parse_url() - */ - public function out_as_local_url_provider() { - global $CFG; - $wwwroot = rtrim($CFG->wwwroot, '/'); - - return [ - 'Environment XML file' => [ - new \moodle_url('/admin/environment.xml'), - '/admin/environment.xml' - ], - 'H5P JS internal resource' => [ - new \moodle_url('/h5p/js/embed.js'), - '/h5p/js/embed.js' - ], - 'A Moodle JS resource using the full path including the proper JS Handler' => [ - new \moodle_url($wwwroot . '/lib/javascript.php/1/lib/editor/tiny/js/tinymce/tinymce.js'), - '/lib/javascript.php/1/lib/editor/tiny/js/tinymce/tinymce.js' - ], - ]; - } - - /** - * Test URL as relative path from $CFG->wwwroot. - * - * @param moodle_url $url The URL pointing to a web resource. - * @param bool $expected The expected result. - * @see \moodle_url::is_local_url() - * @covers \moodle_url::is_local_url - * @dataProvider is_local_url_provider - */ - public function test_is_local_url(\moodle_url $url, bool $expected): void { - $this->assertEquals($expected, $url->is_local_url(), "'{$url}' is not a local URL!"); - } - - /** - * Data provider for testing \moodle_url::is_local_url(). - * - * @return array - * @see \moodle_url::is_local_url() - */ - public function is_local_url_provider() { - global $CFG; - $wwwroot = rtrim($CFG->wwwroot, '/'); - - return [ - 'Google Maps CDN (HTTPS)' => [ - new \moodle_url('https://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), - false - ], - 'Google Maps CDN (HTTP)' => [ - new \moodle_url('http://maps.googleapis.com/maps/api/js', ['key' => 'googlemapkey3', 'sensor' => 'false']), - false - ], - 'wwwroot' => [ - new \moodle_url($wwwroot), - true - ], - 'wwwroot/' => [ - new \moodle_url($wwwroot . '/'), - true - ], - 'Environment XML file' => [ - new \moodle_url('/admin/environment.xml'), - true - ], - 'H5P JS internal resource' => [ - new \moodle_url('/h5p/js/embed.js'), - true - ], - ]; - } - /** * Data provider for strip_querystring tests. * From c8a538de29d7ea0c27a4844ec1b86e707873d5f4 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 11 Jun 2024 12:10:43 +0800 Subject: [PATCH 5/8] MDL-81960 core: Move progress_trace classes to legacy autoloader --- lib/classes/output/progress_trace.php | 41 +++ .../combined_progress_trace.php | 61 ++++ .../error_log_progress_trace.php | 46 +++ .../html_list_progress_trace.php | 66 ++++ .../progress_trace/html_progress_trace.php | 36 +++ .../progress_trace/null_progress_trace.php | 34 ++ .../progress_trace/progress_trace_buffer.php | 90 ++++++ .../progress_trace/text_progress_trace.php | 35 +++ lib/db/legacyclasses.php | 10 + lib/weblib.php | 292 ------------------ 10 files changed, 419 insertions(+), 292 deletions(-) create mode 100644 lib/classes/output/progress_trace.php create mode 100644 lib/classes/output/progress_trace/combined_progress_trace.php create mode 100644 lib/classes/output/progress_trace/error_log_progress_trace.php create mode 100644 lib/classes/output/progress_trace/html_list_progress_trace.php create mode 100644 lib/classes/output/progress_trace/html_progress_trace.php create mode 100644 lib/classes/output/progress_trace/null_progress_trace.php create mode 100644 lib/classes/output/progress_trace/progress_trace_buffer.php create mode 100644 lib/classes/output/progress_trace/text_progress_trace.php diff --git a/lib/classes/output/progress_trace.php b/lib/classes/output/progress_trace.php new file mode 100644 index 00000000000..8259ddb7d71 --- /dev/null +++ b/lib/classes/output/progress_trace.php @@ -0,0 +1,41 @@ +. + +/** + * Progress trace class. + * + * Use this class from long operations where you want to output occasional information about + * what is going on, but don't know if, or in what format, the output should be. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +abstract class progress_trace { + /** + * Output an progress message in whatever format. + * + * @param string $message the message to output. + * @param integer $depth indent depth for this message. + */ + abstract public function output($message, $depth = 0); + + /** + * Called when the processing is finished. + */ + public function finished() { + } +} diff --git a/lib/classes/output/progress_trace/combined_progress_trace.php b/lib/classes/output/progress_trace/combined_progress_trace.php new file mode 100644 index 00000000000..084114de521 --- /dev/null +++ b/lib/classes/output/progress_trace/combined_progress_trace.php @@ -0,0 +1,61 @@ +. + +/** + * Special type of trace that can be used for redirecting to multiple other traces. + * + * @copyright Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class combined_progress_trace extends progress_trace { + + /** + * An array of traces. + * @var array + */ + protected $traces; + + /** + * Constructs a new instance. + * + * @param array $traces multiple traces + */ + public function __construct(array $traces) { + $this->traces = $traces; + } + + /** + * Output an progress message in whatever format. + * + * @param string $message the message to output. + * @param integer $depth indent depth for this message. + */ + public function output($message, $depth = 0) { + foreach ($this->traces as $trace) { + $trace->output($message, $depth); + } + } + + /** + * Called when the processing is finished. + */ + public function finished() { + foreach ($this->traces as $trace) { + $trace->finished(); + } + } +} diff --git a/lib/classes/output/progress_trace/error_log_progress_trace.php b/lib/classes/output/progress_trace/error_log_progress_trace.php new file mode 100644 index 00000000000..f27d5e791a1 --- /dev/null +++ b/lib/classes/output/progress_trace/error_log_progress_trace.php @@ -0,0 +1,46 @@ +. + +/** + * This subclass of progress_trace outputs to error log. + * + * @copyright Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class error_log_progress_trace extends progress_trace { + /** @var string log prefix */ + protected $prefix; + + /** + * Constructor. + * @param string $prefix optional log prefix + */ + public function __construct($prefix = '') { + $this->prefix = $prefix; + } + + /** + * Output the trace message. + * + * @param string $message + * @param int $depth + * @return void Output is sent to error log. + */ + public function output($message, $depth = 0) { + error_log($this->prefix . str_repeat(' ', $depth) . $message); + } +} diff --git a/lib/classes/output/progress_trace/html_list_progress_trace.php b/lib/classes/output/progress_trace/html_list_progress_trace.php new file mode 100644 index 00000000000..ef1629fb04b --- /dev/null +++ b/lib/classes/output/progress_trace/html_list_progress_trace.php @@ -0,0 +1,66 @@ +. + +/** + * HTML List Progress Tree + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class html_list_progress_trace extends progress_trace { + /** @var int */ + protected $currentdepth = -1; + + /** + * Echo out the list + * + * @param string $message The message to display + * @param int $depth + * @return void Output is echoed + */ + public function output($message, $depth = 0) { + $samedepth = true; + while ($this->currentdepth > $depth) { + echo "\n\n"; + $this->currentdepth -= 1; + if ($this->currentdepth == $depth) { + echo '
  • '; + } + $samedepth = false; + } + while ($this->currentdepth < $depth) { + echo "
      \n
    • "; + $this->currentdepth += 1; + $samedepth = false; + } + if ($samedepth) { + echo "
    • \n
    • "; + } + echo htmlspecialchars($message, ENT_COMPAT); + flush(); + } + + /** + * Called when the processing is finished. + */ + public function finished() { + while ($this->currentdepth >= 0) { + echo "
    • \n
    \n"; + $this->currentdepth -= 1; + } + } +} diff --git a/lib/classes/output/progress_trace/html_progress_trace.php b/lib/classes/output/progress_trace/html_progress_trace.php new file mode 100644 index 00000000000..93208f62c78 --- /dev/null +++ b/lib/classes/output/progress_trace/html_progress_trace.php @@ -0,0 +1,36 @@ +. + +/** + * This subclass of progress_trace outputs as HTML. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class html_progress_trace extends progress_trace { + /** + * Output the trace message. + * + * @param string $message + * @param int $depth + * @return void Output is echo'd + */ + public function output($message, $depth = 0) { + echo '

    ', str_repeat('  ', $depth), htmlspecialchars($message, ENT_COMPAT), "

    \n"; + flush(); + } +} diff --git a/lib/classes/output/progress_trace/null_progress_trace.php b/lib/classes/output/progress_trace/null_progress_trace.php new file mode 100644 index 00000000000..c0b1def2680 --- /dev/null +++ b/lib/classes/output/progress_trace/null_progress_trace.php @@ -0,0 +1,34 @@ +. + +/** + * This subclass of progress_trace does not ouput anything. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class null_progress_trace extends progress_trace { + /** + * Does Nothing + * + * @param string $message + * @param int $depth + * @return void Does Nothing + */ + public function output($message, $depth = 0) { + } +} diff --git a/lib/classes/output/progress_trace/progress_trace_buffer.php b/lib/classes/output/progress_trace/progress_trace_buffer.php new file mode 100644 index 00000000000..68788c46721 --- /dev/null +++ b/lib/classes/output/progress_trace/progress_trace_buffer.php @@ -0,0 +1,90 @@ +. + +/** + * Special type of trace that can be used for catching of output of other traces. + * + * @copyright Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class progress_trace_buffer extends progress_trace { + /** @var progress_trace */ + protected $trace; + /** @var bool do we pass output out */ + protected $passthrough; + /** @var string output buffer */ + protected $buffer; + + /** + * Constructor. + * + * @param progress_trace $trace + * @param bool $passthrough true means output and buffer, false means just buffer and no output + */ + public function __construct(progress_trace $trace, $passthrough = true) { + $this->trace = $trace; + $this->passthrough = $passthrough; + $this->buffer = ''; + } + + /** + * Output the trace message. + * + * @param string $message the message to output. + * @param int $depth indent depth for this message. + * @return void output stored in buffer + */ + public function output($message, $depth = 0) { + ob_start(); + $this->trace->output($message, $depth); + $this->buffer .= ob_get_contents(); + if ($this->passthrough) { + ob_end_flush(); + } else { + ob_end_clean(); + } + } + + /** + * Called when the processing is finished. + */ + public function finished() { + ob_start(); + $this->trace->finished(); + $this->buffer .= ob_get_contents(); + if ($this->passthrough) { + ob_end_flush(); + } else { + ob_end_clean(); + } + } + + /** + * Reset internal text buffer. + */ + public function reset_buffer() { + $this->buffer = ''; + } + + /** + * Return internal text buffer. + * @return string buffered plain text + */ + public function get_buffer() { + return $this->buffer; + } +} diff --git a/lib/classes/output/progress_trace/text_progress_trace.php b/lib/classes/output/progress_trace/text_progress_trace.php new file mode 100644 index 00000000000..21ee17f449b --- /dev/null +++ b/lib/classes/output/progress_trace/text_progress_trace.php @@ -0,0 +1,35 @@ +. + +/** + * This subclass of progress_trace outputs to plain text. + * + * @copyright 2009 Tim Hunt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package core + */ +class text_progress_trace extends progress_trace { + /** + * Output the trace message. + * + * @param string $message + * @param int $depth + * @return void Output is echo'd + */ + public function output($message, $depth = 0) { + mtrace(str_repeat(' ', $depth) . $message); + } +} diff --git a/lib/db/legacyclasses.php b/lib/db/legacyclasses.php index 0cc09322714..bc77cabdbd6 100644 --- a/lib/db/legacyclasses.php +++ b/lib/db/legacyclasses.php @@ -43,4 +43,14 @@ $legacyclasses = [ \require_login_session_timeout_exception::class => 'exception/require_login_session_timeout_exception.php', \required_capability_exception::class => 'exception/required_capability_exception.php', \webservice_parameter_exception::class => 'exception/webservice_parameter_exception.php', + + // The progress_trace classes. + \combined_progress_trace::class => 'output/progress_trace/combined_progress_trace.php', + \error_log_progress_trace::class => 'output/progress_trace/error_log_progress_trace.php', + \html_list_progress_trace::class => 'output/progress_trace/html_list_progress_trace.php', + \html_progress_trace::class => 'output/progress_trace/html_progress_trace.php', + \null_progress_trace::class => 'output/progress_trace/null_progress_trace.php', + \progress_trace::class => 'output/progress_trace.php', + \progress_trace_buffer::class => 'output/progress_trace/progress_trace_buffer.php', + \text_progress_trace::class => 'output/progress_trace/text_progress_trace.php', ]; diff --git a/lib/weblib.php b/lib/weblib.php index 7e9634c890c..e1a0bd345af 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -79,9 +79,6 @@ define('URL_MATCH_PARAMS', 1); */ define('URL_MATCH_EXACT', 2); -// TODO MDL-81933 Remove after Moodle 4.5 release. -require_once($CFG->libdir . '/classes/url.php'); - // Functions. /** @@ -2591,295 +2588,6 @@ function is_in_popup() { return ($inpopup); } -/** - * Progress trace class. - * - * Use this class from long operations where you want to output occasional information about - * what is going on, but don't know if, or in what format, the output should be. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -abstract class progress_trace { - /** - * Output an progress message in whatever format. - * - * @param string $message the message to output. - * @param integer $depth indent depth for this message. - */ - abstract public function output($message, $depth = 0); - - /** - * Called when the processing is finished. - */ - public function finished() { - } -} - -/** - * This subclass of progress_trace does not ouput anything. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class null_progress_trace extends progress_trace { - /** - * Does Nothing - * - * @param string $message - * @param int $depth - * @return void Does Nothing - */ - public function output($message, $depth = 0) { - } -} - -/** - * This subclass of progress_trace outputs to plain text. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class text_progress_trace extends progress_trace { - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is echo'd - */ - public function output($message, $depth = 0) { - mtrace(str_repeat(' ', $depth) . $message); - } -} - -/** - * This subclass of progress_trace outputs as HTML. - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class html_progress_trace extends progress_trace { - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is echo'd - */ - public function output($message, $depth = 0) { - echo '

    ', str_repeat('  ', $depth), htmlspecialchars($message, ENT_COMPAT), "

    \n"; - flush(); - } -} - -/** - * HTML List Progress Tree - * - * @copyright 2009 Tim Hunt - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class html_list_progress_trace extends progress_trace { - /** @var int */ - protected $currentdepth = -1; - - /** - * Echo out the list - * - * @param string $message The message to display - * @param int $depth - * @return void Output is echoed - */ - public function output($message, $depth = 0) { - $samedepth = true; - while ($this->currentdepth > $depth) { - echo "
  • \n\n"; - $this->currentdepth -= 1; - if ($this->currentdepth == $depth) { - echo '
  • '; - } - $samedepth = false; - } - while ($this->currentdepth < $depth) { - echo "
      \n
    • "; - $this->currentdepth += 1; - $samedepth = false; - } - if ($samedepth) { - echo "
    • \n
    • "; - } - echo htmlspecialchars($message, ENT_COMPAT); - flush(); - } - - /** - * Called when the processing is finished. - */ - public function finished() { - while ($this->currentdepth >= 0) { - echo "
    • \n
    \n"; - $this->currentdepth -= 1; - } - } -} - -/** - * This subclass of progress_trace outputs to error log. - * - * @copyright Petr Skoda {@link http://skodak.org} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class error_log_progress_trace extends progress_trace { - /** @var string log prefix */ - protected $prefix; - - /** - * Constructor. - * @param string $prefix optional log prefix - */ - public function __construct($prefix = '') { - $this->prefix = $prefix; - } - - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is sent to error log. - */ - public function output($message, $depth = 0) { - error_log($this->prefix . str_repeat(' ', $depth) . $message); - } -} - -/** - * Special type of trace that can be used for catching of output of other traces. - * - * @copyright Petr Skoda {@link http://skodak.org} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class progress_trace_buffer extends progress_trace { - /** @var progress_trace */ - protected $trace; - /** @var bool do we pass output out */ - protected $passthrough; - /** @var string output buffer */ - protected $buffer; - - /** - * Constructor. - * - * @param progress_trace $trace - * @param bool $passthrough true means output and buffer, false means just buffer and no output - */ - public function __construct(progress_trace $trace, $passthrough = true) { - $this->trace = $trace; - $this->passthrough = $passthrough; - $this->buffer = ''; - } - - /** - * Output the trace message. - * - * @param string $message the message to output. - * @param int $depth indent depth for this message. - * @return void output stored in buffer - */ - public function output($message, $depth = 0) { - ob_start(); - $this->trace->output($message, $depth); - $this->buffer .= ob_get_contents(); - if ($this->passthrough) { - ob_end_flush(); - } else { - ob_end_clean(); - } - } - - /** - * Called when the processing is finished. - */ - public function finished() { - ob_start(); - $this->trace->finished(); - $this->buffer .= ob_get_contents(); - if ($this->passthrough) { - ob_end_flush(); - } else { - ob_end_clean(); - } - } - - /** - * Reset internal text buffer. - */ - public function reset_buffer() { - $this->buffer = ''; - } - - /** - * Return internal text buffer. - * @return string buffered plain text - */ - public function get_buffer() { - return $this->buffer; - } -} - -/** - * Special type of trace that can be used for redirecting to multiple other traces. - * - * @copyright Petr Skoda {@link http://skodak.org} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @package core - */ -class combined_progress_trace extends progress_trace { - - /** - * An array of traces. - * @var array - */ - protected $traces; - - /** - * Constructs a new instance. - * - * @param array $traces multiple traces - */ - public function __construct(array $traces) { - $this->traces = $traces; - } - - /** - * Output an progress message in whatever format. - * - * @param string $message the message to output. - * @param integer $depth indent depth for this message. - */ - public function output($message, $depth = 0) { - foreach ($this->traces as $trace) { - $trace->output($message, $depth); - } - } - - /** - * Called when the processing is finished. - */ - public function finished() { - foreach ($this->traces as $trace) { - $trace->finished(); - } - } -} - /** * Returns a localized sentence in the current language summarizing the current password policy * From a9869ff0a72281918a64f27e2666151981545d7a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 11 Jun 2024 12:21:36 +0800 Subject: [PATCH 6/8] MDL-81960 core: Coding style update for progress_trace --- lib/classes/output/progress_trace.php | 7 ++- .../combined_progress_trace.php | 31 +++++-------- .../error_log_progress_trace.php | 23 +++++----- .../html_list_progress_trace.php | 23 ++++------ .../progress_trace/html_progress_trace.php | 13 +++--- .../progress_trace/null_progress_trace.php | 13 +++--- .../progress_trace/progress_trace_buffer.php | 43 ++++++++----------- .../progress_trace/text_progress_trace.php | 13 +++--- 8 files changed, 69 insertions(+), 97 deletions(-) diff --git a/lib/classes/output/progress_trace.php b/lib/classes/output/progress_trace.php index 8259ddb7d71..fdd5d5ecac1 100644 --- a/lib/classes/output/progress_trace.php +++ b/lib/classes/output/progress_trace.php @@ -29,9 +29,12 @@ abstract class progress_trace { * Output an progress message in whatever format. * * @param string $message the message to output. - * @param integer $depth indent depth for this message. + * @param int $depth indent depth for this message. */ - abstract public function output($message, $depth = 0); + abstract public function output( + string $message, + int $depth = 0, + ); /** * Called when the processing is finished. diff --git a/lib/classes/output/progress_trace/combined_progress_trace.php b/lib/classes/output/progress_trace/combined_progress_trace.php index 084114de521..88b34839f48 100644 --- a/lib/classes/output/progress_trace/combined_progress_trace.php +++ b/lib/classes/output/progress_trace/combined_progress_trace.php @@ -22,38 +22,29 @@ * @package core */ class combined_progress_trace extends progress_trace { - - /** - * An array of traces. - * @var array - */ - protected $traces; - /** * Constructs a new instance. * * @param array $traces multiple traces */ - public function __construct(array $traces) { - $this->traces = $traces; + public function __construct( + /** @var array The list of traces */ + protected array $traces, + ) { } - /** - * Output an progress message in whatever format. - * - * @param string $message the message to output. - * @param integer $depth indent depth for this message. - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { foreach ($this->traces as $trace) { $trace->output($message, $depth); } } - /** - * Called when the processing is finished. - */ - public function finished() { + #[\Override] + public function finished(): void { foreach ($this->traces as $trace) { $trace->finished(); } diff --git a/lib/classes/output/progress_trace/error_log_progress_trace.php b/lib/classes/output/progress_trace/error_log_progress_trace.php index f27d5e791a1..2715864bc4d 100644 --- a/lib/classes/output/progress_trace/error_log_progress_trace.php +++ b/lib/classes/output/progress_trace/error_log_progress_trace.php @@ -22,25 +22,22 @@ * @package core */ class error_log_progress_trace extends progress_trace { - /** @var string log prefix */ - protected $prefix; - /** * Constructor. * @param string $prefix optional log prefix */ - public function __construct($prefix = '') { - $this->prefix = $prefix; + public function __construct( + /** @var string The prefix to use in the error_log messages */ + protected string $prefix = '', + ) { } - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is sent to error log. - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { + // phpcs:ignore moodle.PHP.ForbiddenFunctions.FoundWithAlternative error_log($this->prefix . str_repeat(' ', $depth) . $message); } } diff --git a/lib/classes/output/progress_trace/html_list_progress_trace.php b/lib/classes/output/progress_trace/html_list_progress_trace.php index ef1629fb04b..b110ea22ae0 100644 --- a/lib/classes/output/progress_trace/html_list_progress_trace.php +++ b/lib/classes/output/progress_trace/html_list_progress_trace.php @@ -22,17 +22,14 @@ * @package core */ class html_list_progress_trace extends progress_trace { - /** @var int */ - protected $currentdepth = -1; + /** @var int The current depth of the trace*/ + protected int $currentdepth = -1; - /** - * Echo out the list - * - * @param string $message The message to display - * @param int $depth - * @return void Output is echoed - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { $samedepth = true; while ($this->currentdepth > $depth) { echo "
  • \n\n"; @@ -54,10 +51,8 @@ class html_list_progress_trace extends progress_trace { flush(); } - /** - * Called when the processing is finished. - */ - public function finished() { + #[\Override] + public function finished(): void { while ($this->currentdepth >= 0) { echo "\n\n"; $this->currentdepth -= 1; diff --git a/lib/classes/output/progress_trace/html_progress_trace.php b/lib/classes/output/progress_trace/html_progress_trace.php index 93208f62c78..d2772e0a09c 100644 --- a/lib/classes/output/progress_trace/html_progress_trace.php +++ b/lib/classes/output/progress_trace/html_progress_trace.php @@ -22,14 +22,11 @@ * @package core */ class html_progress_trace extends progress_trace { - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is echo'd - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { echo '

    ', str_repeat('  ', $depth), htmlspecialchars($message, ENT_COMPAT), "

    \n"; flush(); } diff --git a/lib/classes/output/progress_trace/null_progress_trace.php b/lib/classes/output/progress_trace/null_progress_trace.php index c0b1def2680..4579b5837cb 100644 --- a/lib/classes/output/progress_trace/null_progress_trace.php +++ b/lib/classes/output/progress_trace/null_progress_trace.php @@ -22,13 +22,10 @@ * @package core */ class null_progress_trace extends progress_trace { - /** - * Does Nothing - * - * @param string $message - * @param int $depth - * @return void Does Nothing - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { } } diff --git a/lib/classes/output/progress_trace/progress_trace_buffer.php b/lib/classes/output/progress_trace/progress_trace_buffer.php index 68788c46721..4bf56c1c7e9 100644 --- a/lib/classes/output/progress_trace/progress_trace_buffer.php +++ b/lib/classes/output/progress_trace/progress_trace_buffer.php @@ -22,12 +22,8 @@ * @package core */ class progress_trace_buffer extends progress_trace { - /** @var progress_trace */ - protected $trace; - /** @var bool do we pass output out */ - protected $passthrough; /** @var string output buffer */ - protected $buffer; + protected string $buffer = ''; /** * Constructor. @@ -35,20 +31,20 @@ class progress_trace_buffer extends progress_trace { * @param progress_trace $trace * @param bool $passthrough true means output and buffer, false means just buffer and no output */ - public function __construct(progress_trace $trace, $passthrough = true) { - $this->trace = $trace; - $this->passthrough = $passthrough; + public function __construct( + /** @var progress_trace The progress_trace to pass content to */ + protected progress_trace $trace, + /** @var bool Whether we pass output out */ + protected bool $passthrough = true, + ) { $this->buffer = ''; } - /** - * Output the trace message. - * - * @param string $message the message to output. - * @param int $depth indent depth for this message. - * @return void output stored in buffer - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { ob_start(); $this->trace->output($message, $depth); $this->buffer .= ob_get_contents(); @@ -59,10 +55,8 @@ class progress_trace_buffer extends progress_trace { } } - /** - * Called when the processing is finished. - */ - public function finished() { + #[\Override] + public function finished(): void { ob_start(); $this->trace->finished(); $this->buffer .= ob_get_contents(); @@ -74,17 +68,18 @@ class progress_trace_buffer extends progress_trace { } /** - * Reset internal text buffer. + * Reset the internal text buffer. */ - public function reset_buffer() { + public function reset_buffer(): void { $this->buffer = ''; } /** - * Return internal text buffer. + * Return the internal text buffer. + * * @return string buffered plain text */ - public function get_buffer() { + public function get_buffer(): string { return $this->buffer; } } diff --git a/lib/classes/output/progress_trace/text_progress_trace.php b/lib/classes/output/progress_trace/text_progress_trace.php index 21ee17f449b..b9ed47c3962 100644 --- a/lib/classes/output/progress_trace/text_progress_trace.php +++ b/lib/classes/output/progress_trace/text_progress_trace.php @@ -22,14 +22,11 @@ * @package core */ class text_progress_trace extends progress_trace { - /** - * Output the trace message. - * - * @param string $message - * @param int $depth - * @return void Output is echo'd - */ - public function output($message, $depth = 0) { + #[\Override] + public function output( + string $message, + int $depth = 0, + ): void { mtrace(str_repeat(' ', $depth) . $message); } } From b6d08ad1d7fe2e440b81bdf672e2589d25e9933b Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 11 Jun 2024 12:44:38 +0800 Subject: [PATCH 7/8] MDL-81960 core: Update namespace of progress_trace classes --- .upgradenotes/MDL-81960-2024061004043644.yml | 26 +++++++++++++++++-- lib/classes/output/progress_trace.php | 7 +++++ .../combined_progress_trace.php | 11 +++++++- .../error_log_progress_trace.php | 9 +++++++ .../html_list_progress_trace.php | 9 +++++++ .../progress_trace/html_progress_trace.php | 9 +++++++ .../progress_trace/null_progress_trace.php | 9 +++++++ .../progress_trace/progress_trace_buffer.php | 11 +++++++- .../progress_trace/text_progress_trace.php | 9 +++++++ 9 files changed, 96 insertions(+), 4 deletions(-) diff --git a/.upgradenotes/MDL-81960-2024061004043644.yml b/.upgradenotes/MDL-81960-2024061004043644.yml index 964e907be25..c371a6aff54 100644 --- a/.upgradenotes/MDL-81960-2024061004043644.yml +++ b/.upgradenotes/MDL-81960-2024061004043644.yml @@ -2,6 +2,28 @@ issueNumber: MDL-81960 notes: core: - message: >- - The `\moodle_url` class has been renamed to `\core\url` and now supports - autoloading. Existing uses are currently unaffected. + The following classes have been renamed and now support autoloading. + Existing classes are currently unaffected. + + | Old class name | New class name | + + | --- | --- | + + | `\moodle_url` | `\core\url` | + + | `\progress_trace` | `\core\output\progress_trace` | + + | `\combined_progress_trace` | `\core\output\progress_trace\combined_progress_trace` | + + | `\error_log_progress_trace` | `\core\output\progress_trace\error_log_progress_trace` | + + | `\html_list_progress_trace` | `\core\output\progress_trace\html_list_progress_trace` | + + | `\html_progress_trace` | `\core\output\progress_trace\html_progress_trace` | + + | `\null_progress_trace` | `\core\output\progress_trace\null_progress_trace` | + + | `\progress_trace_buffer` | `\core\output\progress_trace\progress_trace_buffer` | + + | `\text_progress_trace` | `\core\output\progress_trace\text_progress_trace` | type: improved diff --git a/lib/classes/output/progress_trace.php b/lib/classes/output/progress_trace.php index fdd5d5ecac1..8a590b0aebc 100644 --- a/lib/classes/output/progress_trace.php +++ b/lib/classes/output/progress_trace.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output; + /** * Progress trace class. * @@ -42,3 +44,8 @@ abstract class progress_trace { public function finished() { } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(progress_trace::class, \progress_trace::class); diff --git a/lib/classes/output/progress_trace/combined_progress_trace.php b/lib/classes/output/progress_trace/combined_progress_trace.php index 88b34839f48..7027440a473 100644 --- a/lib/classes/output/progress_trace/combined_progress_trace.php +++ b/lib/classes/output/progress_trace/combined_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * Special type of trace that can be used for redirecting to multiple other traces. * @@ -21,7 +25,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @package core */ -class combined_progress_trace extends progress_trace { +class combined_progress_trace extends \progress_trace { /** * Constructs a new instance. * @@ -50,3 +54,8 @@ class combined_progress_trace extends progress_trace { } } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(combined_progress_trace::class, \combined_progress_trace::class); diff --git a/lib/classes/output/progress_trace/error_log_progress_trace.php b/lib/classes/output/progress_trace/error_log_progress_trace.php index 2715864bc4d..c3f17d87da7 100644 --- a/lib/classes/output/progress_trace/error_log_progress_trace.php +++ b/lib/classes/output/progress_trace/error_log_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * This subclass of progress_trace outputs to error log. * @@ -41,3 +45,8 @@ class error_log_progress_trace extends progress_trace { error_log($this->prefix . str_repeat(' ', $depth) . $message); } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(error_log_progress_trace::class, \error_log_progress_trace::class); diff --git a/lib/classes/output/progress_trace/html_list_progress_trace.php b/lib/classes/output/progress_trace/html_list_progress_trace.php index b110ea22ae0..4c21c79a869 100644 --- a/lib/classes/output/progress_trace/html_list_progress_trace.php +++ b/lib/classes/output/progress_trace/html_list_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * HTML List Progress Tree * @@ -59,3 +63,8 @@ class html_list_progress_trace extends progress_trace { } } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(html_list_progress_trace::class, \html_list_progress_trace::class); diff --git a/lib/classes/output/progress_trace/html_progress_trace.php b/lib/classes/output/progress_trace/html_progress_trace.php index d2772e0a09c..eda3587068e 100644 --- a/lib/classes/output/progress_trace/html_progress_trace.php +++ b/lib/classes/output/progress_trace/html_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * This subclass of progress_trace outputs as HTML. * @@ -31,3 +35,8 @@ class html_progress_trace extends progress_trace { flush(); } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(html_progress_trace::class, \html_progress_trace::class); diff --git a/lib/classes/output/progress_trace/null_progress_trace.php b/lib/classes/output/progress_trace/null_progress_trace.php index 4579b5837cb..6f9af4e60ef 100644 --- a/lib/classes/output/progress_trace/null_progress_trace.php +++ b/lib/classes/output/progress_trace/null_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * This subclass of progress_trace does not ouput anything. * @@ -29,3 +33,8 @@ class null_progress_trace extends progress_trace { ): void { } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(null_progress_trace::class, \null_progress_trace::class); diff --git a/lib/classes/output/progress_trace/progress_trace_buffer.php b/lib/classes/output/progress_trace/progress_trace_buffer.php index 4bf56c1c7e9..dd9be00eb3a 100644 --- a/lib/classes/output/progress_trace/progress_trace_buffer.php +++ b/lib/classes/output/progress_trace/progress_trace_buffer.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * Special type of trace that can be used for catching of output of other traces. * @@ -21,7 +25,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @package core */ -class progress_trace_buffer extends progress_trace { +class progress_trace_buffer extends \progress_trace { /** @var string output buffer */ protected string $buffer = ''; @@ -83,3 +87,8 @@ class progress_trace_buffer extends progress_trace { return $this->buffer; } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(progress_trace_buffer::class, \progress_trace_buffer::class); diff --git a/lib/classes/output/progress_trace/text_progress_trace.php b/lib/classes/output/progress_trace/text_progress_trace.php index b9ed47c3962..94652ab489a 100644 --- a/lib/classes/output/progress_trace/text_progress_trace.php +++ b/lib/classes/output/progress_trace/text_progress_trace.php @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +namespace core\output\progress_trace; + +use core\output\progress_trace; + /** * This subclass of progress_trace outputs to plain text. * @@ -30,3 +34,8 @@ class text_progress_trace extends progress_trace { mtrace(str_repeat(' ', $depth) . $message); } } + +// Alias this class to the old name. +// This file will be autoloaded by the legacyclasses autoload system. +// In future all uses of this class will be corrected and the legacy references will be removed. +class_alias(text_progress_trace::class, \text_progress_trace::class); From e3f795fc72eaa4d4a66328673645646fb34d391a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 11 Jun 2024 12:44:49 +0800 Subject: [PATCH 8/8] MDL-81960 core: Move progress_trace tests out of weblib --- .../combined_progress_trace.php | 4 +- .../progress_trace/progress_trace_buffer.php | 2 +- .../html_list_progress_trace_test.php | 40 +++++++ .../html_progress_trace_test.php | 40 +++++++ .../null_progress_trace_test.php | 42 +++++++ .../progress_trace_buffer_test.php | 55 +++++++++ .../test_combined_progress_trace_test.php | 45 ++++++++ .../text_progress_trace_test.php | 42 +++++++ lib/tests/weblib_test.php | 106 ------------------ 9 files changed, 267 insertions(+), 109 deletions(-) create mode 100644 lib/tests/output/progress_trace/html_list_progress_trace_test.php create mode 100644 lib/tests/output/progress_trace/html_progress_trace_test.php create mode 100644 lib/tests/output/progress_trace/null_progress_trace_test.php create mode 100644 lib/tests/output/progress_trace/progress_trace_buffer_test.php create mode 100644 lib/tests/output/progress_trace/test_combined_progress_trace_test.php create mode 100644 lib/tests/output/progress_trace/text_progress_trace_test.php diff --git a/lib/classes/output/progress_trace/combined_progress_trace.php b/lib/classes/output/progress_trace/combined_progress_trace.php index 7027440a473..9344e7a5afa 100644 --- a/lib/classes/output/progress_trace/combined_progress_trace.php +++ b/lib/classes/output/progress_trace/combined_progress_trace.php @@ -25,14 +25,14 @@ use core\output\progress_trace; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @package core */ -class combined_progress_trace extends \progress_trace { +class combined_progress_trace extends progress_trace { /** * Constructs a new instance. * * @param array $traces multiple traces */ public function __construct( - /** @var array The list of traces */ + /** @var progress_trace[] The list of traces */ protected array $traces, ) { } diff --git a/lib/classes/output/progress_trace/progress_trace_buffer.php b/lib/classes/output/progress_trace/progress_trace_buffer.php index dd9be00eb3a..85085b9493d 100644 --- a/lib/classes/output/progress_trace/progress_trace_buffer.php +++ b/lib/classes/output/progress_trace/progress_trace_buffer.php @@ -25,7 +25,7 @@ use core\output\progress_trace; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @package core */ -class progress_trace_buffer extends \progress_trace { +class progress_trace_buffer extends progress_trace { /** @var string output buffer */ protected string $buffer = ''; diff --git a/lib/tests/output/progress_trace/html_list_progress_trace_test.php b/lib/tests/output/progress_trace/html_list_progress_trace_test.php new file mode 100644 index 00000000000..f593fe08403 --- /dev/null +++ b/lib/tests/output/progress_trace/html_list_progress_trace_test.php @@ -0,0 +1,40 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\html_list_progress_trace. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\html_list_progress_trace + */ +final class html_list_progress_trace_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $trace = new html_list_progress_trace(); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $this->expectOutputString("
      \n
    • do
        \n
      • re
          \n
        • mi
        • \n
        \n
      • \n
      \n
    • \n
    \n"); + } +} diff --git a/lib/tests/output/progress_trace/html_progress_trace_test.php b/lib/tests/output/progress_trace/html_progress_trace_test.php new file mode 100644 index 00000000000..ae0bcb25f9c --- /dev/null +++ b/lib/tests/output/progress_trace/html_progress_trace_test.php @@ -0,0 +1,40 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\html_progress_trace. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\html_progress_trace + */ +final class html_progress_trace_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $trace = new html_progress_trace(); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $this->expectOutputString("

    do

    \n

      re

    \n

        mi

    \n"); + } +} diff --git a/lib/tests/output/progress_trace/null_progress_trace_test.php b/lib/tests/output/progress_trace/null_progress_trace_test.php new file mode 100644 index 00000000000..6d34a3214aa --- /dev/null +++ b/lib/tests/output/progress_trace/null_progress_trace_test.php @@ -0,0 +1,42 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\null_progress_trace. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\null_progress_trace + */ +final class null_progress_trace_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $trace = new null_progress_trace(); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $output = ob_get_contents(); + $this->assertSame('', $output); + $this->expectOutputString(''); + } +} diff --git a/lib/tests/output/progress_trace/progress_trace_buffer_test.php b/lib/tests/output/progress_trace/progress_trace_buffer_test.php new file mode 100644 index 00000000000..53c63ca18b9 --- /dev/null +++ b/lib/tests/output/progress_trace/progress_trace_buffer_test.php @@ -0,0 +1,55 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\progress_trace_buffer. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\progress_trace_buffer + */ +final class progress_trace_buffer_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $trace = new progress_trace_buffer(new html_progress_trace()); + ob_start(); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $output = ob_get_contents(); + ob_end_clean(); + $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $output); + $this->assertSame($output, $trace->get_buffer()); + + $trace = new progress_trace_buffer(new html_progress_trace(), false); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace->get_buffer()); + $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace->get_buffer()); + $trace->reset_buffer(); + $this->assertSame('', $trace->get_buffer()); + $this->expectOutputString(''); + } +} diff --git a/lib/tests/output/progress_trace/test_combined_progress_trace_test.php b/lib/tests/output/progress_trace/test_combined_progress_trace_test.php new file mode 100644 index 00000000000..663bac655ff --- /dev/null +++ b/lib/tests/output/progress_trace/test_combined_progress_trace_test.php @@ -0,0 +1,45 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\test_combined_progress_trace. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\test_combined_progress_trace + */ +final class test_combined_progress_trace_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $trace1 = new progress_trace_buffer(new html_progress_trace(), false); + $trace2 = new progress_trace_buffer(new text_progress_trace(), false); + + $trace = new combined_progress_trace([$trace1, $trace2]); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace1->get_buffer()); + $this->assertSame("do\n re\n mi\n", $trace2->get_buffer()); + $this->expectOutputString(''); + } +} diff --git a/lib/tests/output/progress_trace/text_progress_trace_test.php b/lib/tests/output/progress_trace/text_progress_trace_test.php new file mode 100644 index 00000000000..fd8d3fe8ebd --- /dev/null +++ b/lib/tests/output/progress_trace/text_progress_trace_test.php @@ -0,0 +1,42 @@ +. + +namespace core\output\progress_trace; + +/** + * Tests for \core\progress_trace\text_progress_trace. + * + * @package core + * @category test + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \core\progress_trace\text_progress_trace + */ +final class text_progress_trace_test extends \advanced_testcase { + /** + * Tests for the trace. + */ + public function test_trace(): void { + $this->resetAfterTest(false); + + $trace = new text_progress_trace(); + $trace->output('do'); + $trace->output('re', 1); + $trace->output('mi', 2); + $trace->finished(); + $this->expectOutputString("do\n re\n mi\n"); + } +} diff --git a/lib/tests/weblib_test.php b/lib/tests/weblib_test.php index 4de970c12d0..1a5d4f84553 100644 --- a/lib/tests/weblib_test.php +++ b/lib/tests/weblib_test.php @@ -510,112 +510,6 @@ class weblib_test extends advanced_testcase { $this->assertSame($CFG->wwwroot.'/course/view.php?id=1', qualified_me()); } - /** - * @covers \null_progress_trace - */ - public function test_null_progress_trace(): void { - $this->resetAfterTest(false); - - $trace = new null_progress_trace(); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $output = ob_get_contents(); - $this->assertSame('', $output); - $this->expectOutputString(''); - } - - /** - * @covers \null_progress_trace - */ - public function test_text_progress_trace(): void { - $this->resetAfterTest(false); - - $trace = new text_progress_trace(); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $this->expectOutputString("do\n re\n mi\n"); - } - - /** - * @covers \html_progress_trace - */ - public function test_html_progress_trace(): void { - $this->resetAfterTest(false); - - $trace = new html_progress_trace(); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $this->expectOutputString("

    do

    \n

      re

    \n

        mi

    \n"); - } - - /** - * @covers \html_list_progress_trace - */ - public function test_html_list_progress_trace(): void { - $this->resetAfterTest(false); - - $trace = new html_list_progress_trace(); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $this->expectOutputString("
      \n
    • do
        \n
      • re
          \n
        • mi
        • \n
        \n
      • \n
      \n
    • \n
    \n"); - } - - /** - * @covers \progress_trace_buffer - */ - public function test_progress_trace_buffer(): void { - $this->resetAfterTest(false); - - $trace = new progress_trace_buffer(new html_progress_trace()); - ob_start(); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $output = ob_get_contents(); - ob_end_clean(); - $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $output); - $this->assertSame($output, $trace->get_buffer()); - - $trace = new progress_trace_buffer(new html_progress_trace(), false); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace->get_buffer()); - $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace->get_buffer()); - $trace->reset_buffer(); - $this->assertSame('', $trace->get_buffer()); - $this->expectOutputString(''); - } - - /** - * @covers \combined_progress_trace - */ - public function test_combined_progress_trace(): void { - $this->resetAfterTest(false); - - $trace1 = new progress_trace_buffer(new html_progress_trace(), false); - $trace2 = new progress_trace_buffer(new text_progress_trace(), false); - - $trace = new combined_progress_trace(array($trace1, $trace2)); - $trace->output('do'); - $trace->output('re', 1); - $trace->output('mi', 2); - $trace->finished(); - $this->assertSame("

    do

    \n

      re

    \n

        mi

    \n", $trace1->get_buffer()); - $this->assertSame("do\n re\n mi\n", $trace2->get_buffer()); - $this->expectOutputString(''); - } - /** * @covers ::set_debugging */