Skip to content

Commit

Permalink
get_string: Refactoring, performance improvements, bug fixes and unit…
Browse files Browse the repository at this point in the history
… tests

MDL-18669 get_string refactored to elimiate duplicate code and make it easier to understand.
MDL-17763 parent language not processed correctly when getting a plugin string.
MDL-16181 more intelligent caching to avoid repeated file_exists checks.
MDL-12434 move values to array keys to improve lookup times.

The main part of the refactoring is to create a singleton string_manager class to encapsulate the cached data and the processing, while breaking the code up into more smaller methods.

Other performance improvements include:
* Cache results of plugin name -> locations to search array.
* Cache parent lang lookup.
* Skip eval if the string does not contain $ \ or %.
* Remove the unnecessary sprintf from the eval.

There is a performance testing script in lib/simpletest/getstringperformancetester.php. For now this script has the old get_string implementation copied and pasted to the end, and renamed to old_get_string to allow for comparitive timings.

There are now some unit tests for get_string in lib/simpletest/teststringmanager.php. I think I have managed to cover most of the tricky cases.
  • Loading branch information
tjhunt committed Mar 30, 2009
1 parent edbc93a commit bb45fe6
Show file tree
Hide file tree
Showing 21 changed files with 6,034 additions and 461 deletions.
193 changes: 52 additions & 141 deletions help.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
require_once('config.php');

// Get URL parameters.
$file = optional_param('file', '', PARAM_PATH);
$text = optional_param('text', 'No text to display', PARAM_CLEAN);
$file = optional_param('file', '', PARAM_PATH);
$text = optional_param('text', 'No text to display', PARAM_CLEAN);
$module = optional_param('module', 'moodle', PARAM_ALPHAEXT);
$forcelang = optional_param('forcelang', '', PARAM_SAFEDIR);
$skiplocal = optional_param('skiplocal', 0, PARAM_INT); // shall _local help files be skipped?
Expand All @@ -30,89 +30,27 @@

