"; /** * Greater than or equal to comparison operator. */ const GREATER_THAN_OR_EQUAL = ">="; /** * IN comparison operator. */ const IN = "IN"; /** * NOT IN comparison operator. */ const NOT_IN = "NOT IN"; /** * LIKE comparison operator. */ const LIKE = "LIKE"; /** * NOT LIKE comparison operator. */ const NOT_LIKE = "NOT LIKE"; /** * ILIKE comparison operator. */ const ILIKE = "ILIKE"; /** * REGEXP comparison operator. */ const REGEX = "REGEXP"; /** * NOT REGEXP comparison operator. */ const NOT_REGEX = "NOT REGEXP"; /** * BETWEEN comparison operator. */ const BETWEEN = "BETWEEN"; /** * NOT BETWEEN comparison operator. */ const NOT_BETWEEN = "NOT BETWEEN"; /** * IS comparison operator. */ const IS = "IS"; /** * IS NOT comparison operator. */ const IS_NOT = "IS NOT"; /** * Ascending ORDER BY direction. */ const ORDER_BY_ASC = "ASC"; /** * Descending ORDER BY direction. */ const ORDER_BY_DESC = "DESC"; /** * Open bracket for grouping criteria. */ const BRACKET_OPEN = "("; /** * Closing bracket for grouping criteria. */ const BRACKET_CLOSE = ")"; /** * @var MysqlAsynPool MySQL连接池 */ public $mysqlPool; /** * @var array MySQL连接池配置 */ protected $activeConfig; /** * PDO database connection to use in executing the statement. * * @var \PDO|null */ private $PdoConnection; /** * Whether to automatically escape values. * * @var bool|null */ private $autoQuote; /** * Execution options like DISTINCT and SQL_CALC_FOUND_ROWS. * * @var array */ private $option; /** * Columns, tables, and expressions to SELECT from. * * @var array */ private $select; /** * Table to INSERT into. * * @var string */ private $insert; /** * Table to REPLACE into. * * @var string */ private $replace; /** * Table to UPDATE. * * @var string */ private $update; /** * Tables to DELETE from, or true if deleting from the FROM table. * * @var array|true */ private $delete; /** * Column values to INSERT, UPDATE, or REPLACE. * * @var array */ private $set; /** * Table to select FROM. * * @var array */ private $from; /** * JOIN tables and ON criteria. * * @var array */ private $join; /** * WHERE criteria. * * @var array */ private $where; /** * Columns to GROUP BY. * * @var array */ private $groupBy; /** * HAVING criteria. * * @var array */ private $having; /** * Columns to ORDER BY. * * @var array */ private $orderBy; /** * Number of rows to return from offset. * * @var array */ private $limit; /** * SET placeholder values. * * @var array */ private $setPlaceholderValues; /** * WHERE placeholder values. * * @var array */ private $wherePlaceholderValues; /** * HAVING placeholder values. * * @var array */ private $havingPlaceholderValues; /** * InTo * @var bool */ private $isInto = false; /** * into colums * @var array */ private $intoColums; /** * into values * @var array */ private $intoValues; /** * Miner constructor. * @param $mysqlPool */ public function __construct($mysqlPool = null) { $this->option = array(); $this->select = array(); $this->delete = array(); $this->set = array(); $this->from = array(); $this->join = array(); $this->where = array(); $this->groupBy = array(); $this->having = array(); $this->orderBy = array(); $this->limit = array(); $this->intoColums = array(); $this->intoValues = array(); $this->setPlaceholderValues = array(); $this->wherePlaceholderValues = array(); $this->havingPlaceholderValues = array(); $this->mysqlPool = $mysqlPool; $this->setAutoQuote(true); } /** * Add SQL_CALC_FOUND_ROWS execution option. * * @return Miner */ public function calcFoundRows() { return $this->option('SQL_CALC_FOUND_ROWS'); } /** * Add an execution option like DISTINCT or SQL_CALC_FOUND_ROWS. * * @param string $option execution option to add * @return Miner */ public function option($option) { $this->option[] = $option; return $this; } /** * Add DISTINCT execution option. * * @return Miner */ public function distinct() { return $this->option('DISTINCT'); } /** * Add an array of columns => values to INSERT, UPDATE, or REPLACE. * * @param array $values columns => values * @return Miner */ public function values(array $values) { return $this->set($values); } /** * Add one or more column values to INSERT, UPDATE, or REPLACE. * * @param string|array $column column name or array of columns => values * @param mixed|null $value optional value for single column * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function set($column, $value = null, $quote = null) { if (is_array($column)) { foreach ($column as $columnName => $columnValue) { $this->set($columnName, $columnValue, $quote); } } else { $this->set[] = array( 'column' => $column, 'value' => $value, 'quote' => $quote ); } return $this; } /** * 配合into使用 * @param array $columns * @return $this */ public function intoColumns(array $columns) { $this->intoColums = $columns; return $this; } /** * 配合into使用 * @param array $values * @return $this */ public function intoValues(array $values) { if (is_array($values[0])) { $this->intoValues = $values; } else { $this->intoValues[] = $values; } return $this; } /** * Add an INNER JOIN table with optional ON criteria. * * @param string $table table name * @param string|array $criteria optional ON criteria * @param string $alias optional alias * @return Miner */ public function innerJoin($table, $criteria = null, $alias = null) { return $this->join($table, $criteria, self::INNER_JOIN, $alias); } /** * Add a JOIN table with optional ON criteria. * * @param string $table table name * @param string|array $criteria optional ON criteria * @param string $type optional type of join, default INNER JOIN * @param string $alias optional alias * @return Miner */ public function join($table, $criteria = null, $type = self::INNER_JOIN, $alias = null) { if (!$this->isJoinUnique($table, $alias)) { return $this; } if (is_string($criteria)) { $criteria = array($criteria); } $this->join[] = array( 'table' => $table, 'criteria' => $criteria, 'type' => $type, 'alias' => $alias ); return $this; } /** * Whether the join table and alias is unique (hasn't already been joined). * * @param string $table table name * @param string $alias table alias * @return bool whether the join table and alias is unique */ private function isJoinUnique($table, $alias) { foreach ($this->join as $join) { if ($join['table'] == $table && $join['alias'] == $alias) { return false; } } return true; } /** * Add a LEFT JOIN table with optional ON criteria. * * @param string $table table name * @param string|array $criteria optional ON criteria * @param string $alias optional alias * @return Miner */ public function leftJoin($table, $criteria = null, $alias = null) { return $this->join($table, $criteria, self::LEFT_JOIN, $alias); } /** * Add a RIGHT JOIN table with optional ON criteria. * * @param string $table table name * @param string|array $criteria optional ON criteria * @param string $alias optional alias * @return Miner */ public function rightJoin($table, $criteria = null, $alias = null) { return $this->join($table, $criteria, self::RIGHT_JOIN, $alias); } /** * Add an AND WHERE condition. * * @param string $column colum name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function andWhere($column, $value, $operator = self::EQUALS, $quote = null) { return $this->criteria($this->where, $column, $value, $operator, self::LOGICAL_AND, $quote); } /** * Add a condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function criteria( array &$criteria, $column, $value, $operator = self::EQUALS, $connector = self::LOGICAL_AND, $quote = null ) { $criteria[] = array( 'column' => $column, 'value' => $value, 'operator' => $operator, 'connector' => $connector, 'quote' => $quote ); return $this; } /** * Add an OR WHERE condition. * * @param string $column colum name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function orWhere($column, $value, $operator = self::EQUALS, $quote = null) { return $this->orCriteria($this->where, $column, $value, $operator, self::LOGICAL_OR, $quote); } /** * Add an OR condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function orCriteria(array &$criteria, $column, $value, $operator = self::EQUALS, $quote = null) { return $this->criteria($criteria, $column, $value, $operator, self::LOGICAL_OR, $quote); } /** * Add an IN WHERE condition. * * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function whereIn($column, array $values, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaIn($this->where, $column, $values, $connector, $quote); } /** * Add an IN condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function criteriaIn( array &$criteria, $column, array $values, $connector = self::LOGICAL_AND, $quote = null ) { return $this->criteria($criteria, $column, $values, self::IN, $connector, $quote); } /** * Add a NOT IN WHERE condition. * * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function whereNotIn($column, array $values, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaNotIn($this->where, $column, $values, $connector, $quote); } /** * Add a NOT IN condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function criteriaNotIn( array &$criteria, $column, array $values, $connector = self::LOGICAL_AND, $quote = null ) { return $this->criteria($criteria, $column, $values, self::NOT_IN, $connector, $quote); } /** * Add a BETWEEN WHERE condition. * * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function whereBetween($column, $min, $max, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaBetween($this->where, $column, $min, $max, $connector, $quote); } /** * Add a BETWEEN condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function criteriaBetween( array &$criteria, $column, $min, $max, $connector = self::LOGICAL_AND, $quote = null ) { return $this->criteria($criteria, $column, array($min, $max), self::BETWEEN, $connector, $quote); } /** * Add a NOT BETWEEN WHERE condition. * * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function whereNotBetween($column, $min, $max, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaNotBetween($this->where, $column, $min, $max, $connector, $quote); } /** * Add a NOT BETWEEN condition to the specified WHERE or HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ private function criteriaNotBetween( array &$criteria, $column, $min, $max, $connector = self::LOGICAL_AND, $quote = null ) { return $this->criteria($criteria, $column, array($min, $max), self::NOT_BETWEEN, $connector, $quote); } /** * Add an AND HAVING condition. * * @param string $column colum name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function andHaving($column, $value, $operator = self::EQUALS, $quote = null) { return $this->criteria($this->having, $column, $value, $operator, self::LOGICAL_AND, $quote); } /** * Add an OR HAVING condition. * * @param string $column colum name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function orHaving($column, $value, $operator = self::EQUALS, $quote = null) { return $this->orCriteria($this->having, $column, $value, $operator, self::LOGICAL_OR, $quote); } /** * Add an IN WHERE condition. * * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function havingIn($column, array $values, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaIn($this->having, $column, $values, $connector, $quote); } /** * Add a NOT IN HAVING condition. * * @param string $column column name * @param array $values values * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function havingNotIn($column, array $values, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaNotIn($this->having, $column, $values, $connector, $quote); } /** * Add a BETWEEN HAVING condition. * * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function havingBetween($column, $min, $max, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaBetween($this->having, $column, $min, $max, $connector, $quote); } /** * Add a NOT BETWEEN HAVING condition. * * @param string $column column name * @param mixed $min minimum value * @param mixed $max maximum value * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function havingNotBetween($column, $min, $max, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteriaNotBetween($this->having, $column, $min, $max, $connector, $quote); } /** * ISet the INSERT INTO table. * @param $table * @return Miner */ public function insertInto($table) { $this->isInto = true; return $this->insert($table); } /** * Set the INSERT table. * * @param string $table INSERT table * @return Miner */ public function insert($table) { $this->insert = $table; return $this; } /** * ISet the REPLACE INTO table. * @param $table * @return Miner */ public function replaceInto($table) { $this->isInto = true; return $this->replace($table); } /** * Set the REPLACE table. * * @param string $table REPLACE table * @return Miner */ public function replace($table) { $this->replace = $table; return $this; } /** * ISet the UPDATE INTO table. * @param $table * @return Miner */ public function updateInto($table) { $this->isInto = true; return $this->update($table); } /** * Set the UPDATE table. * * @param string $table UPDATE table * @return Miner */ public function update($table) { $this->update = $table; return $this; } /** * Get the full SQL statement without value placeholders. * * @return string full SQL statement */ public function __toString() { return $this->getStatement(false); } /** * Get the full SQL statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full SQL statement */ public function getStatement($usePlaceholders = true) { $statement = ""; if ($this->isSelect()) { $statement = $this->getSelectStatement($usePlaceholders); } elseif ($this->isInsert()) { $statement = $this->getInsertStatement($usePlaceholders); } elseif ($this->isReplace()) { $statement = $this->getReplaceStatement($usePlaceholders); } elseif ($this->isUpdate()) { $statement = $this->getUpdateStatement($usePlaceholders); } elseif ($this->isDelete()) { $statement = $this->getDeleteStatement($usePlaceholders); } return $statement; } /** * Whether this is a SELECT statement. * * @return bool whether this is a SELECT statement */ public function isSelect() { return !empty($this->select); } /** * Get the full SELECT statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full SELECT statement */ private function getSelectStatement($usePlaceholders = true) { $statement = ""; if (!$this->isSelect()) { return $statement; } $statement .= $this->getSelectString(); if ($this->from) { $statement .= " " . $this->getFromString(); } if ($this->where) { $statement .= " " . $this->getWhereString($usePlaceholders); } if ($this->groupBy) { $statement .= " " . $this->getGroupByString(); } if ($this->having) { $statement .= " " . $this->getHavingString($usePlaceholders); } if ($this->orderBy) { $statement .= " " . $this->getOrderByString(); } if ($this->limit) { $statement .= " " . $this->getLimitString(); } return $statement; } /** * Get the SELECT portion of the statement as a string. * * @param bool $includeText optional include 'SELECT' text, default true * @return string SELECT portion of the statement */ public function getSelectString($includeText = true) { $statement = ""; if (!$this->select) { return $statement; } $statement .= $this->getOptionsString(true); foreach ($this->select as $column => $alias) { $statement .= $column; if ($alias) { $statement .= " AS " . $alias; } $statement .= ", "; } $statement = substr($statement, 0, -2); if ($includeText && $statement) { $statement = "SELECT " . $statement; } return $statement; } /** * Get the execution options portion of the statement as a string. * * @param bool $includeTrailingSpace optional include space after options * @return string execution options portion of the statement */ public function getOptionsString($includeTrailingSpace = false) { $statement = ""; if (!$this->option) { return $statement; } $statement .= implode(' ', $this->option); if ($includeTrailingSpace) { $statement .= " "; } return $statement; } /** * Get the FROM portion of the statement, including all JOINs, as a string. * * @param bool $includeText optional include 'FROM' text, default true * @return string FROM portion of the statement */ public function getFromString($includeText = true) { $statement = ""; if (!$this->from) { return $statement; } $statement .= $this->getFrom(); if ($this->getFromAlias()) { $statement .= " AS " . $this->getFromAlias(); } // Add any JOINs. $statement .= " " . $this->getJoinString(); $statement = rtrim($statement); if ($includeText && $statement) { $statement = "FROM " . $statement; } return $statement; } /** * Get the FROM table. * * @return string FROM table */ public function getFrom() { return $this->from['table']; } /** * Get the FROM table alias. * * @return string FROM table alias */ public function getFromAlias() { return $this->from['alias']; } /** * Get the JOIN portion of the statement as a string. * * @return string JOIN portion of the statement */ public function getJoinString() { $statement = ""; foreach ($this->join as $i => $join) { $statement .= " " . $join['type'] . " " . $join['table']; if ($join['alias']) { $statement .= " AS " . $join['alias']; } // Add ON criteria if specified. if ($join['criteria']) { $statement .= " ON "; foreach ($join['criteria'] as $x => $criterion) { // Logically join each criterion with AND. if ($x != 0) { $statement .= " " . self::LOGICAL_AND . " "; } // If the criterion does not include an equals sign, assume a // column name and join against the same column from the previous // table. if (strpos($criterion, '=') === false) { $statement .= $this->getJoinCriteriaUsingPreviousTable($i, $join['table'], $criterion); } else { $statement .= $criterion; } } } } $statement = trim($statement); return $statement; } /** * Get an ON criteria string joining the specified table and column to the * same column of the previous JOIN or FROM table. * * @param int $joinIndex index of current join * @param string $table current table name * @param string $column current column name * @return string ON join criteria */ private function getJoinCriteriaUsingPreviousTable($joinIndex, $table, $column) { $joinCriteria = ""; $previousJoinIndex = $joinIndex - 1; // If the previous table is from a JOIN, use that. Otherwise, use the // FROM table. if (array_key_exists($previousJoinIndex, $this->join)) { $previousTable = $this->join[$previousJoinIndex]['table']; } elseif ($this->isSelect()) { $previousTable = $this->getFrom(); } elseif ($this->isUpdate()) { $previousTable = $this->getUpdate(); } else { $previousTable = false; } // In the off chance there is no previous table. if ($previousTable) { $joinCriteria .= $previousTable . "."; } $joinCriteria .= $column . " " . self::EQUALS . " " . $table . "." . $column; return $joinCriteria; } /** * Whether this is an UPDATE statement. * * @return bool whether this is an UPDATE statement */ public function isUpdate() { return !empty($this->update); } /** * Get the UPDATE table. * * @return string UPDATE table */ public function getUpdate() { return $this->update; } /** * Get the WHERE portion of the statement as a string. * * @param bool $usePlaceholders optional use ? placeholders, default true * @param bool $includeText optional include 'WHERE' text, default true * @return string WHERE portion of the statement */ public function getWhereString($usePlaceholders = true, $includeText = true) { $statement = $this->getCriteriaString($this->where, $usePlaceholders, $this->wherePlaceholderValues); if ($includeText && $statement) { $statement = "WHERE " . $statement; } return $statement; } /** * Get the WHERE or HAVING portion of the statement as a string. * * @param array $criteria WHERE or HAVING criteria * @param bool $usePlaceholders optional use ? placeholders, default true * @param array $placeholderValues optional placeholder values array * @return string WHERE or HAVING portion of the statement */ private function getCriteriaString( array &$criteria, $usePlaceholders = true, array &$placeholderValues = array() ) { $statement = ""; $placeholderValues = array(); $useConnector = false; foreach ($criteria as $i => $criterion) { if (array_key_exists('bracket', $criterion)) { // If an open bracket, include the logical connector. if (strcmp($criterion['bracket'], self::BRACKET_OPEN) == 0) { if ($useConnector) { $statement .= " " . $criterion['connector'] . " "; } $useConnector = false; } else { $useConnector = true; } $statement .= $criterion['bracket']; } else { if ($useConnector) { $statement .= " " . $criterion['connector'] . " "; } $useConnector = true; $autoQuote = $this->getAutoQuote($criterion['quote']); switch ($criterion['operator']) { case self::BETWEEN: case self::NOT_BETWEEN: if ($usePlaceholders && $autoQuote) { $value = "? " . self::LOGICAL_AND . " ?"; $placeholderValues[] = $criterion['value'][0]; $placeholderValues[] = $criterion['value'][1]; } else { $value = $this->autoQuote( $criterion['value'][0], $autoQuote ) . " " . self::LOGICAL_AND . " " . $this->autoQuote($criterion['value'][1], $autoQuote); } break; case self::IN: case self::NOT_IN: if ($usePlaceholders && $autoQuote) { $value = self::BRACKET_OPEN . substr(str_repeat('?, ', count($criterion['value'])), 0, -2) . self::BRACKET_CLOSE; $placeholderValues = array_merge($placeholderValues, $criterion['value']); } else { $value = self::BRACKET_OPEN; foreach ($criterion['value'] as $criterionValue) { $value .= $this->autoQuote($criterionValue, $autoQuote) . ", "; } $value = substr($value, 0, -2); $value .= self::BRACKET_CLOSE; } break; case self::IS: case self::IS_NOT: $value = $criterion['value']; break; default: if ($usePlaceholders && $autoQuote) { $value = "?"; $placeholderValues[] = $criterion['value']; } else { $value = $this->autoQuote($criterion['value'], $autoQuote); } break; } $statement .= $criterion['column'] . " " . $criterion['operator'] . " " . $value; } } return $statement; } /** * Get whether values will be automatically escaped. * * The $override parameter is for convenience in checking if a specific * value should be quoted differently than the rest. 'null' defers to the * global setting. * * @param bool|null $override value-specific override for convenience * @return bool */ public function getAutoQuote($override = null) { return $override === null ? $this->autoQuote : $override; } /** * Set whether to automatically escape values. * * @param bool|null $autoQuote whether to automatically escape values * @return Miner */ public function setAutoQuote($autoQuote) { $this->autoQuote = $autoQuote; return $this; } /** * Safely escape a value if auto-quoting is enabled, or do nothing if * disabled. * * The $override parameter is for convenience in checking if a specific * value should be quoted differently than the rest. 'null' defers to the * global setting. * * @param mixed $value value to escape (or not) * @param bool|null $override value-specific override for convenience * @return mixed|false value (escaped or original) or false if failed */ public function autoQuote($value, $override = null) { return $this->getAutoQuote($override) ? $this->quote($value) : $value; } /** * Safely escape a value for use in a statement. * * @param mixed $value value to escape * @return mixed|false escaped value or false if failed */ public function quote($value) { $PdoConnection = $this->getPdoConnection(); // If a PDO database connection is set, use it to quote the value using // the underlying database. Otherwise, quote it manually. if ($PdoConnection) { return $PdoConnection->quote($value); } elseif (is_null($value)) { return "NULL"; } else { return "'" . addslashes($value) . "'"; } } /** * Get the PDO database connection to use in executing this statement. * * @return \PDO|null */ public function getPdoConnection() { return $this->PdoConnection; } /** * Set the PDO database connection to use in executing this statement. * * @param \PDO|null $PdoConnection optional PDO database connection * @return Miner */ public function setPdoConnection(\PDO $PdoConnection = null) { $this->PdoConnection = $PdoConnection; return $this; } /** * Get the GROUP BY portion of the statement as a string. * * @param bool $includeText optional include 'GROUP BY' text, default true * @return string GROUP BY portion of the statement */ public function getGroupByString($includeText = true) { $statement = ""; foreach ($this->groupBy as $groupBy) { $statement .= $groupBy['column']; if ($groupBy['order']) { $statement .= " " . $groupBy['order']; } $statement .= ", "; } $statement = substr($statement, 0, -2); if ($includeText && $statement) { $statement = "GROUP BY " . $statement; } return $statement; } /** * Get the HAVING portion of the statement as a string. * * @param bool $usePlaceholders optional use ? placeholders, default true * @param bool $includeText optional include 'HAVING' text, default true * @return string HAVING portion of the statement */ public function getHavingString($usePlaceholders = true, $includeText = true) { $statement = $this->getCriteriaString($this->having, $usePlaceholders, $this->havingPlaceholderValues); if ($includeText && $statement) { $statement = "HAVING " . $statement; } return $statement; } /** * Get the ORDER BY portion of the statement as a string. * * @param bool $includeText optional include 'ORDER BY' text, default true * @return string ORDER BY portion of the statement */ public function getOrderByString($includeText = true) { $statement = ""; foreach ($this->orderBy as $orderBy) { $statement .= $orderBy['column'] . " " . $orderBy['order'] . ", "; } $statement = substr($statement, 0, -2); if ($includeText && $statement) { $statement = "ORDER BY " . $statement; } return $statement; } /** * Get the LIMIT portion of the statement as a string. * * @param bool $includeText optional include 'LIMIT' text, default true * @return string LIMIT portion of the statement */ public function getLimitString($includeText = true) { $statement = ""; if (!$this->limit) { return $statement; } $statement .= $this->limit['limit']; if ($this->limit['offset'] !== 0) { $statement .= " OFFSET " . $this->limit['offset']; } if ($includeText && $statement) { $statement = "LIMIT " . $statement; } return $statement; } /** * Whether this is an INSERT statement. * * @return bool whether this is an INSERT statement */ public function isInsert() { return !empty($this->insert); } /** * Get the full INSERT statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full INSERT statement */ private function getInsertStatement($usePlaceholders = true) { $statement = ""; if (!$this->isInsert()) { return $statement; } $statement .= $this->getInsertString(); if ($this->isInto) { $statement .= " " . $this->getIntoString($usePlaceholders); } else { if ($this->set) { $statement .= " " . $this->getSetString($usePlaceholders); } } return $statement; } /** * Get the INSERT portion of the statement as a string. * * @param bool $includeText optional include 'INSERT' text, default true * @return string INSERT portion of the statement */ public function getInsertString($includeText = true) { $statement = ""; if (!$this->insert) { return $statement; } if ($this->isInto) { $statement .= $this->getOptionsString(true) . 'INTO '; } else { $statement .= $this->getOptionsString(true); } $statement .= $this->getInsert(); if ($includeText && $statement) { $statement = "INSERT " . $statement; } return $statement; } /** * Get the INSERT table. * * @return string INSERT table */ public function getInsert() { return $this->insert; } /** * Get the INTO portion of the statement as a string. * @param bool $usePlaceholders * @return string */ public function getIntoString($usePlaceholders = true) { $statement = '('; $this->setPlaceholderValues = array(); foreach ($this->intoColums as $colum) { $statement .= "`$colum`" . ", "; } $statement = substr($statement, 0, -2); $statement .= ') VALUES '; foreach ($this->intoValues as $values) { $value_statement = '('; foreach ($values as $value) { if ($usePlaceholders) { $value_statement .= "?, "; $this->setPlaceholderValues[] = $value; } else { $value_statement .= $this->autoQuote($value) . ", "; } } $value_statement = substr($value_statement, 0, -2); $value_statement .= '), '; $statement .= $value_statement; } $statement = substr($statement, 0, -2); return $statement; } /** * Get the SET portion of the statement as a string. * * @param bool $usePlaceholders optional use ? placeholders, default true * @param bool $includeText optional include 'SET' text, default true * @return string SET portion of the statement */ public function getSetString($usePlaceholders = true, $includeText = true) { $statement = ""; $this->setPlaceholderValues = array(); foreach ($this->set as $set) { $autoQuote = $this->getAutoQuote($set['quote']); if ($usePlaceholders && $autoQuote) { $statement .= $set['column'] . " " . self::EQUALS . " ?, "; $this->setPlaceholderValues[] = $set['value']; } else { $statement .= $set['column'] . " " . self::EQUALS . " " . $this->autoQuote( $set['value'], $autoQuote ) . ", "; } } $statement = substr($statement, 0, -2); if ($includeText && $statement) { $statement = "SET " . $statement; } return $statement; } /** * Whether this is a REPLACE statement. * * @return bool whether this is a REPLACE statement */ public function isReplace() { return !empty($this->replace); } /** * Get the full REPLACE statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full REPLACE statement */ private function getReplaceStatement($usePlaceholders = true) { $statement = ""; if (!$this->isReplace()) { return $statement; } $statement .= $this->getReplaceString(); if ($this->isInto) { $statement .= " " . $this->getIntoString($usePlaceholders); } else { if ($this->set) { $statement .= " " . $this->getSetString($usePlaceholders); } } return $statement; } /** * Get the REPLACE portion of the statement as a string. * * @param bool $includeText optional include 'REPLACE' text, default true * @return string REPLACE portion of the statement */ public function getReplaceString($includeText = true) { $statement = ""; if (!$this->replace) { return $statement; } if ($this->isInto) { $statement .= $this->getOptionsString(true) . 'INTO '; } else { $statement .= $this->getOptionsString(true); } $statement .= $this->getReplace(); if ($includeText && $statement) { $statement = "REPLACE " . $statement; } return $statement; } /** * Get the REPLACE table. * * @return string REPLACE table */ public function getReplace() { return $this->replace; } /** * Get the full UPDATE statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full UPDATE statement */ private function getUpdateStatement($usePlaceholders = true) { $statement = ""; if (!$this->isUpdate()) { return $statement; } $statement .= $this->getUpdateString(); if ($this->isInto) { $statement .= " " . $this->getIntoString($usePlaceholders); } else { if ($this->set) { $statement .= " " . $this->getSetString($usePlaceholders); } } if ($this->where) { $statement .= " " . $this->getWhereString($usePlaceholders); } // ORDER BY and LIMIT are only applicable when updating a single table. if (!$this->join) { if ($this->orderBy) { $statement .= " " . $this->getOrderByString(); } if ($this->limit) { $statement .= " " . $this->getLimitString(); } } return $statement; } /** * Get the UPDATE portion of the statement as a string. * * @param bool $includeText optional include 'UPDATE' text, default true * @return string UPDATE portion of the statement */ public function getUpdateString($includeText = true) { $statement = ""; if (!$this->update) { return $statement; } if ($this->isInto) { $statement .= $this->getOptionsString(true) . 'INTO '; } else { $statement .= $this->getOptionsString(true); } $statement .= $this->getUpdate(); // Add any JOINs. $statement .= " " . $this->getJoinString(); $statement = rtrim($statement); if ($includeText && $statement) { $statement = "UPDATE " . $statement; } return $statement; } /** * Whether this is a DELETE statement. * * @return bool whether this is a DELETE statement */ public function isDelete() { return !empty($this->delete); } /** * Get the full DELETE statement. * * @param bool $usePlaceholders optional use ? placeholders, default true * @return string full DELETE statement */ private function getDeleteStatement($usePlaceholders = true) { $statement = ""; if (!$this->isDelete()) { return $statement; } $statement .= $this->getDeleteString(); if ($this->from) { $statement .= " " . $this->getFromString(); } if ($this->where) { $statement .= " " . $this->getWhereString($usePlaceholders); } // ORDER BY and LIMIT are only applicable when deleting from a single // table. if ($this->isDeleteTableFrom()) { if ($this->orderBy) { $statement .= " " . $this->getOrderByString(); } if ($this->limit) { $statement .= " " . $this->getLimitString(); } } return $statement; } /** * Get the DELETE portion of the statement as a string. * * @param bool $includeText optional include 'DELETE' text, default true * @return string DELETE portion of the statement */ public function getDeleteString($includeText = true) { $statement = ""; if (!$this->delete && !$this->isDeleteTableFrom()) { return $statement; } $statement .= $this->getOptionsString(true); if (is_array($this->delete)) { $statement .= implode(', ', $this->delete); } if ($includeText && ($statement || $this->isDeleteTableFrom())) { $statement = "DELETE " . $statement; // Trim in case the table is specified in FROM. $statement = trim($statement); } return $statement; } /** * Whether the FROM table is the single table to delete from. * * @return bool whether the delete table is FROM */ private function isDeleteTableFrom() { return $this->delete === true; } /** * 协程的方式 * @param null $bindId * @param null $sql * @return mixed|MySql */ public function go($bindId = null, $sql = null) { if ($sql == null) { $sql = $this->getStatement(false); } if (getInstance()->isTaskWorker()) {//如果是task进程自动转换为同步模式 $this->mergeInto($this->mysqlPool->getSync()); $this->clear(); $data = array(); switch ($sql) { case 'commit': $this->mysqlPool->getSync()->pdoCommitTrans(); break; case 'begin': $this->mysqlPool->getSync()->pdoBeginTrans(); break; case 'rollback': $this->mysqlPool->getSync()->pdoRollBackTrans(); break; default: $data = $this->mysqlPool->getSync()->pdoQuery($sql); } return $data; } else { $this->clear(); return $this->getObject(MySql::class, [$this->mysqlPool, $bindId, $sql]); } } /** * Merge this Miner into the given Miner. * * @param Miner $Miner to merge into * @param bool $overrideLimit optional override limit, default true * @return Miner */ public function mergeInto(Miner $Miner, $overrideLimit = true) { if ($this->isSelect()) { $this->mergeSelectInto($Miner); $this->mergeFromInto($Miner); $this->mergeJoinInto($Miner); $this->mergeWhereInto($Miner); $this->mergeGroupByInto($Miner); $this->mergeHavingInto($Miner); $this->mergeOrderByInto($Miner); if ($overrideLimit) { $this->mergeLimitInto($Miner); } } elseif ($this->isInsert()) { $this->mergeInsertInto($Miner); $this->mergeSetInto($Miner); } elseif ($this->isReplace()) { $this->mergeReplaceInto($Miner); $this->mergeSetInto($Miner); } elseif ($this->isUpdate()) { $this->mergeUpdateInto($Miner); $this->mergeJoinInto($Miner); $this->mergeSetInto($Miner); $this->mergeWhereInto($Miner); // ORDER BY and LIMIT are only applicable when updating a single table. if (!$this->join) { $this->mergeOrderByInto($Miner); if ($overrideLimit) { $this->mergeLimitInto($Miner); } } } elseif ($this->isDelete()) { $this->mergeDeleteInto($Miner); $this->mergeFromInto($Miner); $this->mergeJoinInto($Miner); $this->mergeWhereInto($Miner); // ORDER BY and LIMIT are only applicable when deleting from a single // table. if ($this->isDeleteTableFrom()) { $this->mergeOrderByInto($Miner); if ($overrideLimit) { $this->mergeLimitInto($Miner); } } } return $Miner; } /** * Merge this Miner's SELECT into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeSelectInto(Miner $Miner) { $this->mergeOptionsInto($Miner); foreach ($this->select as $column => $alias) { $Miner->select($column, $alias); } return $Miner; } /** * Merge this Miner's execution options into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeOptionsInto(Miner $Miner) { foreach ($this->option as $option) { $Miner->option($option); } return $Miner; } /** * Add a SELECT column, table, or expression with optional alias. * * @param string $column column name, table name, or expression * @param string $alias optional alias * @return Miner */ public function select($column, $alias = null) { $this->select[$column] = $alias; return $this; } /** * Merge this Miner's FROM into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeFromInto(Miner $Miner) { if ($this->from) { $Miner->from($this->getFrom(), $this->getFromAlias()); } return $Miner; } /** * Set the FROM table with optional alias. * * @param string $table table name * @param string $alias optional alias * @return Miner */ public function from($table, $alias = null) { $this->from['table'] = $table; $this->from['alias'] = $alias; return $this; } /** * Merge this Miner's JOINs into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeJoinInto(Miner $Miner) { foreach ($this->join as $join) { $Miner->join($join['table'], $join['criteria'], $join['type'], $join['alias']); } return $Miner; } /** * Merge this Miner's WHERE into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeWhereInto(Miner $Miner) { foreach ($this->where as $where) { // Handle open/close brackets differently than other criteria. if (array_key_exists('bracket', $where)) { if (strcmp($where['bracket'], self::BRACKET_OPEN) == 0) { $Miner->openWhere($where['connector']); } else { $Miner->closeWhere(); } } else { $Miner->where( $where['column'], $where['value'], $where['operator'], $where['connector'], $where['quote'] ); } } return $Miner; } /** * Add an open bracket for nesting WHERE conditions. * * @param string $connector optional logical connector, default AND * @return Miner */ public function openWhere($connector = self::LOGICAL_AND) { return $this->openCriteria($this->where, $connector); } /** * Add an open bracket for nesting conditions to the specified WHERE or * HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @param string $connector optional logical connector, default AND * @return Miner */ private function openCriteria(array &$criteria, $connector = self::LOGICAL_AND) { $criteria[] = array( 'bracket' => self::BRACKET_OPEN, 'connector' => $connector ); return $this; } /** * Add a closing bracket for nesting WHERE conditions. * * @return Miner */ public function closeWhere() { return $this->closeCriteria($this->where); } /** * Add a closing bracket for nesting conditions to the specified WHERE or * HAVING criteria. * * @param array $criteria WHERE or HAVING criteria * @return Miner */ private function closeCriteria(array &$criteria) { $criteria[] = array( 'bracket' => self::BRACKET_CLOSE, 'connector' => null ); return $this; } /** * Add a WHERE condition. * * @param string $column column name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function where($column, $value, $operator = self::EQUALS, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteria($this->where, $column, $value, $operator, $connector, $quote); } /** * Merge this Miner's GROUP BY into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeGroupByInto(Miner $Miner) { foreach ($this->groupBy as $groupBy) { $Miner->groupBy($groupBy['column'], $groupBy['order']); } return $Miner; } /** * Add a GROUP BY column. * * @param string $column column name * @param string|null $order optional order direction, default none * @return Miner */ public function groupBy($column, $order = null) { $this->groupBy[] = array( 'column' => $column, 'order' => $order ); return $this; } /** * Merge this Miner's HAVING into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeHavingInto(Miner $Miner) { foreach ($this->having as $having) { // Handle open/close brackets differently than other criteria. if (array_key_exists('bracket', $having)) { if (strcmp($having['bracket'], self::BRACKET_OPEN) == 0) { $Miner->openHaving($having['connector']); } else { $Miner->closeHaving(); } } else { $Miner->having( $having['column'], $having['value'], $having['operator'], $having['connector'], $having['quote'] ); } } return $Miner; } /** * Add an open bracket for nesting HAVING conditions. * * @param string $connector optional logical connector, default AND * @return Miner */ public function openHaving($connector = self::LOGICAL_AND) { return $this->openCriteria($this->having, $connector); } /** * Add a closing bracket for nesting HAVING conditions. * * @return Miner */ public function closeHaving() { return $this->closeCriteria($this->having); } /** * Add a HAVING condition. * * @param string $column colum name * @param mixed $value value * @param string $operator optional comparison operator, default = * @param string $connector optional logical connector, default AND * @param bool|null $quote optional auto-escape value, default to global * @return Miner */ public function having($column, $value, $operator = self::EQUALS, $connector = self::LOGICAL_AND, $quote = null) { return $this->criteria($this->having, $column, $value, $operator, $connector, $quote); } /** * Merge this Miner's ORDER BY into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeOrderByInto(Miner $Miner) { foreach ($this->orderBy as $orderBy) { $Miner->orderBy($orderBy['column'], $orderBy['order']); } return $Miner; } /** * Add a column to ORDER BY. * * @param string $column column name * @param string $order optional order direction, default ASC * @return Miner */ public function orderBy($column, $order = self::ORDER_BY_ASC) { $this->orderBy[] = array( 'column' => $column, 'order' => $order ); return $this; } /** * Merge this Miner's LIMIT into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeLimitInto(Miner $Miner) { if ($this->limit) { $Miner->limit($this->getLimit(), $this->getLimitOffset()); } return $Miner; } /** * Set the LIMIT on number of rows to return with optional offset. * * @param int|string $limit number of rows to return * @param int|string $offset optional row number to start at, default 0 * @return Miner */ public function limit($limit, $offset = 0) { $this->limit['limit'] = $limit; $this->limit['offset'] = $offset; return $this; } /** * Get the LIMIT on number of rows to return. * * @return int|string LIMIT on number of rows to return */ public function getLimit() { return $this->limit['limit']; } /** * Get the LIMIT row number to start at. * * @return int|string LIMIT row number to start at */ public function getLimitOffset() { return $this->limit['offset']; } /** * Merge this Miner's INSERT into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeInsertInto(Miner $Miner) { $this->mergeOptionsInto($Miner); if ($this->insert) { $Miner->insert($this->getInsert()); } return $Miner; } /** * Merge this Miner's SET into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeSetInto(Miner $Miner) { foreach ($this->set as $set) { $Miner->set($set['column'], $set['value'], $set['quote']); } return $Miner; } /** * Merge this Miner's REPLACE into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeReplaceInto(Miner $Miner) { $this->mergeOptionsInto($Miner); if ($this->replace) { $Miner->replace($this->getReplace()); } return $Miner; } /** * Merge this Miner's UPDATE into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeUpdateInto(Miner $Miner) { $this->mergeOptionsInto($Miner); if ($this->update) { $Miner->update($this->getUpdate()); } return $Miner; } /** * Merge this Miner's DELETE into the given Miner. * * @param Miner $Miner to merge into * @return Miner */ public function mergeDeleteInto(Miner $Miner) { $this->mergeOptionsInto($Miner); if ($this->isDeleteTableFrom()) { $Miner->delete(); } else { foreach ($this->delete as $delete) { $Miner->delete($delete); } } return $Miner; } /** * Add a table to DELETE from, or false if deleting from the FROM table. * * @param string|false $table optional table name, default false * @return Miner */ public function delete($table = false) { if ($table === false) { $this->delete = true; } else { // Reset the array in case the class variable was previously set to a // boolean value. if (!is_array($this->delete)) { $this->delete = array(); } $this->delete[] = $table; } return $this; } /** * 清除Build Query相关变量数据 */ public function clear() { $this->option = array(); $this->select = array(); $this->delete = array(); $this->set = array(); $this->from = array(); $this->join = array(); $this->where = array(); $this->groupBy = array(); $this->having = array(); $this->orderBy = array(); $this->limit = array(); $this->insert = array(); $this->replace = array(); $this->update = array(); $this->intoColums = array(); $this->intoValues = array(); $this->setPlaceholderValues = array(); $this->wherePlaceholderValues = array(); $this->havingPlaceholderValues = array(); $this->isInto = false; } /** * 提交事务 */ public function pdoCommitTrans() { $this->PdoConnection->commit(); } /** * 开始事务 */ public function pdoBeginTrans() { $this->PdoConnection->beginTransaction(); } /** * 事务回滚 */ public function pdoRollBackTrans() { if ($this->PdoConnection->inTransaction()) { $this->PdoConnection->rollBack(); } } /** * pdo Query * @param string $sql * @param array $palceholderValues * @param int $fetchmode * @return array|bool|int|null|string */ public function pdoQuery($sql = null, $palceholderValues = null, $fetchmode = \PDO::FETCH_ASSOC) { $pdoStatement = $this->pdoExecute($sql, $palceholderValues); if (!$pdoStatement) { $data = false; } $data['result'] = 1; $isSelect = false; if ($sql != null) {//代表手动执行的sql $str = strtolower(substr(trim($sql), 0, 6)); if ($str == 'select') { $isSelect = true; } } if ($this->isSelect() || $isSelect) { $data['result'] = $pdoStatement->fetchAll($fetchmode); $data['insert_id'] = 0; $data['affected_rows'] = 0; } else { $data['insert_id'] = $this->pdoInsertId(); $data['affected_rows'] = $pdoStatement->rowCount(); } $pdoStatement->closeCursor(); $this->clear(); return $data; } /** * Execute the statement using the PDO database connection. * @param $sql string * @param $palceholderValues array * @return \PDOStatement|false executed statement or false if failed */ protected function pdoExecute($sql, $palceholderValues) { $PdoStatement = null; $PdoConnection = $this->getPdoConnection(); // Without a PDO database connection, the statement cannot be executed. if (!$PdoConnection) { $this->pdoConnect($this->activeConfig); } if (empty($sql)) { $statement = $this->getStatement(); } else { $statement = $sql; } // Only execute if a statement is set. if ($statement) { try { $PdoStatement = $PdoConnection->prepare($statement); if (empty($palceholderValues)) { $palceholderValues = $this->getPlaceholderValues(); } $PdoStatement->execute($palceholderValues); } catch (\PDOException $e) { // 服务端断开时重连一次 if ($e->errorInfo[1] == 2006 || $e->errorInfo[1] == 2013) { $this->setPdoConnection(null); $this->pdoConnect($this->activeConfig); try { $PdoConnection = $this->getPdoConnection(); $PdoStatement = $PdoConnection->prepare($statement); $PdoStatement->execute($this->getPlaceholderValues()); } catch (\PDOException $ex) { $this->pdoRollBackTrans(); throw $ex; } } else { $this->pdoRollBackTrans(); throw $e; } } return $PdoStatement; } else { return false; } } /** * PDO连接 * @param $activeConfig */ public function pdoConnect($activeConfig) { $this->activeConfig = $activeConfig; $dsn = 'mysql:dbname=' . $activeConfig["database"] . ';host=' . $activeConfig["host"] . ';port=' . $activeConfig['port']??3306; $pdo = new \PDO( $dsn, $activeConfig["user"], $activeConfig["password"], [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $activeConfig['charset']??'utf8'] ); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false); $this->setPdoConnection($pdo); } /** * Get all placeholder values (SET, WHERE, and HAVING). * * @return array all placeholder values */ public function getPlaceholderValues() { return array_merge( $this->getSetPlaceholderValues(), $this->getWherePlaceholderValues(), $this->getHavingPlaceholderValues() ); } /** * Get the SET placeholder values. * * @return array SET placeholder values */ public function getSetPlaceholderValues() { return $this->setPlaceholderValues; } /** * Get the WHERE placeholder values. * * @return array WHERE placeholder values */ public function getWherePlaceholderValues() { return $this->wherePlaceholderValues; } /** * Get the HAVING placeholder values. * * @return array HAVING placeholder values */ public function getHavingPlaceholderValues() { return $this->havingPlaceholderValues; } /** * 返回 lastInsertId * * @return string */ public function pdoInsertId() { return $this->PdoConnection->lastInsertId(); } }