src/Leevel/Database/Condition.php
protected function analyseCondition(string $condType, bool $child = false): string
    {
        $sqlCond = [];
        $table = $this->getTable();

        foreach ($this->options[$condType] as $key => $cond) {
            // 逻辑连接符
            if (in_array($cond, [static::LOGIC_AND, static::LOGIC_OR], true)) {
                $sqlCond[] = strtoupper($cond);

                continue;
            }

            // 特殊处理
            if (is_string($key)) {
                // 嵌套 string
                if (':string' === $key) {
                    $sqlCond[] = implode(' AND ', $cond);
                } elseif (':stringSimple' === $key) {
                    foreach ($cond as $c) {
                        // 逻辑连接符
                        if (in_array($c, [static::LOGIC_AND, static::LOGIC_OR], true)) {
                            $sqlCond[] = strtoupper($c);
                        } else {
                            $sqlCond[] = $c;
                        }
                    }
                }
            } elseif (is_array($cond)) {
                // 表达式支持
                if (false !== strpos($cond[0], '{') &&
                    preg_match('/^{(.+?)}$/', $cond[0], $matches)) {
                    $cond[0] = $this->connect->normalizeExpression(
                        $matches[1],
                        $table
                    );
                } else {
                    // 字段处理
                    if (false !== strpos($cond[0], '.')) {
                        $tmp = explode('.', $cond[0]);
                        $currentTable = $tmp[0];
                        $cond[0] = $tmp[1];
                    } else {
                        $currentTable = $table;
                    }

                    $cond[0] = $this->connect->normalizeColumn(
                        $cond[0],
                        $currentTable
                    );
                }

                // 分析是否存在自动格式化时间标识
                $findTime = null;

                if (0 === strpos($cond[1], '@')) {
                    foreach (['date', 'month', 'day', 'year'] as $timeType) {
                        if (0 === stripos($cond[1], '@'.$timeType)) {
                            $findTime = $timeType;
                            $cond[1] = ltrim(substr($cond[1], strlen($timeType) + 1));

                            break;
                        }
                    }
                }

                // 格式化字段值，支持数组
                if (isset($cond[2])) {
                    $isArray = true;

                    if (!is_array($cond[2])) {
                        $cond[2] = [$cond[2]];
                        $isArray = false;
                    }

                    foreach ($cond[2] as $condKey => $tmp) {
                        // 对象子表达式支持
                        if (is_object($tmp) && ($tmp instanceof self || $tmp instanceof Select)) {
                            $tmp = $tmp instanceof Select ? $tmp->databaseCondition()->makeSql(true) : $tmp->makeSql(true);
                        }

                        // 回调方法子表达式支持
                        elseif (is_object($tmp) && $tmp instanceof Closure) {
                            $select = new static($this->connect);
                            $select->setTable($this->getTable());
                            call_user_func_array($tmp, [
                                $select,
                            ]);

                            $tmp = $select->makeSql(true);
                        }

                        // 字符串子表达式支持
                        elseif (is_string($tmp) && 0 === strpos($tmp, '(')) {
                        }

                        // 表达式支持
                        elseif (is_string($tmp) &&
                            false !== strpos($tmp, '{') &&
                            preg_match('/^{(.+?)}$/', $tmp, $matches)) {
                            $tmp = $this->connect->normalizeExpression(
                                $matches[1],
                                $table
                            );
                        } else {
                            // 自动格式化时间
                            if (null !== $findTime) {
                                $tmp = $this->parseTime($cond[0], $tmp, $findTime);
                            }

                            $tmp = $this->connect->normalizeColumnValue($tmp);
                        }

                        $cond[2][$condKey] = $tmp;
                    }

                    if (false === $isArray ||
                        (1 === count($cond[2]) && 0 === strpos((string) ($cond[2][0]), '('))) {
                        $cond[2] = reset($cond[2]);
                    }
                }

                // 拼接结果
                if (in_array($cond[1], ['null', 'not null'], true)) {
                    $sqlCond[] = $cond[0].' IS '.strtoupper($cond[1]);
                } elseif (in_array($cond[1], ['in', 'not in'], true)) {
                    $sqlCond[] = $cond[0].' '.
                        strtoupper($cond[1]).' '.
                        (
                            is_array($cond[2]) ?
                            '('.implode(',', $cond[2]).')' :
                            $cond[2]
                        );
                } elseif (in_array($cond[1], ['between', 'not between'], true)) {
                    if (!is_array($cond[2]) || count($cond[2]) < 2) {
                        $e =
                            'The [not] between parameter value must be '.
                            'an array which not less than two elements.';

                        throw new InvalidArgumentException($e);
                    }

                    $sqlCond[] = $cond[0].' '.strtoupper($cond[1]).' '.$cond[2][0].' AND '.$cond[2][1];
                } elseif (is_scalar($cond[2])) {
                    $sqlCond[] = $cond[0].' '.strtoupper($cond[1]).' '.$cond[2];
                }
            }
        }

        // 剔除第一个逻辑符
        array_shift($sqlCond);

        return (false === $child ? strtoupper($condType).' ' : '').
            implode(' ', $sqlCond);
    }
