: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Adds a column to the list of columns to GROUP BY.
* @param string|array $column_name The table column.
public function group_by( $column_name ) {
$column_name = $this->quote_identifier( $column_name );
$this->group_by[] = $column_name;
* Adds an unquoted expression to the list of columns to GROUP BY.
* @param string $expr The expression.
public function group_by_expr( $expr ) {
$this->group_by[] = $expr;
* Adds a HAVING column = value clause to your query.
* Each time this is called in the chain, an additional HAVING will be added, and these will be ANDed together when
* the final query is built.
* If you use an array in $column_name, a new clause will be added for each element. In this case, $value is
* @param string|array $column_name The table column.
* @param mixed|null $value The value.
public function having( $column_name, $value = null ) {
return $this->having_equal( $column_name, $value );
* Adds a having equal to your query.
* More explicitly named version of for the having() method. Can be used if preferred.
* @param string|array $column_name The table column.
* @param mixed|null $value The value.
public function having_equal( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '=', $value );
* Adds a HAVING column != value clause to your query.
* @param string|array $column_name The table column.
* @param mixed|null $value The value.
public function having_not_equal( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '!=', $value );
* Queries the table by its primary key. Special method.
* If primary key is compound, only the columns that belong to they key will be used for the query.
* @param string $id The ID.
public function having_id_is( $id ) {
return \is_array( $this->get_id_column_name() ) ? $this->having( $this->get_compound_id_column_values( $id ), null ) : $this->having( $this->get_id_column_name(), $id );
* Adds a HAVING ... LIKE clause to your query.
* @param string|array $column_name The table column.
* @param string|null $value The value.
public function having_like( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, 'LIKE', $value );
* Adds where HAVING ... NOT LIKE clause to your query.
* @param string|array $column_name The table column.
* @param string|null $value The value.
public function having_not_like( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, 'NOT LIKE', $value );
* Adds a HAVING ... > clause to your query.
* @param string|array $column_name The table column.
* @param mixed $value The value.
public function having_gt( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '>', $value );
* Adds a HAVING ... < clause to your query.
* @param string|array $column_name The table column.
* @param mixed $value The value.
public function having_lt( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '<', $value );
* Adds a HAVING ... >= clause to your query.
* @param string|array $column_name The table column.
* @param mixed $value The value. Defaults to null.
public function having_gte( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '>=', $value );
* Adds a HAVING ... <= clause to your query.
* @param string|array $column_name The table column.
* @param mixed $value The value.
public function having_lte( $column_name, $value = null ) {
return $this->add_simple_having( $column_name, '<=', $value );
* Adds a HAVING ... IN clause to your query.
* @param string|array $column_name The table column.
* @param array|null $values The values. Defaults to null.
public function having_in( $column_name, $values = null ) {
return $this->add_having_placeholder( $column_name, 'IN', $values );
* Adds a HAVING ... NOT IN clause to your query.
* @param string|array $column_name The table column.
* @param array|null $values The values. Defaults to null.
public function having_not_in( $column_name, $values = null ) {
return $this->add_having_placeholder( $column_name, 'NOT IN', $values );
* Adds a HAVING column IS NULL clause to your query.
* @param string|array $column_name The table column.
public function having_null( $column_name ) {
return $this->add_having_no_value( $column_name, 'IS NULL' );
* Adds a HAVING column IS NOT NULL clause to your query.
* @param string|array $column_name The table column.
public function having_not_null( $column_name ) {
return $this->add_having_no_value( $column_name, 'IS NOT NULL' );
* Adds a raw HAVING clause to the query. The clause should contain question mark placeholders, which will be bound
* to the parameters supplied in the second argument.
* @param string $clause The clause that should contain question mark placeholders.
* @param array $parameters The parameters to include in the query.
public function having_raw( $clause, $parameters = [] ) {
return $this->add_having( $clause, $parameters );
* Builds a SELECT statement based on the clauses that have been passed to this instance by chaining method calls.
protected function build_select() {
// If the query is raw, just set the $this->values to be the raw query parameters and return the raw query.
if ( $this->is_raw_query ) {
$this->values = $this->raw_parameters;
// Build and return the full SELECT statement by concatenating the results of calling each separate builder method.
return $this->join_if_not_empty(
$this->build_select_start(),
* Builds the start of the SELECT statement.
protected function build_select_start() {
$result_columns = \implode( ', ', $this->result_columns );
$result_columns = 'DISTINCT ' . $result_columns;
$fragment .= "{$result_columns} FROM " . $this->quote_identifier( $this->table_name );
if ( ! \is_null( $this->table_alias ) ) {
$fragment .= ' ' . $this->quote_identifier( $this->table_alias );
* Builds the JOIN sources.
protected function build_join() {
if ( \count( $this->join_sources ) === 0 ) {
return \implode( ' ', $this->join_sources );
* Builds the WHERE clause(s).
protected function build_where() {
return $this->build_conditions( 'where' );
* Build the HAVING clause(s)
protected function build_having() {
return $this->build_conditions( 'having' );
protected function build_group_by() {
if ( \count( $this->group_by ) === 0 ) {
return 'GROUP BY ' . \implode( ', ', $this->group_by );
* Builds a WHERE or HAVING clause.
* @param string $type Where or having.
protected function build_conditions( $type ) {
$conditions_class_property_name = "{$type}_conditions";
// If there are no clauses, return empty string.
if ( \count( $this->{$conditions_class_property_name} ) === 0 ) {
foreach ( $this->{$conditions_class_property_name} as $condition ) {
$conditions[] = $condition[ self::CONDITION_FRAGMENT ];
$this->values = \array_merge( $this->values, $condition[ self::CONDITION_VALUES ] );
return \strtoupper( $type ) . ' ' . \implode( ' AND ', $conditions );
protected function build_order_by() {
if ( \count( $this->order_by ) === 0 ) {
return 'ORDER BY ' . \implode( ', ', $this->order_by );
protected function build_limit() {
if ( ! \is_null( $this->limit ) ) {
return "LIMIT {$this->limit}";
protected function build_offset() {
if ( ! \is_null( $this->offset ) ) {
return 'OFFSET ' . $this->offset;
* Joins strings if they are not empty.
* @param string $glue Glue.
* @param string[] $pieces Pieces to join.
protected function join_if_not_empty( $glue, $pieces ) {
foreach ( $pieces as $piece ) {
if ( \is_string( $piece ) ) {
$piece = \trim( $piece );
if ( ! empty( $piece ) ) {
$filtered_pieces[] = $piece;
return \implode( $glue, $filtered_pieces );
* Quotes a string that is used as an identifier (table names, column names etc).
* This method can also deal with dot-separated identifiers eg table.column.
* @param string|string[] $identifier One or more identifiers.
protected function quote_one_identifier( $identifier ) {
$parts = \explode( '.', $identifier );
$parts = \array_map( [ $this, 'quote_identifier_part' ], $parts );
return \implode( '.', $parts );
* Quotes a string that is used as an identifier (table names, column names etc) or an array containing multiple
* identifiers. This method can also deal with dot-separated identifiers eg table.column.
* @param string|string[] $identifier One or more identifiers.
protected function quote_identifier( $identifier ) {
if ( \is_array( $identifier ) ) {
$result = \array_map( [ $this, 'quote_one_identifier' ], $identifier );
return \implode( ', ', $result );
return $this->quote_one_identifier( $identifier );
* Quotes a single part of an identifier, using the identifier quote character specified in the config
* @param string $part The part to quote.
protected function quote_identifier_part( $part ) {
// Double up any identifier quotes to escape them.
return $quote_character . \str_replace( $quote_character, $quote_character . $quote_character, $part ) . $quote_character;
* Executes the SELECT query that has been built up by chaining methods on this class.
* Return an array of rows as associative arrays.
* @return array|false The result rows. False if the query failed.
protected function run() {
$query = $this->build_select();
$success = self::execute( $query, $this->values );
if ( $success === false ) {
// If the query fails run the migrations and try again.
// Action is intentionally undocumented and should not be used by third-parties.
\do_action( '_yoast_run_migrations' );
$success = self::execute( $query, $this->values );
$this->reset_idiorm_state();
if ( $success === false ) {
foreach ( $wpdb->last_result as $row ) {
$rows[] = \get_object_vars( $row );
* Resets the Idiorm instance state.
private function reset_idiorm_state() {
$this->result_columns = [ '*' ];
$this->using_default_result_columns = true;
* Returns the raw data wrapped by this ORM instance as an associative array. Column names may optionally be
* supplied as arguments, if so, only those keys will be returned.
* @return array Associative array of the raw data.
public function as_array() {
if ( \func_num_args() === 0 ) {