diff --git a/f_install/j_setup_db.php b/f_install/j_setup_db.php
index 5990f0ae..fca276b2 100644
--- a/f_install/j_setup_db.php
+++ b/f_install/j_setup_db.php
@@ -1,305 +1,313 @@
- false,
- 'reason' => '입력 값이 올바르지 않습니다'
- ]);
-}
-
-if (!filter_var($servHost, FILTER_VALIDATE_URL)) {
- Json::die([
- 'result' => false,
- 'reason' => '접속 경로가 올바르지 않습니다.'
- ]);
-}
-
-if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_setting/RootDB.php 가 디렉토리입니다'
- ]);
-}
-
-if (class_exists('\\sammo\\RootDB')) {
- Json::die([
- 'result' => false,
- 'reason' => '이미 RootDB.php 파일이 있습니다'
- ]);
-}
-
-//파일 권한 검사
-if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) {
- Json::die([
- 'result' => false,
- 'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다'
- ]);
-}
-
-if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_log 가 디렉토리가 아닙니다'
- ]);
-}
-
-if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_shared 가 디렉토리가 아닙니다'
- ]);
-}
-
-if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_shared 가 디렉토리가 아닙니다'
- ]);
-}
-
-if (
- !file_exists(ROOT . '/d_log')
- || !file_exists(ROOT . '/d_shared')
- || !file_exists(ROOT . '/d_setting')
- || !file_exists(AppConf::getUserIconPathFS())
-) {
- if (!is_writable(ROOT)) {
- Json::die([
- 'result' => false,
- 'reason' => '하위 디렉토리 생성 권한이 없습니다'
- ]);
- }
-
- //기본 파일 생성
- if (!file_exists(AppConf::getUserIconPathFS())) {
- mkdir(AppConf::getUserIconPathFS());
- }
-
- if (!file_exists(ROOT . '/d_log')) {
- mkdir(ROOT . '/d_log');
- }
-
- if (!file_exists(ROOT . '/d_setting')) {
- mkdir(ROOT . '/d_setting');
- }
-
- if (!file_exists(ROOT . '/d_shared')) {
- mkdir(ROOT . '/d_shared');
- }
-}
-
-
-if (!is_writable(AppConf::getUserIconPathFS())) {
- Json::die([
- 'result' => false,
- 'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다'
- ]);
-}
-
-if (!is_writable(ROOT . '/d_log')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다'
- ]);
-}
-
-if (!is_writable(ROOT . '/d_shared')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다'
- ]);
-}
-
-if (!is_writable(ROOT . '/d_setting')) {
- Json::die([
- 'result' => false,
- 'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.'
- ]);
-}
-
-
-
-if (!file_exists(ROOT . '/d_log/.htaccess')) {
- @file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all');
-}
-
-if (!file_exists(ROOT . '/d_setting/.htaccess')) {
- @file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all');
-}
-
-//DB 접근 권한 검사
-
-$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4');
-$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
-
-$rootDB->throw_exception_on_nonsql_error = false;
-$rootDB->nonsql_error_handler = function ($params) {
- Json::die([
- 'result' => false,
- 'reason' => 'DB 접속에 실패했습니다.'
- ]);
-};
-
-$rootDB->error_handler = function ($params) {
- Json::die([
- 'result' => false,
- 'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.'
- ]);
-};
-
-$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
-
-if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) {
- while (true) {
- if (!$mysqli_obj->more_results()) {
- break;
- }
- if (!$mysqli_obj->next_result()) {
- break;
- }
- }
-}
-
-$rootDB->insert('system', array(
- 'REG' => 'N',
- 'LOGIN' => 'N',
- 'CRT_DATE' => TimeUtil::now(),
- 'MDF_DATE' => TimeUtil::now()
-));
-
-$globalSalt = bin2hex(random_bytes(16));
-
-'@phan-var-force string $servHost';
-'@phan-var-force string $sharedIconPath';
-'@phan-var-force string $gameImagePath';
-
-$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost);
-$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost);
-$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost);
-$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost);
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/ServConfig.orig.php',
- ROOT . '/d_setting/ServConfig.php',
- [
- 'serverBasePath' => $servHost,
- 'sharedIconPath' => $sharedIconPath,
- 'gameImagePath' => $gameImagePath,
- 'imageRequestPath' => $imageRequestPath,
- 'imageRequestKey' => $imageRequestKey
- ],
- true
-);
-
-if ($imageRequestKey) {
- @file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey);
-}
-
-if ($result !== true) {
- Json::die([
- 'result' => false,
- 'reason' => $result
- ]);
-}
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/common_path.orig.js',
- ROOT . '/d_shared/common_path.js',
- [
- 'serverBasePath' => $servHost,
- 'sharedIconPath' => $sharedIconPath,
- 'gameImagePath' => $gameImagePath
- ],
- true
-);
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/menu.orig.json',
- ROOT . '/d_shared/menu.json',
- [],
- true
-);
-
-if ($result !== true) {
- Json::die([
- 'result' => false,
- 'reason' => $result
- ]);
-}
-
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/common.orig.css',
- ROOT . '/d_shared/common.css',
- [
- 'serverBasePath' => $servHost,
- 'sharedIconPath' => $sharedIconPath,
- 'gameImagePath' => $gameImagePath
- ],
- true
-);
-
-if ($result !== true) {
- Json::die([
- 'result' => false,
- 'reason' => $result
- ]);
-}
-
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/RootDB.orig.php',
- ROOT . '/d_setting/RootDB.php',
- [
- 'host' => $host,
- 'user' => $username,
- 'password' => $password,
- 'dbName' => $dbName,
- 'port' => $port,
- 'globalSalt' => $globalSalt,
- ]
-);
-
-$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/');
-
-Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/templates/KakaoKey.orig.php',
- ROOT . '/d_setting/KakaoKey.php',
- [
- 'REST_API_KEY' => $kakaoRESTKey,
- 'ADMIN_KEY' => $kakaoAdminKey,
- 'REDIRECT_URI' => $kakaoRedirectURI
- ],
- true
-);
-
-if ($result !== true) {
- Json::die([
- 'result' => false,
- 'reason' => $result
- ]);
-}
-
-Json::die([
- 'result' => true,
- 'reason' => 'success',
- 'globalSalt' => $globalSalt
-]);
+ false,
+ 'reason' => '입력 값이 올바르지 않습니다'
+ ]);
+}
+
+if (!filter_var($servHost, FILTER_VALIDATE_URL)) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '접속 경로가 올바르지 않습니다.'
+ ]);
+}
+
+if (file_exists(ROOT . '/d_setting/RootDB.php') && is_dir(ROOT . '/d_setting/RootDB.php')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_setting/RootDB.php 가 디렉토리입니다'
+ ]);
+}
+
+if (class_exists('\\sammo\\RootDB')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '이미 RootDB.php 파일이 있습니다'
+ ]);
+}
+
+//파일 권한 검사
+if (file_exists(AppConf::getUserIconPathFS()) && !is_dir(AppConf::getUserIconPathFS())) {
+ Json::die([
+ 'result' => false,
+ 'reason' => AppConf::$userIconPath . ' 이 디렉토리가 아닙니다'
+ ]);
+}
+
+if (file_exists(ROOT . '/d_log') && !is_dir(ROOT . '/d_log')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_log 가 디렉토리가 아닙니다'
+ ]);
+}
+
+if (file_exists(ROOT . '/d_shared') && !is_dir(ROOT . '/d_shared')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_shared 가 디렉토리가 아닙니다'
+ ]);
+}
+
+if (file_exists(ROOT . '/d_setting') && !is_dir(ROOT . '/d_setting')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_shared 가 디렉토리가 아닙니다'
+ ]);
+}
+
+if (
+ !file_exists(ROOT . '/d_log')
+ || !file_exists(ROOT . '/d_shared')
+ || !file_exists(ROOT . '/d_setting')
+ || !file_exists(AppConf::getUserIconPathFS())
+) {
+ if (!is_writable(ROOT)) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '하위 디렉토리 생성 권한이 없습니다'
+ ]);
+ }
+
+ //기본 파일 생성
+ if (!file_exists(AppConf::getUserIconPathFS())) {
+ mkdir(AppConf::getUserIconPathFS());
+ }
+
+ if (!file_exists(ROOT . '/d_log')) {
+ mkdir(ROOT . '/d_log');
+ }
+
+ if (!file_exists(ROOT . '/d_setting')) {
+ mkdir(ROOT . '/d_setting');
+ }
+
+ if (!file_exists(ROOT . '/d_shared')) {
+ mkdir(ROOT . '/d_shared');
+ }
+}
+
+
+if (!is_writable(AppConf::getUserIconPathFS())) {
+ Json::die([
+ 'result' => false,
+ 'reason' => AppConf::$userIconPath . ' 디렉토리의 쓰기 권한이 없습니다'
+ ]);
+}
+
+if (!is_writable(ROOT . '/d_log')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_log 디렉토리의 쓰기 권한이 없습니다'
+ ]);
+}
+
+if (!is_writable(ROOT . '/d_shared')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_shared 디렉토리의 쓰기 권한이 없습니다'
+ ]);
+}
+
+if (!is_writable(ROOT . '/d_setting')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'd_setting 디렉토리의 쓰기 권한이 없습니다.'
+ ]);
+}
+
+
+
+if (!file_exists(ROOT . '/d_log/.htaccess')) {
+ @file_put_contents(ROOT . '/d_log/.htaccess', 'Deny from all');
+}
+
+if (!file_exists(ROOT . '/d_setting/.htaccess')) {
+ @file_put_contents(ROOT . '/d_setting/.htaccess', 'Deny from all');
+}
+
+//DB 접근 권한 검사
+
+$rootDB = new \MeekroDB($host, $username, $password, $dbName, $port, 'utf8mb4');
+$rootDB->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
+
+$rootDB->throw_exception_on_nonsql_error = false;
+$rootDB->nonsql_error_handler = function ($params) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'DB 접속에 실패했습니다.'
+ ]);
+};
+
+$rootDB->error_handler = function ($params) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'SQL을 제대로 실행하지 못했습니다. DB상태를 확인해 주세요.'
+ ]);
+};
+
+$mysqli_obj = $rootDB->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
+
+if ($mysqli_obj->multi_query(file_get_contents(__DIR__ . '/sql/common_schema.sql'))) {
+ while (true) {
+ if (!$mysqli_obj->more_results()) {
+ break;
+ }
+ if (!$mysqli_obj->next_result()) {
+ break;
+ }
+ }
+}
+
+$rootDB->insert('system', array(
+ 'REG' => 'N',
+ 'LOGIN' => 'N',
+ 'CRT_DATE' => TimeUtil::now(),
+ 'MDF_DATE' => TimeUtil::now()
+));
+
+$globalSalt = bin2hex(random_bytes(16));
+
+'@phan-var-force string $servHost';
+'@phan-var-force string $sharedIconPath';
+'@phan-var-force string $gameImagePath';
+
+$sharedIconPath = WebUtil::resolveRelativePath($sharedIconPath, $servHost);
+$gameImagePath = WebUtil::resolveRelativePath($gameImagePath, $servHost);
+$imageRequestPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/git_pull.php', $servHost);
+$imageKeyInstallPath = WebUtil::resolveRelativePath($gameImagePath . '/../hook/InstallKey.php', $servHost);
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/ServConfig.orig.php',
+ ROOT . '/d_setting/ServConfig.php',
+ [
+ 'serverBasePath' => $servHost,
+ 'sharedIconPath' => $sharedIconPath,
+ 'gameImagePath' => $gameImagePath,
+ 'imageRequestPath' => $imageRequestPath,
+ 'imageRequestKey' => $imageRequestKey,
+ 'serverList' => [
+ ['che', '체', 'white'],
+ ['kwe', '퀘', 'yellow'],
+ ['pwe', '풰', 'orange'],
+ ['twe', '퉤', 'magenta'],
+ ['nya', '냐', '#e67e22'],
+ ['pya', '퍄', '#9b59b6']
+ ]
+ ],
+ true
+);
+
+if ($imageRequestKey) {
+ @file_get_contents($imageKeyInstallPath . '?key=' . $imageRequestKey);
+}
+
+if ($result !== true) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $result
+ ]);
+}
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/common_path.orig.js',
+ ROOT . '/d_shared/common_path.js',
+ [
+ 'serverBasePath' => $servHost,
+ 'sharedIconPath' => $sharedIconPath,
+ 'gameImagePath' => $gameImagePath
+ ],
+ true
+);
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/menu.orig.json',
+ ROOT . '/d_shared/menu.json',
+ [],
+ true
+);
+
+if ($result !== true) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $result
+ ]);
+}
+
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/common.orig.css',
+ ROOT . '/d_shared/common.css',
+ [
+ 'serverBasePath' => $servHost,
+ 'sharedIconPath' => $sharedIconPath,
+ 'gameImagePath' => $gameImagePath
+ ],
+ true
+);
+
+if ($result !== true) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $result
+ ]);
+}
+
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/RootDB.orig.php',
+ ROOT . '/d_setting/RootDB.php',
+ [
+ 'host' => $host,
+ 'user' => $username,
+ 'password' => $password,
+ 'dbName' => $dbName,
+ 'port' => $port,
+ 'globalSalt' => $globalSalt,
+ ]
+);
+
+$kakaoRedirectURI = WebUtil::resolveRelativePath('oauth_kakao/oauth.php', $servHost . '/');
+
+Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/templates/KakaoKey.orig.php',
+ ROOT . '/d_setting/KakaoKey.php',
+ [
+ 'REST_API_KEY' => $kakaoRESTKey,
+ 'ADMIN_KEY' => $kakaoAdminKey,
+ 'REDIRECT_URI' => $kakaoRedirectURI
+ ],
+ true
+);
+
+if ($result !== true) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $result
+ ]);
+}
+
+Json::die([
+ 'result' => true,
+ 'reason' => 'success',
+ 'globalSalt' => $globalSalt
+]);
diff --git a/f_install/templates/ServConfig.orig.php b/f_install/templates/ServConfig.orig.php
index 304f9bbb..3b4e3478 100644
--- a/f_install/templates/ServConfig.orig.php
+++ b/f_install/templates/ServConfig.orig.php
@@ -1,55 +1,74 @@
-queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
- Json::die([
- 'result'=>true,
- 'affected'=>0,
- 'status'=>'no_reserved_table'
- ]);
-}
-
-$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1');
-
-if(!$reserved){
- Json::die([
- 'result'=>true,
- 'affected'=>0,
- 'status'=>'no_reserved'
- ]);
-}
-
-$reservedDate = new \DateTime($reserved['date']);
-$now = new \DateTime();
-
-
-$status = 'not_yet';
-
-list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']);
-if($isUnited === null || $lastTurn === null){
- $isUnited = 2;
- $lastTurn = '2000-01-01';
-}
-
-if($lastTurn !== null){
- $lastTurn = new \DateTime($lastTurn);
-}
-
-if($lastTurn === null){
- //이미 리셋된 상태임
-}
-else if(file_exists(__DIR__.'/.htaccess')){
- //일단 서버는 닫혀 있음
-}
-else if(
- $isUnited == 2 &&
- $now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp()
-){
- //정지 상태 & 중간 넘음
- AppConf::getList()[DB::prefix()]->closeServer();
- $status = 'closed';
-}
-else if(
- $isUnited > 0 &&
- $now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2
-){
- //천통 & 비정지 상태 & 2/3 넘음
- AppConf::getList()[DB::prefix()]->closeServer();
- $status = 'closed';
-}
-else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){
- //어쨌든 간에 10분 남았다면.
- AppConf::getList()[DB::prefix()]->closeServer();
- $status = 'closed';
-}
-
-if($now < $reservedDate){
- Json::die([
- 'result'=>true,
- 'affected'=>0,
- 'status'=>$status
- ]);
-}
-
-$options = Json::decode($reserved['options']);
-
-$result = ResetHelper::buildScenario(
- $options['turnterm'],
- $options['sync'],
- $options['scenario'],
- $options['fiction'],
- $options['extend'],
- $options['npcmode'],
- $options['show_img_level'],
- $options['tournament_trig'],
- $options['join_mode'],
- $options['starttime'],
- $options['autorun_user']?:null
-);
-
-$result['affected']=1;
-
-$prefix = DB::prefix();
-AppConf::getList()[$prefix]->openServer();
-
+queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
+ Json::die([
+ 'result'=>true,
+ 'affected'=>0,
+ 'status'=>'no_reserved_table'
+ ]);
+}
+
+$reserved = $db->queryFirstRow('SELECT `date`, options FROM reserved_open ORDER BY `date` ASC LIMIT 1');
+
+if(!$reserved){
+ Json::die([
+ 'result'=>true,
+ 'affected'=>0,
+ 'status'=>'no_reserved'
+ ]);
+}
+
+$reservedDate = new \DateTime($reserved['date']);
+$now = new \DateTime();
+
+
+$status = 'not_yet';
+
+list($isUnited, $lastTurn) = $gameStor->getValuesAsArray(['isunited', 'turntime']);
+if($isUnited === null || $lastTurn === null){
+ $isUnited = 2;
+ $lastTurn = '2000-01-01';
+}
+
+if($lastTurn !== null){
+ $lastTurn = new \DateTime($lastTurn);
+}
+
+if($lastTurn === null){
+ //이미 리셋된 상태임
+}
+else if(file_exists(__DIR__.'/.htaccess')){
+ //일단 서버는 닫혀 있음
+}
+else if(
+ $isUnited == 2 &&
+ $now->getTimestamp() - $lastTurn->getTimestamp() > $reservedDate->getTimestamp() - $now->getTimestamp()
+){
+ //정지 상태 & 중간 넘음
+ ServConfig::getServerList()[DB::prefix()]->closeServer();
+ $status = 'closed';
+}
+else if(
+ $isUnited > 0 &&
+ $now->getTimestamp() - $lastTurn->getTimestamp() > ($reservedDate->getTimestamp() - $now->getTimestamp()) * 2
+){
+ //천통 & 비정지 상태 & 2/3 넘음
+ ServConfig::getServerList()[DB::prefix()]->closeServer();
+ $status = 'closed';
+}
+else if($reservedDate->getTimestamp() - $now->getTimestamp() <= 60*10){
+ //어쨌든 간에 10분 남았다면.
+ ServConfig::getServerList()[DB::prefix()]->closeServer();
+ $status = 'closed';
+}
+
+if($now < $reservedDate){
+ Json::die([
+ 'result'=>true,
+ 'affected'=>0,
+ 'status'=>$status
+ ]);
+}
+
+$options = Json::decode($reserved['options']);
+
+$result = ResetHelper::buildScenario(
+ $options['turnterm'],
+ $options['sync'],
+ $options['scenario'],
+ $options['fiction'],
+ $options['extend'],
+ $options['npcmode'],
+ $options['show_img_level'],
+ $options['tournament_trig'],
+ $options['join_mode'],
+ $options['starttime'],
+ $options['autorun_user']?:null
+);
+
+$result['affected']=1;
+
+$prefix = DB::prefix();
+ServConfig::getServerList()[$prefix]->openServer();
+
Json::die($result);
\ No newline at end of file
diff --git a/hwe/j_install.php b/hwe/j_install.php
index 63dc1455..e69aaaaf 100644
--- a/hwe/j_install.php
+++ b/hwe/j_install.php
@@ -1,209 +1,209 @@
-setReadOnly();
-
-if(!class_exists('\\sammo\\DB')){
- Json::die([
- 'result'=>false,
- 'reason'=>'DB리셋 필요'
- ]);
-}
-
-$serverName = DB::prefix();
-$serverAcl = $session->acl[$serverName]??[];
-$allowReset = in_array('reset', $serverAcl);
-$allowFullReset = in_array('fullReset',$serverAcl);
-$allowReset |= $allowFullReset;
-
-$reserve_open = Util::getPost('reserve_open');
-if($reserve_open && $reserve_open < date('Y-m-d H:i')){
- Json::die([
- 'result'=>false,
- 'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.'
- ]);
-}
-
-$pre_reserve_open = Util::getPost('pre_reserve_open');
-if($pre_reserve_open && !$reserve_open){
- Json::die([
- 'result'=>false,
- 'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.'
- ]);
-}
-if($pre_reserve_open && $pre_reserve_open >= $reserve_open){
- Json::die([
- 'result'=>false,
- 'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.'
- ]);
-}
-
-if($session->userGrade < 5 && !$allowReset){
- Json::die([
- 'result'=>false,
- 'reason'=>'관리자 아님'
- ]);
-}
-
-$v = new Validator($_POST);
-$v->rule('required', [
- 'turnterm',
- 'sync',
- 'scenario',
- 'fiction',
- 'extend',
- 'join_mode',
- 'npcmode',
- 'show_img_level',
- 'autorun_user_minutes'
-])->rule('integer', [
- 'turnterm',
- 'sync',
- 'scenario',
- 'fiction',
- 'extend',
- 'npcmode',
- 'show_img_level',
- 'tournament_trig',
- 'autorun_user_minutes'
-])->rule('in', 'join_mode', ['onlyRandom', 'full']);
-if(!$v->validate()){
- Json::die([
- 'result'=>false,
- 'reason'=>$v->errorStr()
- ]);
-}
-
-$allowReset = true;
-if($session->userGrade < 5 && !$allowFullReset){
- //리셋 가능한 조건인지 테스트
- $allowReset = false;
-
- if(file_exists(__DIR__.'/.htaccess')){
- $allowReset = true;
- }
- else{
- $db = DB::db();
- $gameStor = KVStorage::getStorage($db, 'game_env');
- if($gameStor->isunited){
- $allowReset = true;
- }
- }
-}
-
-if(!$allowReset){
- Json::die([
- 'result'=>false,
- 'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.'
- ]);
-}
-
-$turnterm = (int)$_POST['turnterm'];
-$sync = (int)$_POST['sync'];
-$scenario = (int)$_POST['scenario'];
-$fiction = (int)$_POST['fiction'];
-$extend = (int)$_POST['extend'];
-$npcmode = (int)$_POST['npcmode'];
-$show_img_level = (int)$_POST['show_img_level'];
-$tournament_trig = (int)$_POST['tournament_trig'];
-$join_mode = $_POST['join_mode'];
-$autorun_user_minutes = (int)$_POST['autorun_user_minutes'];
-$autorun_user_options = [];
-foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){
- $autorun_user_options[$autorun_option] = 1;
-}
-
-if($autorun_user_minutes > 0 && !$autorun_user_options){
- Json::die([
- 'result'=>false,
- 'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.'
- ]);
-}
-
-if($autorun_user_minutes < 0){
- Json::die([
- 'result'=>false,
- 'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.'
- ]);
-}
-
-$autorun_user = $autorun_user_minutes?[
- 'limit_minutes'=>$autorun_user_minutes,
- 'options'=>$autorun_user_options
-]:null;
-
-if($reserve_open){
- $reserve_open = new \DateTime($reserve_open);
- $db = DB::db();
-
- if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) {
- $clearResult = ResetHelper::clearDB();
- if(!$clearResult['result']){
- Json::die($clearResult);
- }
- }
-
- if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
- Json::die([
- 'result'=>false,
- 'reason'=>'예약 테이블이 없음!'
- ]);
- }
-
- $scenarioObj = new Scenario($scenario, true);
- $open_date = $reserve_open->format('Y-m-d H:i:s');
-
- $reserveInfo = [
- 'turnterm'=>$turnterm,
- 'sync'=>$sync,
- 'scenario'=>$scenario,
- 'scenarioName'=>$scenarioObj->getTitle(),
- 'fiction'=>$fiction,
- 'extend'=>$extend,
- 'npcmode'=>$npcmode,
- 'show_img_level'=>$show_img_level,
- 'tournament_trig'=>$tournament_trig,
- 'gameConf'=>$scenarioObj->getGameConf(),
- 'join_mode'=>$join_mode,
- 'starttime'=>$open_date,
- 'autorun_user'=>$autorun_user
- ];
-
-
- if($pre_reserve_open){
- $pre_reserve_open = new \DateTime($pre_reserve_open);
- $open_date = $pre_reserve_open->format('Y-m-d H:i:s');
- }
-
-
-
- $db->delete('reserved_open', true);
- $db->insert('reserved_open', [
- 'options'=>Json::encode($reserveInfo),
- 'date'=>$open_date
- ]);
- AppConf::getList()[DB::prefix()]->closeServer();
- Json::die([
- 'result'=>true,
- 'reason'=>'예약'
- ]);
-}
-
-Json::die(ResetHelper::buildScenario(
- $turnterm,
- $sync,
- $scenario,
- $fiction,
- $extend,
- $npcmode,
- $show_img_level,
- $tournament_trig,
- $join_mode,
- TimeUtil::now(),
- $autorun_user
+setReadOnly();
+
+if(!class_exists('\\sammo\\DB')){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'DB리셋 필요'
+ ]);
+}
+
+$serverName = DB::prefix();
+$serverAcl = $session->acl[$serverName]??[];
+$allowReset = in_array('reset', $serverAcl);
+$allowFullReset = in_array('fullReset',$serverAcl);
+$allowReset |= $allowFullReset;
+
+$reserve_open = Util::getPost('reserve_open');
+if($reserve_open && $reserve_open < date('Y-m-d H:i')){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'현재 시간보다 이전 시간대를 예약 시간으로 지정했습니다.'
+ ]);
+}
+
+$pre_reserve_open = Util::getPost('pre_reserve_open');
+if($pre_reserve_open && !$reserve_open){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'가오픈 예약을 위해선 오픈 예약을 지정해야합니다.'
+ ]);
+}
+if($pre_reserve_open && $pre_reserve_open >= $reserve_open){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'가오픈 시간이 오픈 예약 시점보다 이전이어야 합니다.'
+ ]);
+}
+
+if($session->userGrade < 5 && !$allowReset){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'관리자 아님'
+ ]);
+}
+
+$v = new Validator($_POST);
+$v->rule('required', [
+ 'turnterm',
+ 'sync',
+ 'scenario',
+ 'fiction',
+ 'extend',
+ 'join_mode',
+ 'npcmode',
+ 'show_img_level',
+ 'autorun_user_minutes'
+])->rule('integer', [
+ 'turnterm',
+ 'sync',
+ 'scenario',
+ 'fiction',
+ 'extend',
+ 'npcmode',
+ 'show_img_level',
+ 'tournament_trig',
+ 'autorun_user_minutes'
+])->rule('in', 'join_mode', ['onlyRandom', 'full']);
+if(!$v->validate()){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>$v->errorStr()
+ ]);
+}
+
+$allowReset = true;
+if($session->userGrade < 5 && !$allowFullReset){
+ //리셋 가능한 조건인지 테스트
+ $allowReset = false;
+
+ if(file_exists(__DIR__.'/.htaccess')){
+ $allowReset = true;
+ }
+ else{
+ $db = DB::db();
+ $gameStor = KVStorage::getStorage($db, 'game_env');
+ if($gameStor->isunited){
+ $allowReset = true;
+ }
+ }
+}
+
+if(!$allowReset){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'부족한 권한: 서버가 닫혀있거나, 천통되어 있을 경우에만 리셋 가능합니다.'
+ ]);
+}
+
+$turnterm = (int)$_POST['turnterm'];
+$sync = (int)$_POST['sync'];
+$scenario = (int)$_POST['scenario'];
+$fiction = (int)$_POST['fiction'];
+$extend = (int)$_POST['extend'];
+$npcmode = (int)$_POST['npcmode'];
+$show_img_level = (int)$_POST['show_img_level'];
+$tournament_trig = (int)$_POST['tournament_trig'];
+$join_mode = $_POST['join_mode'];
+$autorun_user_minutes = (int)$_POST['autorun_user_minutes'];
+$autorun_user_options = [];
+foreach(Util::getPost('autorun_user', 'array_string', []) as $autorun_option){
+ $autorun_user_options[$autorun_option] = 1;
+}
+
+if($autorun_user_minutes > 0 && !$autorun_user_options){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'적어도 자동 행동 중 하나는 선택을 해야합니다.'
+ ]);
+}
+
+if($autorun_user_minutes < 0){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'자동 행동 기한이 0보다 작을 수 없습니다.'
+ ]);
+}
+
+$autorun_user = $autorun_user_minutes?[
+ 'limit_minutes'=>$autorun_user_minutes,
+ 'options'=>$autorun_user_options
+]:null;
+
+if($reserve_open){
+ $reserve_open = new \DateTime($reserve_open);
+ $db = DB::db();
+
+ if (!$db->queryFirstField("SHOW TABLES LIKE 'storage'")) {
+ $clearResult = ResetHelper::clearDB();
+ if(!$clearResult['result']){
+ Json::die($clearResult);
+ }
+ }
+
+ if (!$db->queryFirstField("SHOW TABLES LIKE 'reserved_open'")) {
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'예약 테이블이 없음!'
+ ]);
+ }
+
+ $scenarioObj = new Scenario($scenario, true);
+ $open_date = $reserve_open->format('Y-m-d H:i:s');
+
+ $reserveInfo = [
+ 'turnterm'=>$turnterm,
+ 'sync'=>$sync,
+ 'scenario'=>$scenario,
+ 'scenarioName'=>$scenarioObj->getTitle(),
+ 'fiction'=>$fiction,
+ 'extend'=>$extend,
+ 'npcmode'=>$npcmode,
+ 'show_img_level'=>$show_img_level,
+ 'tournament_trig'=>$tournament_trig,
+ 'gameConf'=>$scenarioObj->getGameConf(),
+ 'join_mode'=>$join_mode,
+ 'starttime'=>$open_date,
+ 'autorun_user'=>$autorun_user
+ ];
+
+
+ if($pre_reserve_open){
+ $pre_reserve_open = new \DateTime($pre_reserve_open);
+ $open_date = $pre_reserve_open->format('Y-m-d H:i:s');
+ }
+
+
+
+ $db->delete('reserved_open', true);
+ $db->insert('reserved_open', [
+ 'options'=>Json::encode($reserveInfo),
+ 'date'=>$open_date
+ ]);
+ ServConfig::getServerList()[DB::prefix()]->closeServer();
+ Json::die([
+ 'result'=>true,
+ 'reason'=>'예약'
+ ]);
+}
+
+Json::die(ResetHelper::buildScenario(
+ $turnterm,
+ $sync,
+ $scenario,
+ $fiction,
+ $extend,
+ $npcmode,
+ $show_img_level,
+ $tournament_trig,
+ $join_mode,
+ TimeUtil::now(),
+ $autorun_user
));
\ No newline at end of file
diff --git a/hwe/j_install_db.php b/hwe/j_install_db.php
index 11908fea..64d23225 100644
--- a/hwe/j_install_db.php
+++ b/hwe/j_install_db.php
@@ -1,103 +1,103 @@
-setReadOnly();
-if($session->userGrade < 6){
- Json::die([
- 'result'=>false,
- 'reason'=>'관리자 아님'
- ]);
-}
-
-$fullReset = Util::getPost('full_reset', 'bool', false);
-$host = Util::getPost('db_host');
-$port = Util::getPost('db_port', 'int');
-$username = Util::getPost('db_id');
-$password = Util::getPost('db_pw');
-$dbName = Util::getPost('db_name');
-
-if(!$host || !$port || !$username || !$password || !$dbName){
- Json::die([
- 'result'=>false,
- 'reason'=>'입력 값이 올바르지 않습니다'
- ]);
-}
-
-if($fullReset && class_exists('\\sammo\\DB')){
- $mysqli_obj = DB::db()->get();
-
- if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){
- while(true){
- if (!$mysqli_obj->more_results()) {
- break;
- }
- if(!$mysqli_obj->next_result()){
- break;
- }
- }
- }
-}
-if($fullReset){
- FileUtil::delInDir(__DIR__."/logs");
- FileUtil::delInDir(__DIR__."/data");
- if(file_exists(__DIR__.'/d_setting/DB.php')){
- @unlink(__DIR__.'/d_setting/DB.php');
- }
- if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){
- @unlink(__DIR__.'/d_setting/UniqueConst.php');
- }
-}
-
-function dbConnFail($params){
- Json::die([
- 'result'=>false,
- 'reason'=>'DB 접속에 실패했습니다.'
- ]);
-}
-
-
-$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4');
-$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
-
-$db->throw_exception_on_nonsql_error = false;
-$db->nonsql_error_handler = 'dbConnFail';
-
-
-$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
-
-$prefix = basename(__DIR__);
-
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__.'/d_setting/DB.orig.php',
- __DIR__.'/d_setting/DB.php',[
- 'host'=>$host,
- 'user'=>$username,
- 'password'=>$password,
- 'dbName'=>$dbName,
- 'port'=>$port,
- 'prefix'=>$prefix
- ], true
-);
-
-
-
-if($result !== true){
- Json::die([
- 'result'=>false,
- 'reason'=>$result
- ]);
-}
-
-ResetHelper::clearDB();
-
-AppConf::getList()[$prefix]->closeServer();
-
-Json::die([
- 'result'=>true,
- 'reason'=>'success'
+setReadOnly();
+if($session->userGrade < 6){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'관리자 아님'
+ ]);
+}
+
+$fullReset = Util::getPost('full_reset', 'bool', false);
+$host = Util::getPost('db_host');
+$port = Util::getPost('db_port', 'int');
+$username = Util::getPost('db_id');
+$password = Util::getPost('db_pw');
+$dbName = Util::getPost('db_name');
+
+if(!$host || !$port || !$username || !$password || !$dbName){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'입력 값이 올바르지 않습니다'
+ ]);
+}
+
+if($fullReset && class_exists('\\sammo\\DB')){
+ $mysqli_obj = DB::db()->get();
+
+ if($mysqli_obj->multi_query(file_get_contents(__DIR__.'/sql/reset.sql'))){
+ while(true){
+ if (!$mysqli_obj->more_results()) {
+ break;
+ }
+ if(!$mysqli_obj->next_result()){
+ break;
+ }
+ }
+ }
+}
+if($fullReset){
+ FileUtil::delInDir(__DIR__."/logs");
+ FileUtil::delInDir(__DIR__."/data");
+ if(file_exists(__DIR__.'/d_setting/DB.php')){
+ @unlink(__DIR__.'/d_setting/DB.php');
+ }
+ if(file_exists(__DIR__.'/d_setting/UniqueConst.php')){
+ @unlink(__DIR__.'/d_setting/UniqueConst.php');
+ }
+}
+
+function dbConnFail($params){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'DB 접속에 실패했습니다.'
+ ]);
+}
+
+
+$db = new \MeekroDB($host,$username,$password,$dbName,$port,'utf8mb4');
+$db->connect_options[MYSQLI_OPT_INT_AND_FLOAT_NATIVE] = true;
+
+$db->throw_exception_on_nonsql_error = false;
+$db->nonsql_error_handler = 'dbConnFail';
+
+
+$mysqli_obj = $db->get(); //로그인에 실패할 경우 자동으로 dbConnFail()이 실행됨.
+
+$prefix = basename(__DIR__);
+
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__.'/d_setting/DB.orig.php',
+ __DIR__.'/d_setting/DB.php',[
+ 'host'=>$host,
+ 'user'=>$username,
+ 'password'=>$password,
+ 'dbName'=>$dbName,
+ 'port'=>$port,
+ 'prefix'=>$prefix
+ ], true
+);
+
+
+
+if($result !== true){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>$result
+ ]);
+}
+
+ResetHelper::clearDB();
+
+ServConfig::getServerList()[$prefix]->closeServer();
+
+Json::die([
+ 'result'=>true,
+ 'reason'=>'success'
]);
\ No newline at end of file
diff --git a/hwe/sammo/ResetHelper.php b/hwe/sammo/ResetHelper.php
index ddaecf07..94c0ecd7 100644
--- a/hwe/sammo/ResetHelper.php
+++ b/hwe/sammo/ResetHelper.php
@@ -1,358 +1,358 @@
-false,
- 'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.'
- ];
- }
- mkdir($servRoot.'/logs', 0755);
- mkdir($servRoot.'/data', 0755);
- }
-
- if(!is_writable($servRoot.'/logs')){
- return [
- 'result'=>false,
- 'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다'
- ];
- }
-
- if(!is_writable($servRoot.'/data')){
- return [
- 'result'=>false,
- 'reason'=>'data 디렉토리의 쓰기 권한이 없습니다'
- ];
- }
-
- if(!is_writable($servRoot.'/d_setting')){
- return [
- 'result'=>false,
- 'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다'
- ];
- }
-
- if(!file_exists($servRoot.'/logs/preserved')){
- mkdir($servRoot.'/logs/preserved', 0755);
- }
-
- if(!file_exists($servRoot.'/logs/.htaccess')){
- @file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all');
- }
-
- if(!file_exists($servRoot.'/data/.htaccess')){
- @file_put_contents($servRoot.'/data/.htaccess', 'Deny from all');
- }
-
- $dir = new \DirectoryIterator($servRoot.'/logs');
- foreach ($dir as $fileinfo) {
- /** @var \DirectoryIterator $fileinfo */
- if (!$fileinfo->isDir() || $fileinfo->isDot()) {
- continue;
- }
- $basename = $fileinfo->getFilename();
- if($basename == 'preserved'){
- continue;
- }
- if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
- exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
- } else {
- exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
- }
- }
-
- $prefix = DB::prefix();
- AppConf::getList()[$prefix]->closeServer();
-
- $db = DB::db();
- $mysqli_obj = $db->get();
-
- $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4);
-
- mkdir($servRoot.'/logs/'.$serverID, 0755);
- mkdir($servRoot.'/data/'.$serverID, 0755);
-
- $seasonIdx = 1;
- if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){
- $gameStor = KVStorage::getStorage($db, 'game_env');
- $nextSeasonIdx = $gameStor->next_season_idx;
- if($nextSeasonIdx !== null){
- $seasonIdx = $nextSeasonIdx;
- }
- $gameStor->resetCache();
- }
-
- $result = Util::generateFileUsingSimpleTemplate(
- $servRoot.'/d_setting/UniqueConst.orig.php',
- $servRoot.'/d_setting/UniqueConst.php',[
- 'serverID'=>$serverID,
- 'serverName'=>AppConf::getList()[$prefix]->getKorName(),
- 'seasonIdx'=>$seasonIdx,
- ], true
- );
-
- copy(
- $servRoot.'/d_setting/GameConst.orig.php',
- $servRoot.'/d_setting/GameConst.php',
- );
-
- if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){
- while(true){
- if (!$mysqli_obj->more_results()) {
- break;
- }
- if(!$mysqli_obj->next_result()){
- break;
- }
- }
-
- }
-
- if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){
- while(true){
- if (!$mysqli_obj->more_results()) {
- break;
- }
- if(!$mysqli_obj->next_result()){
- break;
- }
- }
- }
-
- $gameStor = KVStorage::getStorage($db, 'game_env');
- $gameStor->resetValues();
- $gameStor->next_season_idx = $seasonIdx;
-
- return [
- 'result'=>true,
- 'serverID'=>$serverID,
- 'seasonIdx'=>$seasonIdx
- ];
- }
-
- static public function buildScenario(
- int $turnterm,
- int $sync,
- int $scenario,
- int $fiction,
- int $extend,
- int $npcmode,
- int $show_img_level,
- int $tournament_trig,
- string $join_mode,
- string $turntime,
- ?array $autorun_user
- ):array{
- //FIXME: 분리할 것
- if(120 % $turnterm != 0){
- return [
- 'result'=>false,
- 'reason'=>'turnterm은 120의 약수여야 합니다.'
- ];
- }
-
- if($tournament_trig < 0 || $tournament_trig > 7){
- return [
- 'result'=>false,
- 'reason'=>'올바르지 않은 토너먼트 주기입니다.'
- ];
- }
-
- $clearResult = self::clearDB();
- if(!$clearResult['result']){
- return $clearResult;
- }
-
- $serverID = $clearResult['serverID'];
- $seasonIdx = $clearResult['seasonIdx'];
-
- $scenarioObj = new Scenario($scenario, false);
- $scenarioObj->buildConf();
-
- $startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear;
-
- $db = DB::db();
- $gameStor = KVStorage::getStorage($db, 'game_env');
- $db->insert('plock', [
- 'plock'=>1,
- 'locktime'=>TimeUtil::now(true)
- ]);
-
- $prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1');
- $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1;
-
- CityConst::build();
- $cityPositions = [];
- foreach(CityConst::all() as $city){
- $cityPositions[$city->id] = [
- $city->name,
- $city->posX,
- $city->posY
- ];
- }
- Util::generateFileUsingSimpleTemplate(
- __DIR__.'/../templates/base_map.orig.js',
- __DIR__.'/../d_shared/base_map.js',
- [
- 'cityPosition'=>Json::encode($cityPositions),
- 'regionMap'=>Json::encode(CityConst::$regionMap),
- 'levelMap'=>Json::encode(CityConst::$levelMap),
- ],
- true
- );
-
- if($sync == 0) {
- // 현재 시간을 1월로 맞춤
- $starttime = cutTurn($turntime, $turnterm);
- $month = 1;
- $year = $startyear;
- } else {
- // 현재 시간과 동기화
- [$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm);
- if($yearPulled){
- $year = $startyear-1;
- }
- else{
- $year = $startyear;
- }
- }
-
- $killturn = 4800 / $turnterm;
- if($npcmode == 1) { $killturn = intdiv($killturn, 3); }
-
- $develcost = ($year - $startyear + 10) * 2;
-
- $env = [
- 'scenario'=>$scenario,
- 'scenario_text'=>$scenarioObj->getTitle(),
- 'icon_path'=>$scenarioObj->getIconPath(),
- 'startyear'=>$startyear,
- 'year'=> $year,
- 'month'=> $month,
- 'init_year'=> $year,
- 'init_month'=>$month,
- 'map_theme' => $scenarioObj->getMapTheme(),
- 'season'=>$seasonIdx,
- 'msg'=>'공지사항',//TODO:공지사항
- 'maxgeneral'=>GameConst::$defaultMaxGeneral,
- 'maxnation'=>GameConst::$defaultMaxNation,
- 'conlimit'=>30000,
- 'develcost'=>$develcost,
- 'turntime'=>$turntime,
- 'starttime'=>$starttime,
- 'opentime'=>$turntime,
- 'turnterm'=>$turnterm,
- 'killturn'=>$killturn,
- 'genius'=>GameConst::$defaultMaxGenius,
- 'show_img_level'=>$show_img_level,
- 'join_mode'=>$join_mode,
- 'npcmode'=>$npcmode,
- 'extended_general'=>$extend,
- 'fiction'=>$fiction,
- 'tnmt_trig'=>$tournament_trig,
- 'prev_winner'=>$prevWinner,
- 'autorun_user'=>$autorun_user,
- 'tournament'=>0,
- 'server_cnt'=>$serverCnt,
- ];
-
- $db->insert('betting', [
- 'general_id'=>0,
- 'bet0'=>0,
- 'bet1'=>0,
- 'bet2'=>0,
- 'bet3'=>0,
- 'bet4'=>0,
- 'bet5'=>0,
- 'bet6'=>0,
- 'bet7'=>0,
- 'bet8'=>0,
- 'bet9'=>0,
- 'bet10'=>0,
- 'bet11'=>0,
- 'bet12'=>0,
- 'bet13'=>0,
- 'bet14'=>0,
- 'bet15'=>0,
- ]);
-
- foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){
- $db->insert('general', [
- 'owner'=>$admin['no'],
- 'name'=>$admin['name'],
- 'picture'=>$admin['picture'],
- 'imgsvr'=>$admin['imgsvr'],
- 'turntime'=>$turntime,
- 'killturn'=>9999,
- 'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE
- ]);
- $generalID = $db->insertId();
- $turnRows = [];
- foreach(Util::range(GameConst::$maxTurn) as $turnIdx){
- $turnRows[] = [
- 'general_id'=>$generalID,
- 'turn_idx'=>$turnIdx,
- 'action'=>'휴식',
- 'arg'=>null,
- 'brief'=>'휴식'
- ];
- }
- $db->insert('general_turn', $turnRows);
-
-
- $rank_data = [];
- foreach(array_keys(General::RANK_COLUMN) as $rankColumn){
- $rank_data[] = [
- 'general_id'=>$generalID,
- 'nation_id'=>0,
- 'type'=>$rankColumn,
- 'value'=>0
- ];
- }
- $db->insert('rank_data', $rank_data);
- $db->insert('betting', [
- 'general_id'=>$generalID,
- ]);
- }
-
- foreach($env as $key=>$value){
- $gameStor->$key = $value;
- }
-
- $db->insert('ng_games', [
- 'server_id'=>$serverID,
- 'date'=>$turntime,
- 'winner_nation'=>null,
- 'map'=>$scenarioObj->getMapTheme(),
- 'season'=>$seasonIdx,
- 'scenario'=>$scenario,
- 'scenario_name'=>$scenarioObj->getTitle(),
- 'env'=>Json::encode($env)
- ]);
-
- $scenarioObj->build($env);
-
- $db->update('plock', [
- 'plock'=>0
- ], true);
-
- LogHistory(1);
-
- $prefix = DB::prefix();
- AppConf::getList()[$prefix]->closeServer();
- opcache_reset();
-
- return [
- 'result'=>true
- ];
- }
+false,
+ 'reason'=>'logs, data 디렉토리를 생성할 권한이 없습니다.'
+ ];
+ }
+ mkdir($servRoot.'/logs', 0755);
+ mkdir($servRoot.'/data', 0755);
+ }
+
+ if(!is_writable($servRoot.'/logs')){
+ return [
+ 'result'=>false,
+ 'reason'=>'logs 디렉토리의 쓰기 권한이 없습니다'
+ ];
+ }
+
+ if(!is_writable($servRoot.'/data')){
+ return [
+ 'result'=>false,
+ 'reason'=>'data 디렉토리의 쓰기 권한이 없습니다'
+ ];
+ }
+
+ if(!is_writable($servRoot.'/d_setting')){
+ return [
+ 'result'=>false,
+ 'reason'=>'d_setting 디렉토리의 쓰기 권한이 없습니다'
+ ];
+ }
+
+ if(!file_exists($servRoot.'/logs/preserved')){
+ mkdir($servRoot.'/logs/preserved', 0755);
+ }
+
+ if(!file_exists($servRoot.'/logs/.htaccess')){
+ @file_put_contents($servRoot.'/logs/.htaccess', 'Deny from all');
+ }
+
+ if(!file_exists($servRoot.'/data/.htaccess')){
+ @file_put_contents($servRoot.'/data/.htaccess', 'Deny from all');
+ }
+
+ $dir = new \DirectoryIterator($servRoot.'/logs');
+ foreach ($dir as $fileinfo) {
+ /** @var \DirectoryIterator $fileinfo */
+ if (!$fileinfo->isDir() || $fileinfo->isDot()) {
+ continue;
+ }
+ $basename = $fileinfo->getFilename();
+ if($basename == 'preserved'){
+ continue;
+ }
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+ exec("move ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
+ } else {
+ exec("mv ".escapeshellarg($servRoot.'/logs/'.$basename)." ".escapeshellarg($servRoot.'/logs/preserved/'.$basename));
+ }
+ }
+
+ $prefix = DB::prefix();
+ ServConfig::getServerList()[$prefix]->closeServer();
+
+ $db = DB::db();
+ $mysqli_obj = $db->get();
+
+ $serverID = DB::prefix().'_'.date("ymd").'_'.Util::randomStr(4);
+
+ mkdir($servRoot.'/logs/'.$serverID, 0755);
+ mkdir($servRoot.'/data/'.$serverID, 0755);
+
+ $seasonIdx = 1;
+ if($db->queryFirstField('SHOW TABLES LIKE %s', 'storage') !== null){
+ $gameStor = KVStorage::getStorage($db, 'game_env');
+ $nextSeasonIdx = $gameStor->next_season_idx;
+ if($nextSeasonIdx !== null){
+ $seasonIdx = $nextSeasonIdx;
+ }
+ $gameStor->resetCache();
+ }
+
+ $result = Util::generateFileUsingSimpleTemplate(
+ $servRoot.'/d_setting/UniqueConst.orig.php',
+ $servRoot.'/d_setting/UniqueConst.php',[
+ 'serverID'=>$serverID,
+ 'serverName'=>ServConfig::getServerList()[$prefix]->getKorName(),
+ 'seasonIdx'=>$seasonIdx,
+ ], true
+ );
+
+ copy(
+ $servRoot.'/d_setting/GameConst.orig.php',
+ $servRoot.'/d_setting/GameConst.php',
+ );
+
+ if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/reset.sql'))){
+ while(true){
+ if (!$mysqli_obj->more_results()) {
+ break;
+ }
+ if(!$mysqli_obj->next_result()){
+ break;
+ }
+ }
+
+ }
+
+ if($mysqli_obj->multi_query(file_get_contents($servRoot.'/sql/schema.sql'))){
+ while(true){
+ if (!$mysqli_obj->more_results()) {
+ break;
+ }
+ if(!$mysqli_obj->next_result()){
+ break;
+ }
+ }
+ }
+
+ $gameStor = KVStorage::getStorage($db, 'game_env');
+ $gameStor->resetValues();
+ $gameStor->next_season_idx = $seasonIdx;
+
+ return [
+ 'result'=>true,
+ 'serverID'=>$serverID,
+ 'seasonIdx'=>$seasonIdx
+ ];
+ }
+
+ static public function buildScenario(
+ int $turnterm,
+ int $sync,
+ int $scenario,
+ int $fiction,
+ int $extend,
+ int $npcmode,
+ int $show_img_level,
+ int $tournament_trig,
+ string $join_mode,
+ string $turntime,
+ ?array $autorun_user
+ ):array{
+ //FIXME: 분리할 것
+ if(120 % $turnterm != 0){
+ return [
+ 'result'=>false,
+ 'reason'=>'turnterm은 120의 약수여야 합니다.'
+ ];
+ }
+
+ if($tournament_trig < 0 || $tournament_trig > 7){
+ return [
+ 'result'=>false,
+ 'reason'=>'올바르지 않은 토너먼트 주기입니다.'
+ ];
+ }
+
+ $clearResult = self::clearDB();
+ if(!$clearResult['result']){
+ return $clearResult;
+ }
+
+ $serverID = $clearResult['serverID'];
+ $seasonIdx = $clearResult['seasonIdx'];
+
+ $scenarioObj = new Scenario($scenario, false);
+ $scenarioObj->buildConf();
+
+ $startyear = $scenarioObj->getYear()??GameConst::$defaultStartYear;
+
+ $db = DB::db();
+ $gameStor = KVStorage::getStorage($db, 'game_env');
+ $db->insert('plock', [
+ 'plock'=>1,
+ 'locktime'=>TimeUtil::now(true)
+ ]);
+
+ $prevWinner = $db->queryFirstField('SELECT l12name FROM emperior ORDER BY `no` DESC LIMIT 1');
+ $serverCnt = $db->queryFirstField('SELECT count(*) FROM ng_games') + 1;
+
+ CityConst::build();
+ $cityPositions = [];
+ foreach(CityConst::all() as $city){
+ $cityPositions[$city->id] = [
+ $city->name,
+ $city->posX,
+ $city->posY
+ ];
+ }
+ Util::generateFileUsingSimpleTemplate(
+ __DIR__.'/../templates/base_map.orig.js',
+ __DIR__.'/../d_shared/base_map.js',
+ [
+ 'cityPosition'=>Json::encode($cityPositions),
+ 'regionMap'=>Json::encode(CityConst::$regionMap),
+ 'levelMap'=>Json::encode(CityConst::$levelMap),
+ ],
+ true
+ );
+
+ if($sync == 0) {
+ // 현재 시간을 1월로 맞춤
+ $starttime = cutTurn($turntime, $turnterm);
+ $month = 1;
+ $year = $startyear;
+ } else {
+ // 현재 시간과 동기화
+ [$starttime, $yearPulled, $month] = cutDay($turntime, $turnterm);
+ if($yearPulled){
+ $year = $startyear-1;
+ }
+ else{
+ $year = $startyear;
+ }
+ }
+
+ $killturn = 4800 / $turnterm;
+ if($npcmode == 1) { $killturn = intdiv($killturn, 3); }
+
+ $develcost = ($year - $startyear + 10) * 2;
+
+ $env = [
+ 'scenario'=>$scenario,
+ 'scenario_text'=>$scenarioObj->getTitle(),
+ 'icon_path'=>$scenarioObj->getIconPath(),
+ 'startyear'=>$startyear,
+ 'year'=> $year,
+ 'month'=> $month,
+ 'init_year'=> $year,
+ 'init_month'=>$month,
+ 'map_theme' => $scenarioObj->getMapTheme(),
+ 'season'=>$seasonIdx,
+ 'msg'=>'공지사항',//TODO:공지사항
+ 'maxgeneral'=>GameConst::$defaultMaxGeneral,
+ 'maxnation'=>GameConst::$defaultMaxNation,
+ 'conlimit'=>30000,
+ 'develcost'=>$develcost,
+ 'turntime'=>$turntime,
+ 'starttime'=>$starttime,
+ 'opentime'=>$turntime,
+ 'turnterm'=>$turnterm,
+ 'killturn'=>$killturn,
+ 'genius'=>GameConst::$defaultMaxGenius,
+ 'show_img_level'=>$show_img_level,
+ 'join_mode'=>$join_mode,
+ 'npcmode'=>$npcmode,
+ 'extended_general'=>$extend,
+ 'fiction'=>$fiction,
+ 'tnmt_trig'=>$tournament_trig,
+ 'prev_winner'=>$prevWinner,
+ 'autorun_user'=>$autorun_user,
+ 'tournament'=>0,
+ 'server_cnt'=>$serverCnt,
+ ];
+
+ $db->insert('betting', [
+ 'general_id'=>0,
+ 'bet0'=>0,
+ 'bet1'=>0,
+ 'bet2'=>0,
+ 'bet3'=>0,
+ 'bet4'=>0,
+ 'bet5'=>0,
+ 'bet6'=>0,
+ 'bet7'=>0,
+ 'bet8'=>0,
+ 'bet9'=>0,
+ 'bet10'=>0,
+ 'bet11'=>0,
+ 'bet12'=>0,
+ 'bet13'=>0,
+ 'bet14'=>0,
+ 'bet15'=>0,
+ ]);
+
+ foreach(RootDB::db()->query('SELECT `no`, `name`, `picture`, `imgsvr` FROM member WHERE grade >= 5') as $admin){
+ $db->insert('general', [
+ 'owner'=>$admin['no'],
+ 'name'=>$admin['name'],
+ 'picture'=>$admin['picture'],
+ 'imgsvr'=>$admin['imgsvr'],
+ 'turntime'=>$turntime,
+ 'killturn'=>9999,
+ 'crewtype'=>GameUnitConst::DEFAULT_CREWTYPE
+ ]);
+ $generalID = $db->insertId();
+ $turnRows = [];
+ foreach(Util::range(GameConst::$maxTurn) as $turnIdx){
+ $turnRows[] = [
+ 'general_id'=>$generalID,
+ 'turn_idx'=>$turnIdx,
+ 'action'=>'휴식',
+ 'arg'=>null,
+ 'brief'=>'휴식'
+ ];
+ }
+ $db->insert('general_turn', $turnRows);
+
+
+ $rank_data = [];
+ foreach(array_keys(General::RANK_COLUMN) as $rankColumn){
+ $rank_data[] = [
+ 'general_id'=>$generalID,
+ 'nation_id'=>0,
+ 'type'=>$rankColumn,
+ 'value'=>0
+ ];
+ }
+ $db->insert('rank_data', $rank_data);
+ $db->insert('betting', [
+ 'general_id'=>$generalID,
+ ]);
+ }
+
+ foreach($env as $key=>$value){
+ $gameStor->$key = $value;
+ }
+
+ $db->insert('ng_games', [
+ 'server_id'=>$serverID,
+ 'date'=>$turntime,
+ 'winner_nation'=>null,
+ 'map'=>$scenarioObj->getMapTheme(),
+ 'season'=>$seasonIdx,
+ 'scenario'=>$scenario,
+ 'scenario_name'=>$scenarioObj->getTitle(),
+ 'env'=>Json::encode($env)
+ ]);
+
+ $scenarioObj->build($env);
+
+ $db->update('plock', [
+ 'plock'=>0
+ ], true);
+
+ LogHistory(1);
+
+ $prefix = DB::prefix();
+ ServConfig::getServerList()[$prefix]->closeServer();
+ opcache_reset();
+
+ return [
+ 'result'=>true
+ ];
+ }
}
\ No newline at end of file
diff --git a/i_entrance/j_get_user_info.php b/i_entrance/j_get_user_info.php
index 45ff4a48..5a48dc77 100644
--- a/i_entrance/j_get_user_info.php
+++ b/i_entrance/j_get_user_info.php
@@ -1,90 +1,90 @@
-setReadOnly();
-$userID = Session::getUserID();
-
-// 외부 파라미터
-
-$db = RootDB::db();
-$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID);
-
-if(!$member['picture']){
- $picture = ServConfig::getSharedIconPath().'/default.jpg';
-}
-else{
- $picture = $member['picture'];
- if(strlen($picture) > 11){
- $picture = substr($picture, 0, -10);
- }
-
- $pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture;
-
- if(file_exists($pictureFSPath)){
- $picture = AppConf::getUserIconPathWeb().'/'.$picture;
- }
- else{
- $picture = ServConfig::getSharedIconPath().'/'.$picture;
- }
-}
-
-$tokenValidUntil = $member['token_valid_until'];
-
-if($member['grade'] == 6) {
- $grade = '운영자';
-} elseif($member['grade'] == 5) {
- $grade = '부운영자';
-} elseif($member['grade'] > 1) {
- $grade = '특별회원';
-} elseif($member['grade'] == 1) {
- $grade = '일반회원';
-} elseif($member['grade'] == 0) {
- $grade = '블럭회원';
-}
-
-$acl = [];
-//TODO: Acl을 관리하기 위한 별도의 객체 필요
-foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){
- $serverKorName = AppConf::getList()[$serverName]->getKorName();
- $aclTextList = array_map(function($aclName){
- $aclText = "알수없음[{$aclName}]";
- switch($aclName){
- case 'openClose':$aclText='서버여닫기';break;
- case 'reset':$aclText='서버리셋';break;
- case 'update':$aclText='업데이트';break;
- case 'fullUpdate':$aclText='임의업데이트';break;
- case 'vote':$aclText='설문조사';break;
- case 'globalNotice':$aclText='전역공지';break;
- case 'notice':$aclText='공지';break;
- case 'blockGeneral':$aclText='장수징계';break;
- }
- return $aclText;
- }, $aclList);
-
- $acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList));
-}
-
-if($acl){
- $acl = join(",
", $acl);
-}
-else{
- $acl = "-";
-}
-
-Json::die([
- 'result'=>true,
- 'reason'=>'success',
- 'id'=>$member['id'],
- 'name'=>$member['name'],
- 'grade'=>$grade,
- 'picture'=>$picture,
- 'global_salt'=>RootDB::getGlobalSalt(),
- 'join_date'=>$member['reg_date'],
- 'third_use'=>($member['third_use']!=0),
- 'acl'=>$acl,
- 'oauth_type'=>$member['oauth_type'],
- 'token_valid_until'=>$tokenValidUntil
-
-]);
+setReadOnly();
+$userID = Session::getUserID();
+
+// 외부 파라미터
+
+$db = RootDB::db();
+$member = $db->queryFirstRow('SELECT `id`, `name`, `grade`, `picture`, reg_date, third_use, acl, oauth_type, token_valid_until FROM `member` WHERE `NO` = %i', $userID);
+
+if(!$member['picture']){
+ $picture = ServConfig::getSharedIconPath().'/default.jpg';
+}
+else{
+ $picture = $member['picture'];
+ if(strlen($picture) > 11){
+ $picture = substr($picture, 0, -10);
+ }
+
+ $pictureFSPath = AppConf::getUserIconPathFS().'/'.$picture;
+
+ if(file_exists($pictureFSPath)){
+ $picture = AppConf::getUserIconPathWeb().'/'.$picture;
+ }
+ else{
+ $picture = ServConfig::getSharedIconPath().'/'.$picture;
+ }
+}
+
+$tokenValidUntil = $member['token_valid_until'];
+
+if($member['grade'] == 6) {
+ $grade = '운영자';
+} elseif($member['grade'] == 5) {
+ $grade = '부운영자';
+} elseif($member['grade'] > 1) {
+ $grade = '특별회원';
+} elseif($member['grade'] == 1) {
+ $grade = '일반회원';
+} elseif($member['grade'] == 0) {
+ $grade = '블럭회원';
+}
+
+$acl = [];
+//TODO: Acl을 관리하기 위한 별도의 객체 필요
+foreach(Json::decode($member['acl']??'{}') as $serverName=>$aclList){
+ $serverKorName = ServConfig::getServerList()[$serverName]->getKorName();
+ $aclTextList = array_map(function($aclName){
+ $aclText = "알수없음[{$aclName}]";
+ switch($aclName){
+ case 'openClose':$aclText='서버여닫기';break;
+ case 'reset':$aclText='서버리셋';break;
+ case 'update':$aclText='업데이트';break;
+ case 'fullUpdate':$aclText='임의업데이트';break;
+ case 'vote':$aclText='설문조사';break;
+ case 'globalNotice':$aclText='전역공지';break;
+ case 'notice':$aclText='공지';break;
+ case 'blockGeneral':$aclText='장수징계';break;
+ }
+ return $aclText;
+ }, $aclList);
+
+ $acl[] = sprintf("%s(%s)", $serverKorName, join(",", $aclTextList));
+}
+
+if($acl){
+ $acl = join(",
", $acl);
+}
+else{
+ $acl = "-";
+}
+
+Json::die([
+ 'result'=>true,
+ 'reason'=>'success',
+ 'id'=>$member['id'],
+ 'name'=>$member['name'],
+ 'grade'=>$grade,
+ 'picture'=>$picture,
+ 'global_salt'=>RootDB::getGlobalSalt(),
+ 'join_date'=>$member['reg_date'],
+ 'third_use'=>($member['third_use']!=0),
+ 'acl'=>$acl,
+ 'oauth_type'=>$member['oauth_type'],
+ 'token_valid_until'=>$tokenValidUntil
+
+]);
diff --git a/i_entrance/j_get_userlist.php b/i_entrance/j_get_userlist.php
index 505112f8..03c39bcb 100644
--- a/i_entrance/j_get_userlist.php
+++ b/i_entrance/j_get_userlist.php
@@ -1,58 +1,58 @@
-setReadOnly();
-$db = RootDB::db();
-
-if($session->userGrade < 5){
- Json::die([
- 'result'=>false,
- 'reason'=>'권한이 부족합니다'
- ]);
-}
-
-$userList = [];
-foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member
- left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login"
- group by member.no order by member.no asc') as $member){
-
- if($member['IMGSVR']){
- $icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE'];
- }
- else{
- $icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE'];
- }
-
- $userList[] = [
- 'userID'=>$member['NO'],
- 'userName'=>$member['ID'],
- 'email'=>$member['EMAIL'],
- 'authType'=>$member['oauth_type'],
- 'userGrade'=>$member['GRADE'],
- 'blockUntil'=>$member['BLOCK_DATE'],
- 'nickname'=>$member['NAME'],
- 'icon'=>$icon,
- 'joinDate'=>$member['REG_DATE'],
- 'loginDate'=>$member['loginDate'],
- 'deleteAfter'=>$member['delete_after']
- ];
-}
-
-$serverList = [];
-foreach(AppConf::getList() as $serverName => $setting){
- if($setting->isRunning()){
- $serverList[] = $serverName;
- }
-}
-
-$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1');
-
-Json::die([
- 'result'=>true,
- 'users'=>$userList,
- 'servers'=>$serverList,
- 'allowJoin'=>$system['REG']=='Y',
- 'allowLogin'=>$system['LOGIN']=='Y'
+setReadOnly();
+$db = RootDB::db();
+
+if($session->userGrade < 5){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'권한이 부족합니다'
+ ]);
+}
+
+$userList = [];
+foreach($db->query('SELECT member.*, max(member_log.date) as loginDate from member
+ left join member_log on member.`NO` = member_log.member_no and member_log.action_type="login"
+ group by member.no order by member.no asc') as $member){
+
+ if($member['IMGSVR']){
+ $icon = AppConf::getUserIconPathWeb().'/'.$member['PICTURE'];
+ }
+ else{
+ $icon = ServConfig::getSharedIconPath().'/'.$member['PICTURE'];
+ }
+
+ $userList[] = [
+ 'userID'=>$member['NO'],
+ 'userName'=>$member['ID'],
+ 'email'=>$member['EMAIL'],
+ 'authType'=>$member['oauth_type'],
+ 'userGrade'=>$member['GRADE'],
+ 'blockUntil'=>$member['BLOCK_DATE'],
+ 'nickname'=>$member['NAME'],
+ 'icon'=>$icon,
+ 'joinDate'=>$member['REG_DATE'],
+ 'loginDate'=>$member['loginDate'],
+ 'deleteAfter'=>$member['delete_after']
+ ];
+}
+
+$serverList = [];
+foreach(ServConfig::getServerList() as $serverName => $setting){
+ if($setting->isRunning()){
+ $serverList[] = $serverName;
+ }
+}
+
+$system = $db->queryFirstRow('SELECT `REG`, `LOGIN` FROM `system` limit 1');
+
+Json::die([
+ 'result'=>true,
+ 'users'=>$userList,
+ 'servers'=>$serverList,
+ 'allowJoin'=>$system['REG']=='Y',
+ 'allowLogin'=>$system['LOGIN']=='Y'
]);
\ No newline at end of file
diff --git a/i_entrance/j_icon_change.php b/i_entrance/j_icon_change.php
index 487c36a4..eed93411 100644
--- a/i_entrance/j_icon_change.php
+++ b/i_entrance/j_icon_change.php
@@ -1,107 +1,107 @@
-setReadOnly();
-$userID = Session::getUserID();
-
-// 외부 파라미터
-// $_FILES['image_upload'] : 사진파일
-
-$defaultImg = 'default.jpg';
-
-$image = $_FILES['image_upload'];
-$ext = strrchr($image['name'], ".");
-$size = getImageSize($image['tmp_name']);
-
-
-
-$imageType = $size[2];
-
-$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF);
-
-$db = RootDB::db();
-$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID);
-
-
-$picName = $member['PICTURE'];
-$newExt = array_search($imageType, $availableImageType, true);
-
-if($picName && strlen($picName) > 11){
- $dt = substr($picName, -8);
- $picName = substr($picName, 0, -10);
-}
-else{
- $dt = '00000000';
-}
-
-$rf = date('Ymd');
-
-$response['result'] = false;
-$response['reason'] = '요청이 올바르지 않습니다!';
-if(!is_uploaded_file($image['tmp_name'])) {
- //진짜 전송된 파일인지 검증
- $response['reason'] = '업로드가 되지 않았습니다!';
- $response['result'] = false;
-} elseif(!$newExt) {
- //확장자 검사
- $response['reason'] = 'jpg, gif, png 파일이 아닙니다!';
- $response['result'] = false;
-} elseif($image['size'] > 30720) {
- //파일크기 검사
- $response['reason'] = '30kb 이하로 올려주세요!';
- $response['result'] = false;
-} elseif($size[0] < 64 || 128 < $size[0]) {
- //이미지크기 검사
- $response['reason'] = '64x64 ~ 128x128 사이여야 합니다.';
- $response['result'] = false;
-} elseif($size[0] != $size[1]) {
- //이미지크기 검사
- $response['reason'] = '1:1 비율 이미지여야 합니다.';
- $response['result'] = false;
-}elseif($dt == $rf) {
- //갱신날짜 검사
- $response['reason'] = '1일 1회 변경 가능합니다!';
- $response['result'] = false;
-} else {
- //이미지 저장
-
- while(true){
- $newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt;
- $dest = AppConf::getUserIconPathFS().'/'.$newPicName;
- if(file_exists($dest)){
- continue;
- }
- break;
- }
-
- if(!move_uploaded_file($image['tmp_name'], $dest)) {
- $response['reason'] = '업로드에 실패했습니다!';
- $response['result'] = false;
- } else {
- $pic = "{$newPicName}?={$rf}";
- RootDB::db()->update('member',[
- 'PICTURE' => $pic,
- 'IMGSVR' => 1
- ], 'NO=%i', $userID);
-
- $servers = [];
-
- foreach(AppConf::getList() as $key=>$setting){
- if($setting->isRunning()){
- $servers[] = [$key, $setting->getKorName()];
- }
- }
-
- $response['servers'] = $servers;
-
- $response['reason'] = '업로드에 성공했습니다!';
- $response['result'] = true;
- }
-}
-
-
+setReadOnly();
+$userID = Session::getUserID();
+
+// 외부 파라미터
+// $_FILES['image_upload'] : 사진파일
+
+$defaultImg = 'default.jpg';
+
+$image = $_FILES['image_upload'];
+$ext = strrchr($image['name'], ".");
+$size = getImageSize($image['tmp_name']);
+
+
+
+$imageType = $size[2];
+
+$availableImageType = array('.jpg'=>IMAGETYPE_JPEG, '.png'=>IMAGETYPE_PNG, '.gif'=>IMAGETYPE_GIF);
+
+$db = RootDB::db();
+$member = $db->queryFirstRow('SELECT `ID`, `PICTURE` FROM `member` WHERE `NO` = %i', $userID);
+
+
+$picName = $member['PICTURE'];
+$newExt = array_search($imageType, $availableImageType, true);
+
+if($picName && strlen($picName) > 11){
+ $dt = substr($picName, -8);
+ $picName = substr($picName, 0, -10);
+}
+else{
+ $dt = '00000000';
+}
+
+$rf = date('Ymd');
+
+$response['result'] = false;
+$response['reason'] = '요청이 올바르지 않습니다!';
+if(!is_uploaded_file($image['tmp_name'])) {
+ //진짜 전송된 파일인지 검증
+ $response['reason'] = '업로드가 되지 않았습니다!';
+ $response['result'] = false;
+} elseif(!$newExt) {
+ //확장자 검사
+ $response['reason'] = 'jpg, gif, png 파일이 아닙니다!';
+ $response['result'] = false;
+} elseif($image['size'] > 30720) {
+ //파일크기 검사
+ $response['reason'] = '30kb 이하로 올려주세요!';
+ $response['result'] = false;
+} elseif($size[0] < 64 || 128 < $size[0]) {
+ //이미지크기 검사
+ $response['reason'] = '64x64 ~ 128x128 사이여야 합니다.';
+ $response['result'] = false;
+} elseif($size[0] != $size[1]) {
+ //이미지크기 검사
+ $response['reason'] = '1:1 비율 이미지여야 합니다.';
+ $response['result'] = false;
+}elseif($dt == $rf) {
+ //갱신날짜 검사
+ $response['reason'] = '1일 1회 변경 가능합니다!';
+ $response['result'] = false;
+} else {
+ //이미지 저장
+
+ while(true){
+ $newPicName = dechex(rand(0x000000f,0xfffffff)).$newExt;
+ $dest = AppConf::getUserIconPathFS().'/'.$newPicName;
+ if(file_exists($dest)){
+ continue;
+ }
+ break;
+ }
+
+ if(!move_uploaded_file($image['tmp_name'], $dest)) {
+ $response['reason'] = '업로드에 실패했습니다!';
+ $response['result'] = false;
+ } else {
+ $pic = "{$newPicName}?={$rf}";
+ RootDB::db()->update('member',[
+ 'PICTURE' => $pic,
+ 'IMGSVR' => 1
+ ], 'NO=%i', $userID);
+
+ $servers = [];
+
+ foreach(ServConfig::getServerList() as $key=>$setting){
+ if($setting->isRunning()){
+ $servers[] = [$key, $setting->getKorName()];
+ }
+ }
+
+ $response['servers'] = $servers;
+
+ $response['reason'] = '업로드에 성공했습니다!';
+ $response['result'] = true;
+ }
+}
+
+
Json::die($response);
\ No newline at end of file
diff --git a/i_entrance/j_icon_delete.php b/i_entrance/j_icon_delete.php
index 4b1ae842..bc4914ce 100644
--- a/i_entrance/j_icon_delete.php
+++ b/i_entrance/j_icon_delete.php
@@ -1,56 +1,56 @@
-setReadOnly();
-$userID = Session::getUserID();
-
-// 외부 파라미터
-
-$respone = [];
-$db = RootDB::db();
-$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID);
-
-if($picName && strlen($picName) > 11){
- $dt = substr($picName, -8);
- $picName = substr($picName, 0, -10);
-}
-else{
- $dt = '00000000';
-}
-
-$dest = AppConf::getUserIconPathFS().'/'.$picName;
-
-$rf = date('Ymd');
-
-$response['result'] = false;
-$response['reason'] = '요청이 올바르지 않습니다!';
-
-if($dt == $rf) {
- //갱신날짜 검사
- $response['reason'] = '1일 1회 변경 가능합니다!';
- $response['result'] = false;
-} else {
- $db->update('member', [
- 'PICTURE'=>'default.jpg',
- 'IMGSVR'=>0,
- ], 'NO=%i', $userID);
-
- $servers = [];
-
- foreach(AppConf::getList() as $key=>$setting){
-
- if($setting->isRunning()){
- $servers[] = [$key, $setting->getKorName()];
- }
- }
-
- $response['servers'] = $servers;
- $response['reason'] = '제거에 성공했습니다!';
- $response['result'] = true;
-}
-
+setReadOnly();
+$userID = Session::getUserID();
+
+// 외부 파라미터
+
+$respone = [];
+$db = RootDB::db();
+$picName = $db->queryFirstField('SELECT picture FROM `member` WHERE `NO` = %i', $userID);
+
+if($picName && strlen($picName) > 11){
+ $dt = substr($picName, -8);
+ $picName = substr($picName, 0, -10);
+}
+else{
+ $dt = '00000000';
+}
+
+$dest = AppConf::getUserIconPathFS().'/'.$picName;
+
+$rf = date('Ymd');
+
+$response['result'] = false;
+$response['reason'] = '요청이 올바르지 않습니다!';
+
+if($dt == $rf) {
+ //갱신날짜 검사
+ $response['reason'] = '1일 1회 변경 가능합니다!';
+ $response['result'] = false;
+} else {
+ $db->update('member', [
+ 'PICTURE'=>'default.jpg',
+ 'IMGSVR'=>0,
+ ], 'NO=%i', $userID);
+
+ $servers = [];
+
+ foreach(ServConfig::getServerList() as $key=>$setting){
+
+ if($setting->isRunning()){
+ $servers[] = [$key, $setting->getKorName()];
+ }
+ }
+
+ $response['servers'] = $servers;
+ $response['reason'] = '제거에 성공했습니다!';
+ $response['result'] = true;
+}
+
Json::die($response);
\ No newline at end of file
diff --git a/i_entrance/j_server_change_status.php b/i_entrance/j_server_change_status.php
index 22b2bc14..982bd581 100644
--- a/i_entrance/j_server_change_status.php
+++ b/i_entrance/j_server_change_status.php
@@ -1,117 +1,117 @@
-userGrade;
-$acl = $session->acl;
-$session->setReadOnly();
-
-if($userGrade < 5 && !$acl) {
- Json::die([
- 'result'=>'FAIL',
- 'msg'=>'운영자 권한이 없습니다.'
- ]);
-}
-
-function doServerModeSet($server, $action, &$response, $session){
-
- $serverList = AppConf::getList();
- $settingObj = $serverList[$server];
- $serverAcl = $session->acl[$server]??[];
- $userGrade = $session->userGrade;
-
- $serverDir = $settingObj->getShortName();
- $serverPath = $settingObj->getBasePath();
- $realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath;
-
- if($action == 'close') { //폐쇄
- $doClose = false;
- if($userGrade >= 5){
- $doClose = true;
- }
- else if(in_array('openClose', $serverAcl)){
- $doClose = true;
- }
-
- if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){
- require($serverPath.'/lib.php');
- $localGameStorage = KVStorage::getStorage(DB::db(), 'game_env');
- //천통 이후, 오픈 직후는 닫을 수 있음
- $localGameStorage->cacheValues(['isunited', 'startyear', 'year']);
-
- if($localGameStorage->isunited){
- $doClose = true;
- }
- else if($localGameStorage->year < $localGameStorage->startyear + 2){
- $doClose = true;
- }
-
- }
-
- if(!$doClose){
- if(in_array('reset', $serverAcl)){
- $response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.';
- }
- else{
- $response['msg'] = '서버 닫기 권한이 부족합니다.';
- }
- return false;
- }
- return $settingObj->closeServer();
- } elseif($action == 'reset' && $userGrade >= 6) {//리셋
- //FIXME: reset, reset_full 구현
- if(file_exists($serverPath.'/d_setting/DB.php')){
- @unlink($serverPath.'/d_setting/DB.php');
- }
-
- $response['installURL'] = $serverDir."/install.php";
- } elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈
- return $settingObj->openServer();
- } else{
- $response['msg'] = '올바르지 않은 요청입니다';
- return false;
- }
- return true;
-}
-
-function doAdminPost($action, $notice, $server, $session){
- $response = ['result' => 'FAIL'];
-
- $globalAcl = $session->acl['global']??[];
- $userGrade = $session->userGrade;
-
- if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) {
- RootDB::db()->update('system', ['NOTICE'=>$notice], true);
- $response['result'] = 'SUCCESS';
- return $response;
- }
-
- if(doServerModeSet($server, $action, $response, $session)){
- $response['result'] = 'SUCCESS';
- return $response;
- }
-
- return $response;
-
-}
-
-$response = doAdminPost($action, $notice, $server, $session);
-
-Json::die($response);
+userGrade;
+$acl = $session->acl;
+$session->setReadOnly();
+
+if($userGrade < 5 && !$acl) {
+ Json::die([
+ 'result'=>'FAIL',
+ 'msg'=>'운영자 권한이 없습니다.'
+ ]);
+}
+
+function doServerModeSet($server, $action, &$response, $session){
+
+ $serverList = ServConfig::getServerList();
+ $settingObj = $serverList[$server];
+ $serverAcl = $session->acl[$server]??[];
+ $userGrade = $session->userGrade;
+
+ $serverDir = $settingObj->getShortName();
+ $serverPath = $settingObj->getBasePath();
+ $realServerPath = realpath(dirname(__FILE__)).'/'.$serverPath;
+
+ if($action == 'close') { //폐쇄
+ $doClose = false;
+ if($userGrade >= 5){
+ $doClose = true;
+ }
+ else if(in_array('openClose', $serverAcl)){
+ $doClose = true;
+ }
+
+ if(!$doClose && in_array('reset', $serverAcl) && file_exists($serverPath.'/d_setting/DB.php')){
+ require($serverPath.'/lib.php');
+ $localGameStorage = KVStorage::getStorage(DB::db(), 'game_env');
+ //천통 이후, 오픈 직후는 닫을 수 있음
+ $localGameStorage->cacheValues(['isunited', 'startyear', 'year']);
+
+ if($localGameStorage->isunited){
+ $doClose = true;
+ }
+ else if($localGameStorage->year < $localGameStorage->startyear + 2){
+ $doClose = true;
+ }
+
+ }
+
+ if(!$doClose){
+ if(in_array('reset', $serverAcl)){
+ $response['msg'] = '서버 시작 직후, 또는 천하통일 이후에만 닫을 수 있습니다.';
+ }
+ else{
+ $response['msg'] = '서버 닫기 권한이 부족합니다.';
+ }
+ return false;
+ }
+ return $settingObj->closeServer();
+ } elseif($action == 'reset' && $userGrade >= 6) {//리셋
+ //FIXME: reset, reset_full 구현
+ if(file_exists($serverPath.'/d_setting/DB.php')){
+ @unlink($serverPath.'/d_setting/DB.php');
+ }
+
+ $response['installURL'] = $serverDir."/install.php";
+ } elseif($action == 'open' && ($userGrade >= 5 || in_array('openClose', $serverAcl))) {//오픈
+ return $settingObj->openServer();
+ } else{
+ $response['msg'] = '올바르지 않은 요청입니다';
+ return false;
+ }
+ return true;
+}
+
+function doAdminPost($action, $notice, $server, $session){
+ $response = ['result' => 'FAIL'];
+
+ $globalAcl = $session->acl['global']??[];
+ $userGrade = $session->userGrade;
+
+ if($action == 'notice' && ($userGrade >= 5 || in_array('notice', $globalAcl))) {
+ RootDB::db()->update('system', ['NOTICE'=>$notice], true);
+ $response['result'] = 'SUCCESS';
+ return $response;
+ }
+
+ if(doServerModeSet($server, $action, $response, $session)){
+ $response['result'] = 'SUCCESS';
+ return $response;
+ }
+
+ return $response;
+
+}
+
+$response = doAdminPost($action, $notice, $server, $session);
+
+Json::die($response);
diff --git a/i_entrance/j_server_get_admin_status.php b/i_entrance/j_server_get_admin_status.php
index 575430d7..ca8b13e3 100644
--- a/i_entrance/j_server_get_admin_status.php
+++ b/i_entrance/j_server_get_admin_status.php
@@ -1,90 +1,90 @@
-setReadOnly();
-if($session->userGrade < 5 && !$session->acl){
- Json::die([
- 'result'=>false,
- 'reason'=>'권한이 부족합니다.'
- ]);
-}
-
-$result = [];
-
-$server = [];
-
-$storage = new \sammo\KVStorage(RootDB::db(), 'git_path');
-$serverGitPath = $storage->getAll();
-
-$rootServer = Util::array_last(AppConf::getList())->getShortName();
-
-foreach (AppConf::getList() as $setting) {
- $serverColor = $setting->getColor();
- $serverKorName = $setting->getKorName();
-
- $serverPath = $setting->getBasePath();
- $serverDir = $setting->getShortName();
- //TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함
- if (!is_dir($serverPath)) {
- $state = [
- 'valid'=>false,
- 'run'=>false,
- 'installed'=>false,
- 'version'=>$setting->getVersion(),
- 'reason'=>'디렉토리 없음'
- ];
- } elseif (!file_exists($serverPath.'/index.php')) {
- $state = [
- 'valid'=>false,
- 'run'=>false,
- 'installed'=>false,
- 'version'=>$setting->getVersion(),
- 'reason'=>'index.php 없음'
- ];
- } elseif (!$setting->isExists()) {
- $state = [
- 'valid'=>false,
- 'run'=>false,
- 'installed'=>true,
- 'version'=>$setting->getVersion(),
- 'reason'=>'설정 파일 없음'
- ];
- } elseif (!$setting->isRunning()) {
- // 폐쇄중
- $state = [
- 'valid'=>true,
- 'run'=>false,
- 'installed'=>true,
- 'version'=>$setting->getVersion(),
- 'reason'=>'폐쇄됨'
- ];
- } else {
- // 오픈중
- $state = [
- 'valid'=>true,
- 'run'=>true,
- 'installed'=>true,
- 'version'=>$setting->getVersion(),
- 'reason'=>'운영중'
- ];
- }
-
- $state += [
- 'name' => $serverDir,
- 'korName' => $serverKorName,
- 'color' => $serverColor,
- 'isRoot' => $serverDir == $rootServer,
- 'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel')
- ];
- $server[] = $state;
-}
-
-Json::die([
- 'acl' => $session->acl,
- 'server' => $server,
- 'grade' => $session->userGrade
+setReadOnly();
+if($session->userGrade < 5 && !$session->acl){
+ Json::die([
+ 'result'=>false,
+ 'reason'=>'권한이 부족합니다.'
+ ]);
+}
+
+$result = [];
+
+$server = [];
+
+$storage = new \sammo\KVStorage(RootDB::db(), 'git_path');
+$serverGitPath = $storage->getAll();
+
+$rootServer = Util::array_last(ServConfig::getServerList())->getShortName();
+
+foreach (ServConfig::getServerList() as $setting) {
+ $serverColor = $setting->getColor();
+ $serverKorName = $setting->getKorName();
+
+ $serverPath = $setting->getBasePath();
+ $serverDir = $setting->getShortName();
+ //TODO: .htaccess가 서버 오픈에도 사용될 수 있으니 별도의 방법이 필요함
+ if (!is_dir($serverPath)) {
+ $state = [
+ 'valid'=>false,
+ 'run'=>false,
+ 'installed'=>false,
+ 'version'=>$setting->getVersion(),
+ 'reason'=>'디렉토리 없음'
+ ];
+ } elseif (!file_exists($serverPath.'/index.php')) {
+ $state = [
+ 'valid'=>false,
+ 'run'=>false,
+ 'installed'=>false,
+ 'version'=>$setting->getVersion(),
+ 'reason'=>'index.php 없음'
+ ];
+ } elseif (!$setting->isExists()) {
+ $state = [
+ 'valid'=>false,
+ 'run'=>false,
+ 'installed'=>true,
+ 'version'=>$setting->getVersion(),
+ 'reason'=>'설정 파일 없음'
+ ];
+ } elseif (!$setting->isRunning()) {
+ // 폐쇄중
+ $state = [
+ 'valid'=>true,
+ 'run'=>false,
+ 'installed'=>true,
+ 'version'=>$setting->getVersion(),
+ 'reason'=>'폐쇄됨'
+ ];
+ } else {
+ // 오픈중
+ $state = [
+ 'valid'=>true,
+ 'run'=>true,
+ 'installed'=>true,
+ 'version'=>$setting->getVersion(),
+ 'reason'=>'운영중'
+ ];
+ }
+
+ $state += [
+ 'name' => $serverDir,
+ 'korName' => $serverKorName,
+ 'color' => $serverColor,
+ 'isRoot' => $serverDir == $rootServer,
+ 'lastGitPath' => ($serverGitPath[$serverDir][0])??($serverDir == $rootServer?'devel':'origin/devel')
+ ];
+ $server[] = $state;
+}
+
+Json::die([
+ 'acl' => $session->acl,
+ 'server' => $server,
+ 'grade' => $session->userGrade
]);
\ No newline at end of file
diff --git a/i_entrance/j_server_get_status.php b/i_entrance/j_server_get_status.php
index c5b05bf2..4a765843 100644
--- a/i_entrance/j_server_get_status.php
+++ b/i_entrance/j_server_get_status.php
@@ -1,25 +1,25 @@
-$setting->getColor(),
- 'korName'=>$setting->getKorName(),
- 'name'=>$setting->getShortName(),
- 'exists'=>$setting->isExists(),
- 'enable'=>$setting->isRunning()
- ];
-
- $response['server'][] = $serverObj;
-}
-
-$response['result'] = 'SUCCESS';
-Json::die($response);
-
-
+$setting->getColor(),
+ 'korName'=>$setting->getKorName(),
+ 'name'=>$setting->getShortName(),
+ 'exists'=>$setting->isExists(),
+ 'enable'=>$setting->isRunning()
+ ];
+
+ $response['server'][] = $serverObj;
+}
+
+$response['result'] = 'SUCCESS';
+Json::die($response);
+
+
diff --git a/j_updateServer.php b/j_updateServer.php
index 7cad2607..ed426d18 100644
--- a/j_updateServer.php
+++ b/j_updateServer.php
@@ -1,240 +1,240 @@
-setReadOnly();
-
-$request = $_POST + $_GET;
-
-$rootDB = RootDB::db();
-$storage = new \sammo\KVStorage($rootDB, 'git_path');
-$tmpFile = 'd_log/arc.zip';
-
-$v = new Validator($request);
-
-$v->rule('required', [
- 'server'
-])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/');
-
-if (!$v->validate()) {
- Json::die([
- 'result' => false,
- 'reason' => $v->errorStr()
- ]);
-}
-
-$target = Util::getPost('target');
-
-$server = basename($request['server']);
-
-$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []);
-$allowFullUpdate |= $session->userGrade >= 6;
-
-$allowUpdate = in_array('update', $session->acl[$server] ?? []);
-$allowUpdate |= $session->userGrade >= 5;
-$allowUpdate |= $allowFullUpdate;
-
-
-if (!$allowUpdate) {
- Json::die([
- 'result' => false,
- 'reason' => '권한이 충분하지 않습니다'
- ]);
-}
-
-$src_target = $storage->$server;
-if ($src_target) {
- $src_target = $src_target[0];
-}
-
-if (!$allowFullUpdate || !$target) {
- $target = $src_target;
-} else {
- $target = $request['target'];
-}
-
-$baseServerName = Util::array_last_key(AppConf::getList());
-
-if (!$target && $server != $baseServerName) {
- Json::die([
- 'result' => false,
- 'reason' => 'git -ish target이 제대로 지정되지 않았습니다.'
- ]);
-}
-
-
-$targetDir = $target . ':' . $baseServerName;
-
-if (!key_exists($server, AppConf::getList())) {
- Json::die([
- 'result' => false,
- 'reason' => '불가능한 서버 이름입니다.'
- ]);
-}
-
-if (\file_exists($server) && !is_dir($server)) {
- Json::die([
- 'result' => false,
- 'reason' => '같은 이름을 가진 파일이 있습니다.'
- ]);
-}
-
-if (file_exists($server) && !is_writable($server)) {
- Json::die([
- 'result' => false,
- 'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.'
- ]);
-}
-
-if (!file_exists($server)) {
- if (!is_writable('.')) {
- Json::die([
- 'result' => false,
- 'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.'
- ]);
- }
- mkdir($server, 0755);
-}
-
-
-if ($server == $baseServerName) {
-
- exec("git fetch -q 2>&1", $output);
- if ($output) {
- Json::die([
- 'result' => false,
- 'reason' => 'git pull 작업 : ' . join(', ', $output)
- ]);
- }
-
- if ($target != $src_target) {
- $command = sprintf('git checkout %s -q 2>&1', $target);
- exec($command, $output);
- if ($output) {
- Json::die([
- 'result' => false,
- 'reason' => join(', ', $output)
- ]);
- }
- }
-
- exec("git pull -q 2>&1", $output);
- if ($output && $output[0] != 'Already up-to-date.') {
- Json::die([
- 'result' => false,
- 'reason' => 'git pull 작업 : ' . join(', ', $output)
- ]);
- }
-
- $version = getVersion();
- $result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
- __DIR__ . '/' . $server . '/d_setting/VersionGit.php',
- [
- 'verionGit' => $version
- ],
- true
- );
-
- if (ServConfig::$imageRequestKey) {
- try {
- $imagePullPath = ServConfig::getImagePullURI();
- $pullResult = @file_get_contents($imagePullPath);
- if ($pullResult === false) {
- throw new \ErrorException('Invalid URI');
- }
- $pullResult = Json::decode($pullResult);
- if ($pullResult['result']) {
- $imgResult = true;
- $imgDetail = $pullResult['version'];
- } else {
- $imgResult = false;
- $imgDetail = $pullResult['reason'];
- }
- } catch (\Exception $e) {
- $imgResult = false;
- $imgDetail = $e->getMessage();
- }
- } else {
- $imgResult = true;
- $imgDetail = 'No key';
- }
-
-
-
- $storage->$server = [$target, $version];
- opcache_reset();
-
- Json::die([
- 'server' => $server,
- 'result' => true,
- 'version' => $version,
- 'imgResult' => $imgResult,
- 'imgDetail' => $imgDetail,
- ]);
-}
-
-$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir));
-exec("$command 2>&1", $output);
-if ($output) {
- Json::die([
- 'result' => false,
- 'reason' => join(', ', $output)
- ]);
-}
-
-$zip = new \ZipArchive;
-if ($zip->open($tmpFile) !== true) {
- Json::die([
- 'result' => false,
- 'reason' => 'archive가 생성되지 않았습니다.'
- ]);
-}
-
-if (!$zip->extractTo($server)) {
- Json::die([
- 'result' => false,
- 'reason' => '생성한 archive를 제대로 옮기지 못했습니다.'
- ]);
-}
-
-$zip->close();
-@unlink($tmpFile);
-
-$version = getVersion($target);
-$result = Util::generateFileUsingSimpleTemplate(
- __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
- __DIR__ . '/' . $server . '/d_setting/VersionGit.php',
- [
- 'verionGit' => $version
- ],
- true
-);
-
-$storage->$server = [$target, $version];
-//AppConf::getList()[$server]->closeServer();
-opcache_reset();
-
-Json::die([
- 'server' => $server,
- 'result' => true,
- 'version' => $version
-]);
+setReadOnly();
+
+$request = $_POST + $_GET;
+
+$rootDB = RootDB::db();
+$storage = new \sammo\KVStorage($rootDB, 'git_path');
+$tmpFile = 'd_log/arc.zip';
+
+$v = new Validator($request);
+
+$v->rule('required', [
+ 'server'
+])->rule('regex', 'target', '/^[0-9a-zA-Z^{}\\/\-_,.@]+$/');
+
+if (!$v->validate()) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $v->errorStr()
+ ]);
+}
+
+$target = Util::getPost('target');
+
+$server = basename($request['server']);
+
+$allowFullUpdate = in_array('fullUpdate', $session->acl[$server] ?? []);
+$allowFullUpdate |= $session->userGrade >= 6;
+
+$allowUpdate = in_array('update', $session->acl[$server] ?? []);
+$allowUpdate |= $session->userGrade >= 5;
+$allowUpdate |= $allowFullUpdate;
+
+
+if (!$allowUpdate) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '권한이 충분하지 않습니다'
+ ]);
+}
+
+$src_target = $storage->$server;
+if ($src_target) {
+ $src_target = $src_target[0];
+}
+
+if (!$allowFullUpdate || !$target) {
+ $target = $src_target;
+} else {
+ $target = $request['target'];
+}
+
+$baseServerName = Util::array_last_key(ServConfig::getServerList());
+
+if (!$target && $server != $baseServerName) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'git -ish target이 제대로 지정되지 않았습니다.'
+ ]);
+}
+
+
+$targetDir = $target . ':' . $baseServerName;
+
+if (!key_exists($server, ServConfig::getServerList())) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '불가능한 서버 이름입니다.'
+ ]);
+}
+
+if (\file_exists($server) && !is_dir($server)) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '같은 이름을 가진 파일이 있습니다.'
+ ]);
+}
+
+if (file_exists($server) && !is_writable($server)) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $server . ' 디렉토리 쓰기 권한이 없습니다.'
+ ]);
+}
+
+if (!file_exists($server)) {
+ if (!is_writable('.')) {
+ Json::die([
+ 'result' => false,
+ 'reason' => $server . ' 디렉토리가 없지만 생성할 권한이 없습니다.'
+ ]);
+ }
+ mkdir($server, 0755);
+}
+
+
+if ($server == $baseServerName) {
+
+ exec("git fetch -q 2>&1", $output);
+ if ($output) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'git pull 작업 : ' . join(', ', $output)
+ ]);
+ }
+
+ if ($target != $src_target) {
+ $command = sprintf('git checkout %s -q 2>&1', $target);
+ exec($command, $output);
+ if ($output) {
+ Json::die([
+ 'result' => false,
+ 'reason' => join(', ', $output)
+ ]);
+ }
+ }
+
+ exec("git pull -q 2>&1", $output);
+ if ($output && $output[0] != 'Already up-to-date.') {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'git pull 작업 : ' . join(', ', $output)
+ ]);
+ }
+
+ $version = getVersion();
+ $result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
+ __DIR__ . '/' . $server . '/d_setting/VersionGit.php',
+ [
+ 'verionGit' => $version
+ ],
+ true
+ );
+
+ if (ServConfig::$imageRequestKey) {
+ try {
+ $imagePullPath = ServConfig::getImagePullURI();
+ $pullResult = @file_get_contents($imagePullPath);
+ if ($pullResult === false) {
+ throw new \ErrorException('Invalid URI');
+ }
+ $pullResult = Json::decode($pullResult);
+ if ($pullResult['result']) {
+ $imgResult = true;
+ $imgDetail = $pullResult['version'];
+ } else {
+ $imgResult = false;
+ $imgDetail = $pullResult['reason'];
+ }
+ } catch (\Exception $e) {
+ $imgResult = false;
+ $imgDetail = $e->getMessage();
+ }
+ } else {
+ $imgResult = true;
+ $imgDetail = 'No key';
+ }
+
+
+
+ $storage->$server = [$target, $version];
+ opcache_reset();
+
+ Json::die([
+ 'server' => $server,
+ 'result' => true,
+ 'version' => $version,
+ 'imgResult' => $imgResult,
+ 'imgDetail' => $imgDetail,
+ ]);
+}
+
+$command = sprintf('git archive --format=zip -o %s %s', escapeshellarg($tmpFile), escapeshellarg($targetDir));
+exec("$command 2>&1", $output);
+if ($output) {
+ Json::die([
+ 'result' => false,
+ 'reason' => join(', ', $output)
+ ]);
+}
+
+$zip = new \ZipArchive;
+if ($zip->open($tmpFile) !== true) {
+ Json::die([
+ 'result' => false,
+ 'reason' => 'archive가 생성되지 않았습니다.'
+ ]);
+}
+
+if (!$zip->extractTo($server)) {
+ Json::die([
+ 'result' => false,
+ 'reason' => '생성한 archive를 제대로 옮기지 못했습니다.'
+ ]);
+}
+
+$zip->close();
+@unlink($tmpFile);
+
+$version = getVersion($target);
+$result = Util::generateFileUsingSimpleTemplate(
+ __DIR__ . '/' . $server . '/d_setting/VersionGit.orig.php',
+ __DIR__ . '/' . $server . '/d_setting/VersionGit.php',
+ [
+ 'verionGit' => $version
+ ],
+ true
+);
+
+$storage->$server = [$target, $version];
+//ServConfig::getServerList()[$server]->closeServer();
+opcache_reset();
+
+Json::die([
+ 'server' => $server,
+ 'result' => true,
+ 'version' => $version
+]);
diff --git a/src/sammo/AppConf.php b/src/sammo/AppConf.php
index 1cc9a1b8..ab3162ff 100644
--- a/src/sammo/AppConf.php
+++ b/src/sammo/AppConf.php
@@ -1,79 +1,69 @@
-new Setting(ROOT.'/che', '체', 'white'),
- 'kwe'=>new Setting(ROOT.'/kwe', '퀘', 'yellow'),
- 'pwe'=>new Setting(ROOT.'/pwe', '풰', 'orange'),
- 'twe'=>new Setting(ROOT.'/twe', '퉤', 'magenta'),
- 'nya'=>new Setting(ROOT.'/nya', '냐', '#e67e22'),
- 'pya'=>new Setting(ROOT.'/pya', '퍄', '#9b59b6'),
- 'hwe'=>new Setting(ROOT.'/hwe', '훼', 'red')
- ];
- }
- return self::$serverList;
- }
-
- /**
- * DB 객체 생성
- *
- * @return \MeekroDB
- */
- public static function requireRootDB()
- {
- if (!class_exists('\\sammo\\RootDB')) {
- if(!trigger_error('RootDB.php가 설정되지 않았습니다.', E_USER_ERROR)){
- die();
- }
-
- }
- return RootDB::db();
- }
-
- /**
- * DB 객체 생성
- *
- * @return \MeekroDB
- */
- public static function requireDB()
- {
- if (!class_exists('\\sammo\\DB')) {
- if(!trigger_error('DB.php가 설정되지 않았습니다.', E_USER_ERROR)){
- die();
- }
-
- }
- return DB::db();
- }
-
- public static function getUserIconPathFS(string $filepath='') : string{
- $path = ROOT.'/'.static::$userIconPath;
- if($filepath){
- $path .= '/'.$filepath;
- }
- return $path;
- }
-
- public static function getUserIconPathWeb(string $filepath='') : string{
- $path = ServConfig::$serverWebPath.'/'.static::$userIconPath;
- if($filepath){
- $path .= '/'.$filepath;
- }
- return $path;
- }
-}
+valid()){
- $values[] = $iter->send(NULL);
- $hasValue = true;
- }
- }
- yield $values;
-
- if(!$hasValue){
- break;
- }
- }
- }
-
- /**
- * $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능.
- * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
- *
- * @param string $name 가져오고자 하는 key 이름.
- * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
- * @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
- * @return int|float|string|array|null
- * @throws \InvalidArgumentException
- */
- public static function getReq(string $name, string $type = 'string', $ifNotExists = null)
- {
- if (isset($_POST[$name])) {
- $value = $_POST[$name];
- } elseif (isset($_GET[$name])) {
- $value = $_GET[$name];
- } else {
- return $ifNotExists;
- }
-
- return static::_parseReq($value, $type);
- }
-
- /**
- * $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용.
- * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
- *
- * @param string $name 가져오고자 하는 key 이름.
- * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
- * @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
- * @return int|float|string|array|null
- * @throws \InvalidArgumentException
- */
- public static function getPost(string $name, string $type = 'string', $ifNotExists = null)
- {
- if (isset($_POST[$name])) {
- $value = $_POST[$name];
- } else {
- return $ifNotExists;
- }
-
- return static::_parseReq($value, $type);
- }
-
- public static function hashPassword($salt, $password)
- {
- return hash('sha512', $salt.$password.$salt);
- }
-
- /**
- * 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수.
- */
- public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false)
- {
- if ($destFilePath === $srcFilePath) {
- return 'invalid destFilePath';
- }
- if (!file_exists($srcFilePath)) {
- return 'srcFilePath is not exists';
- }
- if (file_exists($destFilePath) && !$canOverwrite) {
- return 'destFilePath is already exists';
- }
- if (!is_writable(dirname($destFilePath))) {
- return 'destFilePath is not writable';
- }
-
- $text = file_get_contents($srcFilePath);
- foreach ($params as $key => $value) {
- $text = str_replace("_tK_{$key}_", $value, $text);
- }
- file_put_contents($destFilePath, $text);
-
- return true;
- }
-
- /**
- * params에 맞도록 class를 생성해주는 함수
- */
- public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){
- if (!is_writable(dirname($destFilePath))) {
- return 'destFilePath is not writable';
- }
-
- $newClassName = basename($destFilePath, '.php');
- $newClassName = basename($newClassName, '.orig');
- $head = [];
- $head[] = '$value){
-
- if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){
- return "$key is not valid variable name";
- }
-
- $body[] = ' public static $'.$key.' = '.var_export($value, true).';';
- }
- $tail = "\n}";
-
- if(file_exists($destFilePath)){
- unlink($destFilePath);
- }
- $result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX);
- assert($result);
-
- return true;
- }
-
- /**
- * '비교적' 안전한 int 변환
- * null -> null
- * int -> int
- * float -> int
- * numeric(int, float) 포함 -> int
- * 기타 -> 예외처리
- *
- * @return int|null
- */
- public static function toInt($val, $silent=false)
- {
- if (!isset($val)) {
- return null;
- }
- if ($val === null) {
- return null;
- }
- if (is_int($val)) {
- return $val;
- }
- if (is_numeric($val)) {
- return intval($val);//
- }
- if (strtolower($val) === 'null') {
- return null;
- }
-
- if ($silent) {
- if ($val == null) {
- return null;
- }
- if ($val == ''){
- return null;
- }
- return intval($val);
- }
- throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val);
- }
-
- /**
- * Generate a random string, using a cryptographically secure
- * pseudorandom number generator (random_int)
- *
- * For PHP 7, random_int is a PHP core function
- * For PHP 5.x, depends on https://github.com/paragonie/random_compat
- *
- * @param int $length How many characters do we want?
- * @param string $keyspace A string of all possible characters
- * to select from
- * @return string
- */
- public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
- {
- $str = '';
- $max = mb_strlen($keyspace, '8bit') - 1;
- for ($i = 0; $i < $length; ++$i) {
- $str .= $keyspace[random_int(0, $max)];
- }
- return $str;
- }
-
- public static function mapWithKey($callback, $dict)
- {
- $result = [];
- foreach ($dict as $key=>$value) {
- $result[$key] = ($callback)($key, $value);
- }
- return $result;
- }
-
- public static function convertArrayToDict($arr, $keyName)
- {
- $result = [];
-
- foreach ($arr as $obj) {
- $key = $obj[$keyName];
- $result[$key] = $obj;
- }
-
- return $result;
- }
-
- public static function convertArrayToSetLike($arr, $valueIsKey=true){
- $result = [];
- foreach($arr as $datum){
- $result[$datum] = $valueIsKey?$datum:1;
- }
- return $result;
- }
-
- public static function convertPairArrayToDict($arr){
- $result = [];
- foreach($arr as [$key, $val]){
- $result[$key] = $val;
- }
- return $result;
- }
-
- public static function convertDictToArray($dict, bool $withKey=true)
- {
- $result = [];
-
- foreach($dict as $key=>$value){
- if($withKey){
- $result[] = [$key, $value];
- }
- else{
- $result[] = $value;
- }
- }
-
- return $result;
- }
-
- public static function squeezeFromArray(array $dict, $key){
- $result = [];
- foreach($dict as $dictKey=>$value){
- $result[$dictKey] = $value[$key];
- }
-
- return $result;
- }
-
- public static function isDict($array)
- {
- if($array === null){
- return false;
- }
- if (!is_array($array)) {
- //배열이 아니면 dictionary 조차 아님.
- return false;
- }
- if(count($array) === 0){
- return true;
- }
-
- $idx = 0;
- foreach (array_keys($array) as $key) {
- if (is_string($key)) {
- return true;
- }
- if($idx !== $key){
- return true;
- }
- $idx = $key + 1;
- }
-
- return false;
- }
-
- /**
- * @param null|mixed|mixed[] $dict
- * @return null|mixed|mixed[]
- */
- public static function eraseNullValue($dict, int $depth=512)
- {
- //TODO:Test 추가
- if ($dict === null) {
- return null;
- }
-
- if (is_array($dict) && empty($dict)) {
- return null;
- }
-
- if ($depth <= 0) {
- return $dict;
- }
-
- foreach ($dict as $key=>$value) {
- if ($value === null) {
- unset($dict[$key]);
- continue;
- }
- if (!Util::isDict($value)) {
- continue;
- }
-
- $newValue = Util::eraseNullValue($value, $depth - 1);
- if ($newValue === null) {
- unset($dict[$key]);
- } else {
- $dict[$key] = $newValue;
- }
- }
-
- return $dict;
- }
-
- /**
- * key=>value pair를 보존한 섞기
- */
- public static function shuffle_assoc(&$array) {
- $keys = array_keys($array);
-
- shuffle($keys);
-
- $new = [];
-
- foreach($keys as $key) {
- $new[$key] = $array[$key];
- }
-
- $array = $new;
-
- return true;
- }
-
-
- /**
- * [0.0, 1.0] 사이의 선형 랜덤 float
- * @return float
- */
- public static function randF()
- {
- return mt_rand() / mt_getrandmax();
- }
-
- /**
- * [min, max] 사이의 선형 랜덤 float
- * @return float
- */
- public static function randRange(float $min, float $max)
- {
- return static::randF()*($max - $min) + $min;
- }
-
- /**
- * [min, max] 사이의 선형 랜덤 int
- * 현재는 rand(min, max)와 동일
- * @return int
- */
- public static function randRangeInt(int $min, int $max){
- return mt_rand($min, $max);
- }
-
- /**
- * $prob의 확률로 true를 반환
- * @return boolean
- */
- public static function randBool($prob = 0.5)
- {
- return self::randF() < $prob;
- }
-
- /**
- * aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0
- * @return float|null
- */
- public static function convPercentStrToFloat(string $text):?float{
- preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches);
- if($matches === null){
- return null;
- }
-
- return (float)$matches[1] / 100;
- }
-
-
- /**
- * $min과 $max 사이의 값으로 교정
- */
- public static function valueFit($value, $min = null, $max = null)
- {
- if($max !== null && $min !== null && $max < $min){
- return $min;
- }
- if ($min !== null && $value < $min) {
- return $min;
- }
- if ($max !== null && $value > $max) {
- return $max;
- }
- return $value;
- }
-
- /**
- * 각 값의 비중에 따라 랜덤한 값을 선택
- *
- * @param array $items 각 수치의 비중
- *
- * @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index
- */
- public static function choiceRandomUsingWeight(array $items)
- {
- $sum = 0;
- foreach ($items as $value) {
- if($value <= 0){
- continue;
- }
- $sum += $value;
- }
-
- $rd = self::randF()*$sum;
- foreach ($items as $key=>$value) {
- if($value <= 0){
- $value = 0;
- }
- if ($rd <= $value) {
- return $key;
- }
- $rd -= $value;
- }
-
- //fallback. 이곳으로 빠지지 않음
- end($items);
- return key($items);
- }
-
- /**
- * 각 값의 비중에 따라 랜덤한 값을 선택.
- *
- * @param array $items 각 수치와 비중. [값, weight] 으로 보관
- *
- * @return object 선택된 랜덤 값의 첫번째 값
- */
- public static function choiceRandomUsingWeightPair(array $items)
- {
- $sum = 0;
- foreach ($items as [$item, $value]) {
- if($value <= 0){
- continue;
- }
- $sum += $value;
- }
-
- $rd = self::randF()*$sum;
- foreach ($items as [$item, $value]) {
- if($value <= 0){
- $value = 0;
- }
- if ($rd <= $value) {
- return $item;
- }
- $rd -= $value;
- }
-
- //fallback. 이곳으로 빠지지 않음
- end($items);
- return $items[key($items)][0];
- }
-
- /**
- * 2중 배열에서 특정 키의 합
- *
- * @param array $array 배열. 1차원 배열 또는 2차원 배열
- * @param int|string|null $key 2차원 배열에서 참조할 키.
- * @return int|float 합계
- */
- public static function arraySum(array $array, $key = null){
- if($key === null){
- return array_sum($array);
- }
- $sum = 0;
- foreach($array as $val){
- $sum += $val[$key];
- }
- return $sum;
- }
-
- /**
- * 특정 키를 가진 값으로 묶음
- *
- * @param array $array 배열. 1차원 배열 또는 2차원 배열
- * @param int|string|null $key 2차원 배열에서 참조할 키.
- * @return array
- */
- public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) {
- $result = array();
-
- if($preserveRowKey){
- foreach($array as $rowKey=>$val) {
- if(key_exists($key, $val)){
- $result[$val[$key]][$rowKey] = $val;
- }else{
- $result[""][$rowKey] = $val;
- }
- }
- }
- else{
- foreach($array as $val) {
- if(key_exists($key, $val)){
- $result[$val[$key]][] = $val;
- }else{
- $result[""][] = $val;
- }
- }
- }
-
- return $result;
- }
-
- public static function getKeyOfMaxValue(array $array){
- $max = null;
- $result = null;
- foreach ($array as $key => $value) {
- if ($max === null || $value > $max) {
- $result = $key;
- $max = $value;
- }
- }
-
- return $result;
- }
-
- /**
- * 배열의 아무거나 고름. Python의 random.choice()
- *
- * @param array $items 선택하고자 하는 배열
- *
- * @return int|float|string|object 선택된 value값.
- */
- public static function choiceRandom(array $items)
- {
- return $items[array_rand($items)];
- }
-
- /**
- * fqn 클래스 경로에서 클래스 이름을 받아옴
- */
- public static function getClassName(string $classpath)
- {
- if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1);
- return $pos;
- }
-
- public static function getClassNameFromObj($object){
- $reflect = new \ReflectionClass($object);
- return $reflect->getShortName();
- }
-
- /**
- * 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인
- */
- public static function testArrayValues(array $array, ?callable $callback):bool{
- if($callback === null){
- foreach($array as $value){
- if(!$value){
- return false;
- }
- }
- return true;
- }
-
- foreach($array as $value){
- if(!($callback)($value)){
- return false;
- }
- }
- return true;
- }
-
- /**
- * MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드
- */
- public static function formatListOfBackticks(array $array):string{
- if(!$array){
- throw new MustNotBeReachedException('backtick 목록에 없음');
- }
- //.이 들어간 경우에는 분리해서 묶어야함.
- return join(',', array_map(function($value){
- $value = preg_replace('/\s/', '', $value);
- if(strpos($value, '.') !== false){
- $value = explode('.', $value);
- $value = join('`.`', $value);
- }
- return "`{$value}`";
- }, $array));
- }
-
- public static function joinYearMonth(int $year, int $month):int{
- return $year * 12 + $month - 1;
- }
-
- public static function parseYearMonth(int $yearMonth):array{
- return [intdiv($yearMonth, 12), $yearMonth%12 + 1];
- }
-
- /**
- * 변수의 값을 첫번째 값부터 비교해서 대소를 반환
- * 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리
- */
- public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){
- if($lhs instanceof \Traversable){
- $lhsIter = new \IteratorIterator($lhs);
- }
- else if(is_array($lhs)){
- $lhsIter = new \ArrayIterator($lhs);
- }
- else{
- throw new \InvalidArgumentException('$lhs is not Traversable');
- }
-
- if($rhs instanceof \Traversable){
- $rhsIter = new \IteratorIterator($rhs);
- }
- else if(is_array($rhs)){
- $rhsIter = new \ArrayIterator($rhs);
- }
- else{
- throw new \InvalidArgumentException('$rhs is not Traversable');
- }
-
- while($lhsIter->valid() && $rhsIter->valid()){
- $lhsVal = $lhsIter->current();
- $rhsVal = $rhsIter->current();
-
- if($comp !== null){
- $compResult = $comp($lhsVal, $rhsVal);
- }
- else{
- $compResult = $lhsVal <=> $rhsVal;
- }
-
- if($compResult !== 0){
- return $compResult;
- }
-
- $lhsIter->next();
- $rhsIter->next();
- }
-
- $rhsVal = null;
- while($lhsIter->valid()){
- $lhsVal = $lhsIter->current();
-
- if($comp !== null){
- $compResult = $comp($lhsVal, $rhsVal);
- }
- else{
- $compResult = $lhsVal <=> $rhsVal;
- }
-
- if($compResult !== 0){
- return $compResult;
- }
-
- $lhsIter->next();
- }
-
- $lhsVal = null;
- while($rhsIter->valid()){
- $rhsVal = $rhsIter->current();
-
- if($comp !== null){
- $compResult = $comp($lhsVal, $rhsVal);
- }
- else{
- $compResult = $lhsVal <=> $rhsVal;
- }
-
- if($compResult !== 0){
- return $compResult;
- }
-
- $rhsIter->next();
- }
-
- return 0;
- }
-
- public static function isPowerOfTwo(int $number):bool{
- if($number <= 0){
- return false;
- }
- return ($number & ($number - 1)) == 0;
- }
-
- /**
- * Python 3의 range와 동일
- * @param int $from
- * @param null|int $to
- * @param null|int $step
- * @return \Traversable
- * @throws \InvalidArgumentException
- */
- public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{
- if($to === null){
- $to = $from;
- $from = 0;
- }
- if($step === null){
- $step = 1;
- }
- else if($step === 0){
- throw new \InvalidArgumentException('xrange() arg 3 must not be zero');
- }
-
- if($step > 0){
- while($from < $to){
- yield $from;
- $from += $step;
- }
- }
- else{
- while($from > $to){
- yield $from;
- $from += $step;
- }
- }
- }
-
-};
+valid()){
+ $values[] = $iter->send(NULL);
+ $hasValue = true;
+ }
+ }
+ yield $values;
+
+ if(!$hasValue){
+ break;
+ }
+ }
+ }
+
+ /**
+ * $_POST, $_GET에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능.
+ * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
+ *
+ * @param string $name 가져오고자 하는 key 이름.
+ * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
+ * @param mixed $ifNotExists 만약 $_POST와 $_GET에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
+ * @return int|float|string|array|null
+ * @throws \InvalidArgumentException
+ */
+ public static function getReq(string $name, string $type = 'string', $ifNotExists = null)
+ {
+ if (isset($_POST[$name])) {
+ $value = $_POST[$name];
+ } elseif (isset($_GET[$name])) {
+ $value = $_GET[$name];
+ } else {
+ return $ifNotExists;
+ }
+
+ return static::_parseReq($value, $type);
+ }
+
+ /**
+ * $_POST에서 값을 가져오는 함수. Util::array_get($_POST[$name])을 축약 가능. $_GET에서도 가져올 수 있다면 getReq 사용.
+ * 타입이 복잡해질 경우 이 함수를 통하지 않고 json으로 요청할 것을 권장.
+ *
+ * @param string $name 가져오고자 하는 key 이름.
+ * @param string $type 가져오고자 하는 type. [string, int, float, bool, array, array_string, array_int]
+ * @param mixed $ifNotExists 만약 $_GET과 $_POST에 값이 없을 경우 반환하는 변수. 이 값은 $type을 검사하지 않음.
+ * @return int|float|string|array|null
+ * @throws \InvalidArgumentException
+ */
+ public static function getPost(string $name, string $type = 'string', $ifNotExists = null)
+ {
+ if (isset($_POST[$name])) {
+ $value = $_POST[$name];
+ } else {
+ return $ifNotExists;
+ }
+
+ return static::_parseReq($value, $type);
+ }
+
+ public static function hashPassword($salt, $password)
+ {
+ return hash('sha512', $salt.$password.$salt);
+ }
+
+ /**
+ * 변환할 내용이 _tK_$key_ 형태로 작성된 단순한 템플릿 파일을 이용하여 결과물을 생성해주는 함수.
+ */
+ public static function generateFileUsingSimpleTemplate(string $srcFilePath, string $destFilePath, array $params, bool $canOverwrite=false)
+ {
+ if ($destFilePath === $srcFilePath) {
+ return 'invalid destFilePath';
+ }
+ if (!file_exists($srcFilePath)) {
+ return 'srcFilePath is not exists';
+ }
+ if (file_exists($destFilePath) && !$canOverwrite) {
+ return 'destFilePath is already exists';
+ }
+ if (!is_writable(dirname($destFilePath))) {
+ return 'destFilePath is not writable';
+ }
+
+ $text = file_get_contents($srcFilePath);
+ foreach ($params as $key => $value) {
+ if(is_array($value)){
+ $text = str_replace("[/*_tK_{$key}_*/]", var_export($value, true), $text);
+ }
+ else{
+ $text = str_replace("_tK_{$key}_", $value, $text);
+ }
+
+ }
+ file_put_contents($destFilePath, $text);
+
+ return true;
+ }
+
+ /**
+ * params에 맞도록 class를 생성해주는 함수
+ */
+ public static function generatePHPClassFile(string $destFilePath, array $params, ?string $srcClassName=null, string $namespace='sammo'){
+ if (!is_writable(dirname($destFilePath))) {
+ return 'destFilePath is not writable';
+ }
+
+ $newClassName = basename($destFilePath, '.php');
+ $newClassName = basename($newClassName, '.orig');
+ $head = [];
+ $head[] = '$value){
+
+ if(!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/',$key)){
+ return "$key is not valid variable name";
+ }
+
+ $body[] = ' public static $'.$key.' = '.var_export($value, true).';';
+ }
+ $tail = "\n}";
+
+ if(file_exists($destFilePath)){
+ unlink($destFilePath);
+ }
+ $result = file_put_contents($destFilePath, $head.join("\n", $body).$tail, LOCK_EX);
+ assert($result);
+
+ return true;
+ }
+
+ /**
+ * '비교적' 안전한 int 변환
+ * null -> null
+ * int -> int
+ * float -> int
+ * numeric(int, float) 포함 -> int
+ * 기타 -> 예외처리
+ *
+ * @return int|null
+ */
+ public static function toInt($val, $silent=false)
+ {
+ if (!isset($val)) {
+ return null;
+ }
+ if ($val === null) {
+ return null;
+ }
+ if (is_int($val)) {
+ return $val;
+ }
+ if (is_numeric($val)) {
+ return intval($val);//
+ }
+ if (strtolower($val) === 'null') {
+ return null;
+ }
+
+ if ($silent) {
+ if ($val == null) {
+ return null;
+ }
+ if ($val == ''){
+ return null;
+ }
+ return intval($val);
+ }
+ throw new \InvalidArgumentException('올바르지 않은 타입형 :'.$val);
+ }
+
+ /**
+ * Generate a random string, using a cryptographically secure
+ * pseudorandom number generator (random_int)
+ *
+ * For PHP 7, random_int is a PHP core function
+ * For PHP 5.x, depends on https://github.com/paragonie/random_compat
+ *
+ * @param int $length How many characters do we want?
+ * @param string $keyspace A string of all possible characters
+ * to select from
+ * @return string
+ */
+ public static function randomStr($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
+ {
+ $str = '';
+ $max = mb_strlen($keyspace, '8bit') - 1;
+ for ($i = 0; $i < $length; ++$i) {
+ $str .= $keyspace[random_int(0, $max)];
+ }
+ return $str;
+ }
+
+ public static function mapWithKey($callback, $dict)
+ {
+ $result = [];
+ foreach ($dict as $key=>$value) {
+ $result[$key] = ($callback)($key, $value);
+ }
+ return $result;
+ }
+
+ public static function convertArrayToDict($arr, $keyName)
+ {
+ $result = [];
+
+ foreach ($arr as $obj) {
+ $key = $obj[$keyName];
+ $result[$key] = $obj;
+ }
+
+ return $result;
+ }
+
+ public static function convertArrayToSetLike($arr, $valueIsKey=true){
+ $result = [];
+ foreach($arr as $datum){
+ $result[$datum] = $valueIsKey?$datum:1;
+ }
+ return $result;
+ }
+
+ public static function convertPairArrayToDict($arr){
+ $result = [];
+ foreach($arr as [$key, $val]){
+ $result[$key] = $val;
+ }
+ return $result;
+ }
+
+ public static function convertDictToArray($dict, bool $withKey=true)
+ {
+ $result = [];
+
+ foreach($dict as $key=>$value){
+ if($withKey){
+ $result[] = [$key, $value];
+ }
+ else{
+ $result[] = $value;
+ }
+ }
+
+ return $result;
+ }
+
+ public static function squeezeFromArray(array $dict, $key){
+ $result = [];
+ foreach($dict as $dictKey=>$value){
+ $result[$dictKey] = $value[$key];
+ }
+
+ return $result;
+ }
+
+ public static function isDict($array)
+ {
+ if($array === null){
+ return false;
+ }
+ if (!is_array($array)) {
+ //배열이 아니면 dictionary 조차 아님.
+ return false;
+ }
+ if(count($array) === 0){
+ return true;
+ }
+
+ $idx = 0;
+ foreach (array_keys($array) as $key) {
+ if (is_string($key)) {
+ return true;
+ }
+ if($idx !== $key){
+ return true;
+ }
+ $idx = $key + 1;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param null|mixed|mixed[] $dict
+ * @return null|mixed|mixed[]
+ */
+ public static function eraseNullValue($dict, int $depth=512)
+ {
+ //TODO:Test 추가
+ if ($dict === null) {
+ return null;
+ }
+
+ if (is_array($dict) && empty($dict)) {
+ return null;
+ }
+
+ if ($depth <= 0) {
+ return $dict;
+ }
+
+ foreach ($dict as $key=>$value) {
+ if ($value === null) {
+ unset($dict[$key]);
+ continue;
+ }
+ if (!Util::isDict($value)) {
+ continue;
+ }
+
+ $newValue = Util::eraseNullValue($value, $depth - 1);
+ if ($newValue === null) {
+ unset($dict[$key]);
+ } else {
+ $dict[$key] = $newValue;
+ }
+ }
+
+ return $dict;
+ }
+
+ /**
+ * key=>value pair를 보존한 섞기
+ */
+ public static function shuffle_assoc(&$array) {
+ $keys = array_keys($array);
+
+ shuffle($keys);
+
+ $new = [];
+
+ foreach($keys as $key) {
+ $new[$key] = $array[$key];
+ }
+
+ $array = $new;
+
+ return true;
+ }
+
+
+ /**
+ * [0.0, 1.0] 사이의 선형 랜덤 float
+ * @return float
+ */
+ public static function randF()
+ {
+ return mt_rand() / mt_getrandmax();
+ }
+
+ /**
+ * [min, max] 사이의 선형 랜덤 float
+ * @return float
+ */
+ public static function randRange(float $min, float $max)
+ {
+ return static::randF()*($max - $min) + $min;
+ }
+
+ /**
+ * [min, max] 사이의 선형 랜덤 int
+ * 현재는 rand(min, max)와 동일
+ * @return int
+ */
+ public static function randRangeInt(int $min, int $max){
+ return mt_rand($min, $max);
+ }
+
+ /**
+ * $prob의 확률로 true를 반환
+ * @return boolean
+ */
+ public static function randBool($prob = 0.5)
+ {
+ return self::randF() < $prob;
+ }
+
+ /**
+ * aaa(.bbb)?% 의 텍스트를 float으로 변환. 100% = 1.0
+ * @return float|null
+ */
+ public static function convPercentStrToFloat(string $text):?float{
+ preg_match('/^(\d+(\.\d+)?)\%$/', $text, $matches);
+ if($matches === null){
+ return null;
+ }
+
+ return (float)$matches[1] / 100;
+ }
+
+
+ /**
+ * $min과 $max 사이의 값으로 교정
+ */
+ public static function valueFit($value, $min = null, $max = null)
+ {
+ if($max !== null && $min !== null && $max < $min){
+ return $min;
+ }
+ if ($min !== null && $value < $min) {
+ return $min;
+ }
+ if ($max !== null && $value > $max) {
+ return $max;
+ }
+ return $value;
+ }
+
+ /**
+ * 각 값의 비중에 따라 랜덤한 값을 선택
+ *
+ * @param array $items 각 수치의 비중
+ *
+ * @return int|string 선택된 랜덤 값의 key값. 단순 배열인 경우에는 index
+ */
+ public static function choiceRandomUsingWeight(array $items)
+ {
+ $sum = 0;
+ foreach ($items as $value) {
+ if($value <= 0){
+ continue;
+ }
+ $sum += $value;
+ }
+
+ $rd = self::randF()*$sum;
+ foreach ($items as $key=>$value) {
+ if($value <= 0){
+ $value = 0;
+ }
+ if ($rd <= $value) {
+ return $key;
+ }
+ $rd -= $value;
+ }
+
+ //fallback. 이곳으로 빠지지 않음
+ end($items);
+ return key($items);
+ }
+
+ /**
+ * 각 값의 비중에 따라 랜덤한 값을 선택.
+ *
+ * @param array $items 각 수치와 비중. [값, weight] 으로 보관
+ *
+ * @return object 선택된 랜덤 값의 첫번째 값
+ */
+ public static function choiceRandomUsingWeightPair(array $items)
+ {
+ $sum = 0;
+ foreach ($items as [$item, $value]) {
+ if($value <= 0){
+ continue;
+ }
+ $sum += $value;
+ }
+
+ $rd = self::randF()*$sum;
+ foreach ($items as [$item, $value]) {
+ if($value <= 0){
+ $value = 0;
+ }
+ if ($rd <= $value) {
+ return $item;
+ }
+ $rd -= $value;
+ }
+
+ //fallback. 이곳으로 빠지지 않음
+ end($items);
+ return $items[key($items)][0];
+ }
+
+ /**
+ * 2중 배열에서 특정 키의 합
+ *
+ * @param array $array 배열. 1차원 배열 또는 2차원 배열
+ * @param int|string|null $key 2차원 배열에서 참조할 키.
+ * @return int|float 합계
+ */
+ public static function arraySum(array $array, $key = null){
+ if($key === null){
+ return array_sum($array);
+ }
+ $sum = 0;
+ foreach($array as $val){
+ $sum += $val[$key];
+ }
+ return $sum;
+ }
+
+ /**
+ * 특정 키를 가진 값으로 묶음
+ *
+ * @param array $array 배열. 1차원 배열 또는 2차원 배열
+ * @param int|string|null $key 2차원 배열에서 참조할 키.
+ * @return array
+ */
+ public static function arrayGroupBy(array $array, $key, bool $preserveRowKey=false) {
+ $result = array();
+
+ if($preserveRowKey){
+ foreach($array as $rowKey=>$val) {
+ if(key_exists($key, $val)){
+ $result[$val[$key]][$rowKey] = $val;
+ }else{
+ $result[""][$rowKey] = $val;
+ }
+ }
+ }
+ else{
+ foreach($array as $val) {
+ if(key_exists($key, $val)){
+ $result[$val[$key]][] = $val;
+ }else{
+ $result[""][] = $val;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public static function getKeyOfMaxValue(array $array){
+ $max = null;
+ $result = null;
+ foreach ($array as $key => $value) {
+ if ($max === null || $value > $max) {
+ $result = $key;
+ $max = $value;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * 배열의 아무거나 고름. Python의 random.choice()
+ *
+ * @param array $items 선택하고자 하는 배열
+ *
+ * @return int|float|string|object 선택된 value값.
+ */
+ public static function choiceRandom(array $items)
+ {
+ return $items[array_rand($items)];
+ }
+
+ /**
+ * fqn 클래스 경로에서 클래스 이름을 받아옴
+ */
+ public static function getClassName(string $classpath)
+ {
+ if ($pos = strrpos($classpath, '\\')) return substr($classpath, $pos + 1);
+ return $pos;
+ }
+
+ public static function getClassNameFromObj($object){
+ $reflect = new \ReflectionClass($object);
+ return $reflect->getShortName();
+ }
+
+ /**
+ * 배열의 원소에 대해서 테스트를 수행하고 모두 true인지 확인
+ */
+ public static function testArrayValues(array $array, ?callable $callback):bool{
+ if($callback === null){
+ foreach($array as $value){
+ if(!$value){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ foreach($array as $value){
+ if(!($callback)($value)){
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * MeekroDB에서 %lb의 처리가 이상하여 따로 만든 코드
+ */
+ public static function formatListOfBackticks(array $array):string{
+ if(!$array){
+ throw new MustNotBeReachedException('backtick 목록에 없음');
+ }
+ //.이 들어간 경우에는 분리해서 묶어야함.
+ return join(',', array_map(function($value){
+ $value = preg_replace('/\s/', '', $value);
+ if(strpos($value, '.') !== false){
+ $value = explode('.', $value);
+ $value = join('`.`', $value);
+ }
+ return "`{$value}`";
+ }, $array));
+ }
+
+ public static function joinYearMonth(int $year, int $month):int{
+ return $year * 12 + $month - 1;
+ }
+
+ public static function parseYearMonth(int $yearMonth):array{
+ return [intdiv($yearMonth, 12), $yearMonth%12 + 1];
+ }
+
+ /**
+ * 변수의 값을 첫번째 값부터 비교해서 대소를 반환
+ * 길이가 다른 경우, 앞의 결과를 먼저 비교한 뒤, 짧은쪽의 값을 null으로 가정하여 길이 처리
+ */
+ public static function arrayCompare(iterable $lhs, iterable $rhs, ?callable $comp=null){
+ if($lhs instanceof \Traversable){
+ $lhsIter = new \IteratorIterator($lhs);
+ }
+ else if(is_array($lhs)){
+ $lhsIter = new \ArrayIterator($lhs);
+ }
+ else{
+ throw new \InvalidArgumentException('$lhs is not Traversable');
+ }
+
+ if($rhs instanceof \Traversable){
+ $rhsIter = new \IteratorIterator($rhs);
+ }
+ else if(is_array($rhs)){
+ $rhsIter = new \ArrayIterator($rhs);
+ }
+ else{
+ throw new \InvalidArgumentException('$rhs is not Traversable');
+ }
+
+ while($lhsIter->valid() && $rhsIter->valid()){
+ $lhsVal = $lhsIter->current();
+ $rhsVal = $rhsIter->current();
+
+ if($comp !== null){
+ $compResult = $comp($lhsVal, $rhsVal);
+ }
+ else{
+ $compResult = $lhsVal <=> $rhsVal;
+ }
+
+ if($compResult !== 0){
+ return $compResult;
+ }
+
+ $lhsIter->next();
+ $rhsIter->next();
+ }
+
+ $rhsVal = null;
+ while($lhsIter->valid()){
+ $lhsVal = $lhsIter->current();
+
+ if($comp !== null){
+ $compResult = $comp($lhsVal, $rhsVal);
+ }
+ else{
+ $compResult = $lhsVal <=> $rhsVal;
+ }
+
+ if($compResult !== 0){
+ return $compResult;
+ }
+
+ $lhsIter->next();
+ }
+
+ $lhsVal = null;
+ while($rhsIter->valid()){
+ $rhsVal = $rhsIter->current();
+
+ if($comp !== null){
+ $compResult = $comp($lhsVal, $rhsVal);
+ }
+ else{
+ $compResult = $lhsVal <=> $rhsVal;
+ }
+
+ if($compResult !== 0){
+ return $compResult;
+ }
+
+ $rhsIter->next();
+ }
+
+ return 0;
+ }
+
+ public static function isPowerOfTwo(int $number):bool{
+ if($number <= 0){
+ return false;
+ }
+ return ($number & ($number - 1)) == 0;
+ }
+
+ /**
+ * Python 3의 range와 동일
+ * @param int $from
+ * @param null|int $to
+ * @param null|int $step
+ * @return \Traversable
+ * @throws \InvalidArgumentException
+ */
+ public static function range(int $from, ?int $to=null, ?int $step=null):\Traversable{
+ if($to === null){
+ $to = $from;
+ $from = 0;
+ }
+ if($step === null){
+ $step = 1;
+ }
+ else if($step === 0){
+ throw new \InvalidArgumentException('xrange() arg 3 must not be zero');
+ }
+
+ if($step > 0){
+ while($from < $to){
+ yield $from;
+ $from += $step;
+ }
+ }
+ else{
+ while($from > $to){
+ yield $from;
+ $from += $step;
+ }
+ }
+ }
+
+};