|null|WP_Error Null when there is none, WP_Error when it is unusable. */ public static function load( string $job_id ) { $stored = get_option( self::option( $job_id ) ); if ( false === $stored ) { return null; } if ( ! is_array( $stored ) || ! isset( $stored['schema'] ) ) { return new WP_Error( 'transtrix_state_unreadable', __( 'The saved progress cannot be read. Start it again.', 'transtrix' ) ); } if ( self::SCHEMA !== (int) $stored['schema'] ) { return new WP_Error( 'transtrix_state_schema', __( 'This was started by a different version of the plugin. Start it again.', 'transtrix' ) ); } return $stored; } /** * Write a job's cursor. * * @param string $job_id Job id. * @param array $state Cursor. * @return void */ public static function save( string $job_id, array $state ): void { $state['schema'] = self::SCHEMA; $state['updated_at'] = gmdate( 'c' ); update_option( self::option( $job_id ), $state, false ); } /** * Drop a job's cursor. * * @param string $job_id Job id. * @return void */ public static function forget( string $job_id ): void { delete_option( self::option( $job_id ) ); } /** * The job's work directory, created on first use. * * Holds only bookkeeping — the file list and the archive entry table. No * content is ever staged here: copying a site's uploads into a work * directory before archiving them needs twice the disk, on exactly the * hosts least likely to have it. * * @param string $job_id Job id. * @return string|WP_Error Trailing-slashed path. */ public static function work_dir( string $job_id ) { if ( 1 !== preg_match( '/^[A-Za-z0-9_.]{1,64}$/', $job_id ) ) { return new WP_Error( 'transtrix_invalid_job_id', __( 'That export or import could not be found.', 'transtrix' ) ); } $dir = Package_Store::dir(); if ( is_wp_error( $dir ) ) { return $dir; } $work = $dir . 'work/' . $job_id . '/'; if ( ! wp_mkdir_p( $work ) ) { return new WP_Error( 'transtrix_work_dir_failed', __( 'A temporary folder could not be created. Check that the uploads folder is writable.', 'transtrix' ) ); } return $work; } /** * Remove a job's work directory and everything in it. * * @param string $job_id Job id. * @return void */ public static function clean_work_dir( string $job_id ): void { $work = self::work_dir( $job_id ); if ( is_wp_error( $work ) || ! is_dir( $work ) ) { return; } foreach ( (array) glob( $work . '*' ) as $file ) { if ( is_file( $file ) ) { wp_delete_file( $file ); } } @rmdir( $work ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_rmdir -- direct file I/O; WP_Filesystem needs credentials a REST request does not have. } /** * Option name for a job. * * @param string $job_id Job id. * @return string */ private static function option( string $job_id ): string { return self::PREFIX . preg_replace( '/[^A-Za-z0-9_.]/', '', $job_id ); } }