diff options
-rw-r--r-- | lib/mysql.php | 74 | ||||
-rw-r--r-- | tests/MySqlShimTest.php | 331 |
2 files changed, 353 insertions, 52 deletions
diff --git a/lib/mysql.php b/lib/mysql.php index ecfb767..e7a2a57 100644 --- a/lib/mysql.php +++ b/lib/mysql.php @@ -1,6 +1,11 @@ <?php namespace { if (!function_exists('\mysql_connect')) { + define('MYSQL_CLIENT_COMPRESS', MYSQLI_CLIENT_COMPRESS); + define('MYSQL_CLIENT_IGNORE_SPACE', MYSQLI_CLIENT_IGNORE_SPACE); + define('MYSQL_CLIENT_INTERACTIVE', MYSQLI_CLIENT_INTERACTIVE); + define('MYSQL_CLIENT_SSL', MYSQLI_CLIENT_SSL); + function mysql_connect( $hostname = null, $username = null, @@ -12,21 +17,46 @@ namespace { trigger_error('Argument $new is no longer supported in PHP > 7', E_USER_WARNING); } + $hash = sha1($hostname . $username . $flags); + if ($hostname{1} != ':' && isset(\Dshafik\MySQL::$connections[$hash])) { + \Dshafik\MySQL::$last_connection = \Dshafik\MySQL::$connections[$hash]['conn']; + \Dshafik\MySQL::$connections[$hash]['refcount'] += 1; + return \Dshafik\MySQL::$connections[$hash]['conn']; + } + if ($flags === 0) { - $conn = \Dshafik\MySQL::$last_connection = mysqli_connect($hostname, $username, $password); + \Dshafik\MySQL::$last_connection = $conn = mysqli_connect($hostname, $username, $password); + $conn->hash = $hash; + \Dshafik\MySQL::$connections[$hash] = ['refcount' => 1, 'conn' => $conn]; + return $conn; } try { - $conn = \Dshafik\MySQL::$last_connection = mysqli_init(); + \Dshafik\MySQL::$last_connection = $conn = mysqli_init(); + + mysqli_real_connect( + $conn, + $hostname, + $username, + $password, + '', + null, + '', + $flags + ); - mysqli_options($conn, $flags); + if ($conn === false) { + return false; + } - mysqli_real_connect($conn, $hostname, $username, $password); + $conn->hash = $hash; + \Dshafik\MySQL::$connections[$hash] = ['refcount' => 1, 'conn' => $conn]; return $conn; } catch (\Throwable $e) { - var_dump($e); + trigger_error($e->getMessage(), E_USER_WARNING); + return false; } } @@ -49,10 +79,20 @@ namespace { return false; } - $return = mysqli_close($link); + if (isset(\Dshafik\MySQL::$connections[$link->hash])) { + \Dshafik\MySQL::$connections[$link->hash]['refcount'] -= 1; + } + + $return = true; + if (\Dshafik\MySQL::$connections[$link->hash]['refcount'] == 0) { + $return = mysqli_close($link); + unset(\Dshafik\MySQL::$connections[$link->hash]); + } + if ($isDefault) { Dshafik\MySQL::$last_connection = null; } + return $return; } @@ -62,7 +102,7 @@ namespace { return mysqli_query( $link, - "USE " . mysqli_real_escape_string($link) + "USE " . mysqli_real_escape_string($link, $databaseName) ) !== false; } @@ -74,8 +114,11 @@ namespace { function mysql_unbuffered_query($query, \mysqli $link = null) { $link = \Dshafik\MySQL::getConnection($link); - mysqli_real_query($link, $query); - return $link; + if (mysqli_real_query($link, $query)) { + return mysqli_use_result($link); + } + + return false; } function mysql_db_query($databaseName, $query, \mysqli $link = null) @@ -149,7 +192,11 @@ namespace { function mysql_num_rows(\mysqli_result $result) { - return mysqli_num_rows($result); + $previous = error_reporting(0); + $rows = mysqli_num_rows($result); + error_reporting($previous); + + return $rows; } function mysql_num_fields(\mysqli_result $result) @@ -172,8 +219,12 @@ namespace { return mysqli_fetch_assoc($result); } - function mysql_fetch_object(\mysqli_result $result, $class, array $params = []) /* : object|null */ + function mysql_fetch_object(\mysqli_result $result, $class = null, array $params = []) /* : object|null */ { + if ($class == null) { + return mysqli_fetch_object($result); + } + return mysqli_fetch_object($result, $class, $params); } @@ -374,6 +425,7 @@ namespace { namespace Dshafik { class MySQL { static public $last_connection = null; + static public $connections = []; static public function getConnection($link = null, $func = null) { diff --git a/tests/MySqlShimTest.php b/tests/MySqlShimTest.php index 4739558..35523cf 100644 --- a/tests/MySqlShimTest.php +++ b/tests/MySqlShimTest.php @@ -11,8 +11,6 @@ namespace Dshafik\MySQL\Tests; -use function \mysql_connect; - class MySqlShimTest extends \PHPUnit_Framework_TestCase { static $host; @@ -21,13 +19,8 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase public function test_mysql_connect() { - $mysql = mysql_connect(static::$host, 'root'); - - $this->assertTrue( - is_resource($mysql) && get_resource_type($mysql) == 'mysql link' - || - $mysql instanceof \mysqli - ); + $mysql = \mysql_connect(static::$host, 'root'); + $this->assertConnection($mysql); } /** @@ -36,25 +29,98 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase */ public function test_mysql_connect_fail() { - $mysql = mysql_connect(static::$host); + $mysql = \mysql_connect(static::$host); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + * @expectedExceptionMessage Argument $new is no longer supported in PHP > 7 + * @requires PHP 7.0.0 + */ + public function test_mysql_connect_new() + { + $mysql = \mysql_connect(static::$host, 'root', null, true); + } + + public function test_mysql_connect_options() + { + $mysql = \mysql_connect(static::$host, 'root', null, false, MYSQL_CLIENT_COMPRESS); + $this->assertConnection($mysql); + } + + /** + * @expectedException \PHPUnit_Framework_Error_Warning + * @expectedExceptionMessageRegExp /^mysql((i_real)?)_connect\(\): (\(HY000\/1045\): )?Access denied for user ''@'(.*?)' \(using password: NO\)$/ + */ + public function test_mysql_connect_options_fail() + { + \mysql_connect(static::$host, null, null, false, MYSQL_CLIENT_COMPRESS); + } + + public function test_mysql_connect_multi() + { + $conn = \mysql_connect(static::$host, 'root'); + $conn2 = \mysql_connect(static::$host, 'root'); + + $this->assertEquals($conn, $conn2); + + $result = \mysql_query("SELECT CONNECTION_ID()", $conn); + $row = \mysql_fetch_row($result); + $id = $row[0]; + + $result = \mysql_query("SELECT CONNECTION_ID()", $conn2); + $row = \mysql_fetch_row($result); + $id2 = $row[0]; + + $this->assertEquals($id, $id2); + } + + public function test_mysql_pconnect() + { + $conn = \mysql_pconnect(static::$host, 'root'); + + $result = \mysql_query("SELECT 'persistent'", $conn); + $row = \mysql_fetch_row($result); + $this->assertEquals('persistent', $row[0]); + } - $this->assertFalse($mysql); + public function test_mysql_query_ddl() + { + $conn = \mysql_connect(static::$host, 'root'); + $result = \mysql_query("CREATE DATABASE shim_test;"); + $this->assertTrue($result); + $result = \mysql_select_db('shim_test'); + $this->assertTrue($result); + $result = \mysql_query( + "CREATE TABLE testing ( + id int AUTO_INCREMENT, + one varchar(255), + two varchar(255), + PRIMARY KEY (id) + );" + ); + $this->assertTrue($result, \mysql_error()); + } + + public function test_mysql_query_insert() + { + $this->getConnection("mysql_shim"); + $result = \mysql_query("INSERT INTO testing (one, two) VALUES ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4')"); + + $this->assertTrue($result, \mysql_error()); } public function test_mysql_query() { - \mysql_connect(static::$host, 'root'); + $this->getConnection("mysql_shim"); $result = \mysql_query("SELECT VERSION()"); - $this->assertTrue( - is_resource($result) && get_resource_type($result) == 'mysql result' - || $result instanceof \mysqli_result - ); + $this->assertResult($result); } public function test_mysql_query_nodata() { - \mysql_connect(static::$host, 'root'); + $this->getConnection("mysql_shim"); $result = \mysql_query("SET @test = 'foo'"); $this->assertTrue($result); @@ -62,50 +128,147 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase public function test_mysql_query_fail() { - \mysql_connect(static::$host, 'root'); + $this->getConnection("mysql_shim"); $result = \mysql_query("SELECT VERSION("); $this->assertFalse($result); } - public function test_mysql_fetch_array() + public function test_mysql_unbuffered_query() { - \mysql_connect(static::$host, 'root'); + $this->getConnection("mysql_shim"); - $result = \mysql_query("SELECT 'test' AS col"); + $result = \mysql_unbuffered_query("SELECT one, two FROM testing LIMIT 4"); + $this->assertResult($result); + $i = 0; + while ($row = \mysql_fetch_assoc($result)) { + $i++; + } + $this->assertEquals(4, $i); - $row = \mysql_fetch_array($result); - $this->assertTrue(is_array($row)); - $this->assertArrayHasKey(0, $row); - $this->assertEquals($row[0], 'test'); - $this->assertArrayHasKey('col', $row); - $this->assertEquals($row['col'], 'test'); + $result = \mysql_query("SELECT one, two FROM testing LIMIT 4"); + $this->assertResult($result); } - public function test_mysql_fetch_array_multirow() + public function test_mysql_unbuffered_query_fail() { - \mysql_connect(static::$host, 'root'); + $this->getConnection(); + + $result = \mysql_unbuffered_query("SELECT VERSION("); + $this->assertFalse($result); + } - $result = \mysql_query("SHOW DATABASES"); + public function test_mysql_unbuffered_query_num_rows() + { + $this->getConnection("mysql_shim"); + + \mysql_query( + "CREATE TABLE largetest ( + id int AUTO_INCREMENT, + one varchar(255), + two varchar(255), + PRIMARY KEY (id) + );" + ); - while ($row = \mysql_fetch_array($result)) { - $this->assertTrue(is_array($row)); - $this->assertArrayHasKey(0, $row); - $this->assertTrue(in_array($row[0], ["information_schema", "mysql", "performance_schema", "sys"])); - $this->assertArrayHasKey('Database', $row); - $this->assertTrue(in_array($row['Database'], ["information_schema", "mysql", "performance_schema", "sys"])); + $result = \mysql_unbuffered_query("SELECT one, two FROM testing"); + $this->assertResult($result); + $this->assertEquals(0, \mysql_num_rows($result)); + \mysql_free_result($result); + } + + public function test_mysql_unbuffered_query_close_legacy() + { + if (!version_compare(PHP_VERSION, '7.0.0', '<')) { + $this->markTestSkipped("PHP < 7.0.0 is required"); + } + + $conn = $this->getConnection("mysql_shim"); + + \mysql_query( + "CREATE TABLE largetest ( + id int AUTO_INCREMENT, + one varchar(255), + two varchar(255), + PRIMARY KEY (id) + );" + ); + + $result = \mysql_unbuffered_query("SELECT one, two FROM testing"); + $this->assertResult($result); + try { + \mysql_close($conn); + } catch (\PHPUnit_Framework_Error_Notice $e) { + $this->assertEquals( + "mysql_close(): Function called without first fetching all rows from a previous unbuffered query", + $e->getMessage() + ); } } - public function test_mysql_num_rows() + /** + * @requires PHP 7.0.0 + */ + public function test_mysql_unbuffered_query_close() { - \mysql_connect(static::$host, 'root'); + $conn = $this->getConnection("mysql_shim"); + + \mysql_query( + "CREATE TABLE largetest ( + id int AUTO_INCREMENT, + one varchar(255), + two varchar(255), + PRIMARY KEY (id) + );" + ); + + $result = \mysql_unbuffered_query("SELECT one, two FROM testing"); + $this->assertResult($result); + \mysql_close($conn); + } - $result = \mysql_query("SHOW DATABASES"); + /** + * @dataProvider mysql_fetch_DataProvider + */ + public function test_mysql_fetch($function, $results) + { + $this->getConnection("mysql_shim"); + $result = \mysql_query("SELECT one, two FROM testing"); + $this->assertResult($result); + + $this->assertEquals(sizeof($results), \mysql_num_rows($result)); + + $i = 0; + while ($row = $function($result)) { + $this->assertEquals($results[$i], $row); + $i++; + } + + $this->assertEquals(sizeof($results), $i); + } + + public function test_mysql_num_rows() + { + $this->getConnection("mysql_shim"); + + $result = \mysql_query("SELECT * FROM testing"); + $this->assertResult($result); $this->assertEquals(4, \mysql_num_rows($result)); } + public function test_mysql_affected_rows() + { + $this->getConnection("mysql_shim"); + + $result = \mysql_query("UPDATE testing SET one = one + 1, two = two + 1 ORDER BY one DESC LIMIT 4"); + $this->assertTrue($result); + $this->assertEquals(4, \mysql_affected_rows()); + $result = \mysql_query("UPDATE testing SET one = one - 1, two = two - 1 ORDER BY one DESC LIMIT 4"); + $this->assertTrue($result); + $this->assertEquals(4, \mysql_affected_rows()); + } + public function test_mysql_close() { \mysql_connect(static::$host, 'root'); @@ -118,7 +281,7 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase */ public function test_mysql_close_fail() { - $this->assertFalse(\mysql_close()); + \mysql_close(); } public function tearDown() @@ -136,7 +299,6 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase } if (!empty($dm)) { - fwrite(STDERR, "=> Starting Docker Machine\n"); exec($dm . ' create -d virtualbox mysql-shim'); exec($dm . ' start mysql-shim'); @@ -201,4 +363,91 @@ class MySqlShimTest extends \PHPUnit_Framework_TestCase } fwrite(STDERR, "Done\n"); } + + public function mysql_fetch_DataProvider() + { + $numeric = [ + ['1', '1'], + ['2', '2'], + ['3', '3'], + ['4', '4'], + ]; + + $assoc = [ + ['one' => '1', 'two' => '1'], + ['one' => '2', 'two' => '2'], + ['one' => '3', 'two' => '3'], + ['one' => '4', 'two' => '4'], + ]; + + $array = [ + ['1', '1', 'one' => '1', 'two' => '1'], + ['2', '2', 'one' => '2', 'two' => '2'], + ['3', '3', 'one' => '3', 'two' => '3'], + ['4', '4', 'one' => '4', 'two' => '4'], + ]; + + $object = [ + (object) ['one' => '1', 'two' => '1'], + (object) ['one' => '2', 'two' => '2'], + (object) ['one' => '3', 'two' => '3'], + (object) ['one' => '4', 'two' => '4'], + ]; + + return [ + [ + 'function' => 'mysql_fetch_array', + 'results' => $array + ], + [ + 'function' => 'mysql_fetch_assoc', + 'results' => $assoc + ], + [ + 'function' => 'mysql_fetch_row', + 'results' => $numeric + ], + [ + 'function' => 'mysql_fetch_object', + 'results' => $object, + ] + ]; + } + + /** + * @param $result + */ + protected function assertResult($result) + { + $this->assertTrue( + is_resource($result) && get_resource_type($result) == 'mysql result' + || $result instanceof \mysqli_result, + \mysql_error() + ); + } + + protected function getConnection($db = null) + { + $mysql = \mysql_connect(static::$host, 'root'); + $this->assertConnection($mysql); + + if ($db !== null) { + $this->assertTrue(\mysql_select_db('shim_test')); + } + + return $mysql; + } + + /** + * @param $mysql + */ + protected function assertConnection($mysql) + { + $this->assertTrue( + is_resource($mysql) && get_resource_type($mysql) == 'mysql link' + || + $mysql instanceof \mysqli, + "Not a valid MySQL connection" + ); + } } |