, incomplete: bool} Paths are relative to the stage base. `incomplete` means a subdirectory could not be walked. */ public static function cleanup_stage( string $stage_base, callable $is_protected, int $older_than = 0 ): array { $removed = []; $incomplete = false; $base = rtrim( realpath( $stage_base ) ?: $stage_base, DIRECTORY_SEPARATOR ); $cutoff = $older_than > 0 ? time() - $older_than : 0; if ( '' === $base || ! is_dir( $base ) ) { return [ 'removed' => 0, 'files' => [], 'incomplete' => false, ]; } try { $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $base, \FilesystemIterator::SKIP_DOTS ), \RecursiveIteratorIterator::LEAVES_ONLY ); foreach ( $iterator as $item ) { if ( ! $item->isFile() || ! TransferTempFile::is_temp( $item->getFilename() ) ) { continue; } $absolute = $item->getPathname(); if ( $is_protected( $absolute ) ) { continue; } // A write in progress has a current mtime, so the age floor skips it. if ( $cutoff > 0 ) { $mtime = $item->getMTime(); if ( false !== $mtime && (int) $mtime > $cutoff ) { continue; } } wp_delete_file( $absolute ); if ( ! is_file( $absolute ) ) { $removed[] = str_replace( '\\', '/', substr( $absolute, strlen( $base ) + 1 ) ); } } } catch ( \Throwable $e ) { // Leftover scratch is untidy, not incorrect, so a failed cleanup must not fail a landed transfer. $incomplete = true; unset( $e ); } return [ 'removed' => count( $removed ), 'files' => $removed, 'incomplete' => $incomplete, ]; } }