Merge branch 'wip-MDL-25651' of git://github.com/jennymgray/moodle
authorPetr Skoda <commits@skodak.org>
Mon, 4 Apr 2011 07:55:41 +0000 (09:55 +0200)
committerPetr Skoda <commits@skodak.org>
Mon, 4 Apr 2011 07:55:41 +0000 (09:55 +0200)
admin/report/profiling/settings.php
config-dist.php
lib/dml/moodle_database.php
lib/dml/simpletest/testdml.php
lib/setup.php
lib/tablelib.php
lib/xhprof/readme_moodle.txt
lib/xhprof/xhprof_moodle.php
message/output/email/message_output_email.php
theme/boxxie/style/core.css

index b7c724b..bf2dc5f 100644 (file)
@@ -3,6 +3,6 @@
 defined('MOODLE_INTERNAL') || die;
 
 // profiling report, added to development
-if (extension_loaded('xhprof') && function_exists('xhprof_enable') && !empty($CFG->profilingenabled)) {
+if (extension_loaded('xhprof') && function_exists('xhprof_enable') && (!empty($CFG->profilingenabled) || !empty($CFG->earlyprofilingenabled))) {
     $ADMIN->add('development', new admin_externalpage('reportprofiling', get_string('pluginname', 'report_profiling'), "$CFG->wwwroot/$CFG->admin/report/profiling/index.php", 'moodle/site:config'));
 }
index e5952af..61c4e20 100644 (file)
@@ -297,6 +297,13 @@ $CFG->admin = 'admin';
 //   Print to footer (works with the default theme)
 //   define('MDL_PERFTOFOOT', true);
 //
+//   Enable earlier profiling that causes more code to be covered
+//   on every request (db connections, config load, other inits...).
+//   Requires extra configuration to be defined in config.php like:
+//   profilingincluded, profilingexcluded, profilingautofrec,
+//   profilingallowme, profilingallowall, profilinglifetime
+//       $CFG->earlyprofilingenabled = true;
+//
 // Force displayed usernames
 //   A little hack to anonymise user names for all students.  If you set these
 //   then all non-teachers will always see these for every person.
index 2d6b2be..b647ca9 100644 (file)
@@ -569,12 +569,25 @@ abstract class moodle_database {
      * @param int $type bound param type SQL_PARAMS_QM or SQL_PARAMS_NAMED
      * @param string named param placeholder start
      * @param bool true means equal, false not equal
+     * @param mixed $onemptyitems defines the behavior when the array of items is empty. Defaults to false,
+     *              meaning throw exceptions. Other values will become part of the returned SQL fragment.
      * @return array - $sql and $params
      */
-    public function get_in_or_equal($items, $type=SQL_PARAMS_QM, $start='param0000', $equal=true) {
-        if (is_array($items) and empty($items)) {
+    public function get_in_or_equal($items, $type=SQL_PARAMS_QM, $start='param0000', $equal=true, $onemptyitems=false) {
+        // default behavior, throw exception on empty array
+        if (is_array($items) and empty($items) and $onemptyitems === false) {
             throw new coding_exception('moodle_database::get_in_or_equal() does not accept empty arrays');
         }
+        // handle $onemptyitems on empty array of items
+        if (is_array($items) and empty($items)) {
+            if (is_null($onemptyitems)) {             // Special case, NULL value
+                $sql = $equal ? ' IS NULL' : ' IS NOT NULL';
+                return (array($sql, array()));
+            } else {
+                $items = array($onemptyitems);        // Rest of cases, prepare $items for std processing
+            }
+        }
+
         if ($type == SQL_PARAMS_QM) {
             if (!is_array($items) or count($items) == 1) {
                 $sql = $equal ? '= ?' : '<> ?';
index 6a71f4e..f574bcd 100644 (file)
@@ -223,11 +223,81 @@ class dml_test extends UnitTestCase {
 
         // Correct usage of single value
         $in_value = 'value1';
-        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false);
+        list($usql, $params) = $DB->get_in_or_equal($in_value, SQL_PARAMS_NAMED, 'param01', false);
         $this->assertEqual("<> :param01", $usql);
         $this->assertEqual(1, count($params));
         $this->assertEqual($in_value, $params['param01']);
 
+        // Some incorrect tests
+
+        // Incorrect usage passing not-allowed params type
+        $in_values = array(1, 2, 3);
+        try {
+            list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_DOLLAR, 'param01', false);
+            $this->fail('An Exception is missing, expected due to not supported SQL_PARAMS_DOLLAR');
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            $this->assertEqual($e->errorcode, 'typenotimplement');
+        }
+
+        // Incorrect usage passing empty array
+        $in_values = array();
+        try {
+            list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false);
+            $this->fail('An Exception is missing, expected due to empty array of items');
+        } catch (exception $e) {
+            $this->assertTrue($e instanceof coding_exception);
+        }
+
+        // Test using $onemptyitems
+
+        // Correct usage passing empty array and $onemptyitems = NULL (equal = true, QM)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param01', true, NULL);
+        $this->assertEqual(' IS NULL', $usql);
+        $this->assertIdentical(array(), $params);
+
+        // Correct usage passing empty array and $onemptyitems = NULL (equal = false, NAMED)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false, NULL);
+        $this->assertEqual(' IS NOT NULL', $usql);
+        $this->assertIdentical(array(), $params);
+
+        // Correct usage passing empty array and $onemptyitems = true (equal = true, QM)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param01', true, true);
+        $this->assertEqual('= ?', $usql);
+        $this->assertIdentical(array(true), $params);
+
+        // Correct usage passing empty array and $onemptyitems = true (equal = false, NAMED)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false, true);
+        $this->assertEqual('<> :param01', $usql);
+        $this->assertIdentical(array('param01' => true), $params);
+
+        // Correct usage passing empty array and $onemptyitems = -1 (equal = true, QM)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param01', true, -1);
+        $this->assertEqual('= ?', $usql);
+        $this->assertIdentical(array(-1), $params);
+
+        // Correct usage passing empty array and $onemptyitems = -1 (equal = false, NAMED)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false, -1);
+        $this->assertEqual('<> :param01', $usql);
+        $this->assertIdentical(array('param01' => -1), $params);
+
+        // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = true, QM)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_QM, 'param01', true, 'onevalue');
+        $this->assertEqual('= ?', $usql);
+        $this->assertIdentical(array('onevalue'), $params);
+
+        // Correct usage passing empty array and $onemptyitems = 'onevalue' (equal = false, NAMED)
+        $in_values = array();
+        list($usql, $params) = $DB->get_in_or_equal($in_values, SQL_PARAMS_NAMED, 'param01', false, 'onevalue');
+        $this->assertEqual('<> :param01', $usql);
+        $this->assertIdentical(array('param01' => 'onevalue'), $params);
     }
 
     public function test_fix_table_names() {
index de6f9ff..5c8bcac 100644 (file)
@@ -228,6 +228,14 @@ if (!defined('MOODLE_INTERNAL')) { // necessary because cli installer has to def
     define('MOODLE_INTERNAL', true);
 }
 
+// Early profiling start, based exclusively on config.php $CFG settings
+if (!empty($CFG->earlyprofilingenabled)) {
+    require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');
+    if (profiling_start()) {
+        register_shutdown_function('profiling_stop');
+    }
+}
+
 /**
  * Database connection. Used for all access to the database.
  * @global moodle_database $DB
@@ -669,11 +677,12 @@ session_get_instance();
 $SESSION = &$_SESSION['SESSION'];
 $USER    = &$_SESSION['USER'];
 
-// include and start profiling if needed, and register profiling_stop as shutdown function
+// Late profiling, only happening if early one wasn't started
 if (!empty($CFG->profilingenabled)) {
     require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');
-    profiling_start();
-    register_shutdown_function('profiling_stop');
+    if (profiling_start()) {
+        register_shutdown_function('profiling_stop');
+    }
 }
 
 // Process theme change in the URL.
index e3d905d..efceb9c 100644 (file)
@@ -1346,6 +1346,7 @@ class table_sql extends flexible_table {
         if (!$this->is_downloading()) {
             if ($this->countsql === NULL) {
                 $this->countsql = 'SELECT COUNT(1) FROM '.$this->sql->from.' WHERE '.$this->sql->where;
+                $this->countparams = $this->sql->params;
             }
             if ($useinitialsbar && !$this->is_downloading()) {
                 $totalinitials = $DB->count_records_sql($this->countsql, $this->countparams);
index c75c3d4..6168abd 100644 (file)
@@ -25,9 +25,6 @@ TODO:
  * export/import profiling runs: Allow to pick any profile record, encapsulate
        it into some serialized/encoded way and allow download/upload. It requires
        DB changes in order to be able to specify the source of each record (own/imported).
- * move profiling start to earlier place: detect if all the needed $CFG->profilingXXX variables
-       have been defined in config.php file and if that condition is fullfilled, start profiling
-       @ the very first lines of setup.php (as early as possible).
  * improvements to the listing mode: various commodity details like:
        - allow to filter by various criteria
        - inline (and ajax) editing of reference/comment and deleting
@@ -38,4 +35,5 @@ TODO:
        - cpu times
        (all them are right now enabled for everybody by default)
 
-20101122 - Eloy Lafuente (stronk7): Original import of 0.9.2 release
+20101122 - MDL-24600 - Eloy Lafuente (stronk7): Original import of 0.9.2 release
+20110318 - MDL-26891 - Eloy Lafuente (stronk7): Implemented earlier profiling runs
index 26958c8..b268c32 100644 (file)
@@ -69,7 +69,7 @@ function profiling_start() {
     }
 
     // If profiling isn't enabled, nothing to start
-    if (!$CFG->profilingenabled) {
+    if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) {
         return false;
     }
 
@@ -78,19 +78,22 @@ function profiling_start() {
         return false;
     }
 
+    // Set script (from global if available, else our own)
+    $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script();
+
     // Get PGC variables
     $check = 'PROFILEME';
     $profileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false;
-    $profileme = $profileme && $CFG->profilingallowme;
+    $profileme = $profileme && !empty($CFG->profilingallowme);
     $check = 'DONTPROFILEME';
     $dontprofileme = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false;
-    $dontprofileme = $dontprofileme && $CFG->profilingallowme;
+    $dontprofileme = $dontprofileme && !empty($CFG->profilingallowme);
     $check = 'PROFILEALL';
     $profileall = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false;
-    $profileall = $profileall && $CFG->profilingallowall;
+    $profileall = $profileall && !empty($CFG->profilingallowall);
     $check = 'PROFILEALLSTOP';
     $profileallstop = isset($_POST[$check]) || isset($_GET[$check]) || isset($_COOKIE[$check]) ? true : false;
-    $profileallstop = $profileallstop && $CFG->profilingallowall;
+    $profileallstop = $profileallstop && !empty($CFG->profilingallowall);
 
     // DONTPROFILEME detected, nothing to start
     if ($dontprofileme) {
@@ -98,12 +101,12 @@ function profiling_start() {
     }
 
     // PROFILEALLSTOP detected, clean the mark in seesion and continue
-    if ($profileallstop) {
+    if ($profileallstop && !empty($SESSION)) {
         unset($SESSION->profileall);
     }
 
     // PROFILEALL detected, set the mark in session and continue
-    if ($profileall) {
+    if ($profileall && !empty($SESSION)) {
         $SESSION->profileall = true;
 
     // SESSION->profileall detected, set $profileall
@@ -113,21 +116,23 @@ function profiling_start() {
 
     // Evaluate automatic (random) profiling if necessary
     $profileauto = false;
-    if ($CFG->profilingautofrec) {
+    if (!empty($CFG->profilingautofrec)) {
         $profileauto = (mt_rand(1, $CFG->profilingautofrec) === 1);
     }
 
-    // See if the $SCRIPT matches any of the included patterns
-    $profileincluded = profiling_string_matches($SCRIPT, $CFG->profilingincluded);
+    // See if the $script matches any of the included patterns
+    $included = empty($CFG->profilingincluded) ? '' : $CFG->profilingincluded;
+    $profileincluded = profiling_string_matches($script, $included);
 
-    // See if the $SCRIPT matches any of the excluded patterns
-    $profileexcluded = profiling_string_matches($SCRIPT, $CFG->profilingexcluded);
+    // See if the $script matches any of the excluded patterns
+    $excluded = empty($CFG->profilingexcluded) ? '' : $CFG->profilingexcluded;
+    $profileexcluded = profiling_string_matches($script, $excluded);
 
     // Decide if profile auto must happen (observe matchings)
     $profileauto = $profileauto && $profileincluded && !$profileexcluded;
 
     // Decide if profile by match must happen (only if profileauto is disabled)
-    $profilematch = $profileincluded && !$profileexcluded && !$CFG->profilingautofrec;
+    $profilematch = $profileincluded && !$profileexcluded && empty($CFG->profilingautofrec);
 
     // If not auto, me, all, match have been detected, nothing to do
     if (!$profileauto && !$profileme && !$profileall && !$profilematch) {
@@ -138,6 +143,9 @@ function profiling_start() {
     $ignore = array('call_user_func', 'call_user_func_array');
     xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY, array('ignored_functions' =>  $ignore));
     profiling_is_running(true);
+
+    // Started, return true
+    return true;
 }
 
 /**
@@ -152,7 +160,7 @@ function profiling_stop() {
     }
 
     // If profiling isn't enabled, nothing to stop
-    if (!$CFG->profilingenabled) {
+    if (empty($CFG->profilingenabled) && empty($CFG->earlyprofilingenabled)) {
         return false;
     }
 
@@ -161,17 +169,23 @@ function profiling_stop() {
         return false;
     }
 
+    // Set script (from global if available, else our own)
+    $script = !empty($SCRIPT) ? $SCRIPT : profiling_get_script();
+
     // Arrived here, profiling is running, stop and save everything
     profiling_is_running(false);
     $data = xhprof_disable();
 
     $run = new moodle_xhprofrun();
-    $run->prepare_run($SCRIPT);
+    $run->prepare_run($script);
     $runid = $run->save_run($data, null);
     profiling_is_saved(true);
 
     // Prune old runs
     profiling_prune_old_runs($runid);
+
+    // Finished, return true
+    return true;
 }
 
 function profiling_prune_old_runs($exception = 0) {
@@ -190,6 +204,34 @@ function profiling_prune_old_runs($exception = 0) {
                                              runid != :exception', $params);
 }
 
+/**
+ * Returns the path to the php script being requested
+ *
+ * Note this function is a partial copy of initialise_fullme() and
+ * setup_get_remote_url(), in charge of setting $FULLME, $SCRIPT and
+ * friends. To be used by early profiling runs in situations where
+ * $SCRIPT isn't defined yet
+ *
+ * @return string absolute path (wwwroot based) of the script being executed
+ */
+function profiling_get_script() {
+    global $CFG;
+
+    $wwwroot = parse_url($CFG->wwwroot);
+
+    if (!isset($wwwroot['path'])) {
+        $wwwroot['path'] = '';
+    }
+    $wwwroot['path'] .= '/';
+
+    $path = $_SERVER['SCRIPT_NAME'];
+
+    if (strpos($path, $wwwroot['path']) === 0) {
+        return substr($path, strlen($wwwroot['path']) - 1);
+    }
+    return '';
+}
+
 function profiling_urls($report, $runid, $runid2 = null) {
     global $CFG;
 
index dff4874..0445fbe 100644 (file)
@@ -46,18 +46,8 @@ class message_output_email extends message_output {
             return true;
         }
 
-        //hold onto email preference because /admin/cron.php sends a lot of messages at once
-        static $useremailaddresses = array();
-
         //check user preference for where user wants email sent
-        if (!array_key_exists($eventdata->userto->id, $useremailaddresses)) {
-            $useremailaddresses[$eventdata->userto->id] = get_user_preferences('message_processor_email_email', $eventdata->userto->email, $eventdata->userto->id);
-        }
-        $usertoemailaddress = $useremailaddresses[$eventdata->userto->id];
-
-        if ( !empty($usertoemailaddress)) {
-            $userto->email = $usertoemailaddress;
-        }
+        $eventdata->userto->email = get_user_preferences('message_processor_email_email', $eventdata->userto->email, $eventdata->userto->id);
 
         $result = email_to_user($eventdata->userto, $eventdata->userfrom,
             $eventdata->subject, $eventdata->fullmessage, $eventdata->fullmessagehtml);
index 19770e4..448e676 100644 (file)
@@ -7,6 +7,7 @@ body {
     margin: 10px 5%;
     background: #5b7439;
     padding: 5px;
+    min-width: 930px; /* fixes minimum page width */
 }
 
 #page {
@@ -16,6 +17,10 @@ body {
     
 }
 
+#page-content {
+       min-width: inherit;
+}
+
 a:link,
 a:visited {
     color: #69804e;