, * max_id:int * } */ public function compute( int $sinceId ): array { $empty = [ 'total_logs' => 0, 'new_logs' => 0, 'new_hits' => 0, 'top' => [], 'max_id' => $sinceId, ]; if ( ! class_exists( \AIOSEO\FourNotFour\Models\Log::class ) ) { return $empty; } // Lifetime total — one-row paginate is the cheapest way to read // it from the model without exposing the underlying COUNT(*). $head = \AIOSEO\FourNotFour\Models\Log::paginate( [ 'number' => 1 ] ); $totalLogs = (int) ( $head['total'] ?? 0 ); $newLogs = 0; $newHits = 0; $maxId = $sinceId; $byUrl = []; $page = 1; while ( true ) { $result = \AIOSEO\FourNotFour\Models\Log::paginate( [ 'number' => self::BATCH, 'offset' => ( $page - 1 ) * self::BATCH, 'orderby' => 'id', 'order' => 'ASC', ] ); $items = (array) ( $result['items'] ?? [] ); if ( empty( $items ) ) { break; } foreach ( $items as $row ) { $id = (int) ( $row->id ?? 0 ); if ( $id <= $sinceId ) { // Predates the previous report — skip but keep the // max_id climbing so we still set a sensible // boundary even when nothing qualifies as new. if ( $id > $maxId ) { $maxId = $id; } continue; } $url = (string) ( $row->url ?? '' ); $hits = (int) ( $row->hits ?? 0 ); ++$newLogs; $newHits += $hits; $maxId = $id; $byUrl[ $url ] = ( $byUrl[ $url ] ?? 0 ) + $hits; } if ( count( $items ) < self::BATCH ) { break; } ++$page; } arsort( $byUrl ); /** * Filter the number of top URLs included in the report body. * * @since 4.0.3 * * @param int $count Default `Stats::TOP_COUNT`. */ $topCount = (int) apply_filters( '404_to_301_email_reports_top_count', self::TOP_COUNT ); $topCount = max( 1, $topCount ); $top = []; foreach ( array_slice( $byUrl, 0, $topCount, true ) as $url => $hits ) { $top[] = [ 'url' => (string) $url, 'hits' => (int) $hits, ]; } return [ 'total_logs' => $totalLogs, 'new_logs' => $newLogs, 'new_hits' => $newHits, 'top' => $top, 'max_id' => $maxId, ]; } }