1<?php 2require_once __DIR__ . '/config.inc'; 3require_once dirname(__DIR__, 4) . '/ext/pdo/tests/pdo_test.inc'; 4 5foreach ($env as $k => $v) { 6 define($k, $v); 7} 8 9class MySQLPDOTest extends PDOTest { 10 11 static function factory($classname = PDO::class, $mydsn = null, $myAttr = null) { 12 $dsn = self::getDSN($mydsn); 13 $user = PDO_MYSQL_TEST_USER; 14 $pass = PDO_MYSQL_TEST_PASS; 15 $attr = PDO_MYSQL_TEST_ATTR; 16 17 if ($myAttr) { 18 $attr = $myAttr; 19 } else { 20 $attr = is_string($attr) && strlen($attr) ? unserialize($attr) : null; 21 } 22 23 $db = new $classname($dsn, $user, $pass, $attr); 24 if (!$db) { 25 die("Could not create PDO object (DSN=$dsn, user=$user)\n"); 26 } 27 28 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); 29 $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); 30 31 return $db; 32 } 33 34 static function factoryWithAttr($attr) { 35 return self::factory('PDO', null, $attr); 36 } 37 38 static function createTestTable($table, $db, $engine = null) { 39 if (!$engine) 40 $engine = PDO_MYSQL_TEST_ENGINE; 41 42 $db->exec("DROP TABLE IF EXISTS {$table}"); 43 $db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id)) ENGINE={$engine}"); 44 $db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f')"); 45 } 46 47 static function getTableEngine() { 48 return PDO_MYSQL_TEST_ENGINE; 49 } 50 51 static function getDSN($new_options = null, $addition = '') { 52 if (!$new_options) 53 return PDO_MYSQL_TEST_DSN . $addition; 54 55 $old_options = array(); 56 $dsn = substr(PDO_MYSQL_TEST_DSN, 57 strpos(PDO_MYSQL_TEST_DSN, ':') + 1, 58 strlen(PDO_MYSQL_TEST_DSN)); 59 60 // no real parser - any exotic setting can fool us 61 $parts = explode(';', $dsn); 62 foreach ($parts as $k => $v) { 63 $tmp = explode('=', $v); 64 if (count($tmp) == 2) 65 $old_options[$tmp[0]] = $tmp[1]; 66 } 67 68 $options = $old_options; 69 foreach ($new_options as $k => $v) 70 $options[$k] = $v; 71 72 $dsn = 'mysql:'; 73 foreach ($options as $k => $v) 74 $dsn .= sprintf('%s=%s;', $k, $v); 75 76 if ($addition) 77 $dsn .= $addition; 78 else 79 $dsn = substr($dsn, 0, strlen($dsn) -1); 80 81 return $dsn; 82 } 83 84 static function getClientVersion($db) { 85 return self::extractVersion($db->getAttribute(PDO::ATTR_CLIENT_VERSION)); 86 } 87 88 static function getServerVersion($db) { 89 return self::extractVersion($db->getAttribute(PDO::ATTR_SERVER_VERSION)); 90 } 91 92 static function extractVersion($version_string) { 93 /* 94 TODO: 95 We're a bit in trouble: PDO_MYSQL returns version strings. 96 That's wrong according to the manual. According to the manual 97 integers should be returned. However, this code needs to work 98 with stinky PDO_MYSQL and hopefully better PDO_MYSQLND. 99 */ 100 101 // already an int value? 102 if (is_int($version_string)) 103 return $version_string; 104 105 // string but int value? 106 $tmp = (int)$version_string; 107 if (((string)$tmp) === $version_string) 108 return $tmp; 109 110 // stinky string which we need to parse 111 $parts = explode('.', $version_string); 112 if (count($parts) < 3) 113 return -1; 114 115 $version = (int)$parts[0] * 10000; 116 $version+= (int)$parts[1] * 100; 117 $version+= (int)$parts[2]; 118 119 return $version; 120 } 121 122 static function getTempDir() { 123 if (!empty($_ENV['TMP'])) 124 return realpath( $_ENV['TMP'] ); 125 if (!empty($_ENV['TMPDIR'])) 126 return realpath( $_ENV['TMPDIR'] ); 127 if (!empty($_ENV['TEMP'])) 128 return realpath( $_ENV['TEMP'] ); 129 130 $temp_file = tempnam(md5(uniqid(rand(), TRUE)), ''); 131 if ($temp_file) { 132 $temp_dir = realpath(dirname($temp_file)); 133 unlink($temp_file); 134 return $temp_dir; 135 } 136 return false; 137 } 138 139 static function detect_transactional_mysql_engine($db) { 140 foreach ($db->query("show variables like 'have%'") as $row) { 141 if (!empty($row) && $row[1] == 'YES' && ($row[0] == 'have_innodb' || $row[0] == 'have_bdb')) { 142 return str_replace("have_", "", $row[0]); 143 } 144 } 145 /* MySQL 5.6.1+ */ 146 foreach ($db->query("SHOW ENGINES") as $row) { 147 if (isset($row['engine']) && isset($row['support'])) { 148 if ('InnoDB' == $row['engine'] && ('YES' == $row['support'] || 'DEFAULT' == $row['support'])) 149 return 'innodb'; 150 } 151 } 152 return false; 153 } 154 155 static function isPDOMySQLnd() { 156 ob_start(); 157 phpinfo(); 158 $tmp = ob_get_contents(); 159 ob_end_clean(); 160 return (preg_match('/PDO Driver for MySQL.*enabled/', $tmp) && 161 preg_match('/Client API version.*mysqlnd/', $tmp)); 162 } 163 164 static function skip() { 165 try { 166 $db = self::factory(); 167 } catch (PDOException $e) { 168 die('skip could not connect'); 169 } 170 } 171 172 static function skipInfileNotAllowed() { 173 $db = self::factory(); 174 $stmt = $db->query("SHOW VARIABLES LIKE 'local_infile'"); 175 if (($row = $stmt->fetch(PDO::FETCH_ASSOC)) && ($row['value'] != 'ON')) 176 die("skip Server variable 'local_infile' seems not set to 'ON', found '". $row['value'] ."'"); 177 } 178 179 static function skipVersionThanLess ($expected_version) { 180 $nums = [ 181 (int) substr($expected_version, 0, 1), 182 (int) substr($expected_version, 1, 2), 183 (int) substr($expected_version, 3, 2), 184 ]; 185 $expected_version_str = implode('.', $nums); 186 187 $db = self::factory(); 188 $stmt = $db->query('SELECT VERSION() as _version'); 189 $row = $stmt->fetch(PDO::FETCH_ASSOC); 190 $matches = array(); 191 if (!preg_match('/^(\d+)\.(\d+)\.(\d+)/ismU', $row['_version'], $matches)) 192 die(sprintf("skip Cannot determine MySQL Server version\n")); 193 194 $version = $matches[1] * 10000 + $matches[2] * 100 + $matches[3]; 195 if ($version < $expected_version) 196 die(sprintf("skip Need MySQL Server %s+, found %d.%02d.%02d (%d)\n", 197 $expected_version_str, $matches[1], $matches[2], $matches[3], $version)); 198 } 199 200 static function skipNotMySQLnd(?string $message = null) { 201 $message = $message ?? 'skip only for mysqlnd'; 202 if (!self::isPDOMySQLnd()) die($message); 203 } 204 205 static function skipNotTransactionalEngine(?string $message = null) { 206 $db = self::factory(); 207 $message = $message ?? 'skip Transactional engine not found'; 208 if (false == self::detect_transactional_mysql_engine($db)) die($message); 209 } 210} 211?> 212