Skip to content

Commit

Permalink
Issue #57 _log_query errors when given raw ? or %
Browse files Browse the repository at this point in the history
Thanks to Jeff Roberson <ridgerunner@fluxbb.org> for his regex skills.
  • Loading branch information
treffynnon committed Nov 14, 2012
1 parent baf16a8 commit 318d7cd
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Features
Changelog
---------

#### 1.2.0 - release 2012-XX-XX
#### 1.2.0 - release 2012-11-14

* Setup composer for installation via packagist (j4mie/idiorm)
* Add `order_by_expr` method [[sandermarechal](http://github.com/sandermarechal)]
Expand All @@ -41,6 +41,7 @@ Changelog
* Add `delete_many` method [[CBeerta](https://github.com/CBeerta)]
* Allow unsetting of ORM parameters [[CBeerta](https://github.com/CBeerta)]
* Add `find_array` to get the records as associative arrays [[Surt](https://github.com/Surt)] - closes issue #17
* Fix bug in `_log_query` with `?` and `%` supplied in raw where statements etc. - closes issue #57 [[ridgerunner](https://github.com/ridgerunner)]

#### 1.1.1 - release 2011-01-30

Expand Down
108 changes: 107 additions & 1 deletion idiorm.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,11 @@ protected static function _log_query($query, $parameters) {
$query = str_replace("%", "%%", $query);

// Replace placeholders in the query for vsprintf
$query = str_replace("?", "%s", $query);
if(false !== strpos($query, "'") || false !== strpos($query, '"')) {
$query = IdiormString::str_replace_outside_quotes("?", "%s", $query);
} else {
$query = str_replace("?", "%s", $query);
}

// Replace the question marks in the query with the parameters
$bound_query = vsprintf($query, $parameters);
Expand Down Expand Up @@ -1382,3 +1386,105 @@ public function __isset($key) {
}
}

/**
* A class to handle str_replace operations that involve quoted strings
* @example IdiormString::str_replace_outside_quotes('?', '%s', 'columnA = "Hello?" AND columnB = ?');
* @example IdiormString::value('columnA = "Hello?" AND columnB = ?')->replace_outside_quotes('?', '%s');
* @author Jeff Roberson <ridgerunner@fluxbb.org>
* @author Simon Holywell <treffynnon@php.net>
* @link http://stackoverflow.com/a/13370709/461813 StackOverflow answer
*/
class IdiormString {
protected $subject;
protected $search;
protected $replace;

/**
* Get an easy to use instance of the class
* @param string $subject
* @return \self
*/
public static function value($subject) {
return new self($subject);
}

/**
* Shortcut method: Replace all occurrences of the search string with the replacement
* string where they appear outside quotes.
* @param string $search
* @param string $replace
* @param string $subject
* @return string
*/
public static function str_replace_outside_quotes($search, $replace, $subject) {
return static::value($subject)->replace_outside_quotes($search, $replace);
}

/**
* Set the base string object
* @param string $subject
*/
public function __construct($subject) {
$this->subject = (string) $subject;
}

/**
* Replace all occurrences of the search string with the replacement
* string where they appear outside quotes
* @param string $search
* @param string $replace
* @return string
*/
public function replace_outside_quotes($search, $replace) {
$this->search = $search;
$this->replace = $replace;
return $this->_str_replace_outside_quotes();
}

/**
* Validate an input string and perform a replace on all ocurrences
* of $this->search with $this->replace
* @author Jeff Roberson <ridgerunner@fluxbb.org>
* @link http://stackoverflow.com/a/13370709/461813 StackOverflow answer
* @return string
*/
protected function _str_replace_outside_quotes(){
$re_valid = '/
# Validate string having embedded quoted substrings.
^ # Anchor to start of string.
(?: # Zero or more string chunks.
"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" # Either a double quoted chunk,
| \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' # or a single quoted chunk,
| [^\'"\\\\]+ # or an unquoted chunk (no escapes).
)* # Zero or more string chunks.
\z # Anchor to end of string.
/sx';
if (!preg_match($re_valid, $this->subject)) // Exit if string is invalid.
exit("Error! String not valid.");
$re_parse = '/
# Match one chunk of a valid string having embedded quoted substrings.
( # Either $1: Quoted chunk.
"[^"\\\\]*(?:\\\\.[^"\\\\]*)*" # Either a double quoted chunk,
| \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' # or a single quoted chunk.
) # End $1: Quoted chunk.
| ([^\'"\\\\]+) # or $2: an unquoted chunk (no escapes).
/sx';
return preg_replace_callback($re_parse, array($this, '_str_replace_outside_quotes_cb'), $this->subject);
}

/**
* Process each matching chunk from preg_replace_callback replacing
* each occurrence of $this->search with $this->replace
* @author Jeff Roberson <ridgerunner@fluxbb.org>
* @link http://stackoverflow.com/a/13370709/461813 StackOverflow answer
* @param array $matches
* @return string
*/
protected function _str_replace_outside_quotes_cb($matches) {
// Return quoted string chunks (in group $1) unaltered.
if ($matches[1]) return $matches[1];
// Process only unquoted chunks (in group $2).
return preg_replace('/'. preg_quote($this->search, '/') .'/',
$this->replace, $matches[2]);
}
}
10 changes: 9 additions & 1 deletion test/test_queries.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,15 @@
$widget = ORM::for_table('widget')->select('widget.*')->find_one();
$expected = "SELECT `widget`.* FROM `widget` LIMIT 1";
Tester::check_equal("Issue #12 - incorrect quoting of column wildcard", $expected);


$widget = ORM::for_table('widget')->where_raw('username LIKE "ben%"')->find_many();
$expected = 'SELECT * FROM `widget` WHERE username LIKE "ben%"';
Tester::check_equal('Issue #57 - _log_query method raises a warning when query contains "%"', $expected);

$widget = ORM::for_table('widget')->where_raw('comments LIKE "has been released?%"')->find_many();
$expected = 'SELECT * FROM `widget` WHERE comments LIKE "has been released?%"';
Tester::check_equal('Issue #57 - _log_query method raises a warning when query contains "?"', $expected);

// Tests that alter Idiorm's config are done last

ORM::configure('id_column', 'primary_key');
Expand Down

0 comments on commit 318d7cd

Please sign in to comment.