<?php
declare(strict_types=1);
$root=$argv[1]??'';$projector=$argv[2]??__DIR__.'/OrderReadResponseCandidate.php';$mode=$argv[3]??'green';require $root.'/vendor/autoload.php';require $projector;
function getStoreId():int{return 2147001111;}
function app_name():string{return 'store';}
$common=file_get_contents($root.'/app/common.php');if(!preg_match('/^function format_time\([^\n]+\n\{.*?^\}/ms',$common,$helper))throw new RuntimeException('Exact original format_time missing');eval($helper[0]);
$meta=json_decode(file_get_contents(__DIR__.'/structure-B.json'),true)['tables'];
trait ProjectionNoDbFields {public function connect(array $config=[],$linkNum=0,$autoConnection=false):\PDO{throw new RuntimeException('No PDO permitted');}public function getFields(string $table):array{$table=trim(str_replace(['yoshop_','`','"'],'',$table));$r=[];foreach(($GLOBALS['projection_meta'][$table]['columns']??[]) as $c)$r[$c['column_name']]=['name'=>$c['column_name'],'type'=>$c['column_type'],'primary'=>in_array($c['column_name'],['order_id','order_goods_id','user_id','file_id'],true),'autoinc'=>false];return $r;}}
class ProjectionMysql extends \think\db\connector\Mysql {use ProjectionNoDbFields;}
class ProjectionDm extends \app\common\database\Dm {use ProjectionNoDbFields;}
$GLOBALS['projection_meta']=$meta;$count=0;$check=static function($ok,$why)use(&$count){$count++;if(!$ok)throw new RuntimeException($why);};$same=static function($a,$b){return serialize($a)===serialize($b);};
$manager=static function($dm){$db=new \think\DbManager();$db->setConfig(['default'=>'test','connections'=>['test'=>['type'=>'\\'.($dm?ProjectionDm::class:ProjectionMysql::class),'builder'=>$dm?\app\common\database\DmBuilder::class:\think\db\builder\Mysql::class,'prefix'=>'yoshop_','fields_strict'=>false]]]);\think\Container::getInstance()->instance(\think\DbManager::class,$db);return $db;};
foreach([false,true] as $dm){$db=$manager($dm);foreach([0,0.0,0.01,12.34,99999999.99,-99999999.99,99999999.98,100000000,10000000000000000,1.234,INF,-INF,NAN,'0.00','12.30','12.3','00012.30','9999999999999999.99',null,false,true,'abc','',[]] as $i=>$value){$row=['total_price'=>$value,'order_id'=>99,'weight'=>1.5,'other'=>['total_price'=>0,'balance'=>0],'user'=>['balance'=>$value,'user_id'=>91,'other_money'=>0],'goods'=>[['goods_price'=>$value,'goods_weight'=>0.5,'goods_id'=>90]],'trade'=>['total_amount'=>0],'delivery'=>[]];$before=$row;$actual=\app\common\service\OrderReadResponse::orderDetail($row);$want=$row;$expectedMoney=['0.00','0.00','0.01','12.34','99999999.99','-99999999.99','99999999.98'];if($dm&&array_key_exists($i,$expectedMoney)){$v=$expectedMoney[$i];$want['total_price']=$v;$want['user']['balance']=$v;$want['goods'][0]['goods_price']=$v;}$check($same($actual,$want),'Exact scalar/type/shape/domain '.$dm.' '.$i);$check($same($row,$before),'Raw DTO unchanged '.$dm.' '.$i);}
foreach([[],['data'=>[]],['data'=>null],['data'=>(object)[]],['data'=>[[]]],['data'=>[['user'=>null,'goods'=>[],'total_price'=>null]]]] as $page){$before=$page;$check($same(\app\common\service\OrderReadResponse::orderList($page),$before),'Empty/null/missing order shape');$check($same(\app\common\service\OrderReadResponse::rechargeList($page),$before),'Empty/null/missing recharge shape');}
$d=__DIR__.'/private-evidence/'.($dm?'C':'B');foreach(file($d.'/private-wire.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $line){$wire=json_decode($line,true);if(strpos($wire['scenario'],'_GREEN_')===false)continue;$body=json_decode($wire['raw'],true);if($body['status']!==200)continue;$dto=$body['data'];$before=$dto;if(strpos($wire['path'],'user.recharge')!==false)$dto['list']=\app\common\service\OrderReadResponse::rechargeList($dto['list']);elseif(isset($dto['list']))$dto['list']=\app\common\service\OrderReadResponse::orderList($dto['list']);else$dto['detail']=\app\common\service\OrderReadResponse::orderDetail($dto['detail']);if(!$dm)$check($same($dto,$before),'Whole real MySQL DTO is byte/type equivalent');elseif($mode==='red'){if(!empty($before['list']['data'])){$key=strpos($wire['path'],'user.recharge')!==false?'pay_price':'total_price';$check(is_string($before['list']['data'][0][$key]),'Actual original DM HTTP DEC10,2 must be string');}elseif(isset($before['detail']))$check(is_string($before['detail']['total_price']),'Actual original DM detail DEC10,2 must be string');}$check($same($body['data'],$before),'Actual captured wire input never mutated');}
// Real ThinkORM serialization/getters, with physical metadata and no PDO.
$w=json_decode(file($d.'/private-wire.jsonl',FILE_IGNORE_NEW_LINES)[0],true);$row=$w['original']['order'][0];$rawModel=new \app\store\model\Order($row);$serialized=$rawModel->toArray();$check($serialized['state_text']==='已取消','Real getStateTextAttr');$check($serialized['order_price']==='0.00','Real getOrderPriceAttr');$check($serialized['update_price']===['symbol'=>'+','value'=>'0.00'],'Real getUpdatePriceAttr');$check($serialized['pay_time']===''&&$serialized['delivery_time']===''&&$serialized['receipt_time']==='','Real formatted zero-time getters');$check($serialized['order_source_data']===[],'Real JSON empty-object getter');$projected=\app\common\service\OrderReadResponse::orderDetail($serialized);if(!$dm)$check($same($projected,$serialized),'Real MySQL ORM whole DTO exact unchanged');$check($db->getQueryTimes()===0,'No SQL executed during real ORM and projection');}
echo json_encode(['assertions'=>$count,'PDO_opens'=>0,'real_ORM_serialization'=>true,'mode'=>$mode]),"\n";
