.schema and db/.jsonl entries the database steps ask for come from the work folder the * convert-wpress step fills from database.sql, in exactly the .mmig format. So the import steps run unchanged. * * @package Mench_Migrate */ defined( 'ABSPATH' ) || exit; // phpcs:disable WordPress.WP.AlternativeFunctions -- Streams backups, uploads and job files (often several GB) chunk by chunk inside the plugin's own storage folder and the site being restored; WP_Filesystem has no streaming or locking API. class Mench_Migrate_Wpress_Source implements Mench_Migrate_Import_Source { private const BLOCK = 1048576; private Mench_Migrate_Wpress_Reader $reader; /** Folder holding
.schema and
.jsonl. */ private string $db_dir; public function __construct( Mench_Migrate_Wpress_Reader $reader, string $db_dir ) { $this->reader = $reader; $this->db_dir = $db_dir; } public function reader(): Mench_Migrate_Wpress_Reader { return $this->reader; } public function entries(): array { return $this->reader->entries(); } public function entry_count(): int { return $this->reader->entry_count(); } public function entry_at( int $i ): array { return $this->reader->entry_at( $i ); } public function each(): iterable { return $this->reader->each(); } public function find( string $rel ): ?array { return $this->reader->find( $rel ); } public function manifest(): array { return $this->reader->manifest(); } public function is_encrypted(): bool { return false; } public function read_string( string $rel ): string { $file = $this->converted( $rel ); if ( null === $file ) { return $this->reader->read_string( $rel ); } $data = is_file( $file ) ? file_get_contents( $file ) : false; // phpcs:ignore WordPress.WP.AlternativeFunctions if ( false === $data ) { throw new Mench_Migrate_Archive_Exception( 'unknown_entry', 'archive' ); } return $data; } public function read_head( string $rel, int $max ): string { return null === $this->converted( $rel ) ? $this->reader->read_head( $rel, $max ) : substr( $this->read_string( $rel ), 0, max( 0, $max ) ); } /** Converted table files are copied block by block, resumable from {written}; everything else comes from the archive. */ public function extract( string $rel, string $dest_abs, array $cursor, float $deadline ): array { $file = $this->converted( $rel ); if ( null === $file ) { return $this->reader->extract( $rel, $dest_abs, $cursor, $deadline ); } clearstatcache( true, $file ); $size = is_file( $file ) ? (int) filesize( $file ) : -1; if ( $size < 0 ) { throw new Mench_Migrate_Archive_Exception( 'unknown_entry', 'archive' ); } $written = $cursor['written'] ?? 0; if ( ! is_int( $written ) || $written < 0 || $written > $size ) { throw new Mench_Migrate_Archive_Exception( 'bad_cursor', 'extract' ); } $out = @fopen( $dest_abs, isset( $cursor['written'] ) ? 'c+b' : 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors $in = @fopen( $file, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors if ( false === $out || false === $in ) { throw new Mench_Migrate_Archive_Exception( 'write_failed', 'extract' ); } try { if ( fstat( $out )['size'] < $written ) { throw new Mench_Migrate_Archive_Exception( 'truncated', 'extract' ); } ftruncate( $out, $written ); fseek( $out, $written ); fseek( $in, $written ); while ( $written < $size ) { $data = (string) fread( $in, min( self::BLOCK, $size - $written ) ); if ( '' === $data || fwrite( $out, $data ) !== strlen( $data ) ) { throw new Mench_Migrate_Archive_Exception( 'write_failed', 'extract' ); } $written += strlen( $data ); if ( microtime( true ) >= $deadline ) { break; } } } finally { fclose( $in ); fclose( $out ); } return [ 'done' => $written >= $size, 'cursor' => [ 'written' => $written ] ]; } /** The work file behind db/
.schema|jsonl, or null for an archive entry. */ private function converted( string $rel ): ?string { if ( ! preg_match( '#^db/(.+)\.(schema|jsonl)$#D', $rel, $m ) ) { return null; } if ( ! Mench_Migrate_Schema::is_valid_name( $m[1] ) ) { throw new Mench_Migrate_Archive_Exception( 'unsafe_path', 'archive' ); } return $this->db_dir . '/' . $m[1] . '.' . $m[2]; } }