load(); \support\App::loadAllConfig(['route']); \Webman\ThinkOrm\ThinkOrm::start(null); use support\think\Db; $destination = $argv[1] ?? ''; if (!$destination || !is_dir($destination)) throw new RuntimeException('Existing backup directory required'); $snapshot = ['created_at'=>date(DATE_ATOM),'tables'=>[]]; Db::startTrans(); try { foreach (Db::query('SHOW TABLES') as $record) { $table = array_values($record)[0]; if (!preg_match('/^[a-zA-Z0-9_]+$/D',$table)) throw new RuntimeException('Unexpected table identifier'); $schema = Db::query('SHOW CREATE TABLE `'.$table.'`')[0]['Create Table']; $rows = Db::table($table)->select()->toArray(); $snapshot['tables'][$table] = ['schema'=>$schema,'rows'=>$rows]; } } finally { Db::rollback(); } $file = $destination . '/database.json.gz'; if (file_exists($file)) throw new RuntimeException('Refusing to overwrite existing backup'); $json = json_encode($snapshot,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR); if (file_put_contents($file,gzencode($json,9),LOCK_EX) === false) throw new RuntimeException('Backup write failed'); $verified = json_decode(gzdecode(file_get_contents($file)),true,512,JSON_THROW_ON_ERROR); if ($verified !== $snapshot) throw new RuntimeException('Backup verification failed'); $manifest = ['sha256'=>hash_file('sha256',$file),'tables'=>array_map(fn($table)=>count($table['rows']),$snapshot['tables'])]; file_put_contents($destination.'/database-manifest.json',json_encode($manifest,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)); echo 'Verified database backup: '.count($manifest['tables'])." tables\n";