protected function compileWhereHaving(string $qb_key): string
	{
		if (! empty($this->$qb_key))
		{
			for ($i = 0, $c = count($this->$qb_key); $i < $c; $i ++)
			{
				// Is this condition already compiled?
				if (is_string($this->{$qb_key}[$i]))
				{
					continue;
				}
				elseif ($this->{$qb_key}[$i]['escape'] === false)
				{
					$this->{$qb_key}[$i] = $this->{$qb_key}[$i]['condition'];
					continue;
				}

				// Split multiple conditions
				$conditions = preg_split(
						'/((?:^|\s+)AND\s+|(?:^|\s+)OR\s+)/i', $this->{$qb_key}[$i]['condition'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
				);

				for ($ci = 0, $cc = count($conditions); $ci < $cc; $ci ++)
				{
					if (($op = $this->getOperator($conditions[$ci])) === false
							|| ! preg_match('/^(\(?)(.*)(' . preg_quote($op, '/') . ')\s*(.*(?<!\)))?(\)?)$/i', $conditions[$ci], $matches)
					)
					{
						continue;
					}

					// $matches = array(
					//	0 => '(test <= foo)',	/* the whole thing */
					//	1 => '(',		/* optional */
					//	2 => 'test',		/* the field name */
					//	3 => ' <= ',		/* $op */
					//	4 => 'foo',		/* optional, if $op is e.g. 'IS NULL' */
					//	5 => ')'		/* optional */
					// );

					if (! empty($matches[4]))
					{
						$matches[4] = ' ' . $matches[4];
					}

					$conditions[$ci] = $matches[1] . $this->db->protectIdentifiers(trim($matches[2]))
							. ' ' . trim($matches[3]) . $matches[4] . $matches[5];
				}

				$this->{$qb_key}[$i] = implode('', $conditions);
			}

			return ($qb_key === 'QBHaving' ? "\nHAVING " : "\nWHERE ")
					. implode("\n", $this->$qb_key);
		}

		return '';
	}