summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Holywell <[email protected]>2012-11-14 16:03:52 +0000
committerSimon Holywell <[email protected]>2012-11-14 16:03:52 +0000
commit318d7cdd5ccd2d686cbd6915ff3f58486248c02c (patch)
tree3635c9c04244a6a6c8c29846abd4e5b455048804
parentbaf16a8a6089f69629b0a789eb5670008e96e76a (diff)
Issue #57 _log_query errors when given raw ? or %
Thanks to Jeff Roberson <[email protected]> for his regex skills.
-rw-r--r--README.markdown3
-rw-r--r--idiorm.php108
-rw-r--r--test/test_queries.php10
3 files changed, 118 insertions, 3 deletions
diff --git a/README.markdown b/README.markdown
index f1fb65a..2e5518d 100644
--- a/README.markdown
+++ b/README.markdown
@@ -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)]
@@ -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
diff --git a/idiorm.php b/idiorm.php
index d2200be..d2b4b17 100644
--- a/idiorm.php
+++ b/idiorm.php
@@ -278,7 +278,11 @@
$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);
@@ -1382,3 +1386,105 @@
}
}
+ /**
+ * 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 <[email protected]>
+ * @author Simon Holywell <[email protected]>
+ * @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 <[email protected]>
+ * @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 <[email protected]>
+ * @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]);
+ }
+ } \ No newline at end of file
diff --git a/test/test_queries.php b/test/test_queries.php
index 1f2acc6..bcd4c7e 100644
--- a/test/test_queries.php
+++ b/test/test_queries.php
@@ -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');