Skip to content

Commit

Permalink
Simplifying parameter binding by making it all the same, this also
Browse files Browse the repository at this point in the history
resolves a potential issue that was made evident on cyclic references
not being able to be traversed
  • Loading branch information
lorenzo committed Mar 7, 2013
1 parent bd90102 commit 28d95c4
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 25 deletions.
Expand Up @@ -428,9 +428,10 @@ public function sql() {
*
* Callback function receives as only argument an instance of a QueryExpression
*
* @param callable $callable
* @return void
*/
public function traverse($callable) {
public function traverse(callable $callable) {
foreach ($this->_conditions as $c) {
if ($c instanceof self) {
$c->traverse($callable);
Expand Down
Expand Up @@ -153,6 +153,12 @@ public function traverse(callable $visitor) {
if (!$this->_hasQuery) {
return;
}

foreach ($this->_values as $v) {
if ($v instanceof ExpressionInterface) {
$v->traverse($visitor);
}
}
$this->_values[0]->traverse($visitor);

This comment has been minimized.

Copy link
@markstory

markstory Mar 7, 2013

Member

This will double traverse the 0'th index.

This comment has been minimized.

Copy link
@lorenzo

lorenzo Mar 8, 2013

Author Member

That is correct, for now is an annoyance that I had to overcome. But makes the code much cleaner. Will think on ways to not calling twice

This comment has been minimized.

Copy link
@markstory

markstory Mar 8, 2013

Member

Couldn't you just delete this line?

This comment has been minimized.

Copy link
@lorenzo

lorenzo Mar 8, 2013

Author Member

Ah, sorry. I thought you were referring to another problem I found. Yes, I needed to delete that line, I'm derp :P

}

Expand Down
11 changes: 11 additions & 0 deletions lib/Cake/Model/Datasource/Database/ExpressionInterface.php
Expand Up @@ -29,4 +29,15 @@ interface ExpressionInterface {
*/
public function sql();

/**
* Iterates over each part of the expression recursively for every
* level of the expressions tree and executes the $visitor callable
* passing as first parameter the instance of the expression currently
* being iterated.
*
* @param callable $visitor
* @return void
*/
public function traverse(callable $visitor);

}
28 changes: 16 additions & 12 deletions lib/Cake/Model/Datasource/Database/Query.php
Expand Up @@ -240,10 +240,10 @@ public function sql($transform = true) {
* });
* }}}
*
* @param callback $visitor a function or callable to be executed for each part
* @param callable $visitor a function or callable to be executed for each part
* @return Query
*/
public function traverse($visitor) {
public function traverse(callable $visitor) {
$this->{'_traverse' . ucfirst($this->_type)}($visitor);
return $this;
}
Expand Down Expand Up @@ -1449,24 +1449,28 @@ protected function _bindParams($statement) {
$statement->bind($params, $types);
};

$binder = function($expression, $name) use ($statement, $visitor, &$binder) {
$refs = [];
$binder = function($expression, $name = null) use ($statement, $visitor, &$binder, &$refs) {

This comment has been minimized.

Copy link
@markstory

markstory Mar 7, 2013

Member

6 params is getting a bit silly :)

This comment has been minimized.

Copy link
@lorenzo

lorenzo Mar 8, 2013

Author Member

Fixed that in a later commit. And technically is not a param, is the silly PHP way of using scoped variables :(

This comment has been minimized.

Copy link
@markstory

markstory Mar 8, 2013

Member

I wonder if binding would be simpler if it was a method on a class, then the closed over variables could be properties.

This comment has been minimized.

Copy link
@lorenzo

lorenzo Mar 8, 2013

Author Member

Check my recent commits, I think it reads simpler with the last refactoring I made. We can still make it a class if we think it could make it even more readable

if (is_array($expression)) {
foreach ($expression as $e) {
$binder($e, $name);
}
return;
}

if ($expression instanceof self) {
return $expression->traverse($binder);
}
if ($expression instanceof ExpressionInterface) {
$id = spl_object_hash($expression);

if ($expression instanceof QueryExpression) {
// Visit all expressions and subexpressions to get every bound value
$expression->traverse($visitor);
}
if ($expression instanceof ValuesExpression) {
if (isset($refs[$id])) {
return;
}

$refs[$id] = 1;
$expression->traverse($binder);
$visitor($expression);

if (!($expression instanceof self)) {
$visitor($expression);
}
}
};

Expand Down
32 changes: 20 additions & 12 deletions lib/Cake/Test/TestCase/Model/Datasource/Database/QueryTest.php
Expand Up @@ -1944,21 +1944,11 @@ public function testInsertFailureMixingTypesQueryFirst() {
}

/**
* Assertion for comparing a table's contents with what is in it.
* undocumented function
*
* @param string $table
* @param int $count
* @param array $rows
* @group FunctionExpression
* @return void
*/
protected function assertTable($table, $count, $rows) {
$result = (new Query($this->connection))->select('*')
->from($table)
->execute();
$this->assertCount($count, $result, 'Row count is incorrect');
$this->assertEquals($rows, $result->fetchAll('assoc'));
}

public function testSQLFunctions() {
$this->_insertTwoRecords();
$query = new Query($this->connection);
Expand All @@ -1974,4 +1964,22 @@ public function testSQLFunctions() {
->execute();
debug($result->fetchAll());
}


/**
* Assertion for comparing a table's contents with what is in it.
*
* @param string $table
* @param int $count
* @param array $rows
* @return void
*/
protected function assertTable($table, $count, $rows) {
$result = (new Query($this->connection))->select('*')
->from($table)
->execute();
$this->assertCount($count, $result, 'Row count is incorrect');
$this->assertEquals($rows, $result->fetchAll('assoc'));
}

}

0 comments on commit 28d95c4

Please sign in to comment.