Skip to content

Commit

Permalink
MDL-40299 textlib conversion to core_text and core_collator
Browse files Browse the repository at this point in the history
  • Loading branch information
skodak committed Jun 27, 2013
1 parent f0d37f4 commit d534708
Show file tree
Hide file tree
Showing 13 changed files with 1,685 additions and 1,584 deletions.
2 changes: 1 addition & 1 deletion admin/cli/install.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@
ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));

require_once($CFG->libdir.'/classes/component.php');
require_once($CFG->libdir.'/classes/text.php');
require_once($CFG->libdir.'/installlib.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->libdir.'/setuplib.php');
require_once($CFG->libdir.'/textlib.class.php');
require_once($CFG->libdir.'/weblib.php');
require_once($CFG->libdir.'/dmllib.php');
require_once($CFG->libdir.'/moodlelib.php');
Expand Down
2 changes: 1 addition & 1 deletion install.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
}

// Continue with lib loading
require_once($CFG->libdir.'/textlib.class.php');
require_once($CFG->libdir.'/classes/text.php');
require_once($CFG->libdir.'/weblib.php');
require_once($CFG->libdir.'/outputlib.php');
require_once($CFG->libdir.'/dmllib.php');
Expand Down
307 changes: 307 additions & 0 deletions lib/classes/collator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Defines string apis
*
* @package core
* @copyright 2011 Sam Hemelryk
* 2012 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

/**
* A collator class with static methods that can be used for sorting.
*
* @package core
* @copyright 2011 Sam Hemelryk
* 2012 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_collator {
/** @const compare items using general PHP comparison, equivalent to Collator::SORT_REGULAR, this may bot be locale aware! */
const SORT_REGULAR = 0;

/** @const compare items as strings, equivalent to Collator::SORT_STRING */
const SORT_STRING = 1;

/** @const compare items as numbers, equivalent to Collator::SORT_NUMERIC */
const SORT_NUMERIC = 2;

/** @const compare items like natsort(), equivalent to SORT_NATURAL */
const SORT_NATURAL = 6;

/** @const do not ignore case when sorting, use bitwise "|" with SORT_NATURAL or SORT_STRING, equivalent to Collator::UPPER_FIRST */
const CASE_SENSITIVE = 64;

/** @var Collator|false|null **/
protected static $collator = null;

/** @var string|null The locale that was used in instantiating the current collator **/
protected static $locale = null;

/**
* Prevent class instances, all methods are static.
*/
private function __construct() {
}

/**
* Ensures that a collator is available and created
*
* @return bool Returns true if collation is available and ready
*/
protected static function ensure_collator_available() {
$locale = get_string('locale', 'langconfig');
if (is_null(self::$collator) || $locale != self::$locale) {
self::$collator = false;
self::$locale = $locale;
if (class_exists('Collator', false)) {
$collator = new Collator($locale);
if (!empty($collator) && $collator instanceof Collator) {
// Check for non fatal error messages. This has to be done immediately
// after instantiation as any further calls to collation will cause
// it to reset to 0 again (or another error code if one occurred)
$errorcode = $collator->getErrorCode();
$errormessage = $collator->getErrorMessage();
// Check for an error code, 0 means no error occurred
if ($errorcode !== 0) {
// Get the actual locale being used, e.g. en, he, zh
$localeinuse = $collator->getLocale(Locale::ACTUAL_LOCALE);
// Check for the common fallback warning error codes. If this occurred
// there is normally little to worry about:
// - U_USING_DEFAULT_WARNING (127) - default fallback locale used (pt => UCA)
// - U_USING_FALLBACK_WARNING (128) - fallback locale used (de_CH => de)
// (UCA: Unicode Collation Algorithm http://unicode.org/reports/tr10/)
if ($errorcode === -127 || $errorcode === -128) {
// Check if the locale in use is UCA default one ('root') or
// if it is anything like the locale we asked for
if ($localeinuse !== 'root' && strpos($locale, $localeinuse) !== 0) {
// The locale we asked for is completely different to the locale
// we have received, let the user know via debugging
debugging('Invalid locale: "' . $locale . '", with warning (not fatal) "' . $errormessage .
'", falling back to "' . $collator->getLocale(Locale::VALID_LOCALE) . '"');
} else {
// Nothing to do here, this is expected!
// The Moodle locale setting isn't what the collator expected but
// it is smart enough to match the first characters of our locale
// to find the correct locale or to use UCA collation
}
} else {
// We've received some other sort of non fatal warning - let the
// user know about it via debugging.
debugging('Problem with locale: "' . $locale . '", with message "' . $errormessage .
'", falling back to "' . $collator->getLocale(Locale::VALID_LOCALE) . '"');
}
}
// Store the collator object now that we can be sure it is in a workable condition
self::$collator = $collator;
} else {
// Fatal error while trying to instantiate the collator... something went wrong
debugging('Error instantiating collator for locale: "' . $locale . '", with error [' .
intl_get_error_code() . '] ' . intl_get_error_message($collator));
}
}
}
return (self::$collator instanceof Collator);
}

/**
* Restore array contents keeping new keys.
* @static
* @param array $arr
* @param array $original
* @return void modifies $arr
*/
protected static function restore_array(array &$arr, array &$original) {
foreach ($arr as $key => $ignored) {
$arr[$key] = $original[$key];
}
}

/**
* Normalise numbers in strings for natural sorting comparisons.
* @static
* @param string $string
* @return string string with normalised numbers
*/
protected static function naturalise($string) {
return preg_replace_callback('/[0-9]+/', array('collatorlib', 'callback_naturalise'), $string);
}

/**
* @internal
* @static
* @param array $matches
* @return string
*/
public static function callback_naturalise($matches) {
return str_pad($matches[0], 20, '0', STR_PAD_LEFT);
}

