getPathname(); if ( $current->isDir() ) { foreach ( $exclude_names as $excluded ) { if ( false !== stripos( $pathname, $excluded ) ) { return false; } } return true; } if ( $current->isFile() ) { $extension = strtolower( $current->getExtension() ); return in_array( $extension, $extensions, true ); } return false; } ), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ( $iterator as $file_info ) { $file_path = $file_info->getPathname(); $this->scan_file( $file_path, $term, $results ); } } ksort( $results ); return $results; } /** * Scan a single file line-by-line. * * @param string $file_path File path. * @param string $term Search term. * @param array $results Results array by reference. * @return void */ private function scan_file( $file_path, $term, &$results ) { try { // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Line-by-line scanning is used intentionally to avoid loading full files into memory. $handle = @fopen( $file_path, 'r' ); if ( ! $handle ) { return; } $line_number = 0; while ( false !== ( $line = fgets( $handle ) ) ) { $line_number++; if ( false !== stripos( $line, $term ) ) { $results[ $file_path ][] = array( 'line' => $line_number, 'text' => rtrim( $line, "\r\n" ), ); } } // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Paired with fopen() for efficient line-by-line scanning. fclose( $handle ); } catch ( \Throwable $e ) { // Ignore unreadable files. } } }