<?php
declare(strict_types=1);
function consumeSQL($kind){global $runtime;$f=fopen($runtime.'/sql-budget.jsonl','c+');ck($f!==false&&flock($f,LOCK_EX),'Budget lock');$raw=stream_get_contents($f);$n=$raw===''?0:count(explode("\n",rtrim($raw,"\n")));ck($n<600,'Unified SQL cap600');if(!is_file($runtime.'/preflight-ready.json'))ck($n<180,'Prepare-before scope/constructor cap180');fseek($f,0,SEEK_END);fwrite($f,json_encode(['n'=>$n+1,'kind'=>$kind,'at'=>microtime(true)])."\n");flock($f,LOCK_UN);fclose($f);chmod($runtime.'/sql-budget.jsonl',0600);}
require __DIR__.'/CoreGate.php';
class OwnedStatement extends PDOStatement{private $b=[];private $db;protected function __construct($db){$this->db=$db;}public function bindValue($p,$v,$t=PDO::PARAM_STR){$ok=parent::bindValue($p,$v,$t);$this->b[(string)$p]=['value'=>$v,'pdo_type'=>$t,'php_type'=>gettype($v),'ok'=>$ok];return $ok;}public function bindParam($p,&$v,$t=PDO::PARAM_STR,$len=0,$opts=null){throw new RuntimeException('No bindParam');}public function execute($params=null){$kind=CoreGate::kind($this->db,$this->queryString);consumeSQL('original.'.$kind);CoreGate::$total++;$cap=in_array(CoreGate::$scenario,['W1_WELLAND_ADD','W2_YUNKANGBAO_ADD'],true)?(getenv('CORE_LANE')==='B'?100:180):(getenv('CORE_LANE')==='B'?60:120);if(preg_match('/^[WR][1-6]_/',CoreGate::$scenario))ck(CoreGate::$total<=$cap,'Exact request native cap');$r=['connection'=>$this->db,'layer'=>'actual PDOStatement.execute','queryString'=>$this->queryString,'bindValue'=>$this->b,'execute_array'=>$params,'operation'=>$kind,'server_microtime'=>microtime(true)];try{$ok=parent::execute($params);$r['success']=$ok;$r['rowCount']=$this->rowCount();CoreGate::rec($r);if(in_array($kind,['INSERT','UPDATE'],true)){ck($r['rowCount']===1,'Each original DML affected1');$table=CoreGate::$last[$this->db];CoreGate::$counts[$kind.'.'.$table]=(CoreGate::$counts[$kind.'.'.$table]??0)+1;}return $ok;}catch(Throwable $e){$r['error']=['class'=>get_class($e),'message'=>$e->getMessage()];CoreGate::rec($r);throw $e;}}}
class OwnedPDO extends PDO{public $db;public function prepare($sql,$options=[]){$kind=CoreGate::kind($this->db,$sql);try{$s=parent::prepare($sql,$options);CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.prepare','queryString'=>$sql,'operation'=>$kind,'success'=>$s!==false]);return $s;}catch(Throwable $e){CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.prepare failure','queryString'=>$sql,'error'=>['class'=>get_class($e),'message'=>$e->getMessage()]]);throw $e;}}public function query($sql,...$args){$s=$this->prepare($sql);$s->execute();if($args)$s->setFetchMode(...$args);return $s;}public function exec($sql){ck(in_array($this->db,['AI','ZHCT'],true)&&in_array(CoreGate::$scenario,['W1_WELLAND_ADD','W2_YUNKANGBAO_ADD'],true)&&preg_match('/^(?:SAVEPOINT trans[2-9]|ROLLBACK TO SAVEPOINT trans[2-9])$/D',$sql),'Only original owned savepoint');consumeSQL('original.SAVEPOINT');$n=parent::exec($sql);CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.exec transaction','queryString'=>$sql,'rowCount'=>$n]);return $n;}public function beginTransaction(){ck(in_array($this->db,['AI','ZHCT'],true)&&in_array(CoreGate::$scenario,['W1_WELLAND_ADD','W2_YUNKANGBAO_ADD'],true),'Only original AI writer transaction');$ok=parent::beginTransaction();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.beginTransaction','success'=>$ok,'server_microtime'=>microtime(true)]);return $ok;}public function commit(){$ok=parent::commit();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.commit','success'=>$ok,'server_microtime'=>microtime(true)]);if($this->db==='ZHCT')CoreGate::$storeCommitted=true;else CoreGate::$mainCommitted=true;return $ok;}public function rollBack(){$ok=parent::rollBack();CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.rollBack','success'=>$ok,'server_microtime'=>microtime(true)]);return $ok;}public function lastInsertId($name=null){$id=parent::lastInsertId($name);$table=CoreGate::$last[$this->db];ck(ctype_digit((string)$id)&&(int)$id>0,'Real generated identity');CoreGate::$generated[$table]=(int)$id;CoreGate::rec(['connection'=>$this->db,'layer'=>'actual PDO.lastInsertId','table'=>$table,'value'=>$id,'php_type'=>gettype($id)]);return $id;}}
trait OwnedConnection{protected function createPdo($dsn,$username,$password,$params){$db=$this->getConfig('prefix')==='yoshop_'?'AI':'ZHCT';$dm=getenv('POC_DIALECT')==='dm';$want=$dm?'dm:host=127.0.0.1;port='.getenv('POC_'.$db.'_PORT').';schema='.getenv('POC_'.$db.'_SCHEMA'):'mysql:host='.getenv('POC_'.$db.'_HOST').';port='.getenv('POC_'.$db.'_PORT').';dbname='.getenv('POC_'.$db.'_SCHEMA').';charset='.getenv('POC_'.$db.'_CHARSET');ck($dsn===$want&&$username===getenv('POC_'.$db.'_USER')&&$password===getenv('POC_'.$db.'_PASSWORD')&&!isset($params[1002]),'Exact approved two connections');$p=new OwnedPDO($dsn,$username,$password,$params);$p->db=$db;ck($p->setAttribute(PDO::ATTR_STATEMENT_CLASS,[OwnedStatement::class,[$db]]),'Native statement capture');return $p;}public function getPDOStatement(string $sql,array $bind=[],bool $master=false,bool $procedure=false):PDOStatement{$db=$this->getConfig('prefix')==='yoshop_'?'AI':'ZHCT';CoreGate::$meta=$sql==='CALL SP_TABLEDEF(?, ?)';try{CoreGate::plan($db,$sql,$bind);CoreGate::rec(['connection'=>$db,'layer'=>'original framework bind plan','queryString'=>$sql,'bind_plan'=>$bind,'procedure'=>$procedure]);return parent::getPDOStatement($sql,$bind,$master,$procedure);}catch(Throwable $e){CoreGate::rec(['connection'=>$db,'layer'=>'source gate/plan error','queryString'=>$sql,'bind_plan'=>$bind,'error'=>['class'=>get_class($e),'message'=>$e->getMessage()]]);throw $e;}finally{CoreGate::$meta=false;}}}
class CoreMysql extends \think\db\connector\Mysql{use OwnedConnection;public function getBuilderClass():string{return \think\db\builder\Mysql::class;}}
class CoreDm extends \app\common\database\Dm{use OwnedConnection;}
