files[] = $path; $this->directories[] = $dir; $this->register_cleanup(); return $path; } public function cleanup(): void { foreach ( array_reverse( $this->files ) as $path ) { if ( is_file( $path ) ) { @unlink( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.unlink_unlink -- Best-effort request-shutdown cleanup. } } foreach ( array_reverse( $this->directories ) as $dir ) { if ( is_dir( $dir ) ) { @rmdir( $dir ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Best-effort request-shutdown cleanup. } } $this->files = array(); $this->directories = array(); } public static function cleanup_orphans( int $older_than_seconds = 86400 ): void { $base = function_exists( 'get_temp_dir' ) ? (string) get_temp_dir() : sys_get_temp_dir(); $base = rtrim( $base, '/\\' ) . DIRECTORY_SEPARATOR; $matches = glob( $base . 'lmcc-*', GLOB_ONLYDIR ); if ( false === $matches ) { return; } $base_real = realpath( $base ); if ( false === $base_real ) { return; } $base_real = rtrim( $base_real, '/\\' ) . DIRECTORY_SEPARATOR; $cutoff = time() - max( 3600, $older_than_seconds ); foreach ( $matches as $dir ) { $name = basename( $dir ); if ( 1 !== preg_match( '/^lmcc-[a-f0-9]{16}$/', $name ) || is_link( $dir ) ) { continue; } $dir_real = realpath( $dir ); if ( false === $dir_real || ! str_starts_with( $dir_real . DIRECTORY_SEPARATOR, $base_real ) ) { continue; } $modified = filemtime( $dir_real ); if ( false === $modified || $modified > $cutoff ) { continue; } $files = glob( $dir_real . DIRECTORY_SEPARATOR . '*.pdf' ); if ( false === $files ) { continue; } foreach ( $files as $path ) { $path_real = realpath( $path ); if ( false !== $path_real && ! is_link( $path ) && dirname( $path_real ) === $dir_real && is_file( $path_real ) ) { @unlink( $path_real ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.unlink_unlink -- Restricted orphan temp cleanup after realpath/symlink boundary checks. } } $remaining = glob( $dir_real . DIRECTORY_SEPARATOR . '*' ); if ( is_array( $remaining ) && empty( $remaining ) ) { @rmdir( $dir_real ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- Restricted orphan temp cleanup. } } } private function register_cleanup(): void { if ( $this->cleanup_registered ) { return; } register_shutdown_function( array( $this, 'cleanup' ) ); $this->cleanup_registered = true; } }