r5962 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r5961‎ | r5962 | r5963 >
Date:07:25, 18 October 2004
Author:vibber
Status:old
Tags:
Comment:
* Add support for prepared statements. This should be safer than dumping variables into raw SQL and more flexible than the array-based wrapper functions
* Add some quick PHPUnit tests for Database::fillPrepared()
Modified paths:
  • /trunk/phase3/includes/Database.php (modified) (history)
  • /trunk/phase3/tests (added) (history)
  • /trunk/phase3/tests/DatabaseTest.php (added) (history)
  • /trunk/phase3/tests/RunTests.php (added) (history)

Diff [purge]

Index: trunk/phase3/tests/DatabaseTest.php
@@ -0,0 +1,60 @@
 2+<?php
 3+
 4+require_once( 'PHPUnit.php' );
 5+require_once( '../includes/Defines.php' );
 6+require_once( '../includes/Database.php' );
 7+require_once( '../includes/GlobalFunctions.php' );
 8+
 9+class DatabaseTest extends PHPUnit_TestCase {
 10+ var $db;
 11+
 12+ function DatabaseTest( $name ) {
 13+ $this->PHPUnit_TestCase( $name );
 14+ }
 15+
 16+ function setUp() {
 17+ $this->db =& new Database();
 18+ }
 19+
 20+ function tearDown() {
 21+ unset( $this->db );
 22+ }
 23+
 24+ function testFillPreparedEmpty() {
 25+ $sql = $this->db->fillPrepared(
 26+ 'SELECT * FROM interwiki', array() );
 27+ $this->assertEquals(
 28+ "SELECT * FROM interwiki",
 29+ $sql);
 30+ }
 31+
 32+ function testFillPreparedQuestion() {
 33+ $sql = $this->db->fillPrepared(
 34+ 'SELECT * FROM cur WHERE cur_namespace=? AND cur_title=?',
 35+ array( 4, "Snicker's_paradox" ) );
 36+ $this->assertEquals(
 37+ "SELECT * FROM cur WHERE cur_namespace='4' AND cur_title='Snicker\'s_paradox'",
 38+ $sql);
 39+ }
 40+
 41+ function testFillPreparedBang() {
 42+ $sql = $this->db->fillPrepared(
 43+ 'SELECT user_id FROM ! WHERE user_name=?',
 44+ array( '"user"', "Slash's Dot" ) );
 45+ $this->assertEquals(
 46+ "SELECT user_id FROM \"user\" WHERE user_name='Slash\'s Dot'",
 47+ $sql);
 48+ }
 49+
 50+ function testFillPreparedRaw() {
 51+ $sql = $this->db->fillPrepared(
 52+ "SELECT * FROM cur WHERE cur_title='This_\\&_that,_WTF\\?\\!'",
 53+ array( '"user"', "Slash's Dot" ) );
 54+ $this->assertEquals(
 55+ "SELECT * FROM cur WHERE cur_title='This_&_that,_WTF?!'",
 56+ $sql);
 57+ }
 58+
 59+}
 60+
 61+?>
\ No newline at end of file
Property changes on: trunk/phase3/tests/DatabaseTest.php
___________________________________________________________________
Added: svn:keywords
162 + Author Date Id Revision
Added: svn:eol-style
263 + native
Index: trunk/phase3/tests/RunTests.php
@@ -0,0 +1,12 @@
 2+<?php
 3+error_reporting( E_ALL );
 4+define( "MEDIAWIKI", true );
 5+
 6+require_once( 'PHPUnit.php' );
 7+require_once( 'DatabaseTest.php' );
 8+
 9+$suite = new PHPUnit_TestSuite( "DatabaseTest" );
 10+$result = PHPUnit::run( $suite );
 11+echo $result->toString();
 12+
 13+?>
