<?php
declare(strict_types=1);
final class CoreGate{
public static $scenario='';public static $ids=[];public static $started=0;public static $meta=false;public static $total=0;public static $counts=[];public static $generated=[];public static $last=[];public static $mainCommitted=false;public static $metadataMap=null;
const COLUMNS='SELECT COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID';const PRIMARY="SELECT cc.COLUMN_NAME FROM USER_CONSTRAINTS c JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME=cc.CONSTRAINT_NAME WHERE c.TABLE_NAME=? AND c.CONSTRAINT_TYPE='P'";
public static function rec($r){$r['scenario']=self::$scenario;event('business-native.jsonl',$r);}public static function a(){global $runtime;return json_decode(file_get_contents($runtime.'/owned-actor.json'),true);}
public static function known($db,$table){global $runtime;if(self::$metadataMap===null)self::$metadataMap=json_decode(file_get_contents($runtime.'/metadata-tables.json'),true);ck(self::$metadataMap[$db]['schema']===getenv('POC_'.$db.'_SCHEMA'),'Exact metadata schema');ck(is_string($table)&&preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/D',$table)&&in_array(strtolower($table),self::$metadataMap[$db]['tables'],true),'Frozen actual existing metadata table');}
public static function kind($db,$sql){$plain=preg_replace("/'(?:''|\\\\.|[^'\\\\])*'/s",'?', $sql);ck(!preg_match('/;|--|\/\*|\*\/|#|\b(?:UPDATE|DELETE|REPLACE|MERGE|CREATE|ALTER|DROP|TRUNCATE|GRANT|REVOKE|OUTFILE|DUMPFILE|LOAD|EXEC|EXECUTE|SLEEP|BENCHMARK|UTL_HTTP|NEXTVAL)\b/i',$plain),'No UPDATE/Delete/DDL/unknown SQL');if($sql===self::COLUMNS||$sql===self::PRIMARY)return 'metadata_SELECT';if(preg_match('/^SHOW FULL COLUMNS FROM `([A-Za-z_][A-Za-z0-9_]*)`$/D',$sql,$m)){self::known($db,$m[1]);return 'metadata_SHOW';}if($sql==='CALL SP_TABLEDEF(?, ?)'&&self::$meta)return 'metadata_CALL';ck(!preg_match('/\bUSER_(?:TAB_COLUMNS|CONSTRAINTS|CONS_COLUMNS|TABLES)\b/i',$sql),'Exact original catalog shape');if(preg_match('/^\s*SELECT\b/i',$sql)){ck(!preg_match('/\bINTO\b|\bFOR\s+UPDATE\b/i',$plain),'Only read SELECT');preg_match_all('/\b(yoshop|ydy)_([a-z0-9_]+)/i',$sql,$m,PREG_SET_ORDER);ck(count($m)>0,'Scoped data table');$allowed=$db==='AI'?['user','staff','user_disease','habit','user_other_info','user_bind','config_enum','store','store_setting','upload_file','user_oauth']:['user'];foreach($m as $r)ck(($db==='AI'?strtolower($r[1])==='yoshop':strtolower($r[1])==='ydy')&&in_array(strtolower($r[2]),$allowed,true),'Exact approved data table');return 'SELECT';}if(preg_match('/^INSERT INTO [`"]?(yoshop_(?:user_disease|habit))[`"]?\b/i',$sql)){ck($db==='AI'&&self::$scenario==='W1_DISEASE_EDIT','Only approved writer');return 'INSERT';}throw new RuntimeException('Unknown original SQL');}
public static function plan($db,$sql,$bind){global $runtime;$kind=self::kind($db,$sql);$values=array_values(array_map(static function($v){return is_array($v)?$v[0]:$v;},$bind));if($kind==='metadata_SELECT'){ck(count($values)===1,'One metadata table');self::known($db,$values[0]);return;}if($kind==='metadata_SHOW'){ck($values===[],'Source SHOW0bind');return;}if($kind==='metadata_CALL'){ck(count($values)===2&&$values[0]===strtoupper((string)getenv('POC_'.$db.'_SCHEMA')),'Exact metadata schema');self::known($db,$values[1]);return;}$a=self::a();$flat=array_map('strval',$values);if($kind==='SELECT'){if(preg_match('/\b(?:yoshop_(?:user|user_disease|user_other_info|user_bind|user_oauth)|ydy_user)\b/i',$sql))ck(in_array((string)$a['user_id'],$flat,true),'Own User data only');if(preg_match('/\byoshop_staff\b/i',$sql))ck(in_array((string)$a['user_id'],$flat,true)||in_array((string)$a['staff_id'],$flat,true),'Own Staff data only');if(preg_match('/\byoshop_habit\b/i',$sql))ck(in_array((string)$a['staff_id'],$flat,true),'Own Habit data only');if(preg_match('/\byoshop_store(?:_setting)?\b/i',$sql))ck(in_array('10001',$flat,true),'Only exact tenant config');if(stripos($sql,'yoshop_config_enum')!==false)ck(preg_match('/enum_type\b/i',$sql)&&count(array_intersect($flat,['2','14','15','16','17']))===1&&in_array('0',$flat,true),'Only five original global catalog types');if(stripos($sql,'yoshop_upload_file')!==false){$lease=json_decode(file_get_contents($runtime.'/tenant-file-lease.json'),true);$clean=str_replace(['`','"'],'',$sql);ck(preg_match('/file_id\s*=\s*:(\w+)/i',$clean,$m)&&isset($bind[$m[1]]),'Exact file predicate');$b=$bind[$m[1]];ck(in_array((string)(is_array($b)?$b[0]:$b),$lease['allowed_file_ids'],true)&&in_array('10001',$flat,true),'Exact tenant related file');}return;}
$clean=preg_replace('/[`"]([A-Za-z_][A-Za-z0-9_]*)[`"]/','$1',$sql);$pairs=[];if(preg_match('/^INSERT INTO (\w+) SET (.+)$/is',$clean,$m)){foreach(explode(',',$m[2]) as $f){ck(preg_match('/^\s*(\w+)\s*=\s*(:\w+)\s*$/D',$f,$p),'Single-row named INSERT');$pairs[]=[$p[1],$p[2]];}}else{ck(preg_match('/^INSERT INTO (\w+)\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)\s*$/is',$clean,$m),'Original single row VALUES');$ks=explode(',',$m[2]);$vs=explode(',',$m[3]);ck(count($ks)===count($vs),'Equal field count');foreach($ks as $i=>$k)$pairs[]=[trim($k),trim($vs[$i])];}$table=strtolower($m[1]);ck(empty(self::$counts['INSERT.'.$table]),'Exactly once each INSERT');$schema=json_decode(file_get_contents($runtime.'/schema.json'),true)[substr($table,7)]['columns'];$types=[];foreach($schema as $c)$types[strtolower($c['column_name'])]=strtolower($c['data_type']);$v=[];foreach($pairs as [$k,$p]){ck(preg_match('/^:\w+$/D',$p)&&isset($bind[substr($p,1)])&&!isset($v[$k]),'Exact bound original field');$b=$bind[substr($p,1)];$v[$k]=$b[0];$type=$types[$k]??'';ck($type!==''&&$b[1]===(preg_match('/int|bool|bit/',$type)?1:2),'Original metadata/PDO type');}$want=$table==='yoshop_user_disease'?['user_id'=>$a['user_id'],'disease_type'=>4,'store_id'=>10001,'create_time'=>'@clock','update_time'=>'@clock']:['staff_id'=>$a['staff_id'],'habit'=>3,'store_id'=>10001,'create_by'=>$a['marker'],'create_time'=>'@clock','update_time'=>'@clock'];$ks=array_keys($v);$ws=array_keys($want);sort($ks);sort($ws);ck($ks===$ws,'Exact own INSERT field-set');foreach($want as $k=>$value){if($value==='@clock'){if(preg_match('/int/',$types[$k]))ck(is_int($v[$k])&&$v[$k]>=self::$started-2&&$v[$k]<=time()+2,'Original int clock');else ck(is_string($v[$k])&&preg_match('/^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\.\d{6}$/D',$v[$k])&&abs(strtotime($v[$k])-time())<=3,'Original auto6micro clock');}else ck($v[$k]===$value,'Exact own value and original PHPtype '.$k);}self::$last[$db]=$table;
}}
