array(array('subject_hash'=>'char(64)','revision'=>'char(32)'),array('PRIMARY'=>array(0,array('subject_hash'))))); } private function blockTouch(string $hash): ?string { $r=$this->rows('SELECT revision FROM '.$this->table('native_block_touches_v1').' WHERE subject_hash=? LIMIT 2',array($hash)); if(!$r){return null;} if(count($r)!==1||!preg_match('/^[a-f0-9]{32}$/D',$r[0]['revision'])){throw new OG_Privacy_Case_Refusal('native_block_touch_invalid');} return $r[0]['revision']; } /** Called under the exclusive lifecycle lock, in the block mutation transaction. * Before addon installation the old primitive remains available. Partial addon * presence refuses: never silently stop marking while snapshots can exist. */ private function recordBlockTouchIfEnabled(string $hash): void { $r=$this->rows('SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=? AND TABLE_NAME IN (?,?)',array($this->profile['database'],$this->prefix.'native_block_touches_v1',$this->prefix.'native_flag_projection_v1')); if(!$r){return;} if(count($r)!==2){throw new OG_Privacy_Case_Refusal('native_block_touch_unavailable');} $this->validateTableSpecs(self::blockTouchSpecs());$this->blockTouch($hash); $token=bin2hex(random_bytes(16)); $n=$this->sql('INSERT INTO '.$this->table('native_block_touches_v1').' (subject_hash,revision) VALUES (?,?) ON DUPLICATE KEY UPDATE revision=VALUES(revision)',array($hash,$token)); if(!in_array($n,array(1,2),true)||$this->blockTouch($hash)!==$token){throw new OG_Privacy_Case_Refusal('native_block_touch_unconfirmed');} } }