\ No newline at end of file
Property changes on: trunk/phase3/tests/RunTests.php
___________________________________________________________________
Added: svn:keywords
114 + Author Date Id Revision
Added: svn:eol-style
215 + native
Index: trunk/phase3/includes/Database.php
@@ -352,6 +352,100 @@
353353 }
354354
355355
 356+ /**
 357+ * Intended to be compatible with the PEAR::DB wrapper functions.
 358+ * http://pear.php.net/manual/en/package.database.db.intro-execute.php
 359+ *
 360+ * ? = scalar value, quoted as necessary
 361+ * ! = raw SQL bit (a function for instance)
 362+ * & = filename; reads the file and inserts as a blob
 363+ * (we don't use this though...)
 364+ */
 365+ function prepare( $sql, $func = 'Database::prepare' ) {
 366+ /* MySQL doesn't support prepared statements (yet), so just
 367+ pack up the query for reference. We'll manually replace
 368+ the bits later. */
 369+ return array( 'query' => $sql, 'func' => $func );
 370+ }
 371+
 372+ function freePrepared( $prepared ) {
 373+ /* No-op for MySQL */
 374+ }
 375+
 376+ /**
 377+ * Execute a prepared query with the various arguments
 378+ * @param string $prepared the prepared sql
 379+ * @param mixed $args Either an array here, or put scalars as varargs
 380+ */
 381+ function execute( $prepared, $args = null ) {
 382+ if( !is_array( $args ) ) {
 383+ # Pull the var args
 384+ $args = func_get_args();
 385+ array_shift( $args );
 386+ }
 387+ $sql = $this->fillPrepared( $prepared['query'], $args );
 388+ return $this->query( $sql, $prepared['func'] );
 389+ }
 390+
 391+ /**
 392+ * Prepare & execute an SQL statement, quoting and inserting arguments
 393+ * in the appropriate places.
 394+ * @param
 395+ */
 396+ function safeQuery( $query, $args = null ) {
 397+ $prepared = $this->prepare( $query, 'Database::safeQuery' );
 398+ if( !is_array( $args ) ) {
 399+ # Pull the var args
 400+ $args = func_get_args();
 401+ array_shift( $args );
 402+ }
 403+ $retval = $this->execute( $prepared, $args );
 404+ $this->freePrepared( $prepared );
 405+ return $retval;
 406+ }
 407+
 408+ /**
 409+ * For faking prepared SQL statements on DBs that don't support
 410+ * it directly.
 411+ * @param string $preparedSql - a 'preparable' SQL statement
 412+ * @param array $args - array of arguments to fill it with
 413+ * @return string executable SQL
 414+ */
 415+ function fillPrepared( $preparedQuery, $args ) {
 416+ $n = 0;
 417+ reset( $args );
 418+ $this->preparedArgs =& $args;
 419+ return preg_replace_callback( '/(\\\\[?!&]|[?!&])/',
 420+ array( &$this, 'fillPreparedArg' ), $preparedQuery );
 421+ }
 422+
 423+ /**
 424+ * preg_callback func for fillPrepared()
 425+ * The arguments should be in $this->preparedArgs and must not be touched
 426+ * while we're doing this.
 427+ *
 428+ * @param array $matches
 429+ * @return string
 430+ * @access private
 431+ */
 432+ function fillPreparedArg( $matches ) {
 433+ switch( $matches[1] ) {
 434+ case '\\?': return '?';
 435+ case '\\!': return '!';
 436+ case '\\&': return '&';
 437+ }
 438+ list( $n, $arg ) = each( $this->preparedArgs );
 439+ switch( $matches[1] ) {
 440+ case '?': return $this->addQuotes( $arg );
 441+ case '!': return $arg;
 442+ case '&':
 443+ # return $this->addQuotes( file_get_contents( $arg ) );
 444+ wfDebugDieBacktrace( '& mode is not implemented. If it\'s really needed, uncomment the line above.' );
 445+ default:
 446+ wfDebugDieBacktrace( 'Received invalid match. This should never happen!' );
 447+ }
 448+ }
 449+
356450 /**#@+
357451 * @param mixed $res A SQL result
358452 */

Status & tagging log