Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core;
  18  
  19  use ReflectionClass;
  20  use mysqli;
  21  use moodle_database, mysqli_native_moodle_database;
  22  use moodle_exception;
  23  
  24  /**
  25   * Test specific features of the MySql dml.
  26   *
  27   * @package core
  28   * @category test
  29   * @copyright 2023 Catalyst IT
  30   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   * @covers  \mysqli_native_moodle_database
  32   */
  33  class mysqli_native_moodle_database_test extends \advanced_testcase {
  34  
  35      /**
  36       * Set up.
  37       */
  38      public function setUp(): void {
  39          global $DB;
  40          parent::setUp();
  41          // Skip tests if not using Postgres.
  42          if (!($DB instanceof mysqli_native_moodle_database)) {
  43              $this->markTestSkipped('MySql-only test');
  44          }
  45      }
  46  
  47      /**
  48       * SSL connection helper.
  49       *
  50       * @param bool|null $compress
  51       * @param string|null $ssl
  52       * @return mysqli
  53       * @throws moodle_exception
  54       */
  55      public function new_connection(?bool $compress = false, ?string $ssl = null): mysqli {
  56          global $DB;
  57  
  58          // Open new connection.
  59          $cfg = $DB->export_dbconfig();
  60          if (!isset($cfg->dboptions)) {
  61              $cfg->dboptions = [];
  62          }
  63  
  64          $cfg->dboptions['clientcompress'] = $compress;
  65          $cfg->dboptions['ssl'] = $ssl;
  66  
  67          // Get a separate disposable db connection handle with guaranteed 'readonly' config.
  68          $db2 = moodle_database::get_driver_instance($cfg->dbtype, $cfg->dblibrary);
  69          $db2->raw_connect($cfg->dbhost, $cfg->dbuser, $cfg->dbpass, $cfg->dbname, $cfg->prefix, $cfg->dboptions);
  70  
  71          $reflector = new ReflectionClass($db2);
  72          $rp = $reflector->getProperty('mysqli');
  73          $rp->setAccessible(true);
  74          return $rp->getValue($db2);
  75      }
  76  
  77      /**
  78       * Test client compression helper.
  79       *
  80       * @param mysqli $mysqli
  81       * @return array
  82       */
  83      public function connection_status($mysqli): array {
  84          $mysqli->query("SELECT * FROM INFORMATION_SCHEMA.TABLES");
  85  
  86          $stats = [];
  87          foreach ($mysqli->query('SHOW SESSION STATUS')->fetch_all(MYSQLI_ASSOC) as $r) {
  88              $stats[$r['Variable_name']] = $r['Value'];
  89          }
  90          return $stats;
  91      }
  92  
  93      /**
  94       * Test client compression.
  95       *
  96       * @return void
  97       */
  98      public function test_client_compression(): void {
  99          $mysqli = $this->new_connection();
 100          $stats = $this->connection_status($mysqli);
 101          $this->assertEquals('OFF', $stats['Compression']);
 102          $sent = $stats['Bytes_sent'];
 103  
 104          $mysqlic = $this->new_connection(true);
 105          $stats = $this->connection_status($mysqlic);
 106          $this->assertEquals('ON', $stats['Compression']);
 107          $sentc = $stats['Bytes_sent'];
 108  
 109          $this->assertLessThan($sent, $sentc);
 110      }
 111  
 112      /**
 113       * Test SSL connection.
 114       *
 115       * Well as much as we can, mysqli does not reliably report connect errors.
 116       * @return void
 117       */
 118      public function test_ssl_connection(): void {
 119          try {
 120              $mysqli = $this->new_connection(false, 'require');
 121              // Either connect ...
 122              $this->assertNotNull($mysqli);
 123          } catch (moodle_exception $e) {
 124              // ... or fail.
 125              // Unfortunately we cannot be sure with the error string.
 126              $this->markTestIncomplete('SSL not supported?');
 127          }
 128  
 129          try {
 130              $mysqli = $this->new_connection(false, 'verify-full');
 131              // Either connect ...
 132              $this->assertNotNull($mysqli);
 133          } catch (moodle_exception $e) {
 134              // ... or fail with invalid cert.
 135              // Same as above, but we cannot really expect properly signed cert, so ignore.
 136          }
 137  
 138          $this->expectException(moodle_exception::class);
 139          $this->new_connection(false, 'invalid-mode');
 140      }
 141  }