<?php
declare(strict_types=1);

function runtimeIdentity(): array
{
    $v=PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;
    if(!in_array($v,['7.3','7.4'],true)||PHP_INT_SIZE!==8||defined('PDO::EXTRA_DECIMAL')){throw new RuntimeException('Old runtime gate');}
    $hashes=[
        '7.3'=>[
            '/www/server/php/73/bin/php'=>'96f450e052da40514907e5112d5bb6bdba874d61426896a5d9730ae64b98214d',
            '/home/dmdba/dmdbms/drivers/php_pdo/libphp73_dm.so'=>'ea7631923962f7dc48298ca7a6f3d00a358d99b9997fe19d77ffbd8c66831045',
            '/home/dmdba/dmdbms/drivers/php_pdo/php73_pdo_dm.so'=>'6783f7695cae4228fa2c06cab6bf7b7e113f65dcb27c249237ce7ead89f0a6c9',
        ],
        '7.4'=>[
            '/www/server/php/74/bin/php'=>'7de65d98a793410b40f59da8fde6fcc0c2d0d40ba7788b0d966a319a29b9817e',
            '/home/dmdba/dmdbms/drivers/php_pdo/libphp74_dm.so'=>'342fe4e9b28a2c8066e9b940c96d403ed93f281b8a80325ba2ac4a01af7b1991',
            '/home/dmdba/dmdbms/drivers/php_pdo/php74_pdo_dm.so'=>'5f44eb2b197faf701c483e7e7ae995add204a55e684b6f9dfc61ee64bf1652bc',
        ],
    ];
    $expected=$hashes[$v];$expected['/home/dmdba/dmdbms/bin/libdmdpi.so']='3dc07a419c3fc023ef05db1d8cee2e1f97712bc7c9026cf6f73b69ad692e9fc2';
    foreach($expected as $path=>$sha){if(realpath($path)!==$path||hash_file('sha256',$path)!==$sha){throw new RuntimeException('Installed component identity gate');}}
    if(realpath(PHP_BINARY)!==array_keys($expected)[0]){throw new RuntimeException('PHP identity gate');}
    $limit=posix_getrlimit();if(($limit['soft core']??null)!==0){throw new RuntimeException('Core gate');}
    return ['php'=>PHP_VERSION,'int_size'=>PHP_INT_SIZE,'components'=>$expected,'extra_decimal_defined'=>false,'core_soft'=>0];
}
function probePdo(?string $class=null,array $classArgs=[]): PDO
{
    foreach(['HOST','PORT','SCHEMA','USER','PASSWORD'] as $key){if((string)getenv('SHADOW_'.$key)===''){throw new RuntimeException('Approved environment missing');}}
    if(getenv('SHADOW_HOST')!=='127.0.0.1'||getenv('SHADOW_PORT')!=='5238'||getenv('SHADOW_SCHEMA')!=='AIZHCT_APP'){throw new RuntimeException('Nonproduction loopback gate');}
    $options=[PDO::ATTR_CASE=>PDO::CASE_LOWER,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_ORACLE_NULLS=>PDO::NULL_NATURAL,PDO::ATTR_STRINGIFY_FETCHES=>false,PDO::ATTR_EMULATE_PREPARES=>false,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC];
    if($class!==null){$options[PDO::ATTR_STATEMENT_CLASS]=[$class,$classArgs];}
    return new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',getenv('SHADOW_USER'),getenv('SHADOW_PASSWORD'),$options);
}
function mappedComponents(array $identity): array
{
    $mapped=[];foreach(explode("\n",file_get_contents('/proc/self/maps')) as $line){
        if(preg_match('~(/[^ ]+/(?:libphp[0-9]+_dm|php[0-9]+_pdo_dm|libdmdpi)\.so)$~D',$line,$m)){$mapped[$m[1]]=hash_file('sha256',$m[1]);}
    }
    $expected=$identity['components'];unset($expected[PHP_BINARY]);ksort($expected);ksort($mapped);
    if($mapped!==$expected){throw new RuntimeException('Actual mapped component gate');}return $mapped;
}
function effectiveAttributes(PDO $pdo): array
{
    $expected=[PDO::ATTR_CASE=>PDO::CASE_LOWER,PDO::ATTR_ORACLE_NULLS=>PDO::NULL_NATURAL,PDO::ATTR_STRINGIFY_FETCHES=>false,PDO::ATTR_EMULATE_PREPARES=>false,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION];
    $out=[];foreach($expected as $key=>$value){
        try{$actual=$pdo->getAttribute($key);}catch(PDOException $e){
            if((string)$e->getCode()!=='IM001'){throw $e;}
            $out[(string)$key]=['expected'=>$value,'actual'=>null,'effective'=>'unknown','getter'=>'unsupported','sqlstate'=>'IM001'];continue;
        }
        $out[(string)$key]=['expected'=>$value,'actual'=>$actual,'effective'=>'getter-confirmed'];
        if($actual!==$value){throw new RuntimeException('PDO attribute identity gate');}
    }return $out;
}
function independentTarget(PDO $pdo): array
{
    $owned=[];foreach(scandir('/proc/self/fd') as $fd){if(!ctype_digit($fd)||!is_link('/proc/self/fd/'.$fd)){continue;}$link=readlink('/proc/self/fd/'.$fd);if(preg_match('/^socket:\[([0-9]+)\]$/D',$link,$m)){$owned[$m[1]]=true;}}
    $found=0;foreach(explode("\n",trim(file_get_contents('/proc/self/net/tcp'))) as $line){$f=preg_split('/\s+/',trim($line));if(count($f)>9&&isset($owned[$f[9]])&&$f[2]==='0100007F:1476'&&$f[3]==='01'){++$found;}}
    if($found<1){throw new RuntimeException('Actual socket peer gate');}
    $sql="SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL";
    if($pdo->query($sql)->fetchColumn()!=='AIZHCT_APP'){throw new RuntimeException('Actual schema gate');}
    return ['owned_established_loopback_5238_sockets'=>$found,'schema_matches'=>true,'schema_query_sha256'=>hash('sha256',$sql)];
}
function internalSignatures(): array
{
    $out=[];foreach(['fetch','fetchAll','fetchColumn','setFetchMode','columnCount','getColumnMeta','fetchObject','bindColumn','execute','closeCursor','nextRowset'] as $name){
        $r=new ReflectionMethod(PDOStatement::class,$name);$params=[];
        foreach($r->getParameters() as $p){$params[]=['name'=>$p->getName(),'optional'=>$p->isOptional(),'by_reference'=>$p->isPassedByReference()];}
        $out[$name]=$params;
    }return $out;
}
