$post_types Optional. Post types to check. Default ['post', 'page']. * * @return array Each object has ID, post_title, post_name, post_type, post_date. */ public function detect( int $limit = 500, array $post_types = array( 'post', 'page' ) ): array { global $wpdb; if ( ! isset( $wpdb ) || array() === $post_types ) { return array(); } $limit = max( 1, min( $limit, 5000 ) ); $table = TableNames::report_links(); $type_count = count( $post_types ); if ( $type_count <= 0 ) { return array(); } $placeholders = implode( ', ', array_fill( 0, $type_count, '%s' ) ); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $placeholders is built from %s literals; dynamic IN() list is the standard WP pattern. $rows = $wpdb->get_results( $wpdb->prepare( "SELECT p.ID, p.post_title, p.post_name, p.post_type, p.post_date FROM {$wpdb->posts} AS p LEFT JOIN %i AS rl ON rl.target_id = p.ID AND rl.is_internal = 1 WHERE p.post_status = 'publish' AND p.post_type IN ({$placeholders}) AND rl.id IS NULL ORDER BY p.post_date DESC LIMIT %d", array_merge( array( $table ), $post_types, array( $limit ) ) ) ); // phpcs:enable return is_array( $rows ) ? $rows : array(); } /** * Returns the count of orphaned posts without fetching them. * * @since 1.0.0 * * @param array $post_types Optional. Post types to check. Default ['post', 'page']. * * @return int */ public function count( array $post_types = array( 'post', 'page' ) ): int { global $wpdb; if ( ! isset( $wpdb ) || array() === $post_types ) { return 0; } $table = TableNames::report_links(); $type_count = count( $post_types ); $placeholders = implode( ', ', array_fill( 0, $type_count, '%s' ) ); // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- $placeholders is built from %s literals. $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(p.ID) FROM {$wpdb->posts} AS p LEFT JOIN %i AS rl ON rl.target_id = p.ID AND rl.is_internal = 1 WHERE p.post_status = 'publish' AND p.post_type IN ({$placeholders}) AND rl.id IS NULL", array_merge( array( $table ), $post_types ) ) ); // phpcs:enable return is_numeric( $count ) ? (int) $count : 0; } }