substr( bcal_now(), 0, 16 ) . ':00', 'to' => '', 'day' => '', 'city' => 0, 'venue' => 0, 'categories' => array(), 'organizers' => array(), 'event' => 0, 'exclude_event' => 0, 'featured' => false, 'include_hidden' => false, 'orderby' => 'start', 'limit' => 20, 'offset' => 0, 'count' => false, 'fields' => 'all', ) ); $table = BCal_Install::table(); $where = array( '1=1' ); $params = array(); if ( $args['day'] ) { $where[] = 'o.start_dt <= %s AND o.end_dt >= %s'; $params[] = $args['day'] . ' 23:59:59'; $params[] = $args['day'] . ' 00:00:00'; } else { if ( $args['from'] ) { $where[] = 'o.end_dt >= %s'; $params[] = $args['from']; } if ( $args['to'] ) { $where[] = 'o.start_dt <= %s'; $params[] = $args['to']; } } foreach ( array( 'city' => 'city_id', 'venue' => 'venue_id', 'event' => 'event_id' ) as $arg => $column ) { if ( $args[ $arg ] ) { $where[] = "o.{$column} = %d"; $params[] = (int) $args[ $arg ]; } } if ( $args['exclude_event'] ) { $where[] = 'o.event_id <> %d'; $params[] = (int) $args['exclude_event']; } if ( $args['featured'] ) { $where[] = '( o.featured = 1 OR o.pick = 1 )'; } if ( ! $args['include_hidden'] ) { $where[] = 'o.hidden = 0'; } foreach ( array( 'categories' => 'bcal_category', 'organizers' => 'bcal_organizer' ) as $arg => $taxonomy ) { $tt_ids = self::tt_ids( (array) $args[ $arg ], $taxonomy ); if ( $args[ $arg ] && ! $tt_ids ) { return $args['count'] ? 0 : array(); } if ( $tt_ids ) { $where[] = "EXISTS ( SELECT 1 FROM {$wpdb->term_relationships} tr WHERE tr.object_id = o.event_id AND tr.term_taxonomy_id IN (" . implode( ',', array_map( 'intval', $tt_ids ) ) . ') )'; } } // Every fragment in $where is a fixed string with placeholders; all // values travel through $params and $wpdb->prepare() below. $where_sql = implode( ' AND ', $where ); array_unshift( $params, $table ); if ( $args['count'] ) { $sql = $wpdb->prepare( 'SELECT COUNT(*) FROM %i o WHERE ' . $where_sql, ...$params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Fixed fragments built above, values are placeholders. } else { $select = 'light' === $args['fields'] ? 'o.id, o.event_id, o.start_dt, o.end_dt, o.start_date, o.all_day' : 'o.*'; $order = 'score' === $args['orderby'] ? 'o.score IS NULL, o.score DESC, o.start_dt ASC' : 'o.start_dt ASC, o.id ASC'; $params[] = max( 1, (int) $args['limit'] ); $params[] = max( 0, (int) $args['offset'] ); $sql = $wpdb->prepare( 'SELECT ' . $select . ' FROM %i o WHERE ' . $where_sql . ' ORDER BY ' . $order . ' LIMIT %d OFFSET %d', ...$params ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Fixed fragments built above, values are placeholders. } $last_changed = wp_cache_get( 'last_changed', 'bcal' ); if ( ! $last_changed ) { $last_changed = microtime(); wp_cache_set( 'last_changed', $last_changed, 'bcal' ); } $cache_key = 'q:' . md5( $sql ) . ':' . $last_changed; $cached = wp_cache_get( $cache_key, 'bcal' ); if ( false !== $cached ) { return $cached; } if ( $args['count'] ) { $result = (int) $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Prepared above. } else { $result = (array) $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Prepared above. $ids = array_values( array_unique( wp_list_pluck( $result, 'event_id' ) ) ); if ( $ids ) { _prime_post_caches( array_map( 'intval', $ids ), true, true ); } } wp_cache_set( $cache_key, $result, 'bcal', 5 * MINUTE_IN_SECONDS ); return $result; } /** * Term taxonomy IDs including children. * * @param array $term_ids Term IDs. * @param string $taxonomy Taxonomy. * @return array */ private static function tt_ids( $term_ids, $taxonomy ) { $out = array(); foreach ( array_filter( array_map( 'intval', $term_ids ) ) as $term_id ) { $ids = array( $term_id ); if ( is_taxonomy_hierarchical( $taxonomy ) ) { $children = get_term_children( $term_id, $taxonomy ); if ( ! is_wp_error( $children ) ) { $ids = array_merge( $ids, $children ); } } foreach ( $ids as $id ) { $term = get_term( (int) $id, $taxonomy ); if ( $term && ! is_wp_error( $term ) ) { $out[] = (int) $term->term_taxonomy_id; } } } return array_values( array_unique( $out ) ); } /** * Keeps the first occurrence of each event. * * @param array $rows Rows. * @param int $limit Max rows. * @return array */ public static function unique_events( $rows, $limit = 0 ) { $seen = array(); $out = array(); foreach ( $rows as $row ) { if ( isset( $seen[ $row->event_id ] ) ) { continue; } $seen[ $row->event_id ] = true; $out[] = $row; if ( $limit && count( $out ) >= $limit ) { break; } } return $out; } }