call_kiso_api->hscpMarketPriceInfo($hscp_no, $ptp_no); if (isset($hscpMarketPriceInfo['error'])) { //결과값 확인 if ($hscpMarketPriceInfo['error']['code'] == 'VC027') { $return = array(); } else { $return = $hscpMarketPriceInfo['error']; } } else { $limitH = 0; $limitL = 0; // 상한가, 하한가 체크 ( 상한가 * 2, 하한가 * 0.7) 이내의 범위에 가격이 있어야 함. if ($trade_type == 'A1') { // 매매 if (isset($hscpMarketPriceInfo['result']['deal_uplmt_prc'])) { $limitH = $hscpMarketPriceInfo['result']['deal_uplmt_prc']; } if (isset($hscpMarketPriceInfo['result']['deal_lwlmt_prc'])) { $limitL = $hscpMarketPriceInfo['result']['deal_lwlmt_prc']; } } elseif ($trade_type == 'B1') { // 전세 if (isset($hscpMarketPriceInfo['result']['lease_uplmt_prc'])) { $limitH = $hscpMarketPriceInfo['result']['lease_uplmt_prc']; } if (isset($hscpMarketPriceInfo['result']['lease_lwlmt_prc'])) { $limitL = $hscpMarketPriceInfo['result']['lease_lwlmt_prc']; } } if (!empty($limitH)) { $limitH = $limitH * 2; if ($limitH < $atcl_amt) { $return = array('code' => 'ERC_02', 'message' => '최근 시세 하한가 70% ~ 상한가 200% 혹은 네이버 분양 서비스 내 평균 분양가격 하한가 60%(시세가 없는 분양권)를 벗어나는 가격입니다.'); } } if (!empty($limitL)) { $limitL = $limitL * 0.7; if ($limitL > $atcl_amt) { $return = array('code' => 'ERC_03', 'message' => '최근 시세 하한가 70% ~ 상한가 200% 혹은 네이버 분양 서비스 내 평균 분양가격 하한가 60%(시세가 없는 분양권)를 벗어나는 가격입니다.'); } } } } return $return; } } /** * 공동중개 매물 stat_cd 이름 변경 */ function getCdChangeNm($stat_cd) { switch ($stat_cd) { case "30": return "공동중개 확인중"; break; } } /** * 클라이언트 아이피 체크 */ function getRealClientIp() { $ipaddress = ''; // HTTP_CLIENT_IP 확인 (일부 프록시에서 사용) if (isset($_SERVER['HTTP_CLIENT_IP'])) { $ipaddress = $_SERVER['HTTP_CLIENT_IP']; } // HTTP_X_FORWARDED_FOR 확인 (프록시/로드 밸런서에서 가장 흔하게 사용) else if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR']; // HTTP_X_FORWARDED_FOR는 쉼표로 구분된 여러 IP를 포함할 수 있음 // 보통 첫 번째 IP가 실제 클라이언트 IP if (strpos($ipaddress, ',') !== false) { $ips = explode(',', $ipaddress); $ipaddress = trim($ips[0]); } } // 다른 X-Forwarded 헤더들 확인 (덜 흔하지만 확인 필요) else if (isset($_SERVER['HTTP_X_FORWARDED'])) { $ipaddress = $_SERVER['HTTP_X_FORWARDED']; } else if (isset($_SERVER['HTTP_FORWARDED_FOR'])) { $ipaddress = $_SERVER['HTTP_FORWARDED_FOR']; } else if (isset($_SERVER['HTTP_FORWARDED'])) { $ipaddress = $_SERVER['HTTP_FORWARDED']; } // X-Real-IP 확인 (Nginx와 같은 리버스 프록시에서 주로 사용) else if (isset($_SERVER['X-Real-IP'])) { $ipaddress = $_SERVER['X-Real-IP']; } // REMOTE_ADDR 확인 (가장 직접적인 연결에서 신뢰할 수 있음) else if (isset($_SERVER['REMOTE_ADDR'])) { $ipaddress = $_SERVER['REMOTE_ADDR']; } else { $ipaddress = '알수없음'; } return $ipaddress; } function specailCharChange($str) { if (!$str) return ''; $str = str_replace(">", "〉", $str); $str = str_replace("<", "〈", $str); return $str; } /** * csv 용 * @param mixed $str * @return array|string */ function csvDataReplace($str) { $str = str_replace("\r\n", "", $str); $str = str_replace(",", " ", $str); return $str; } if (!function_exists('str_contains')) { function str_contains($haystack, $needle) { return '' === $needle || false !== strpos($haystack, $needle); } } /** * Checks if a file exists at a given URL by attempting to retrieve its HTTP headers. * This function is specifically designed to work with NCLOUD Object Storage URLs. * It includes a timeout to prevent indefinite blocking due to unresponsive external resources. * * @param string $url The relative URL of the file within NCLOUD Object Storage. * @return bool True if the file exists (HTTP 2xx status), false otherwise or on error/timeout. */ function url_fileExist($url) { // 134: 입력 URL이 비어있는지 확인. 비어있으면 즉시 false 반환. if (empty($url)) { return false; } // 135: NCLOUD_OBJECT_STORAGE_URL 상수를 사용하여 완전한 URL을 구성. // 이 상수는 NCLOUD Object Storage의 기본 URL (예: 'https://kr.object.ncloudstorage.com/')을 포함해야 합니다. $fullUrl = NCLOUD_OBJECT_STORAGE_URL . $url; // 136: (빈 라인) // 137: get_headers() 호출에 타임아웃을 적용하기 위한 스트림 컨텍스트 생성. // 'http' 옵션에 'timeout'을 설정하여 PHP가 응답을 기다리는 최대 시간을 지정합니다. // 'method'를 'HEAD'로 설정하여 파일 내용을 다운로드하지 않고 헤더만 요청하므로 효율적입니다. // 'ssl' 옵션은 HTTPS 연결 시 SSL/TLS 인증서 검증 오류를 방지합니다. // 운영 환경에서는 'verify_peer'와 'verify_peer_name'을 'true'로 설정하여 보안을 강화하는 것을 권장하며, // 적절한 CA 인증서 번들 경로를 'cafile' 또는 'capath'로 지정해야 합니다. $context = stream_context_create(array( 'http' => array( 'timeout' => 5, // 응답을 5초 동안 기다림 (이 값은 네트워크 환경과 외부 서비스 응답 시간에 따라 조정 가능) 'method' => 'HEAD' // HEAD 요청은 파일 존재 여부 확인에 충분하며 대역폭을 절약합니다. ), 'ssl' => array( 'verify_peer' => false, // 경고: 프로덕션 환경에서는 'true'로 설정하고 CA 인증서를 구성하여 보안을 강화해야 합니다. 'verify_peer_name' => false, // 경고: 프로덕션 환경에서는 'true'로 설정하고 CA 인증서를 구성하여 보안을 강화해야 합니다. ) )); // get_headers() 함수를 호출하여 HTTP 헤더를 가져옵니다. // @ suppression operator를 사용하여 네트워크 오류 등으로 인한 PHP 경고를 억제합니다. // $context를 세 번째 인자로 전달하여 위에서 정의한 타임아웃 및 기타 옵션을 적용합니다. $array = @get_headers($fullUrl, 0, $context); // 헤더를 가져오지 못했거나 (예: 타임아웃, 네트워크 오류) false가 반환된 경우 처리. // 이 경우 파일이 존재하지 않거나 접근할 수 없는 것으로 간주합니다. if ($array === false) { // 이 곳에 오류 로깅 또는 추가적인 오류 처리 로직을 추가할 수 있습니다. // 예: error_log("Failed to get headers for URL: " . $fullUrl . " at " . __FILE__ . ":" . __LINE__); return false; } // HTTP 응답의 첫 번째 줄(상태 코드)에서 숫자로 된 상태 코드를 추출합니다. // 정규식을 사용하여 "HTTP/1.1 200 OK"와 같은 문자열에서 "200" 부분을 추출합니다. if (preg_match('/^HTTP\/\d\.\d\s(\d{3})/', $array[0], $matches)) { $statusCode = (int) $matches[1]; // HTTP 상태 코드가 2xx (성공) 범위인지 확인합니다. // 200 OK, 201 Created, 202 Accepted 등 성공적인 응답을 모두 포함합니다. return ($statusCode >= 200 && $statusCode < 300); } // 만약 위 preg_match가 실패하거나, 응답 형식이 예상과 다르면, // 원래 로직대로 "200 OK" 문자열 포함 여부로 한 번 더 확인합니다. // 이는 후방 호환성을 위한 것이며, 위의 상태 코드 확인 로직이 더 견고합니다. return str_contains($array[0], "200 OK"); } function realFilePath($url) { if (empty($url)) return; if (url_fileExist($url)) { $return_url = NCLOUD_OBJECT_STORAGE_URL . $url; } else { $return_url = $url; } return $return_url; } function han($s) { return reset(json_decode('{"s":"' . $s . '"}')); } // function to_han ($str) { return preg_replace('/(\\\u[a-f0-9]+)+/e','han("$0")',$str); } /** * 비밀번호 문자 조합 검사 * - 영문 대문자 / 소문자 / 숫자 / 특수문자 중 최소 $minTypes 종류 이상 */ function checkPasswordTypes(string $password, int $minTypes = 2): bool { $types = 0; // 대문자 if (preg_match('/[A-Z]/', $password)) { $types++; } // 소문자 if (preg_match('/[a-z]/', $password)) { $types++; } // 숫자 if (preg_match('/[0-9]/', $password)) { $types++; } // 특수문자 (공백 제외) if (preg_match('/[^A-Za-z0-9]/', $password)) { $types++; } return $types >= $minTypes; }