| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 |
- <?php
- // Isolated SQLite crash worker. Never boots application providers or loads .env.
- require dirname(__DIR__) . '/bootstrap-agent.php';
- use App\Services\Sharing\ShareSettlementService;
- use Carbon\Carbon;
- use Illuminate\Config\Repository;
- use Illuminate\Database\Capsule\Manager;
- use Illuminate\Foundation\Application;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Support\Facades\Facade;
- $database=$argv[1] ?? '';
- $date=$argv[2] ?? '';
- $point=$argv[3] ?? '';
- $directory=realpath(dirname($database));
- if (!$directory || dirname($directory)!==realpath(sys_get_temp_dir())
- || !preg_match('/^bot28-share-reconcile-[a-f0-9]{12}$/D',basename($directory))
- || realpath($database)!==$directory.'/db.sqlite'
- || !in_array($point,['before','after'],true) || !preg_match('/^\d{4}-\d{2}-\d{2}$/D',$date)
- || !function_exists('posix_kill') || !defined('SIGKILL')) {
- throw new RuntimeException('A dedicated temporary recovery database and POSIX signals are required');
- }
- Carbon::setTestNow(Carbon::parse('2026-10-27 12:00:00','Asia/Shanghai'));
- $app=new Application(dirname(__DIR__,2));
- $app->instance('config',new Repository(['app'=>['timezone'=>'Asia/Shanghai'],'third_game'=>['currency'=>'CNY']]));
- Facade::setFacadeApplication($app);
- $db=new Manager($app); $db->addConnection(['driver'=>'sqlite','database'=>$database,'prefix'=>'bot_']);
- $db->setAsGlobal(); $db->bootEloquent(); $app->instance('db',$db->getDatabaseManager());
- $service=new class($date,$point,$directory.'/interrupted.json') extends ShareSettlementService {
- public function __construct(private string $date,private string $point,private string $marker) {}
- public function settle(string $date,bool $write=true,bool $allowToday=false): array
- {
- if ($date===$this->date && $this->point==='before') $this->interrupt($date);
- $result=parent::settle($date,$write,$allowToday);
- if ($date===$this->date && $this->point==='after') $this->interrupt($date);
- return $result;
- }
- private function interrupt(string $date): void
- {
- file_put_contents($this->marker,json_encode(['date'=>$date,'point'=>$this->point,
- 'pending'=>json_decode(DB::table('share_system_state')->where('key','facts_dirty_dates')->value('value'),true)],JSON_THROW_ON_ERROR));
- if (!posix_kill(getmypid(),SIGKILL)) throw new RuntimeException('Could not terminate the recovery worker');
- // Let the signal terminate us; a normal exit would not exercise crash recovery.
- while (true) usleep(1000);
- }
- };
- $service->reconcile();
- throw new RuntimeException('The expected interruption point was not reached');
|