if (!empty($file)) {
// The help to display is from a help file.
list($filepath, $foundlang) = string_manager::instance()->find_help_file($file, $module, $forcelang, $skiplocal);

// Get the list of parent languages.
if (empty($forcelang)) {
$langs = array(current_language(), get_string('parentlanguage'), 'en_utf8'); // Fallback
} else {
$langs = array($forcelang, 'en_utf8');
}

if (!$skiplocal) {
// _local language packs take precedence with both forced language and non-forced language settings
$xlangs = array();
foreach ($langs as $lang) {
if (!empty($lang)) {
$xlangs[] = $lang . '_local';
$xlangs[] = $lang;
}
}
$langs = $xlangs;
unset($xlangs);
}

// Define possible locations for help file similar to locations for language strings
// Note: Always retain module directory as before
$locations = array();
if ($module == 'moodle') {
$locations[$CFG->dataroot.'/lang/'] = $file;
$locations[$CFG->dirroot.'/lang/'] = $file;
} else {
$modfile = $module.'/'.$file;
$locations[$CFG->dataroot.'/lang/'] = $modfile;
$locations[$CFG->dirroot.'/lang/'] = $modfile;
if ($filepath) {
$helpfound = true;
@include($filepath); // The actual helpfile

$rules = places_to_search_for_lang_strings();
$exceptions = $rules['__exceptions'];
unset($rules['__exceptions']);

if (!in_array($module, $exceptions)) {
$dividerpos = strpos($module, '_');
if ($dividerpos === false) {
$type = '';
$plugin = $module;
} else {
$type = substr($module, 0, $dividerpos + 1);
$plugin = substr($module, $dividerpos + 1);
}
if (!empty($rules[$type])) {
foreach ($rules[$type] as $location) {
$locations[$CFG->dirroot . "/$location/$plugin/lang/"] = "$plugin/$file";
}
}
// Now, we process some special cases.
if ($module == 'moodle' and ($file == 'index.html' or $file == 'mods.html')) {
include_help_for_each_module($file, $forcelang, $skiplocal);
}
if ($module == 'question' && $file == 'types.html') {
include_help_for_each_qtype();
}
}

// Work through the possible languages, starting with the most specific.
while (!$helpfound && (list(,$lang) = each($langs)) && !empty($lang)) {

while (!$helpfound && (list($locationprefix,$locationsuffix) = each($locations))) {
$filepath = $locationprefix.$lang.'/help/'.$locationsuffix;

// Now, try to include the help text from this file, if we can.
if (file_exists_and_readable($filepath)) {
$helpfound = true;
@include($filepath); // The actual helpfile

// Now, we process some special cases.
$helpdir = $locationprefix.$lang.'/help';
if ($module == 'moodle' and ($file == 'index.html' or $file == 'mods.html')) {
include_help_for_each_module($file, $langs, $helpdir);
}
if ($module == 'question' and ($file == 'types.html')) {
include_help_for_each_qtype();
}

// The remaining horrible hardcoded special cases should be delegated to modules somehow.
if ($module == 'moodle' and ($file == 'resource/types.html')) { // RESOURCES
include_help_for_each_resource($file, $langs, $helpdir);
}
if ($module == 'moodle' and ($file == 'assignment/types.html')) { // ASSIGNMENTS
include_help_for_each_assignment_type();
}
}
// The remaining horrible hardcoded special cases should be delegated to modules somehow.
if ($module == 'moodle'&& $file == 'resource/types.html') { // RESOURCES
include_help_for_each_resource($forcelang, $skiplocal);
}
if ($module == 'moodle' && $file == 'assignment/types.html') { // ASSIGNMENTS
include_help_for_each_assignment_type($forcelang, $skiplocal);
}
reset($locations);
}
} else {
// The help to display was given as an argument to this function.
Expand All @@ -121,26 +59,26 @@
}

// Finish buffer
$output=ob_get_contents();
$output = ob_get_contents();
ob_end_clean();

// Determine title
$title=get_string('help'); // Default is just 'Help'
$matches=array();
$title = get_string('help'); // Default is just 'Help'
$matches = array();
// You can include a <title> tag to override the standard behaviour:
// 'Help - title contents'. Otherwise it looks for the text of the first
// heading: 'Help - heading text'. If there aren't even any headings
// you just get 'Help'
if(preg_match('~^(.*?)<title>(.*?)</title>(.*)$~s',$output,$matches)) {
if (preg_match('~^(.*?)<title>(.*?)</title>(.*)$~s', $output, $matches)) {
// Extract title
$title=$title.' - '.$matches[2];
$title = $title.' - '.$matches[2];
// Strip title from output
$output=$matches[1].$matches[3];
$output = $matches[1].$matches[3];
} else if(preg_match('~<h[0-9]+(\s[^>]*)?>(.*?)</h[0-9]+>~s',$output,$matches)) {
// Use first heading as title (obviously leave it in output too). Strip
// any tags from inside
$matches[2]=preg_replace('~<[^>]*>~s','',$matches[2]);
$title=$title.' - '.$matches[2];
$matches[2] = preg_replace('~<[^>]*>~s','',$matches[2]);
$title = $title.' - '.$matches[2];
}

// use ##emoticons_html## to replace the emoticons documentation
Expand All @@ -161,27 +99,21 @@

// End of page.
close_window_button();
echo '<p class="helpindex"><a href="help.php?file=index.html">'. get_string('helpindex') .'</a>';
echo '<p class="helpindex"><a href="help.php?file=index.html">'. get_string('helpindex') .'</a></p>';

// Offer a link to the alternative help file language
if (($helpfound) and (((current_language() != 'en_utf8') and $lang != 'en_utf8') or ($forcelang === 'en_utf8'))) {
$linklang = "{$CFG->wwwroot}/help.php?";
$linklang .= !empty($module) ? "module=$module&amp;" : '';
$linklang .= !empty($file) ? "file=$file&amp;" : '';
$linklang .= !empty($skiplocal) ? "skiplocal=$skiplocal&amp;" : '';

if (empty($forcelang) or $forcelang === current_language()) {
$nextlang = 'en_utf8';
$nextlangname = 'English';
$currentlang = current_language();
if ($helpfound && ($foundlang != 'en_utf8' || ($forcelang == 'en_utf8' && current_language() != 'en_utf8'))) {
$url = new moodle_url();
if ($foundlang != 'en_utf8') {
$url->param('forcelang', 'en_utf8');
$nextlangname = get_string('english');
} else {
$nextlang = current_language();
$url->param('forcelang', $currentlang);
$nextlangname = get_string('thislanguage');
}

$linklang .= "forcelang=$nextlang";
echo "<br /><a href=\"$linklang\">" . get_string('showthishelpinlanguage', 'moodle', $nextlangname) . '</a>';
echo '<p><a href="' . $url->out() . '">' . get_string('showthishelpinlanguage', 'moodle', $nextlangname) . '</a></p>';
}
echo '</p>';

$CFG->docroot = ''; // We don't want a doc link here
print_footer('none');
Expand All @@ -192,13 +124,14 @@ function file_exists_and_readable($filepath) {

// Some functions for handling special cases ========================================

function include_help_for_each_module($file, $langs, $helpdir) {
function include_help_for_each_module($file, $forcelang, $skiplocal) {
global $CFG, $DB;

if (!$modules = $DB->get_records('modules', array('visible'=>1))) {
print_error('nomodules', 'debug'); // Should never happen
if (!$modules = $DB->get_records('modules', array('visible'=> 1))) {
print_error('nomodules', 'debug'); // Should never happen
}


// Horrible hack to show the help about grades here too.
$grade = new stdClass();
$grade->name = 'grade';
$modules[] = $grade;
Expand All @@ -210,23 +143,10 @@ function include_help_for_each_module($file, $langs, $helpdir) {
ksort($modulebyname, SORT_LOCALE_STRING);

foreach ($modulebyname as $mod) {
foreach ($langs as $lang) {
if (empty($lang)) {
continue;
}

$filepath = "$helpdir/$mod->name/$file";

// If that does not exist, try a fallback into the module code folder.
if (!file_exists($filepath)) {
$filepath = "$CFG->dirroot/mod/$mod->name/lang/$lang/help/$mod->name/$file";
}

if (file_exists_and_readable($filepath)) {
echo '<hr />';
@include($filepath); // The actual helpfile
break; // Out of loop over languages.
}
list($filepath, $foundlang) = string_manager::instance()->find_help_file($file, $mod->name, $forcelang, $skiplocal);
if ($filepath) {
echo '<hr />';
include($filepath);
}
}
}
Expand All @@ -253,12 +173,12 @@ function include_help_for_qtype($qtype, $localizedname) {
echo '<p>' . get_string($qtype . 'summary', 'qtype_' . $qtype) . "</p>\n\n";
}

function include_help_for_each_resource($file, $langs, $helpdir) {
function include_help_for_each_resource($forcelang, $skiplocal) {
global $CFG;

require_once($CFG->dirroot .'/mod/resource/lib.php');
$typelist = resource_get_types();

//add label type
$labelType = new object();
$labelType->modclass = MOD_CLASS_RESOURCE;
Expand All @@ -269,19 +189,10 @@ function include_help_for_each_resource($file, $langs, $helpdir) {
$typelist[] = $labelType;

foreach ($typelist as $type) {

foreach ($langs as $lang) {
if (empty($lang)) {
continue;
}

$filepath = "$helpdir/resource/type/".$type->name.".html";

if (file_exists_and_readable($filepath)) {
echo '<hr />';
@include($filepath); // The actual helpfile
break; // Out of loop over languages.
}
list($filepath, $foundlang) = string_manager::instance()->find_help_file('type/' . $type->name . '.html', 'resource', $forcelang, $skiplocal);
if ($filepath) {
echo '<hr />';
include($filepath);
}
}
}
Expand All @@ -293,9 +204,9 @@ function include_help_for_each_assignment_type() {
$typelist = assignment_types();

foreach ($typelist as $type => $name) {
echo '<p><b>'.$name.'</b></p>';
echo '<h2>'.$name.'</h2>';
echo get_string('help'.$type, 'assignment');
echo '<hr size="1" />';
echo '<hr />';
}
}
?>
1 change: 1 addition & 0 deletions lang/en_utf8/moodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@
<a href=\"$a->link\">change your password</a> to something easier to remember.';
$string['enable'] = 'Enable';
$string['encryptedcode'] = 'Encrypted code';
$string['english'] = 'English';
$string['enroldate'] = 'Date range';
$string['enroldetails'] = 'Enrolment details';
$string['enrolenddate'] = 'End date';
Expand Down
Loading

0 comments on commit bb45fe6

Please sign in to comment.