| 1234567891011121314151617181920212223242526272829 |
- <?php
- /** Internal maintenance backup: output contains only counts and paths, never row data. */
- require dirname(__DIR__) . '/vendor/autoload.php';
- \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->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";
|