<?php
class NRGate {
 public static $journal=[],$deny=false;
 public static $tables=['ydy_nutrition','ydy_nutrition_set','ydy_nutrition_category','ydy_nutrition_time','ydy_user'];
 public static function sql($sql){
  if(preg_match('/^CALL SP_TABLEDEF\(\?, \?\)$/D',$sql))return 'metadata-definition';
  if(preg_match('/^SHOW (?:FULL )?COLUMNS FROM `?(ydy_[a-z_]+)`?$/iD',$sql,$m)&&in_array(strtolower($m[1]),self::$tables,true))return 'metadata-show';
  if(!preg_match('/^\s*SELECT\b/i',$sql))throw new RuntimeException('Original lists readonly gate');$plain=preg_replace("/'(?:''|[^'])*'/s","''",$sql);
  if(preg_match('/;|--|\/\*|\b(INSERT|UPDATE|DELETE|MERGE|DROP|ALTER|CREATE|TRUNCATE|CALL|EXEC|INTO|OUTFILE|SLEEP|BENCHMARK|DBMS_LOCK|DBLINK)\b/i',$plain))throw new RuntimeException('Unsafe readonly SQL');
  preg_match_all('/\b(?:FROM|JOIN)\s+([`"]?[A-Za-z_][A-Za-z_0-9]*[`"]?)/i',$plain,$m);$allowed=array_merge(self::$tables,['user_tab_columns','user_constraints','user_cons_columns','user_objects']);if(!$m[1])throw new RuntimeException('No scoped table');foreach($m[1]as $table)if(!in_array(strtolower(str_replace(['`','"'],'',$table)),$allowed,true))throw new RuntimeException('Unfrozen native read table');return 'select';
 }
}
class NRStatement extends PDOStatement {
 private $bound=[];protected function __construct(){}
 public function bindValue($p,$v,$t=PDO::PARAM_STR){$ok=parent::bindValue($p,$v,$t);$this->bound[(string)$p]=['value'=>$v,'type'=>$t,'php_type'=>gettype($v),'ok'=>$ok];return $ok;}
 public function execute($params=null){NRGate::sql($this->queryString);if(strpos($this->queryString,'CALL ')===0&&(($this->bound['1']['value']??'')!=='ZHCT_APP'||!in_array(strtolower($this->bound['2']['value']??''),NRGate::$tables,true)))throw new RuntimeException('Exact metadata CALL binds');$e=['layer'=>'actual PDOStatement.execute','queryString'=>$this->queryString,'bound'=>$this->bound,'execute_params'=>$params,'success'=>false];try{$ok=parent::execute($params);$e['success']=$ok;$e['rowCount']=$this->rowCount();$e['errorInfo']=$this->errorInfo();NRGate::$journal[]=$e;return $ok;}catch(Throwable $x){$e['error']=['class'=>get_class($x),'message'=>$x->getMessage(),'errorInfo'=>$this->errorInfo()];NRGate::$journal[]=$e;throw $x;}}
 public function fetchAll($how=null,$class_name=null,$ctor_args=null){$args=func_get_args();$rows=parent::fetchAll(...$args);if(count($rows)>200)throw new RuntimeException('Native row cap');NRGate::$journal[]=['layer'=>'actual PDOStatement.fetchAll','queryString'=>$this->queryString,'rows'=>$rows];return $rows;}
}
class NRPdo extends PDO {
 public function prepare($sql,$options=[]){$op=NRGate::sql($sql);$s=parent::prepare($sql,$options);NRGate::$journal[]=['layer'=>'actual PDO.prepare','queryString'=>$sql,'operation'=>$op,'prepared'=>$s!==false];return $s;}
 public function query($sql,...$args){NRGate::sql($sql);$s=parent::query($sql,...$args);NRGate::$journal[]=['layer'=>'actual PDO.query','queryString'=>$sql];return $s;}
 public function exec($sql){throw new RuntimeException('No lists exec');}
 public function beginTransaction(){throw new RuntimeException('No lists transaction');}
}
