: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
namespace WPForms\Admin\Payments\Views\Overview;
* Search related methods for Payment and Payment Meta.
const CREDIT_CARD = 'credit_card_last4';
* Customer email meta key.
const EMAIL = 'customer_email';
* Payment title column name.
* Transaction ID column name.
const TRANSACTION_ID = 'transaction_id';
* Subscription ID column name.
const SUBSCRIPTION_ID = 'subscription_id';
* Any column indicator key.
const MODE_EQUALS = 'equals';
const MODE_STARTS = 'starts';
const MODE_CONTAINS = 'contains';
if ( ! self::is_search() ) {
private function hooks() {
add_filter( 'wpforms_db_payments_queries_count_all_query_before_where', [ $this, 'add_search_where_conditions' ], 10, 2 );
add_filter( 'wpforms_db_payments_payment_get_payments_query_before_where', [ $this, 'add_search_where_conditions' ], 10, 2 );
add_filter( 'wpforms_admin_payments_views_overview_filters_renewals_by_subscription_id_query_before_where', [ $this, 'add_search_where_conditions' ], 10, 2 );
public static function is_search() {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
return ! empty( $_GET['s'] );
* Add search where conditions.
* @param string $where Query where string.
* @param array $args Query arguments.
public function add_search_where_conditions( $where, $args ) {
if ( empty( $args['search'] ) ) {
if ( ! empty( $args['search_conditions']['search_mode'] ) && $args['search_conditions']['search_mode'] === self::MODE_CONTAINS ) {
$to_search = explode( ' ', $args['search'] );
$to_search = [ $args['search'] ];
foreach ( $to_search as $counter => $single ) {
$query[] = $this->add_single_search_condition( $single, $args, $counter );
return implode( ' ', $query );
* Add single search condition.
* @param string $word Single searched part.
* @param array $args Query arguments.
* @param int $n Word counter.
private function add_single_search_condition( $word, $args, $n ) {
$mode = $this->prepare_mode( $args );
$where = $this->prepare_where( $args );
list( $operator, $word ) = $this->prepare_operator_and_word( $word, $mode );
$column = $this->prepare_column( $where );
if ( in_array( $column, [ self::EMAIL, self::CREDIT_CARD ], true ) ) {
return $this->select_from_meta_table( $column, $operator, $word, $n );
if ( $column === self::ANY ) {
return $this->select_from_any( $operator, $word, $n );
$payment_table = wpforms()->get( 'payment' )->table_name;
$query = "SELECT id FROM {$payment_table}
WHERE {$payment_table}.{$column} {$operator} {$word}";
return $this->wrap_in_inner_join( $query, $n );
* Prepare search mode part.
* @param array $args Query arguments.
* @return string Mode part for search.
private function prepare_mode( $args ) {
return isset( $args['search_conditions']['search_mode'] ) ? $args['search_conditions']['search_mode'] : self::MODE_EQUALS;
* Prepare search where part.
* @param array $args Query arguments.
* @return string Where part for search.
private function prepare_where( $args ) {
return isset( $args['search_conditions']['search_where'] ) ? $args['search_conditions']['search_where'] : self::TITLE;
* Prepare operator and word parts.
* @param string $word Single word.
* @param string $mode Search mode.
* @return array Array with operator and word parts for search.
private function prepare_operator_and_word( $word, $mode ) {
if ( $mode === self::MODE_CONTAINS ) {
$wpdb->prepare( '%s', '%' . $wpdb->esc_like( $word ) . '%' ),
if ( $mode === self::MODE_STARTS ) {
$wpdb->prepare( '%s', $wpdb->esc_like( $word ) . '%' ),
$wpdb->prepare( '%s', $word ),
* Prepare column to search in.
* @param string $where Search where.
* @return string Column to search in.
private function prepare_column( $where ) {
if ( in_array( $where, [ self::TRANSACTION_ID, self::SUBSCRIPTION_ID, self::EMAIL, self::CREDIT_CARD, self::ANY ], true ) ) {
* Prepare select part to select from payments meta table.
* @param string $meta_key Meta key.
* @param string $operator Comparison operator.
* @param string $word Word to search.
* @param int $n Word count.
* @noinspection CallableParameterUseCaseInTypeContextInspection
private function select_from_meta_table( $meta_key, $operator, $word, $n ) {
$payment_table = wpforms()->get( 'payment' )->table_name;
$meta_table = wpforms()->get( 'payment_meta' )->table_name;
$meta_key = $wpdb->prepare( '%s', $meta_key );
$query = "SELECT id FROM $payment_table
SELECT DISTINCT payment_id FROM $meta_table
WHERE meta_value $operator $word
return $this->wrap_in_inner_join( $query, $n );
* Prepare select part to select from places from both tables.
* @param string $operator Comparison operator.
* @param string $word Word to search.
* @param int $n Word count.
private function select_from_any( $operator, $word, $n ) {
$payment_table = wpforms()->get( 'payment' )->table_name;
$meta_table = wpforms()->get( 'payment_meta' )->table_name;
"SELECT id FROM {$payment_table}
{$payment_table}.%s {$operator} {$word}
OR {$payment_table}.%s {$operator} {$word}
OR {$payment_table}.%s {$operator} {$word}
SELECT DISTINCT payment_id
WHERE meta_value {$operator} {$word}
AND meta_key IN ( '%s', '%s' )
return $this->wrap_in_inner_join( $query, $n );
* Wrap the query in INNER JOIN part.
* @param string $query Partial query.
* @param int $n Word count.
private function wrap_in_inner_join( $query, $n ) {
* Filter to modify the inner join query.
* @param string $query Partial query.
* @param int $n The number of the JOIN clause.
'wpforms_admin_payments_views_overview_search_inner_join_query',
sprintf( 'INNER JOIN ( %1$s ) AS p%2$d ON p.id = p%2$d.id', $query, $n ),