Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 112 additions & 3 deletions src/PDOdb.php
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,44 @@ protected function addRawCondition(string $sql, array $params, string $concat):

return $this;
}

/**
* Adds a marker to open a where Group
*
* @param string $operator Concat operator (defaults to AND')
* @return self
*/
public function openWhereGroup(string $operator = 'AND'): self
{
$this->_where[] = ['__group_open' => true, 'operator' => strtoupper($operator)];
return $this;
}

/**
* Adds a marker to close a where Group
*
* @return self
*/
public function closeWhereGroup(): self
{
$this->_where[] = ['__group_close' => true];
return $this;
}

/**
* Adds a complete where group with a callback.
*
* @param callable $cb Callback to perform inside the group
* @param string $operator Concat operator (defaults to AND')
* @return self
*/
public function whereGroup(callable $cb, string $operator = 'AND'): self
{
$this->openWhereGroup($operator);
$cb($this);
$this->closeWhereGroup();
return $this;
}

/**
* Adds a WHERE condition to the query builder.
Expand Down Expand Up @@ -3918,10 +3956,81 @@ protected function _buildCondition(string $operator, array &$conditions): void
return;
}

// Error handling for grouping
$open = 0;
$close = 0;
$stack = [];
foreach ($conditions as $item) {
if (is_array($item) && isset($item['__group_open'])) {
$open++;
$stack[] = 0; // 0 = no conditions at this group for the moment
}

if (is_array($item) && isset($item['__group_close'])) {
$close++;

if (empty($stack)) {
$this->reset(true);
throw $this->handleException(
new \InvalidArgumentException("Unbalanced WHERE groups: closing group without opening."),
'_buildCondition'
);
}

$count = array_pop($stack);
if ($count === 0) {
$this->reset(true);
throw $this->handleException(
new \InvalidArgumentException("Empty WHERE group detected."),
'_buildCondition'
);
}
}

// normale Condition → markiere aktuelle Gruppe als nicht leer
if (!isset($item['__group_open']) && !isset($item['__group_close'])) {
if (!empty($stack)) {
$stack[count($stack)-1]++;
}
}
}

if ($open !== $close) {
$this->reset(true);
throw $this->handleException(
new \InvalidArgumentException("Unbalanced WHERE groups: missing closing parenthesis."),
'_buildCondition'
);
}

$this->_query .= ' ' . $operator . ' ';

foreach ($conditions as [$cond, $column, $comparison, $value]) {
$this->_query .= $cond ? " {$cond} " : '';
$firstElement = true;
foreach ($conditions as $item) {
// Group open token
if (is_array($item) && isset($item['__group_open'])) {
$bool = $item['operator'] ?? 'AND';
if (!$firstElement) {
$this->_query .= " {$bool} (";
} else {
$this->_query .= " (";
}
$firstElement = true;
continue;
}

// Group close token
if (is_array($item) && isset($item['__group_close'])) {
$this->_query .= ')';
$firstElement = false;
continue;
}

// Normal condition eval
[$cond, $column, $comparison, $value] = $item;
$this->_query .= ($cond && !$firstElement) ? " {$cond} " : '';
$firstElement = false;

// === PATCH: RAW support ===
if ($column === '[RAW]') {
// keine zweite $cond-Anfügung hier!
Expand Down Expand Up @@ -5535,7 +5644,7 @@ protected function _secureSanitizeWhere(array &$stack): void
}

foreach ($stack as $i => $item) {
if (is_array($item) && isset($item[1]) && $item[1] === '[RAW]') {
if (is_array($item) && (isset($item[1]) && $item[1] === '[RAW]' || isset($item['__group_open']) || isset($item['__group_close']))) {
continue;
}
if (!is_array($item) || !isset($item['__DEFERRED__'])) {
Expand Down