false, 'rows' => array(), 'total' => 0, ); } // Strip BOM if present. $bom = fread( $handle, 3 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread if ( ( chr( 0xEF ) . chr( 0xBB ) . chr( 0xBF ) ) !== $bom ) { rewind( $handle ); } $headers = fgetcsv( $handle, 0, ',', '"', '\\' ); if ( false === $headers ) { fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose return array( 'daily' => false, 'rows' => array(), 'total' => 0, ); } $headers = array_map( 'trim', $headers ); // Auto-detect daily vs overall from presence of a "Date" column. $daily = in_array( 'Date', $headers, true ); $col_post_id = 0; $col_count = 1; $col_date = $daily ? 2 : -1; $col_blog_id = $daily ? 3 : 2; $col_url = $daily ? 4 : 3; $has_url = isset( $headers[ $col_url ] ) && 'URL' === $headers[ $col_url ]; $rows = array(); $total = 0; while ( false !== ( $line = fgetcsv( $handle, 0, ',', '"', '\\' ) ) ) { // phpcs:ignore Generic.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition if ( 1 === count( $line ) && '' === $line[0] ) { continue; } $row = array( 'postnumber' => isset( $line[ $col_post_id ] ) ? absint( $line[ $col_post_id ] ) : 0, 'cntaccess' => isset( $line[ $col_count ] ) ? absint( $line[ $col_count ] ) : 0, 'blog_id' => isset( $line[ $col_blog_id ] ) ? absint( $line[ $col_blog_id ] ) : get_current_blog_id(), ); if ( $daily ) { $row['dp_date'] = isset( $line[ $col_date ] ) ? trim( $line[ $col_date ], " \t\n\r\0\x0B" ) : ''; } if ( $has_url && isset( $line[ $col_url ] ) ) { $row['url'] = trim( $line[ $col_url ], " \t\n\r\0\x0B" ); } $rows[] = $row; ++$total; } fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose return array( 'daily' => $daily, 'rows' => $rows, 'total' => $total, ); } /** * Fetch raw count rows from the database for CSV export. * * Called by both the admin Import/Export page and the WP-CLI * `wp top10 counts export` command. * * @since 4.3.0 * * @param bool $daily Whether to query the daily table. * @param bool $network_wide Whether to collect rows from all sites (multisite only). * @param int $blog_id Specific blog ID (0 = current site). Only used when * $network_wide is false. * @param int $limit Max rows to return (0 = all). * @return array Raw result rows (ARRAY_A). */ public static function fetch_export_data( bool $daily, bool $network_wide = false, int $blog_id = 0, int $limit = 0 ): array { global $wpdb; $table = Database::get_table( $daily ); if ( $network_wide && is_multisite() ) { $results = array(); $sites = get_sites( array( 'number' => 1000 ) ); foreach ( $sites as $site ) { switch_to_blog( (int) $site->blog_id ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $sql = $wpdb->prepare( "SELECT * FROM `{$table}` WHERE blog_id = %d", (int) $site->blog_id ); $rows = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared restore_current_blog(); $results = array_merge( $results, is_array( $rows ) ? $rows : array() ); if ( $limit > 0 && count( $results ) >= $limit ) { break; } } return $limit > 0 ? array_slice( $results, 0, $limit ) : $results; } $bid = $blog_id > 0 ? $blog_id : get_current_blog_id(); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $sql = $wpdb->prepare( "SELECT * FROM `{$table}` WHERE blog_id = %d", $bid ); if ( $limit > 0 ) { $sql .= $wpdb->prepare( ' LIMIT %d', $limit ); } $rows = $wpdb->get_results( $sql, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared return is_array( $rows ) ? $rows : array(); } }