Source for file WebAPI.php
Documentation is available at WebAPI.php
* Project: WebAPI:: String API class for WEB<br>
* WebAPI 패키지는 WEB에서 사용되는 문자열 관련 API를 제공한다.
* {@example HTTPRelay/tests/test.php}
* @author JoungKyun.Kim <http://oops.org>
* @copyright (c) 2018, OOPS.org
* @link http://pear.oops.org/package/WebAPI
* @since File available since release 0.0.1
* import oops/HTTPRelay pear package
require_once 'HTTPRelay.php';
* import WebAPI_Autolink API
require_once 'WebAPI/WebAPI_Autolink.php';
* import WebAPI_Filtering API
require_once 'WebAPI/WebAPI_Filter.php';
* import WebAPI_Browser API
require_once 'WebAPI/WebAPI_Browser.php';
* import WebAPI_Mimetype API
require_once 'WebAPI/WebAPI_Mimetype.php';
require_once 'WebAPI/WebAPI_JSON.php';
* WebAPI 패키지는 WEB에서 사용되는 문자열 관련 API를 제공한다.
static public $utf8 = true;
// {{{ +-- static public (boolean) is_injection (&$s)
* URL parameter 값에 inject 공격이 가능한 코드 감지
* - single or double quote
if ( preg_match ('/\.\.\/|[;\'"]|%(00|25|27|2e)/i', $s) ) {
// {{{ +-- static public (bool) is_alpha (&$c)
* 주어진 문자열에 숫자, 알파벳, whtie space만으로 구성이
* @param string 입력값의 처음 1byte만 판단함
// {{{ +-- static public (boolean) is_hangul (&$s, $division = false)
* 주어진 값에 한글(UTF8/EUC-KR)이 포함 되어 있는지를 판단
* 이 method의 경우에는 한글이 아닌 다른 연속된 multibyte 문자의
* 마지막 바이트와 첫번째 바이트에 의한 오차가 발생할 수 있으므로
* 정확도가 필요할 경우에는 KSC5601::is_ksc5601 method를 이용하기
* @param bool (optional) true로 설정할 경우, EUC-KR과 UTF-8을
* - UTF-8 returns WebAPI::UTF8
* - EUC-KR returns WebAPI::EUCKR
* @since 1.0.1 added 2th parameter.
static public function is_hangul (&$s, $division = false) {
if ( preg_match ('/[\x{1100}-\x{11FF}\x{3130}-\x{318F}\x{AC00}-\x{D7AF}]/u', $s) )
return $division ? self::UTF8 : true;
return $division ? self::EUCKR : true;
// {{{ +-- static public (boolean) is_proxy (void)
* Check this connection whethad access through Proxy server.
* @param array (options) User define 헤더. '-'는 '_'로 표시 해야 함.
static public function is_proxy ($udef = null) {
foreach ( $_SERVER as $k => $v ) {
case 'HTTP_X_COMING_FROM':
case 'HTTP_FORWARDED_FOR':
case 'HTTP_X_FORWARDED_FOR':
foreach ( $udef as $v ) {
// {{{ +-- static public (boolean) is_email (&$v)
* 도메인 파트의 경우 MX record가 있거나 또는 inverse domain이 설정되어 있어야
* true로 판별한다. 도메인 파트가 IP주소일 경우에는 MX record나 inverse domain
* - id@domain.com?subject=title&cc=...
* - name <id@domain.com?subject=title&cc=...>
* 이메일 parameter가 존재할 경우에는, CC, BCC, SUBJECT, BODY 만 허가가 된다. 만약,
* 이메일 parameter를 체크하고 싶지 않다면, 이 method에 넘기는 값에서 parameter를
* @param string check email address
// {{{ +-- static public (boolean) is_url (&$v)
* @param string check url address
static public function is_url (&$v) {
return WebAPI_Autolink::is_url ($v);
// {{{ +-- static public (boolean) is_protocol (&$v)
* 주어진 protocol이 valid한지 확인
* @param string check url address
// {{{ +-- static public (string) client_ip ($udef_haeder = false)
* Client의 IP를 구한다. Proxy header가 존재할 경우, 첫번째
* Proxy의 forward IP를 반환한다.
* @param array (optional) User Define additional Proxy Header list
static public function client_ip ($udef_haeder = false) {
$myip = $_SERVER['REMOTE_ADDR'];
'HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_COMING_FROM',
'HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED',
'HTTP_VIA', 'HTTP_COMING_FROM','HTTP_PROXY','HTTP_SP_HOST',
foreach ( $headers as $v ) {
// {{{ +-- static public (void) autolink (&$str)
* 주어진 문장속의 URL을 hyper link로 변경
static public function autolink (&$str) {
// {{{ +-- static public strlen ($str)
* Return length of strings
* 기본적인 사용법은 php의 native strlen과 동일하다.
* native strlen 과의 차이는, UTF-8 인코딩의 한글 1글자를 CP949와 같이
* @param string The input string.
static public function strlen ($str) {
if ( ! ($type = self::is_hangul ($str, true)) )
if ( $type == self::UTF8 )
$str = iconv ('UTF-8', 'CP949', $str);
// {{{ +-- static public (string) substr ($data, $start, $end = false)
* Return part of a string
* 사용법은 php의 native substr과 동일하다.
* native substr과의 차이는, 시작과 끝은 한글이 깨지는 문제를 해결을
* 한다. 이 함수는 한글을 2byte로 처리를 하며, UTF-8이라도 한글을 길이는
* @param string The input string. Must be one character or longer.
* @param int start position
* @param int (optional) length of returning part.
static public function substr ($data, $start, $end = false) {
if ( ! ($type = self::is_hangul ($data, true)) )
return substr ($data, $start, $end);
if ( $type == self::UTF8 )
$data = iconv ('UTF-8', 'CP949', $data);
$start = strlen ($data) + $start;
$end = strlen ($data) - $start;
$end = strlen ($data) + $end - $start;
$buf = substr ($data, 0, $start);
$buf = preg_replace ('/[a-z0-9]|([\x80-\xFE].)/', '', $buf);
if ( intval ($data[$start - 1]) & 0x80 )
$data = substr ($data, $start, $end);
$data = preg_replace ('/(([\x80-\xFE].)*)[\x80-\xFE]?$/', '\\1', $data);
if ( $type === self::UTF8 )
return iconv ('CP949', 'UTF-8', $data);
// {{{ +-- static public (boolean) xss (&$str)
* - Javascript event(onmouseover 등)가 존재할 경우
* - iframe, frame, script, style, link, meta, head tag가 있을 경우
* - src 또는 href attribute에 javascript를 사용할 경우
* - css background* 나 html background* attribute가 존재할 경우
* . youtube link 예외 처리 (iframe)
* @return boolean XSS 탐지시 true
static public function xss (&$str) {
if ( self::$xssinfo === null )
self::$xssinfo = new stdClass;
'abort|activate|afterprint|afterupdate|beforeactivate|beforecopy|' .
'beforecut|beforedeactivate|beforeeditfocus|beforepaste|beforeprint|' .
'beforeunload|beforeupdate|blur|bounce|cellchange|change|click|' .
'contextmenu|controlselect|copy|cut|dataavailable|datasetchanged|' .
'datasetcomplete|dblclick|deactivate|drag|dragend|dragenter|' .
'dragleave|dragover|dragstart|drop|error|errorupdate|filterchange|' .
'finish|focus|focusin|focusout|help|keydown|keypress|keyup|' .
'layoutcomplete|load|losecapture|mousedown|mouseenter|mouseleave|' .
'mousemove|mouseout|mouseover|mouseup|mousewheel|move|moveend|' .
'movestart|paste|propertychange|readystatechange|reset|resize|' .
'resizeend|resizestart|rowenter|rowexit|rowsdelete|rowsinserted|' .
'scroll|select|selectionchange|selectstart|start|stop|submit|unload';
'/<[\s]*(iframe)[\s>]+/i',
'/<[\s]*(script|frame|style|link|meta|head)[\s>]+/i',
'/background[\s]*(:[\s]*url|=)/i',
'/(src|href)[\s]*=["\'\s]*(javascript|j|J|%6A|%4A)/i',
'/on(' . $event . ')[\s]*=/i'
foreach ( $src as $filter ) {
if ( self::xss_youtube_ext ($m[1], $str) === true )
self::$xssinfo->status = true;
self::$xssinfo->msg = sprintf (
'\'%s\' pattern is matched. This is strongly doubt XSS attack.',
# img src에 image가 아닌 소스 확인
$imgs = self::img_tags ($str, 'img');
foreach ( $imgs as $path ) {
$path = sprintf ('%s:%s', $_SERVER['HTTPS'] ? 'https' : 'http', $path);
if ( ($buf = $http->head ($path, 1)) === false )
if ( ! preg_match ('!^image/!i', $buf->{'Content-Type'}) ) {
self::$xssinfo->status = true;
self::$xssinfo->msg = sprintf (
'The value of image src property (%s) is assumed that is not image.' .
'This is storngly doubt XSS attack.',
// {{{ +-- private xss_youtube_ext ($filter, &$data)
* self::xss 체크시에 youbute의 iframe은 허가
private function xss_youtube_ext ($filter, &$data) {
$dom = new DOMDocument ();
$dom->encoding = 'utf-8';
$dom->formatOutput = true;
@$dom->loadHTML ($v[0] . '</iframe>');
$iframe = $dom->getElementsByTagName ('iframe');
if ( $iframe->length < 1 )
$src = $iframe->item(0)->getAttribute ('src');
if ( ! preg_match ('!^(http[s]?:)?//(www\.)?youtube(-nocookie)?\.(be|com)/!i', trim ($src)) )
// {{{ +-- private (array) img_tags ($buf)
* 주어진 문장에서 img의 src property 값을 배열로 반환한다.
private function img_tags ($buf) {
$p = preg_match_all ('/<[\s]*img[^>]*src=[\'"\s]*([^\'"\s>]+)/i', $buf, $matches);
// {{{ +-- static public (string) get_file_extension ($f, $post = false)
* @return string 파일 확장자. 확장자가 없을 경우 null 반환.
* @param string 파일 이름 또는 경로
* @param boolean (optional) true로 설정시에 $_FILES[첫번째인자값]['name']의
$f = $_FILES[$f]['name'];
$tail = $ext[count ($ext) - 1];
// {{{ +-- static public (string) mimetype ($name)
* 주어진 파일의 mimetype을 결정한다.
* @param string mimetype 정보를 확인할 파일경로
* @param WebAPI::browser br (optional) WebAPI::browser 의 반환값.
static public function mimetype ($name, $br = null) {
$r = ($br->name == 'MSIE' && $br->version == '5.5') ?
'application/octet-stream';
// {{{ +-- static public (void) mimeHeader ($name)
* 주어진 파일의 mimetype을 결정하여 다운로드 전송한다.
* Unix/Linux의 숨김 속성 파일은 처리하지 않는다.
* @param string name 다운로드 시킬 파일 경로
* @param WebAPI::browser br (optional) WebAPI::browser 의 반환값.
static public function mimeHeader ($name, $br = null) {
$err = 'Secured problems! download error' . PHP_EOL;
Header ('Content-Type: text/plain');
Header ('Accept-Ranges: bytes');
Header ('Content-Length: ' . strlen ($err));
Header ('Content-Description: WebAPI File sending API');
$mime = self::mimetype ($name, $br);
if ( $br->name == 'MSIE' && $br->version == '5.5' )
Header ('Content-Transfer-Encoding: binary');
Header ('Content-Description: WebAPI File sending API');
$dnname = sprintf ('filename*0*=%s', $dnname);
if ( $br->version > 6 ) {
$dnname = sprintf ('filename*0*=%s', $dnname);
$dnname = 'filename="' . $dnname . '"';
Header ('Content-Type: ' . $mime);
Header ('Accept-Ranges: bytes');
Header ('Content-Disposition: attachment; '. $dnname);
Header ('Pragma: no-cache');
// {{{ +-- static public (boolean) filter_context (&$text, &$pattern)
* @param string 필터링 패턴. 패턴은 PCRE 정규 표현식을 이용한다.
// {{{ +-- static public (boolean) filter_context_file (&$text, &$patfile)
* @param string 필터링 패턴 파일. 패턴은 PCRE 정규 표현식을 이용한다.
// {{{ +-- static public (boolean) filter_ip (&$ip)
* 접속 IP($_SERVER['REMOTE_ADDR'])가 주어진 CIDR/MASK에 포함이 되는지 확인
* @param string 매칭시킬 IP 또는 IP 블럭
* - 1.1.1.1/255.255.255.0
* - 1.1.1.1 - 2.2.2.2 (range)
// {{{ +-- static public (stdClass) browser (void)
static public function browser ($u = null) {
// {{{ +-- static public (string) json_encode ($text, $nopretty = false)
* utf8 conflict 및 binary data 를 해결한 json encode wrapper
* @param mixed encode할 변수
* @param bool pretty 출력 여부. 기본값 false이며 이는 pretty 출력
* 을 의미한다. pretty 인자는 php 5.4 부터 지원한다. [기본값 false]
* @param bool (optional) binary safe를 위한 normalize를 할지 여부 [기본값 true]
static public function json_encode ($text, $nopretty = false, $normal = true) {
// {{{ +-- static public (string) json_decode ($text)
* utf8 conflict 및 binary data 를 해결한 json decode wrapper
* @param string json string data
* @param bool (optional) When TRUE, returned objects will be converted
* into associative arrays.
* @param int (optional) User specified recursion depth (default: 512)
* @param int (optional) Bitmask of JSON decode options. Currently only
* JSON_BIGINT_AS_STRING is supported (default is
* to cast large integers as floats)
static public function json_decode ($data, $assoc = false, $depth = 512, $options = 0) {
* vim: set filetype=php noet sw=4 ts=4 fdm=marker:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
|