true, 'agri'=>true, 'comm'=>true, 'secu'=>true, 'trust'=>true, 'def'=>true, 'wall'=>true, 'trade'=>true, ]; const REGEXP_PERCENT = '/^(\d+(\.\d+)?)%$/';// 123.5% [1]=float const REGEXP_MATH = '/^([\+\-\/\*])(\d+(\.\d+)?)$/'; //+30 [1]=기호, [2]=float private $queries; private $targetType = 'all'; private $targetArgs = []; public function __construct($target, array $actions){ //values 포맷은 key, value로 if(!$target){ $this->targetType = 'all'; } else if(is_string($target)){ $this->targetType = $target; } else if(is_array($target)){ $this->targetType = $target[0]; $this->targetArgs = array_slice($target, 1); } else{ throw new \InvalidArgumentException('올바르지 않은 targetType 입니다.'); } $queries = []; foreach($actions as $key => $value){ if(!key_exists($key, self::AVAILABLE_KEY)){ throw new \InvalidArgumentException('지원하지 않는 city 인자입니다 :'.$key); } if(!is_int($value) && !is_float($value) && !is_string($value)){ throw new \InvalidArgumentException('int, float, string이어야 합니다.'); } if($key == 'trust'){ $queries['trust'] = $this->genSQLTrust($value); continue; } if($key == 'trade'){ if($value === null){ $queries['trade'] = null; } else{ $queries['trade'] = Util::valueFit((float)$value, 95, 105); } continue; } $queries[$key] = $this->genSQLGeneric($key, $value); } $this->queries = $queries; } private function genSQLTrust($value){ //민심은 max값이 100으로 고정이므로 처리 방식이 다름. if(is_float($value)){ if($value < 0){ throw new \InvalidArgumentException('음수를 곱할 수 없습니다.'); } return DB::db()->sqleval('least(100, `trust` * %d)', $value); } if(is_int($value)){ return DB::db()->sqleval('%i', Util::valueFit($value, 0, 100)); } $matches = null; if(preg_match(self::REGEXP_PERCENT, $value, $matches)){ $value = Util::round((float)$matches[1]); return DB::db()->sqleval('%i', Util::valueFit($value, 0, 100)); } if(preg_match(self::REGEXP_MATH, $value, $matches)){ $op = $matches[1]; $value = $matches[2]; if($op == '/' && $value == 0){ throw new \InvalidArgumentException('0으로 나눌 수 없습니다.'); } return DB::db()->sqleval('least(100, greatest(0, `trust` %l %d, 0)))', $op, $value); } throw new \InvalidArgumentException('알 수 없는 패턴입니다.'); } private function genSQLGeneric($key, $value){ $keyMax = $key.'_max'; //comm, comm_max if(is_float($value)){ if($value < 0){ throw new \InvalidArgumentException('음수를 곱할 수 없습니다.'); } return DB::db()->sqleval('least(%b, ROUND(%b * %d, 0))', $keyMax, $key, $value); } if(is_int($value)){ return DB::db()->sqleval('least(%b, %i)', $keyMax, max(0, $value)); } $matches = null; if(preg_match(self::REGEXP_PERCENT, $value, $matches)){ $value = Util::round((float)$matches[1]); return DB::db()->sqleval('ROUND(%b * %d, 0)', $keyMax, $value/100); } if(preg_match(self::REGEXP_MATH, $value, $matches)){ $op = $matches[1]; $value = $matches[2]; if($op == '/' && $value == 0){ throw new \InvalidArgumentException('0으로 나눌 수 없습니다.'); } return DB::db()->sqleval('least(%b, greatest(0, ROUND(%b '.$op.' %i, 0)))', $keyMax, $key, $value); } throw new \InvalidArgumentException('알 수 없는 패턴입니다.'); } private function getTargetCities($env){ $targetType = $this->targetType; if($targetType == 'all'){ return DB::db()->queryFirstColumn('SELECT city FROM city'); } if($targetType == 'free'){ return DB::db()->queryFirstColumn('SELECT city FROM city WHERE nation = 0'); } if($targetType == 'occupied'){ return DB::db()->queryFirstColumn('SELECT city FROM city WHERE nation != 0'); } if($targetType == 'cities'){ if(is_numeric($this->targetArgs)){ return DB::db()->queryFirstColumn('SELECT city FROM city WHERE city IN %ls', $this->targetArgs); } else{ return DB::db()->queryFirstColumn('SELECT city FROM city WHERE name IN %ls', $this->targetArgs); } } throw new \InvalidArgumentException('올바르지 않은 cond 입니다.'); } public function run(array $env){ $cities = $this->getTargetCities($env); DB::db()->update('city', $this->queries , 'city IN %li', $cities); return [__CLASS__, DB::db()->affectedRows()]; } }