protected function analyseCondition(string $condType, bool $child = false): string
    {
        $sqlCond = [];
        $table = $this->getTable();

        foreach ($this->options[$condType] as $key => $cond) {
            // 逻辑连接符
            if (in_array($cond, [static::LOGIC_AND, static::LOGIC_OR], true)) {
                $sqlCond[] = strtoupper($cond);

                continue;
            }

            // 特殊处理
            if (is_string($key)) {
                // 嵌套 string
                if (':string' === $key) {
                    $sqlCond[] = implode(' AND ', $cond);
                } elseif (':stringSimple' === $key) {
                    foreach ($cond as $c) {
                        // 逻辑连接符
                        if (in_array($c, [static::LOGIC_AND, static::LOGIC_OR], true)) {
                            $sqlCond[] = strtoupper($c);
                        } else {
                            $sqlCond[] = $c;
                        }
                    }
                }
            } elseif (is_array($cond)) {
                // 表达式支持
                if (false !== strpos($cond[0], '{') &&
                    preg_match('/^{(.+?)}$/', $cond[0], $matches)) {
                    $cond[0] = $this->connect->normalizeExpression(
                        $matches[1],
                        $table
                    );
                } else {
                    // 字段处理
                    if (false !== strpos($cond[0], '.')) {
                        $tmp = explode('.', $cond[0]);
                        $currentTable = $tmp[0];
                        $cond[0] = $tmp[1];
                    } else {
                        $currentTable = $table;
                    }

                    $cond[0] = $this->connect->normalizeColumn(
                        $cond[0],
                        $currentTable
                    );
                }

                // 分析是否存在自动格式化时间标识
                $findTime = null;

                if (0 === strpos($cond[1], '@')) {
                    foreach (['date', 'month', 'day', 'year'] as $timeType) {
                        if (0 === stripos($cond[1], '@'.$timeType)) {
                            $findTime = $timeType;
                            $cond[1] = ltrim(substr($cond[1], strlen($timeType) + 1));

                            break;
                        }
                    }
                }

                // 格式化字段值，支持数组
                if (isset($cond[2])) {
                    $isArray = true;

                    if (!is_array($cond[2])) {
                        $cond[2] = [$cond[2]];
                        $isArray = false;
                    }

                    foreach ($cond[2] as $condKey => $tmp) {
                        // 对象子表达式支持
                        if (is_object($tmp) && ($tmp instanceof self || $tmp instanceof Select)) {
                            $tmp = $tmp instanceof Select ? $tmp->databaseCondition()->makeSql(true) : $tmp->makeSql(true);
                        }

                        // 回调方法子表达式支持
                        elseif (is_object($tmp) && $tmp instanceof Closure) {
                            $select = new static($this->connect);
                            $select->setTable($this->getTable());
                            call_user_func_array($tmp, [
                                $select,
                            ]);

                            $tmp = $select->makeSql(true);
                        }

                        // 字符串子表达式支持
                        elseif (is_string($tmp) && 0 === strpos($tmp, '(')) {
                        }

                        // 表达式支持
                        elseif (is_string($tmp) &&
                            false !== strpos($tmp, '{') &&
                            preg_match('/^{(.+?)}$/', $tmp, $matches)) {
                            $tmp = $this->connect->normalizeExpression(
                                $matches[1],
                                $table
                            );
                        } else {
                            // 自动格式化时间
                            if (null !== $findTime) {
                                $tmp = $this->parseTime($cond[0], $tmp, $findTime);
                            }

                            $tmp = $this->connect->normalizeColumnValue($tmp);
                        }

                        $cond[2][$condKey] = $tmp;
                    }

                    if (false === $isArray ||
                        (1 === count($cond[2]) && 0 === strpos((string) ($cond[2][0]), '('))) {
                        $cond[2] = reset($cond[2]);
                    }
                }

                // 拼接结果
                if (in_array($cond[1], ['null', 'not null'], true)) {
                    $sqlCond[] = $cond[0].' IS '.strtoupper($cond[1]);
                } elseif (in_array($cond[1], ['in', 'not in'], true)) {
                    $sqlCond[] = $cond[0].' '.
                        strtoupper($cond[1]).' '.
                        (
                            is_array($cond[2]) ?
                            '('.implode(',', $cond[2]).')' :
                            $cond[2]
                        );
                } elseif (in_array($cond[1], ['between', 'not between'], true)) {
                    if (!is_array($cond[2]) || count($cond[2]) < 2) {
                        $e =
                            'The [not] between parameter value must be '.
                            'an array which not less than two elements.';

                        throw new InvalidArgumentException($e);
                    }

                    $sqlCond[] = $cond[0].' '.strtoupper($cond[1]).' '.$cond[2][0].' AND '.$cond[2][1];
                } elseif (is_scalar($cond[2])) {
                    $sqlCond[] = $cond[0].' '.strtoupper($cond[1]).' '.$cond[2];
                }
            }
        }

        // 剔除第一个逻辑符
        array_shift($sqlCond);

        return (false === $child ? strtoupper($condType).' ' : '').
            implode(' ', $sqlCond);
    }
解析 condition　条件（包括 where,having）.

@param string $condType
@param bool   $child

@return string