/**
* Locale aware sorting, the key associations are kept, values are sorted alphabetically.
*
* @param array $arr array to be sorted (reference)
* @param int $sortflag One of collatorlib::SORT_NUMERIC, collatorlib::SORT_STRING, collatorlib::SORT_NATURAL, collatorlib::SORT_REGULAR
* optionally "|" collatorlib::CASE_SENSITIVE
* @return bool True on success
*/
public static function asort(array &$arr, $sortflag = collatorlib::SORT_STRING) {
if (empty($arr)) {
// nothing to do
return true;
}

$original = null;

$casesensitive = (bool)($sortflag & collatorlib::CASE_SENSITIVE);
$sortflag = ($sortflag & ~collatorlib::CASE_SENSITIVE);
if ($sortflag != collatorlib::SORT_NATURAL and $sortflag != collatorlib::SORT_STRING) {
$casesensitive = false;
}

if (self::ensure_collator_available()) {
if ($sortflag == collatorlib::SORT_NUMERIC) {
$flag = Collator::SORT_NUMERIC;

} else if ($sortflag == collatorlib::SORT_REGULAR) {
$flag = Collator::SORT_REGULAR;

} else {
$flag = Collator::SORT_STRING;
}

if ($sortflag == collatorlib::SORT_NATURAL) {
$original = $arr;
if ($sortflag == collatorlib::SORT_NATURAL) {
foreach ($arr as $key => $value) {
$arr[$key] = self::naturalise((string)$value);
}
}
}
if ($casesensitive) {
self::$collator->setAttribute(Collator::CASE_FIRST, Collator::UPPER_FIRST);
} else {
self::$collator->setAttribute(Collator::CASE_FIRST, Collator::OFF);
}
$result = self::$collator->asort($arr, $flag);
if ($original) {
self::restore_array($arr, $original);
}
return $result;
}

// try some fallback that works at least for English

if ($sortflag == collatorlib::SORT_NUMERIC) {
return asort($arr, SORT_NUMERIC);

} else if ($sortflag == collatorlib::SORT_REGULAR) {
return asort($arr, SORT_REGULAR);
}

if (!$casesensitive) {
$original = $arr;
foreach ($arr as $key => $value) {
$arr[$key] = textlib::strtolower($value);
}
}

if ($sortflag == collatorlib::SORT_NATURAL) {
$result = natsort($arr);

} else {
$result = asort($arr, SORT_LOCALE_STRING);
}

if ($original) {
self::restore_array($arr, $original);
}

return $result;
}

/**
* Locale aware sort of objects by a property in common to all objects
*
* @param array $objects An array of objects to sort (handled by reference)
* @param string $property The property to use for comparison
* @param int $sortflag One of collatorlib::SORT_NUMERIC, collatorlib::SORT_STRING, collatorlib::SORT_NATURAL, collatorlib::SORT_REGULAR
* optionally "|" collatorlib::CASE_SENSITIVE
* @return bool True on success
*/
public static function asort_objects_by_property(array &$objects, $property, $sortflag = collatorlib::SORT_STRING) {
$original = $objects;
foreach ($objects as $key => $object) {
$objects[$key] = $object->$property;
}
$result = self::asort($objects, $sortflag);
self::restore_array($objects, $original);
return $result;
}

/**
* Locale aware sort of objects by a method in common to all objects
*
* @param array $objects An array of objects to sort (handled by reference)
* @param string $method The method to call to generate a value for comparison
* @param int $sortflag One of collatorlib::SORT_NUMERIC, collatorlib::SORT_STRING, collatorlib::SORT_NATURAL, collatorlib::SORT_REGULAR
* optionally "|" collatorlib::CASE_SENSITIVE
* @return bool True on success
*/
public static function asort_objects_by_method(array &$objects, $method, $sortflag = collatorlib::SORT_STRING) {
$original = $objects;
foreach ($objects as $key => $object) {
$objects[$key] = $object->{$method}();
}
$result = self::asort($objects, $sortflag);
self::restore_array($objects, $original);
return $result;
}

/**
* Locale aware sorting, the key associations are kept, keys are sorted alphabetically.
*
* @param array $arr array to be sorted (reference)
* @param int $sortflag One of collatorlib::SORT_NUMERIC, collatorlib::SORT_STRING, collatorlib::SORT_NATURAL, collatorlib::SORT_REGULAR
* optionally "|" collatorlib::CASE_SENSITIVE
* @return bool True on success
*/
public static function ksort(array &$arr, $sortflag = collatorlib::SORT_STRING) {
$keys = array_keys($arr);
if (!self::asort($keys, $sortflag)) {
return false;
}
// This is a bit slow, but we need to keep the references
$original = $arr;
$arr = array(); // Surprisingly this does not break references outside
foreach ($keys as $key) {
$arr[$key] = $original[$key];
}

return true;
}
}

/**
* Legacy collatorlib.
* @deprecated since 2.6, use core_collator:: instead.
*/
class collatorlib extends core_collator {
}
6 changes: 3 additions & 3 deletions lib/classes/component.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,9 @@ protected static function fill_classmap_cache() {
}
}

// Note: Add a few extra legacy classes here if necessary.
//self::$classmap['textlib'] = "$CFG->dirroot/lib/textlib.class.php";
//self::$classmap['collatorlib'] = "$CFG->dirroot/lib/textlib.class.php";
// Note: Add extra deprecated legacy classes here as necessary.
self::$classmap['textlib'] = "$CFG->dirroot/lib/classes/text.php";
self::$classmap['collatorlib'] = "$CFG->dirroot/lib/classes/collator.php";
}

/**
Expand Down
Loading

0 comments on commit d534708

Please sign in to comment.