Merge pull request #385 from kijin/pr/more-framework-classes

Rhymix Framework 각종 기능 추가
This commit is contained in:
Kijin Sung 2016-03-20 00:06:01 +09:00
commit 321015e050
269 changed files with 38164 additions and 616 deletions

View file

@ -686,7 +686,7 @@ class Context
public function checkSSO()
{
// pass if it's not GET request or XE is not yet installed
if(!config('use_sso') || isCrawler())
if(!config('use_sso') || Rhymix\Framework\UA::isRobot())
{
return TRUE;
}

View file

@ -8,7 +8,6 @@
*/
class FileHandler
{
/**
* Changes path of target file, directory into absolute path
*
@ -17,12 +16,7 @@ class FileHandler
*/
public static function getRealPath($source)
{
if(strlen($source) >= 2 && substr_compare($source, './', 0, 2) === 0)
{
return _XE_PATH_ . substr($source, 2);
}
return $source;
return (strncmp($source, './', 2) === 0) ? (\RX_BASEDIR . substr($source, 2)) : $source;
}
/**
@ -33,64 +27,11 @@ class FileHandler
* @param string $source_dir Path of source directory
* @param string $target_dir Path of target dir
* @param string $filter Regex to filter files. If file matches this regex, the file is not copied.
* @param string $type If set as 'force'. Even if the file exists in target, the file is copied.
* @return void
*/
public static function copyDir($source_dir, $target_dir, $filter = null, $type = null)
public static function copyDir($source_dir, $target_dir, $filter = null)
{
$source_dir = self::getRealPath($source_dir);
$target_dir = self::getRealPath($target_dir);
if(!is_dir($source_dir))
{
return FALSE;
}
// generate when no target exists
self::makeDir($target_dir);
if(substr($source_dir, -1) != DIRECTORY_SEPARATOR)
{
$source_dir .= DIRECTORY_SEPARATOR;
}
if(substr($target_dir, -1) != DIRECTORY_SEPARATOR)
{
$target_dir .= DIRECTORY_SEPARATOR;
}
$oDir = dir($source_dir);
while($file = $oDir->read())
{
if($file{0} == '.')
{
continue;
}
if($filter && preg_match($filter, $file))
{
continue;
}
if(is_dir($source_dir . $file))
{
self::copyDir($source_dir . $file, $target_dir . $file, $type);
}
else
{
if($type == 'force')
{
@unlink($target_dir . $file);
}
else
{
if(!file_exists($target_dir . $file))
{
@copy($source_dir . $file, $target_dir . $file);
}
}
}
}
$oDir->close();
return Rhymix\Framework\Storage::copyDirectory(self::getRealPath($source_dir), self::getRealPath($target_dir), $filter);
}
/**
@ -104,18 +45,7 @@ class FileHandler
public static function copyFile($source, $target, $force = 'Y')
{
setlocale(LC_CTYPE, 'en_US.UTF8', 'ko_KR.UTF8');
$source = self::getRealPath($source);
$target_dir = self::getRealPath(dirname($target));
$target = basename($target);
self::makeDir($target_dir);
if($force == 'Y')
{
@unlink($target_dir . DIRECTORY_SEPARATOR . $target);
}
@copy($source, $target_dir . DIRECTORY_SEPARATOR . $target);
return Rhymix\Framework\Storage::copy(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -126,12 +56,7 @@ class FileHandler
*/
public static function readFile($filename)
{
if(($filename = self::exists($filename)) === FALSE || filesize($filename) < 1)
{
return;
}
return @file_get_contents($filename);
return Rhymix\Framework\Storage::read(self::getRealPath($filename));
}
/**
@ -144,22 +69,7 @@ class FileHandler
*/
public static function writeFile($filename, $buff, $mode = "w")
{
$filename = self::getRealPath($filename);
$pathinfo = pathinfo($filename);
self::makeDir($pathinfo['dirname']);
$flags = 0;
if(strtolower($mode) == 'a')
{
$flags = FILE_APPEND;
}
@file_put_contents($filename, $buff, $flags|LOCK_EX);
@chmod($filename, 0644);
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return Rhymix\Framework\Storage::write(self::getRealPath($filename), $buff, $mode);
}
/**
@ -170,16 +80,7 @@ class FileHandler
*/
public static function removeFile($filename)
{
if(($filename = self::exists($filename)) === false)
{
return false;
}
$status = @unlink($filename);
if(function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $status;
return Rhymix\Framework\Storage::delete(self::getRealPath($filename));
}
/**
@ -193,7 +94,7 @@ class FileHandler
*/
public static function rename($source, $target)
{
return @rename(self::getRealPath($source), self::getRealPath($target));
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -205,12 +106,7 @@ class FileHandler
*/
public static function moveFile($source, $target)
{
if(($source = self::exists($source)) !== FALSE)
{
self::removeFile($target);
return self::rename($source, $target);
}
return FALSE;
return Rhymix\Framework\Storage::move(self::getRealPath($source), self::getRealPath($target));
}
/**
@ -224,7 +120,7 @@ class FileHandler
*/
public static function moveDir($source_dir, $target_dir)
{
self::rename($source_dir, $target_dir);
return Rhymix\Framework\Storage::move(self::getRealPath($source_dir), self::getRealPath($target_dir));
}
/**
@ -240,45 +136,31 @@ class FileHandler
*/
public static function readDir($path, $filter = '', $to_lower = FALSE, $concat_prefix = FALSE)
{
$path = self::getRealPath($path);
$list = Rhymix\Framework\Storage::readDirectory(self::getRealPath($path), $concat_prefix, true, false);
if (!$list)
{
return array();
}
$output = array();
if(substr($path, -1) != '/')
foreach ($list as $filename)
{
$path .= '/';
}
if(!is_dir($path))
{
return $output;
}
$files = scandir($path);
foreach($files as $file)
{
if($file{0} == '.' || ($filter && !preg_match($filter, $file)))
$filename = str_replace(array('/\\', '//'), '/', $filename);
$basename = $concat_prefix ? basename($filename) : $filename;
if ($basename[0] === '.' || ($filter && !preg_match($filter, $basename)))
{
continue;
}
if($to_lower)
if ($to_lower)
{
$file = strtolower($file);
$filename = strtolower($filename);
}
if($filter)
{
$file = preg_replace($filter, '$1', $file);
$filename = preg_replace($filter, '$1', $filename);
}
if($concat_prefix)
{
$file = sprintf('%s%s', str_replace(_XE_PATH_, '', $path), $file);
}
$output[] = str_replace(array('/\\', '//'), '/', $file);
$output[] = $filename;
}
return $output;
}
@ -292,16 +174,11 @@ class FileHandler
*/
public static function makeDir($path_string)
{
if(self::exists($path_string) !== FALSE)
if (!ini_get('safe_mode'))
{
return TRUE;
}
if(!ini_get('safe_mode'))
{
@mkdir($path_string, 0755, TRUE);
@chmod($path_string, 0755);
return Rhymix\Framework\Storage::createDirectory(self::getRealPath($path_string));
}
// if safe_mode is on, use FTP
else
{
@ -372,37 +249,7 @@ class FileHandler
*/
public static function removeDir($path)
{
if(($path = self::isDir($path)) === FALSE)
{
return;
}
if(self::isDir($path))
{
$files = array_diff(scandir($path), array('..', '.'));
foreach($files as $file)
{
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
if(is_dir($target))
{
self::removeDir($target);
}
else
{
unlink($target);
}
}
rmdir($path);
}
else
{
unlink($path);
}
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path));
}
/**
@ -413,27 +260,14 @@ class FileHandler
*/
public static function removeBlankDir($path)
{
if(($path = self::isDir($path)) === FALSE)
$path = self::getRealPath($path);
if (Rhymix\Framework\Storage::isEmptyDirectory($path))
{
return;
return Rhymix\Framework\Storage::deleteDirectory($path);
}
$files = array_diff(scandir($path), array('..', '.'));
if(count($files) < 1)
else
{
rmdir($path);
return;
}
foreach($files as $file)
{
if(($target = self::isDir($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
self::removeBlankDir($target);
return false;
}
}
@ -447,37 +281,7 @@ class FileHandler
*/
public static function removeFilesInDir($path)
{
if(($path = self::getRealPath($path)) === FALSE)
{
return;
}
if(is_dir($path))
{
$files = array_diff(scandir($path), array('..', '.'));
foreach($files as $file)
{
if(($target = self::getRealPath($path . DIRECTORY_SEPARATOR . $file)) === FALSE)
{
continue;
}
if(is_dir($target))
{
self::removeFilesInDir($target);
}
else
{
unlink($target);
}
}
}
else
{
if(self::exists($path)) unlink($path);
}
return Rhymix\Framework\Storage::deleteDirectory(self::getRealPath($path), false);
}
/**
@ -584,7 +388,14 @@ class FileHandler
if($response->success)
{
return $response->body;
if (isset($request_config['filename']))
{
return true;
}
else
{
return $response->body;
}
}
else
{
@ -611,13 +422,9 @@ class FileHandler
*/
public static function getRemoteFile($url, $target_filename, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array(), $cookies = array(), $post_data = array(), $request_config = array())
{
if(!($body = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers,$cookies,$post_data,$request_config)))
{
return FALSE;
}
self::writeFile($target_filename, $body);
return TRUE;
$request_config['filename'] = $target_filename;
$success = self::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers, $cookies, $post_data, $request_config);
return $success ? true : false;
}
/**
@ -658,12 +465,17 @@ class FileHandler
$channels = 6; //for png
}
$memoryNeeded = round(($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR);
$availableMemory = self::returnBytes(ini_get('memory_limit')) - memory_get_usage();
$memoryLimit = self::returnBytes(ini_get('memory_limit'));
if($memoryLimit < 0)
{
return true;
}
$availableMemory = $memoryLimit - memory_get_usage();
if($availableMemory < $memoryNeeded)
{
return FALSE;
return false;
}
return TRUE;
return false;
}
/**
@ -882,19 +694,13 @@ class FileHandler
*/
public static function readIniFile($filename)
{
if(($filename = self::exists($filename)) === FALSE)
if(!Rhymix\Framework\Storage::isReadable($filename))
{
return FALSE;
}
$arr = parse_ini_file($filename, TRUE);
if(is_array($arr) && count($arr) > 0)
{
return $arr;
}
else
{
return array();
return false;
}
$arr = parse_ini_file($filename, true);
return is_array($arr) ? $arr : array();
}
/**
@ -966,11 +772,9 @@ class FileHandler
*/
public static function openFile($filename, $mode)
{
$pathinfo = pathinfo($filename);
self::makeDir($pathinfo['dirname']);
require_once("FileObject.class.php");
return new FileObject($filename, $mode);
$filename = self::getRealPath($filename);
Rhymix\Framework\Storage::createDirectory(dirname($filename));
return new FileObject($filename, $mode);
}
/**
@ -981,7 +785,7 @@ class FileHandler
*/
public static function hasContent($filename)
{
return (is_readable($filename) && (filesize($filename) > 0));
return Rhymix\Framework\Storage::getSize(self::getRealPath($filename)) > 0;
}
/**
@ -993,7 +797,7 @@ class FileHandler
public static function exists($filename)
{
$filename = self::getRealPath($filename);
return file_exists($filename) ? $filename : FALSE;
return Rhymix\Framework\Storage::exists($filename) ? $filename : false;
}
/**
@ -1005,7 +809,7 @@ class FileHandler
public static function isDir($path)
{
$path = self::getRealPath($path);
return is_dir($path) ? $path : FALSE;
return Rhymix\Framework\Storage::isDirectory($path) ? $path : false;
}
/**
@ -1017,22 +821,7 @@ class FileHandler
public static function isWritableDir($path)
{
$path = self::getRealPath($path);
if(is_dir($path)==FALSE)
{
return FALSE;
}
$checkFile = $path . '/_CheckWritableDir';
$fp = fopen($checkFile, 'w');
if(!is_resource($fp))
{
return FALSE;
}
fclose($fp);
self::removeFile($checkFile);
return TRUE;
return Rhymix\Framework\Storage::isDirectory($path) && Rhymix\Framework\Storage::isWritable($path);
}
}

View file

@ -460,81 +460,7 @@ class Mail
*/
function returnMIMEType($filename)
{
preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix);
switch(strtolower($fileSuffix[1]))
{
case "js" :
return "application/x-javascript";
case "json" :
return "application/json";
case "jpg" :
case "jpeg" :
case "jpe" :
return "image/jpg";
case "png" :
case "gif" :
case "bmp" :
case "tiff" :
return "image/" . strtolower($fileSuffix[1]);
case "css" :
return "text/css";
case "xml" :
return "application/xml";
case "doc" :
case "docx" :
return "application/msword";
case "xls" :
case "xlt" :
case "xlm" :
case "xld" :
case "xla" :
case "xlc" :
case "xlw" :
case "xll" :
return "application/vnd.ms-excel";
case "ppt" :
case "pps" :
return "application/vnd.ms-powerpoint";
case "rtf" :
return "application/rtf";
case "pdf" :
return "application/pdf";
case "html" :
case "htm" :
case "php" :
return "text/html";
case "txt" :
return "text/plain";
case "mpeg" :
case "mpg" :
case "mpe" :
return "video/mpeg";
case "mp3" :
return "audio/mpeg3";
case "wav" :
return "audio/wav";
case "aiff" :
case "aif" :
return "audio/aiff";
case "avi" :
return "video/msvideo";
case "wmv" :
return "video/x-ms-wmv";
case "mov" :
return "video/quicktime";
case "zip" :
return "application/zip";
case "tar" :
return "application/x-tar";
case "swf" :
return "application/x-shockwave-flash";
default :
if(function_exists("mime_content_type"))
{
$fileSuffix = mime_content_type($filename);
}
return "unknown/" . trim($fileSuffix[0], ".");
}
return Rhymix\Framework\MIME::getTypeByFilename($filename);
}
}

View file

@ -55,9 +55,6 @@ class Mobile
return $this->ismobile = false;
}
$xe_web_path = Context::pathToUrl(_XE_PATH_);
// default setting. if there is cookie for a device, XE do not have to check if it is mobile or not and it will enhance performace of the server.
$this->ismobile = FALSE;
$m = Context::get('m');
@ -87,28 +84,14 @@ class Mobile
}
else
{
$this->ismobile = FALSE;
setcookie("mobile", FALSE, 0, $xe_web_path);
setcookie("user-agent", FALSE, 0, $xe_web_path);
if(!self::isMobilePadCheckByAgent() && self::isMobileCheckByAgent())
{
$this->ismobile = TRUE;
}
setcookie("mobile", FALSE, 0, RX_BASEURL);
setcookie("user-agent", FALSE, 0, RX_BASEURL);
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
}
}
else
{
if(self::isMobilePadCheckByAgent())
{
$this->ismobile = FALSE;
}
else
{
if(self::isMobileCheckByAgent())
{
$this->ismobile = TRUE;
}
}
$this->ismobile = Rhymix\Framework\UA::isMobile() && !Rhymix\Framework\UA::isTablet();
}
if($this->ismobile !== NULL)
@ -118,18 +101,18 @@ class Mobile
if($_COOKIE['mobile'] != 'true')
{
$_COOKIE['mobile'] = 'true';
setcookie("mobile", 'true', 0, $xe_web_path);
setcookie("mobile", 'true', 0, RX_BASEURL);
}
}
elseif(isset($_COOKIE['mobile']) && $_COOKIE['mobile'] != 'false')
{
$_COOKIE['mobile'] = 'false';
setcookie("mobile", 'false', 0, $xe_web_path);
setcookie("mobile", 'false', 0, RX_BASEURL);
}
if(isset($_COOKIE['mobile']) && $_COOKIE['user-agent'] != md5($_SERVER['HTTP_USER_AGENT']))
{
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, $xe_web_path);
setcookie("user-agent", md5($_SERVER['HTTP_USER_AGENT']), 0, RX_BASEURL);
}
}
@ -143,31 +126,7 @@ class Mobile
*/
public static function isMobileCheckByAgent()
{
static $UACheck;
if(isset($UACheck))
{
return $UACheck;
}
$oMobile = Mobile::getInstance();
$mobileAgent = array('iPod', 'iPhone', 'Android', 'BlackBerry', 'SymbianOS', 'Bada', 'Tizen', 'Kindle', 'Wii', 'SCH-', 'SPH-', 'CANU-', 'Windows Phone', 'Windows CE', 'POLARIS', 'Palm', 'Dorothy Browser', 'Mobile', 'Opera Mobi', 'Opera Mini', 'Minimo', 'AvantGo', 'NetFront', 'Nokia', 'LGPlayer', 'SonyEricsson', 'HTC');
if($oMobile->isMobilePadCheckByAgent())
{
$UACheck = TRUE;
return TRUE;
}
foreach($mobileAgent as $agent)
{
if(stripos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
{
$UACheck = TRUE;
return TRUE;
}
}
$UACheck = FALSE;
return FALSE;
return Rhymix\Framework\UA::isMobile();
}
/**
@ -177,45 +136,7 @@ class Mobile
*/
public static function isMobilePadCheckByAgent()
{
static $UACheck;
if(isset($UACheck))
{
return $UACheck;
}
$padAgent = array('iPad', 'Android', 'webOS', 'hp-tablet', 'PlayBook');
// Android with 'Mobile' string is not a tablet-like device, and 'Andoroid' without 'Mobile' string is a tablet-like device.
// $exceptionAgent[0] contains exception agents for all exceptions.
$exceptionAgent = array(0 => array('Opera Mini', 'Opera Mobi'), 'Android' => 'Mobile');
foreach($padAgent as $agent)
{
if(strpos($_SERVER['HTTP_USER_AGENT'], $agent) !== FALSE)
{
if(!isset($exceptionAgent[$agent]))
{
$UACheck = TRUE;
return TRUE;
}
elseif(strpos($_SERVER['HTTP_USER_AGENT'], $exceptionAgent[$agent]) === FALSE)
{
// If the agent is the Android, that can be either tablet and mobile phone.
foreach($exceptionAgent[0] as $val)
{
if(strpos($_SERVER['HTTP_USER_AGENT'], $val) !== FALSE)
{
$UACheck = FALSE;
return FALSE;
}
}
$UACheck = TRUE;
return TRUE;
}
}
}
$UACheck = FALSE;
return FALSE;
return Rhymix\Framework\UA::isTablet();
}
/**

View file

@ -238,7 +238,7 @@ class ModuleHandler extends Handler
}
// redirect, if site_srl of module_info is different from one of site's module_info
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !isCrawler())
if($module_info && $module_info->site_srl != $site_module_info->site_srl && !Rhymix\Framework\UA::isRobot())
{
// If the module is of virtual site
if($module_info->site_srl)
@ -1289,6 +1289,8 @@ class ModuleHandler extends Handler
$statusMessageList = array(
'100' => 'Continue',
'101' => 'Switching Protocols',
'102' => 'Processing',
'103' => 'Checkpoint',
'200' => 'OK',
'201' => 'Created',
'202' => 'Accepted',
@ -1296,13 +1298,18 @@ class ModuleHandler extends Handler
'204' => 'No Content',
'205' => 'Reset Content',
'206' => 'Partial Content',
'207' => 'Multi-Status',
'208' => 'Already Reported',
'226' => 'IM Used',
'300' => 'Multiple Choices',
'301' => 'Moved Permanently',
'302' => 'Found',
'303' => 'See Other',
'304' => 'Not Modified',
'305' => 'Use Proxy',
'306' => 'Switch Proxy',
'307' => 'Temporary Redirect',
'308' => 'Permanent Redirect',
'400' => 'Bad Request',
'401' => 'Unauthorized',
'402' => 'Payment Required',
@ -1316,19 +1323,39 @@ class ModuleHandler extends Handler
'410' => 'Gone',
'411' => 'Length Required',
'412' => 'Precondition Failed',
'413' => 'Request Entity Too Large',
'414' => 'Request-URI Too Long',
'413' => 'Payload Too Large',
'414' => 'URI Too Long',
'415' => 'Unsupported Media Type',
'416' => 'Requested Range Not Satisfiable',
'416' => 'Range Not Satisfiable',
'417' => 'Expectation Failed',
'418' => 'I\'m a teapot',
'420' => 'Enhance Your Calm',
'421' => 'Misdirected Request',
'422' => 'Unprocessable Entity',
'423' => 'Locked',
'424' => 'Failed Dependency',
'425' => 'Unordered Collection',
'426' => 'Upgrade Required',
'428' => 'Precondition Required',
'429' => 'Too Many Requests',
'431' => 'Request Header Fields Too Large',
'444' => 'No Response',
'449' => 'Retry With',
'451' => 'Unavailable For Legal Reasons',
'500' => 'Internal Server Error',
'501' => 'Not Implemented',
'502' => 'Bad Gateway',
'503' => 'Service Unavailable',
'504' => 'Gateway Timeout',
'505' => 'HTTP Version Not Supported',
'506' => 'Variant Also Negotiates',
'507' => 'Insufficient Storage',
'508' => 'Loop Detected',
'509' => 'Bandwidth Limit Exceeded',
'510' => 'Not Extended',
'511' => 'Network Authentication Required',
);
$statusMessage = $statusMessageList[$code];
$statusMessage = $statusMessageList[strval($code)];
if(!$statusMessage)
{
$statusMessage = 'OK';

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,503 @@
<?php
/**
* Source: https://ip.kisa.or.kr/ip_cate_stat/stat_05_05.act
* Last Updated: 2016-03-18
*/
return array (
0 =>
array (
0 => '2001000000000000',
1 => '2001000fffffffff',
),
1 =>
array (
0 => '2001022000000000',
1 => '20010220ffffffff',
),
2 =>
array (
0 => '2001023000000000',
1 => '20010230ffffffff',
),
3 =>
array (
0 => '2001027000000000',
1 => '20010270ffffffff',
),
4 =>
array (
0 => '2001028000000000',
1 => '20010280ffffffff',
),
5 =>
array (
0 => '2001029000000000',
1 => '20010290ffffffff',
),
6 =>
array (
0 => '200102b000000000',
1 => '200102b0ffffffff',
),
7 =>
array (
0 => '200102b800000000',
1 => '200102b8ffffffff',
),
8 =>
array (
0 => '200102d800000000',
1 => '200102d8ffffffff',
),
9 =>
array (
0 => '2001032000000000',
1 => '20010320ffffffff',
),
10 =>
array (
0 => '2001033000000000',
1 => '20010330ffffffff',
),
11 =>
array (
0 => '2001037800000000',
1 => '20010378ffffffff',
),
12 =>
array (
0 => '2001039000000000',
1 => '20010390ffffffff',
),
13 =>
array (
0 => '200103a800000000',
1 => '200103a8ffffffff',
),
14 =>
array (
0 => '200107fa00000002',
1 => '200107fa00000002',
),
15 =>
array (
0 => '200107fa00080000',
1 => '200107fa0008ffff',
),
16 =>
array (
0 => '20010c4800000000',
1 => '20010c48ffffffff',
),
17 =>
array (
0 => '20010c9800000000',
1 => '20010c98ffffffff',
),
18 =>
array (
0 => '20010cf000000000',
1 => '20010cf0ffffffff',
),
19 =>
array (
0 => '20010d3800000000',
1 => '20010d38ffffffff',
),
20 =>
array (
0 => '20010dc500000000',
1 => '20010dc5ffffffff',
),
21 =>
array (
0 => '20010dcc00000000',
1 => '20010dccffffffff',
),
22 =>
array (
0 => '20010ea000000000',
1 => '20010ea0ffffffff',
),
23 =>
array (
0 => '20010ea800000000',
1 => '20010ea8ffffffff',
),
24 =>
array (
0 => '20010eb800000000',
1 => '20010eb8ffffffff',
),
25 =>
array (
0 => '20010ed000000000',
1 => '20010ed0ffffffff',
),
26 =>
array (
0 => '20010ee800000000',
1 => '20010ee8ffffffff',
),
27 =>
array (
0 => '20010ef000000000',
1 => '20010ef0ffffffff',
),
28 =>
array (
0 => '20010ef800000000',
1 => '20010ef8ffffffff',
),
29 =>
array (
0 => '20010f2800000000',
1 => '20010f28ffffffff',
),
30 =>
array (
0 => '20010f4800000000',
1 => '20010f48ffffffff',
),
31 =>
array (
0 => '24033e0000000000',
1 => '24033e00ffffffff',
),
32 =>
array (
0 => '2001443000000000',
1 => '20014430ffffffff',
),
33 =>
array (
0 => '2400000000000000',
1 => '24000fffffffffff',
),
34 =>
array (
0 => '2400180000000000',
1 => '24001800ffffffff',
),
35 =>
array (
0 => '2400330000000000',
1 => '24003300ffffffff',
),
36 =>
array (
0 => '2400478000000000',
1 => '24004780ffffffff',
),
37 =>
array (
0 => '2400498000000000',
1 => '24004980ffffffff',
),
38 =>
array (
0 => '24009f8000000000',
1 => '24009f80ffffffff',
),
39 =>
array (
0 => '2400a58000000000',
1 => '2400a580ffffffff',
),
40 =>
array (
0 => '2400ab0000000000',
1 => '2400ab00ffffffff',
),
41 =>
array (
0 => '2400cf0000000000',
1 => '2400cf00ffffffff',
),
42 =>
array (
0 => '2400fd8000000000',
1 => '2400fd80ffffffff',
),
43 =>
array (
0 => '2401270000000000',
1 => '24012700ffffffff',
),
44 =>
array (
0 => '2401400000000000',
1 => '24014000ffffffff',
),
45 =>
array (
0 => '2401a00000000000',
1 => '2401a000ffffffff',
),
46 =>
array (
0 => '2401a80000000000',
1 => '2401a800ffffffff',
),
47 =>
array (
0 => '2401c50000000000',
1 => '2401c500ffffffff',
),
48 =>
array (
0 => '2402000000000000',
1 => '240200ffffffffff',
),
49 =>
array (
0 => '24021a0000000000',
1 => '24021a00ffffffff',
),
50 =>
array (
0 => '2402310000000000',
1 => '24023100ffffffff',
),
51 =>
array (
0 => '2402580000000000',
1 => '24025800ffffffff',
),
52 =>
array (
0 => '2402610000000000',
1 => '24026100ffffffff',
),
53 =>
array (
0 => '2402700000000000',
1 => '24027000ffffffff',
),
54 =>
array (
0 => '2402be0000000000',
1 => '2402be00ffffffff',
),
55 =>
array (
0 => '2402de0000000000',
1 => '2402de00ffffffff',
),
56 =>
array (
0 => '2402f40000000000',
1 => '2402f400ffffffff',
),
57 =>
array (
0 => '2403370000000000',
1 => '24033700ffffffff',
),
58 =>
array (
0 => '2403630000000000',
1 => '24036300ffffffff',
),
59 =>
array (
0 => '2403650000000000',
1 => '24036500ffffffff',
),
60 =>
array (
0 => '2404000000000000',
1 => '2404000fffffffff',
),
61 =>
array (
0 => '2404080000000000',
1 => '24040800ffffffff',
),
62 =>
array (
0 => '2404230000000000',
1 => '24042300ffffffff',
),
63 =>
array (
0 => '2404460000000000',
1 => '24044600ffffffff',
),
64 =>
array (
0 => '2405350000000000',
1 => '24053500ffffffff',
),
65 =>
array (
0 => '2405430000000000',
1 => '24054300ffffffff',
),
66 =>
array (
0 => '2405580000000000',
1 => '24055800ffffffff',
),
67 =>
array (
0 => '20010e6000000000',
1 => '20010e60ffffffff',
),
68 =>
array (
0 => '20010e7000000000',
1 => '20010e70ffffffff',
),
69 =>
array (
0 => '20010e7800000000',
1 => '20010e78ffffffff',
),
70 =>
array (
0 => '20010e9800000000',
1 => '20010e98ffffffff',
),
71 =>
array (
0 => '24009e8000000000',
1 => '24009e80ffffffff',
),
72 =>
array (
0 => '2400e18000000000',
1 => '2400e180ffffffff',
),
73 =>
array (
0 => '2401e20000000000',
1 => '2401e200ffffffff',
),
74 =>
array (
0 => '24053d0000000000',
1 => '24053d00ffffffff',
),
75 =>
array (
0 => '24055f0000000000',
1 => '24055f00ffffffff',
),
76 =>
array (
0 => '24057b0000000000',
1 => '24057b00ffffffff',
),
77 =>
array (
0 => '2405860000000000',
1 => '24058600ffffffff',
),
78 =>
array (
0 => '2405950000000000',
1 => '24059500ffffffff',
),
79 =>
array (
0 => '2405c00000000000',
1 => '2405c000ffffffff',
),
80 =>
array (
0 => '2406400000000000',
1 => '24064000ffffffff',
),
81 =>
array (
0 => '2406590000000000',
1 => '24065900ffffffff',
),
82 =>
array (
0 => '2406660000000000',
1 => '24066600ffffffff',
),
83 =>
array (
0 => '2406680000000000',
1 => '24066800ffffffff',
),
84 =>
array (
0 => '24066a0000000000',
1 => '24066a00ffffffff',
),
85 =>
array (
0 => '2406ad0000000000',
1 => '2406ad00ffffffff',
),
86 =>
array (
0 => '2406b00000000000',
1 => '2406b000ffffffff',
),
87 =>
array (
0 => '2406d00000000000',
1 => '2406d000ffffffff',
),
88 =>
array (
0 => '2406d70000000000',
1 => '2406d700ffffffff',
),
89 =>
array (
0 => '24070b0000000000',
1 => '24070b00ffffffff',
),
90 =>
array (
0 => '2407200000000000',
1 => '24072000ffffffff',
),
91 =>
array (
0 => '2407350000000000',
1 => '24073500ffffffff',
),
92 =>
array (
0 => '2407650000000000',
1 => '24076500ffffffff',
),
93 =>
array (
0 => '2407670000000000',
1 => '24076700ffffffff',
),
94 =>
array (
0 => '2407910000000000',
1 => '24079100ffffffff',
),
95 =>
array (
0 => '2407b20000000000',
1 => '2407b200ffffffff',
),
96 =>
array (
0 => '2407b80000000000',
1 => '2407b800ffffffff',
),
97 =>
array (
0 => '2407c00000000000',
1 => '2407c000ffffffff',
),
98 =>
array (
0 => '2407c70000000000',
1 => '2407c700ffffffff',
),
);

View file

@ -0,0 +1,132 @@
<?php
namespace Rhymix\Framework;
/**
* The calendar class.
*/
class Calendar
{
/**
* This method returns the English name of a month, e.g. 9 = 'September'.
*
* @param int $month_number
* @param bool $long_format (optional, default is true)
* @return string
*/
public static function getMonthName($month_number, $long_format = true)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return date($long_format ? 'F' : 'M', mktime(0, 0, 0, $month_number, 1));
}
/**
* This method returns the day on which a month begins.
*
* 0 = Sunday, 1 = Monday, 2 = Tuesday, 3 = Wednesday, 4 = Thursday, 5 = Friday, 6 = Saturday.
* If you do not specify a year, the current year is assumed.
*
* @param int $month_number
* @param int $year (optional)
* @return int
*/
public static function getMonthStartDayOfWeek($month_number, $year = null)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return (int)date('w', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
}
/**
* This method returns the number of days in a month, e.g. February 2016 has 29 days.
*
* If you do not specify a year, the current year is assumed.
* You must specify a year to get the number of days in February.
*
* @param int $month_number
* @param int $year (optional)
* @return int
*/
public static function getMonthDays($month_number, $year = null)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
return (int)date('t', mktime(0, 0, 0, $month_number, 1, $year ?: date('Y')));
}
/**
* This method returns a complete calendar for a month.
*
* The return value is an array with six members, each representing a week.
* Each week is an array with seven members, each representing a day.
* 6 weeks are returned. Empty cells are represented by nulls.
*
* If you do not specify a year, the current year is assumed.
*
* @param int $month_number
* @param int $year (optional)
* @param int $start_dow (optional)
* @return array
*/
public static function getMonthCalendar($month_number, $year = null, $start_dow = 0)
{
$month_number = intval($month_number, 10);
if (!is_between($month_number, 1, 12))
{
return false;
}
if (!is_between($start_dow, 0, 6))
{
return false;
}
if (!$year || !is_between($year, 1000, 9999))
{
$year = date('Y');
}
$start = self::getMonthStartDayOfWeek($month_number, $year);
$count = self::getMonthDays($month_number, $year);
$initial_blank_cells = (7 + $start - $start_dow) % 7;
$final_blank_cells = 42 - $count - $initial_blank_cells;
$temp = array();
for ($i = 0; $i < $initial_blank_cells; $i++)
{
$temp[] = null;
}
for ($i = 0; $i < $count; $i++)
{
$temp[] = $i + 1;
}
for ($i = 0; $i < $final_blank_cells; $i++)
{
$temp[] = null;
}
$return = array();
for ($i = 0; $i < 6; $i++)
{
$week = array();
for ($j = 0; $j < 7; $j++)
{
$week[] = array_shift($temp);
}
$return[] = $week;
}
return $return;
}
}

View file

@ -28,9 +28,9 @@ class Config
*/
public static function init()
{
if (file_exists(RX_BASEDIR . self::$config_filename))
if (file_exists(\RX_BASEDIR . self::$config_filename))
{
self::$_config = (include RX_BASEDIR . self::$config_filename);
self::$_config = (include \RX_BASEDIR . self::$config_filename);
}
else
{
@ -59,7 +59,7 @@ class Config
*/
public static function getDefaults()
{
return (include RX_BASEDIR . self::$default_config_filename);
return (include \RX_BASEDIR . self::$default_config_filename);
}
/**
@ -135,7 +135,7 @@ class Config
// Save the main config file.
$buff = '<?php' . "\n" . '// Rhymix System Configuration' . "\n" . 'return ' . self::serialize(self::$_config) . ';' . "\n";
$result = \FileHandler::writeFile(RX_BASEDIR . self::$config_filename, $buff) ? true : false;
$result = Storage::write(\RX_BASEDIR . self::$config_filename, $buff) ? true : false;
//if (!$result) return false;
// Save XE-compatible config files.
@ -144,9 +144,9 @@ class Config
$db_info_without_ftp = clone $db_info;
unset($db_info_without_ftp->ftp_info);
$buff = '<?php' . "\n" . '$db_info = ' . self::serialize($db_info_without_ftp) . ';' . "\n";
\FileHandler::writeFile(RX_BASEDIR . self::$old_db_config_filename, $buff);
Storage::write(\RX_BASEDIR . self::$old_db_config_filename, $buff);
$buff = '<?php' . "\n" . '$ftp_info = ' . self::serialize($ftp_info) . ';' . "\n";
\FileHandler::writeFile(RX_BASEDIR . self::$old_ftp_config_filename, $buff);
Storage::write(\RX_BASEDIR . self::$old_ftp_config_filename, $buff);
return true;
}

View file

@ -173,8 +173,8 @@ class Debug
// Rewrite the error message with relative paths.
$message = str_replace(array(
' called in ' . RX_BASEDIR,
' defined in ' . RX_BASEDIR,
' called in ' . \RX_BASEDIR,
' defined in ' . \RX_BASEDIR,
), array(
' called in ',
' defined in ',
@ -485,7 +485,7 @@ class Debug
{
// Collect debug information.
$data = (object)array(
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', RX_TIME),
'timestamp' => DateTime::formatTimestamp('Y-m-d H:i:s', \RX_TIME),
'url' => getCurrentPageUrl(),
'request' => (object)array(
'method' => $_SERVER['REQUEST_METHOD'] . ($_SERVER['REQUEST_METHOD'] !== \Context::getRequestMethod() ? (' (' . \Context::getRequestMethod() . ')') : ''),

View file

@ -3,6 +3,7 @@
namespace Rhymix\Framework\Filters;
use Rhymix\Framework\Security;
use Rhymix\Framework\Storage;
/**
* The HTML filter class.
@ -126,8 +127,8 @@ class HTMLFilter
$config->set('URI.SafeIframeRegexp', MediaFilter::getIframeWhitelistRegex());
// Set the serializer path.
$config->set('Cache.SerializerPath', RX_BASEDIR . 'files/cache/htmlpurifier');
\FileHandler::makeDir(RX_BASEDIR . 'files/cache/htmlpurifier');
$config->set('Cache.SerializerPath', \RX_BASEDIR . 'files/cache/htmlpurifier');
Storage::createDirectory(\RX_BASEDIR . 'files/cache/htmlpurifier');
// Modify the HTML definition to support editor components and widgets.
$def = $config->getHTMLDefinition(true);
@ -313,8 +314,8 @@ class HTMLFilter
// flexbox
$info['display'] = new \HTMLPurifier_AttrDef_Enum(array(
'block', 'flex', '-webkit-flex', 'inline', 'inline-block', 'inline-flex', '-webkit-inline-flex', 'inline-table',
'list-item', 'run-in', 'compact', 'marker', 'table', 'table-row-group', 'table-header-group', 'table-footer-group',
'block', 'flex', '-webkit-flex', 'inline', 'inline-block', 'inline-flex', '-webkit-inline-flex', 'inline-table',
'list-item', 'run-in', 'compact', 'marker', 'table', 'table-row-group', 'table-header-group', 'table-footer-group',
'table-row', 'table-column-group', 'table-column', 'table-cell', 'table-caption',
'none', 'initial', 'inherit',
));

View file

@ -140,7 +140,7 @@ class IpFilter
return false;
}
$cloudflare_ranges = (include RX_BASEDIR . 'common/defaults/cloudflare.php');
$cloudflare_ranges = (include \RX_BASEDIR . 'common/defaults/cloudflare.php');
foreach ($cloudflare_ranges as $cloudflare_range)
{
if (self::inRange($_SERVER['REMOTE_ADDR'], $cloudflare_range))

View file

@ -198,7 +198,7 @@ class MediaFilter
*/
protected static function _loadWhitelists($custom_whitelist = array())
{
$default_whitelist = (include RX_BASEDIR . 'common/defaults/whitelist.php');
$default_whitelist = (include \RX_BASEDIR . 'common/defaults/whitelist.php');
self::$_object_whitelist = array();
self::$_iframe_whitelist = array();

View file

@ -0,0 +1,311 @@
<?php
namespace Rhymix\Framework;
/**
* The formatter class.
*/
class Formatter
{
/**
* Options for text to HTML conversion.
*/
const TEXT_NEWLINE_AS_P = 1;
const TEXT_DOUBLE_NEWLINE_AS_P = 2;
/**
* Options for Markdown to HTML conversion.
*/
const MD_NEWLINE_AS_BR = 16;
const MD_ENABLE_EXTRA = 128;
/**
* Convert plain text to HTML.
*
* @param string $text
* @param int $options (optional)
* @return string
*/
public static function text2html($text, $options = 0)
{
// This option uses <p> instead of <br> to separate lines.
if ($options & self::TEXT_NEWLINE_AS_P)
{
$lines = array_map('trim', explode("\n", escape(trim($text))));
$result = '';
foreach ($lines as $line)
{
$result .= "<p>$line</p>\n";
}
return $result;
}
// This option uses <br> to separate lines and <p> to separate paragraphs.
if ($options & self::TEXT_DOUBLE_NEWLINE_AS_P)
{
$lines = preg_replace('!(<br />)+\s*$!', '', nl2br(escape(trim($text))));
$lines = preg_split('!(<br />\s*)+<br />!', $lines);
foreach ($lines as $line)
{
$result .= "<p>\n" . trim($line) . "\n</p>\n";
}
return $result;
}
// The default is to use <br> always.
return nl2br(escape(trim($text))) . "<br />\n";
}
/**
* Convert HTML to plain text.
*
* @param string $html
* @return string
*/
public static function html2text($html)
{
// Add line breaks after <br> and <p> tags.
$html = preg_replace('!<br[^>]*>\s*!i', "\n", $html);
$html = preg_replace('!<p\b[^>]*>\s*!i', '', $html);
$html = preg_replace('!</p[^>]*>\s*!i', "\n\n", $html);
// Encode links and images to preserve essential information.
$html = preg_replace_callback('!<a\b[^>]*href="([^>"]+)"[^>]*>([^<]*)</a>!i', function($matches) {
return trim($matches[2] . ' &lt;' . $matches[1] . '&gt;');
}, $html);
$html = preg_replace_callback('!<img\b[^>]*src="([^>"]+)"[^>]*>!i', function($matches) {
$title = preg_match('!title="([^>"]+)"!i', $matches[0], $m) ? $m[1] : null;
$title = $title ?: (preg_match('!alt="([^>"]+)"!i', $matches[0], $m) ? $m[1] : 'IMAGE');
return trim('[' . $title . '] &lt;' . $matches[1] . '&gt;');
}, $html);
// Strip all other HTML.
$text = html_entity_decode(strip_tags($html));
unset($html);
// Normalize whitespace and return.
$text = str_replace("\r\n", "\n", $text);
$text = preg_replace('/\n(?:\s*\n)+/', "\n\n", $text);
return trim($text) . "\n";
}
/**
* Convert Markdown to HTML.
*
* @param string $markdown
* @param int $options (optional)
* @return string
*/
public static function markdown2html($markdown, $options = 0)
{
if ($options & self::MD_NEWLINE_AS_BR)
{
$markdown = preg_replace('/(?<!\n)\n(?![\n\*\#\-])/', " \n", $markdown);
}
if ($options & self::MD_ENABLE_EXTRA)
{
$class_name = '\\Michelf\\MarkdownExtra';
}
else
{
$class_name = '\\Michelf\\Markdown';
}
$html = $class_name::defaultTransform($markdown);
return Filters\HTMLFilter::clean($html);
}
/**
* Convert HTML to Markdown.
*
* @param string $html
* @return string
*/
public static function html2markdown($html)
{
$converter = new \League\HTMLToMarkdown\HtmlConverter();
$converter->getConfig()->setOption('bold_style', '**');
$converter->getConfig()->setOption('italic_style', '_');
$converter->getConfig()->setOption('strip_tags', true);
return trim($converter->convert($html)) . "\n";
}
/**
* Convert BBCode to HTML.
*
* @param string $bbcode
* @return string
*/
public static function bbcode($bbcode)
{
$parser = new \JBBCode\Parser;
$parser->addCodeDefinitionSet(new \JBBCode\DefaultCodeDefinitionSet());
$builder = new \JBBCode\CodeDefinitionBuilder('quote', '<blockquote>{param}</blockquote>');
$parser->addCodeDefinition($builder->build());
$builder = new \JBBCode\CodeDefinitionBuilder('code', '<pre><code>{param}</code></pre>');
$builder->setParseContent(false);
$parser->addCodeDefinition($builder->build());
$parser->parse($bbcode);
$html = $parser->getAsHtml();
return Filters\HTMLFilter::clean($html);
}
/**
* Apply smart quotes and other stylistic enhancements to HTML.
*
* @param string $html
* @return string
*/
public static function applySmartQuotes($html)
{
return \Michelf\SmartyPants::defaultTransform($html, 'qbBdDiew');
}
/**
* Compile LESS into CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @param array $variables (optional)
* @parsm bool $minify (optional)
* @return bool
*/
public static function compileLESS($source_filename, $target_filename, $variables = array(), $minify = false)
{
// Get the cleaned and concatenated content.
$content = self::_concatenate($source_filename, $target_filename);
// Compile!
try
{
$less_compiler = new \lessc;
$less_compiler->setFormatter($minify ? 'compressed' : 'lessjs');
$less_compiler->setImportDir(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
if ($variables)
{
$less_compiler->setVariables($variables);
}
$content = '@charset "UTF-8";' . "\n" . $less_compiler->compile($content) . "\n";
$result = true;
}
catch (\Exception $e)
{
$content = '/*' . "\n" . 'Error while compiling LESS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
$result = false;
}
// Save the result to the target file.
Storage::write($target_filename, $content);
return $result;
}
/**
* Compile SCSS into CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @param array $variables (optional)
* @parsm bool $minify (optional)
* @return bool
*/
public static function compileSCSS($source_filename, $target_filename, $variables = array(), $minify = false)
{
// Get the cleaned and concatenated content.
$content = self::_concatenate($source_filename, $target_filename);
// Compile!
try
{
$scss_compiler = new \scssc;
$scss_compiler->setFormatter($minify ? 'scss_formatter_compressed' : 'scss_formatter');
$scss_compiler->setImportPaths(array(dirname(is_array($source_filename) ? array_first($source_filename) : $source_filename)));
if ($variables)
{
$scss_compiler->setVariables($variables);
}
$content = '@charset "UTF-8";' . "\n" . $scss_compiler->compile($content) . "\n";
$result = true;
}
catch (\Exception $e)
{
$content = '/*' . "\n" . 'Error while compiling SCSS:' . "\n" . $e->getMessage() . "\n" . '*/' . "\n";
$result = false;
}
// Save the result to the target file.
Storage::write($target_filename, $content);
return $result;
}
/**
* Minify CSS.
*
* @param string|array $source_filename
* @param string $target_filename
* @return bool
*/
public static function minifyCSS($source_filename, $target_filename)
{
$minifier = new \MatthiasMullie\Minify\CSS($source_filename);
$content = $minifier->execute($target_filename);
Storage::write($target_filename, $content);
return strlen($content) ? true : false;
}
/**
* Minify JS.
*
* @param string|array $source_filename
* @param string $target_filename
* @return bool
*/
public static function minifyJS($source_filename, $target_filename)
{
$minifier = new \MatthiasMullie\Minify\JS($source_filename);
$content = $minifier->execute($target_filename);
Storage::write($target_filename, $content);
return strlen($content) ? true : false;
}
/**
* CSS concatenation subroutine for compileLESS() and compileSCSS().
*
* @param string|array $source_filename
* @param string $target_filename
* @return string
*/
protected static function _concatenate($source_filename, $target_filename)
{
$result = '';
if (!is_array($source_filename))
{
$source_filename = array($source_filename);
}
foreach ($source_filename as $filename)
{
$content = utf8_clean(file_get_contents($filename));
$path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename);
$content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) {
$url = trim($matches[1], '\'"');
if (!strlen($url) || $url[0] === '/')
{
return $matches[0];
}
else
{
return 'url("' . escape_dqstr($path_converter->convert($url)) . '")';
}
}, $content);
unset($path_converter);
$result .= trim($content) . "\n\n";
}
return $result;
}
}

78
common/framework/i18n.php Normal file
View file

@ -0,0 +1,78 @@
<?php
namespace Rhymix\Framework;
/**
* The i18n (internationalization) class.
*/
class i18n
{
/**
* Constants for sorting.
*/
const SORT_CODE_2 = 2;
const SORT_CODE_3 = 3;
const SORT_CODE_NUMERIC = 4;
const SORT_CCTLD = 5;
const SORT_NAME_ENGLISH = 6;
const SORT_NAME_KOREAN = 7;
const SORT_NAME_NATIVE = 8;
/**
* Get the list of all countries.
*
* @param int $sort_by
* @return array
*/
public static function listCountries($sort_by = self::SORT_NAME_ENGLISH)
{
$countries = (include \RX_BASEDIR . 'common/defaults/countries.php');
$result = array();
foreach ($countries as $country)
{
$result[$country['iso_3166_1_alpha3']] = (object)$country;
}
switch ($sort_by)
{
case self::SORT_CODE_2:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_alpha2, $b->iso_3166_1_alpha2);
});
break;
case self::SORT_CODE_3:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_alpha3, $b->iso_3166_1_alpha3);
});
break;
case self::SORT_CODE_NUMERIC:
uasort($result, function($a, $b) {
return strcmp($a->iso_3166_1_numeric, $b->iso_3166_1_numeric);
});
break;
case self::SORT_CCTLD:
uasort($result, function($a, $b) {
return strcmp($a->cctld, $b->cctld);
});
break;
case self::SORT_NAME_ENGLISH:
uasort($result, function($a, $b) {
return strcmp($a->name_english, $b->name_english);
});
break;
case self::SORT_NAME_KOREAN:
uasort($result, function($a, $b) {
return strcmp($a->name_korean, $b->name_korean);
});
break;
case self::SORT_NAME_NATIVE:
uasort($result, function($a, $b) {
return strcmp($a->name_native, $b->name_native);
});
break;
}
return $result;
}
}

453
common/framework/korea.php Normal file
View file

@ -0,0 +1,453 @@
<?php
namespace Rhymix\Framework;
/**
* Class for validating Korea-specific information.
*/
class Korea
{
/**
* Format a phone number.
*
* @param string $num
* @return string
*/
public static function formatPhoneNumber($num)
{
// Remove all non-numbers.
$num = preg_replace('/[^0-9]/', '', $num);
// Remove the country code.
if (strncmp($num, '82', 2) === 0)
{
$num = substr($num, 2);
if (strncmp($num, '0', 1) !== 0)
{
$num = '0' . $num;
}
}
// Apply different format based on the number of digits.
switch (strlen($num))
{
case 8:
return substr($num, 0, 4) . '-' . substr($num, 4);
case 9:
return substr($num, 0, 2) . '-' . substr($num, 2, 3) . '-' . substr($num, 5);
case 10:
if (substr($num, 0, 2) === '02')
{
return substr($num, 0, 2) . '-' . substr($num, 2, 4) . '-' . substr($num, 6);
}
else
{
return substr($num, 0, 3) . '-' . substr($num, 3, 3) . '-' . substr($num, 6);
}
default:
if (substr($num, 0, 4) === '0303' || substr($num, 0, 3) === '050')
{
return substr($num, 0, 4) . '-' . substr($num, 4, 3) . '-' . substr($num, 7);
}
else
{
return substr($num, 0, 3) . '-' . substr($num, 3, 4) . '-' . substr($num, 7);
}
}
}
/**
* Check if a Korean phone number contains a valid area code and the correct number of digits.
*
* @param string $num
* @return bool
*/
public static function isValidPhoneNumber($num)
{
$num = str_replace('-', '', self::formatPhoneNumber($num));
if (preg_match('/^1[0-9]{7}$/', $num))
{
return true;
}
if (preg_match('/^02[2-9][0-9]{6,7}$/', $num))
{
return true;
}
if (preg_match('/^0[13-8][0-9][2-9][0-9]{6,7}$/', $num))
{
return true;
}
return false;
}
/**
* Check if a Korean phone number is a mobile phone number.
*
* @param string $num
* @return bool
*/
public static function isValidMobilePhoneNumber($num)
{
$num = str_replace('-', '', self::formatPhoneNumber($num));
$len = strlen($num);
return preg_match('/^01[016789][2-9][0-9]{6,7}$/', $num) ? true : false;
}
/**
* Check if the given string is a valid resident registration number (주민등록번호)
* or foreigner registration number (외국인등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidJuminNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Return false if the date of birth is in the future.
if (in_array((int)($code[6]), array(3, 4, 7, 8)) && intval(substr($code, 0, 6), 10) > date('ymd'))
{
return false;
}
// Calculate the checksum.
$sum = 0;
for ($i = 0; $i < 12; $i++)
{
$sum += $code[$i] * (($i % 8) + 2);
}
$checksum = (11 - ($sum % 11)) % 10;
if (in_array((int)($code[6]), array(1, 2, 3, 4, 9, 0)))
{
return $checksum === (int)($code[12]);
}
else
{
if (substr($code, 7, 2) % 2 !== 0)
{
return false;
}
else
{
return (($checksum + 2) % 10) === (int)($code[12]);
}
}
}
/**
* Check if the given string is a valid corporation registration number (법인등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidCorporationNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{6}-?[0-9]{7}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Calculate the checksum.
$sum = 0;
for ($i = 0; $i < 12; $i++)
{
$sum += $code[$i] * (($i % 2) + 1);
}
$checksum = (10 - ($sum % 10)) % 10;
// Check the 7th and 13th digits.
if ($code[6] !== '0')
{
return false;
}
return $checksum === (int)($code[12]);
}
/**
* Check if the given string is a valid business registration number (사업자등록번호).
*
* This method only checks the format.
* It does not check that the number is actually in use.
*
* @param string $code
* @return bool
*/
public static function isValidBusinessNumber($code)
{
// Return false if the format is obviously wrong.
if (!preg_match('/^[0-9]{3}-?[0-9]{2}-?[0-9]{5}$/', $code))
{
return false;
}
// Remove hyphen.
$code = str_replace('-', '', $code);
// Calculate the checksum.
$sum = 0;
$sum += $code[0] + ($code[1] * 3) + ($code[2] * 7);
$sum += $code[3] + ($code[4] * 3) + ($code[5] * 7);
$sum += $code[6] + ($code[7] * 3) + ($code[8] * 5);
$sum += floor(($code[8] * 5) / 10);
$checksum = (10 - ($sum % 10)) % 10;
// Check the last digit.
return $checksum === (int)($code[9]);
}
/**
* Check if the given IP address is Korean.
*
* This method may return incorrect results if the IP allocation databases
* (korea.ipv4.php, korea.ipv6.php) are out of date.
*
* @param string $ip
* @return bool
*/
public static function isKoreanIP($ip)
{
// Extract the IPv4 address from an "IPv4-mapped IPv6" address.
if (preg_match('/::ffff:(?:0+:)?([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/', $ip, $matches)) $ip = $matches[1];
// Return false if the IP address is not in the right format.
if (!filter_var($ip, \FILTER_VALIDATE_IP)) return false;
// Check IPv4.
if (filter_var($ip, \FILTER_VALIDATE_IP, array('flags' => \FILTER_FLAG_IPV4)))
{
// Convert to integer.
$ipnum = sprintf('%u', ip2long($ip));
// Treat local addresses as Korean.
if ($ipnum >= 167772160 && $ipnum <= 184549375) return true; // 10.0.0.0/8
if ($ipnum >= 2130706432 && $ipnum <= 2147483647) return true; // 127.0.0.0/8
if ($ipnum >= 3232235520 && $ipnum <= 3232301055) return true; // 192.168.0.0/16
if ($ipnum >= 2886729728 && $ipnum <= 2887778303) return true; // 172.16.0.0/20
// Check the IPv4 allocation database.
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv4.php');
foreach ($ranges as $range)
{
if ($ipnum >= $range[0] && $ipnum <= $range[1]) return true;
}
return false;
}
// Check IPv6.
elseif (function_exists('inet_pton'))
{
// Convert to hexadecimal format.
$ipbin = strtolower(bin2hex(inet_pton($ip)));
// Treat local addresses as Korean.
if ($ipbin == '00000000000000000000000000000001') return true; // ::1
if (preg_match('/^f(?:[cd]|e80{13})/', $ipbin)) return true; // fc00::/8, fd00::/8, fe80::/64
// Check the IPv6 allocation database.
$ranges = (include \RX_BASEDIR . 'common/defaults/korea.ipv6.php');
foreach ($ranges as $range)
{
if (strncmp($ipbin, $range[0], 16) >= 0 && strncmp($ipbin, $range[1], 16) <= 0) return true;
}
return false;
}
return false;
}
/**
* Check if the given email address is hosted by a Korean portal site.
*
* This can be used to tell which recipients may subscribe to the KISA RBL (kisarbl.or.kr).
* If the domain is not found, this method returns false.
*
* @param string $domain
* @param bool $clear_cache (optional)
* @return bool
*/
public static function isKoreanEmailAddress($email_address, $clear_cache = false)
{
// Clear the cache if requested.
if ($clear_cache)
{
self::$_domain_cache = array();
}
// Get the domain from the email address.
if ($pos = strpos($email_address, '@'))
{
$domain = substr($email_address, $pos + 1);
}
else
{
$domain = $email_address;
}
$domain = rtrim(strtolower($domain), '.');
// Return cached result if available.
if (array_key_exists($domain, self::$_domain_cache))
{
return self::$_domain_cache[$domain];
}
// Shortcut for known domains.
if (in_array($domain, self::$known_korean))
{
return self::$_domain_cache[$domain] = true;
}
if (in_array($domain, self::$known_foreign))
{
return self::$_domain_cache[$domain] = false;
}
// For unknown domains, check the MX record.
$mx = self::_getDNSRecords($domain, \DNS_MX);
$i = 0;
foreach ($mx as $mx)
{
$mx = rtrim($mx, '.');
foreach (self::$known_korean as $portal)
{
if ($mx === $portal || ends_with('.' . $portal, $mx))
{
return self::$_domain_cache[$domain] = true;
}
}
foreach (self::$known_foreign as $portal)
{
if ($mx === $portal || ends_with('.' . $portal, $mx))
{
return self::$_domain_cache[$domain] = false;
}
}
foreach (self::_getDNSRecords($domain, \DNS_A) as $mx_ip)
{
return self::$_domain_cache[$domain] = self::isKoreanIP($mx_ip);
}
if (++$i > 2)
{
break;
}
}
return self::$_domain_cache[$domain] = false;
}
/**
* Get the DNS records of a domain.
*
* @param string $domain
* @param int $type
* @return array
*/
protected static function _getDNSRecords($domain, $type)
{
$records = dns_get_record($domain, $type);
if (!$records)
{
return array();
}
$result = array();
foreach ($records as $record)
{
if (isset($record['pri']) && isset($record['target']))
{
$result[intval($record['pri'])] = $record['target'];
}
elseif (isset($record['target']))
{
$result[] = $record['target'];
}
elseif (isset($record['ip']) || isset($record['ipv6']))
{
$result[] = isset($record['ip']) ? $record['ip'] : $record['ipv6'];
}
elseif (isset($record['txt']))
{
$result[] = $record['txt'];
}
}
ksort($result);
return $result;
}
/**
* Prevent multiple lookups for the same domain.
*/
protected static $_domain_cache = array();
/**
* Domains known to be Korean and subscribed to the KISA RBL.
*/
public static $known_korean = array(
'hanmail.net',
'hanmail2.net',
'daum.net',
'paran.com',
'tistory.com',
'naver.com',
'navercorp.com',
'nate.com',
'cyworld.com',
'dreamwiz.com',
'korea.com',
'dreamx.com',
'chol.com',
'chollian.net',
'hanmir.com',
'hitel.com',
'freechal.com',
'empas.com',
'empal.com',
'hanafos.com',
);
/**
* Domains known to be foreign.
*/
public static $known_foreign = array(
'gmail.com',
'googlemail.com',
'google.com',
'yahoo.com',
'yahoo.co.kr',
'hotmail.com',
'hotmail.co.kr',
'live.com',
'outlook.com',
'msn.com',
'me.com',
'mac.com',
'icloud.com',
'facebook.com',
'aol.com',
'gmx.com',
'mail.com',
'fastmail.com',
'fastmail.fm',
'runbox.com',
'inbox.com',
'lycos.com',
'zoho.com',
);
}

View file

@ -75,19 +75,19 @@ class Lang
if ($name === 'common')
{
$this->loadDirectory(RX_BASEDIR . 'common/lang', 'common');
$this->loadDirectory(\RX_BASEDIR . 'common/lang', 'common');
}
elseif (file_exists(RX_BASEDIR . "plugins/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "plugins/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "plugins/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "plugins/$name/lang", $name);
}
elseif (file_exists(RX_BASEDIR . "modules/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "modules/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "modules/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "modules/$name/lang", $name);
}
elseif (file_exists(RX_BASEDIR . "addons/$name/lang"))
elseif (file_exists(\RX_BASEDIR . "addons/$name/lang"))
{
$this->loadDirectory(RX_BASEDIR . "addons/$name/lang", $name);
$this->loadDirectory(\RX_BASEDIR . "addons/$name/lang", $name);
}
}
@ -163,7 +163,7 @@ class Lang
*/
public static function getSupportedList()
{
return (include RX_BASEDIR . 'common/defaults/lang.php');
return (include \RX_BASEDIR . 'common/defaults/lang.php');
}
/**

152
common/framework/mime.php Normal file
View file

@ -0,0 +1,152 @@
<?php
namespace Rhymix\Framework;
/**
* The MIME class.
*/
class MIME
{
/**
* Get the MIME type for the given extension.
*
* @param string $extension
* @return string
*/
public static function getTypeByExtension($extension)
{
$extension = strtolower($extension);
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
}
/**
* Get the MIME type for the given filename.
*
* @param string $filename
* @return string
*/
public static function getTypeByFilename($filename)
{
$extension = strrchr($filename, '.');
if ($extension === false) return self::$_default;
$extension = strtolower(substr($extension, 1));
return array_key_exists($extension, self::$_types) ? self::$_types[$extension] : self::$_default;
}
/**
* Get the most common extension for the given MIME type.
*
* @param string $type
* @return string|false
*/
public static function getExtensionByType($type)
{
foreach (self::$_types as $extension => $mime)
{
if (!strncasecmp($type, $mime, strlen($type))) return $extension;
}
return false;
}
/**
* The default MIME type for unknown extensions.
*/
protected static $_default = 'application/octet-stream';
/**
* The list of known MIME types.
*/
protected static $_types = array(
// Text-based document formats.
'html' => 'text/html',
'htm' => 'text/html',
'shtml' => 'text/html',
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => 'text/plain',
'rtf' => 'text/rtf',
'xml' => 'text/xml',
'xsl' => 'text/xml',
'css' => 'text/css',
'csv' => 'text/csv',
// Binary document formats.
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odb' => 'application/vnd.oasis.opendocument.database',
'pdf' => 'application/pdf',
// Images.
'bmp' => 'image/bmp',
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpe' => 'image/jpeg',
'png' => 'image/png',
'svg' => 'image/svg+xml',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'ico' => 'image/vnd.microsoft.icon',
// Audio.
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'aif' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'ra' => 'audio/x-realaudio',
'wav' => 'audio/x-wav',
'ogg' => 'audio/ogg',
// Video.
'avi' => 'video/x-msvideo',
'flv' => 'video/x-flv',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'mp4' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'movie' => 'video/x-sgi-movie',
'rv' => 'video/vnd.rn-realvideo',
'dvi' => 'application/x-dvi',
// Other multimedia file formats.
'psd' => 'application/x-photoshop',
'swf' => 'application/x-shockwave-flash',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'mif' => 'application/vnd.mif',
'xul' => 'application/vnd.mozilla.xul+xml',
// Source code formats.
'phps' => 'application/x-httpd-php-source',
'js' => 'application/x-javascript',
// Archives.
'bz2' => 'application/x-bzip',
'gz' => 'application/x-gzip',
'tar' => 'application/x-tar',
'tgz' => 'application/x-tar',
'gtar' => 'application/x-gtar',
'rar' => 'application/x-rar-compressed',
'zip' => 'application/x-zip',
// RFC822 email message.
'eml' => 'message/rfc822',
);
}

View file

@ -0,0 +1,153 @@
<?php
namespace Rhymix\Framework;
/**
* The pagination class.
*/
class Pagination
{
/**
* Count style constants.
*/
const COUNT_STYLE_NORMAL = 1;
const COUNT_STYLE_CONTINUOUS = 2;
/**
* Calculate the number of pages.
*
* @param int $total_items
* @param int $items_per_page
* @param int $minimum (optional)
* @return int
*/
public static function countPages($total_items, $items_per_page, $minimum = 1)
{
if (!$items_per_page)
{
return (int)$minimum;
}
else
{
return (int)max($minimum, ceil($total_items / $items_per_page));
}
}
/**
* Create HTML for pagination.
*
* @param string $base_url ($PAGE will be replaced with the page number)
* @param int $current_page
* @param int $total_pages
* @param int $count (optional)
*/
public static function createLinks($base_url, $total_pages, $current_page, $count = 10, $count_style = self::COUNT_STYLE_NORMAL)
{
// Only integers are allowed here.
$current_page = (int)$current_page;
$total_pages = (int)$total_pages;
$count = (int)$count;
// Determine the range of pages to show.
if ($count_style === self::COUNT_STYLE_NORMAL)
{
$last_shown = ceil($current_page / $count) * $count;
$first_shown = max(1, $last_shown - $count + 1);
if ($last_shown > $total_pages)
{
$last_shown = $total_pages;
}
}
else
{
$first_shown = $current_page - floor(($count - 1) / 2);
if ($first_shown < 1)
{
$first_shown = 1;
}
$last_shown = $first_shown + $count - 1;
if ($last_shown > $total_pages)
{
$last_shown = $total_pages;
$first_shown = max(1, $last_shown - $count + 1);
}
}
// Open the <div> tag.
$return = array('<div class="pagination">');
// Compose the link to the first page.
if ($first_shown > 1)
{
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', 1, $base_url);
}
else
{
$target_url = $base_url . 1;
}
$return[] = self::_composeLink($target_url, '<span class="arrow">&laquo;</span> <span class="page_number first_page">1</span>');
$return[] = '<span class="ellipsis">...</span>';
}
// Compose links for each page.
for ($page = $first_shown; $page <= $last_shown; $page++)
{
if ($page == $current_page)
{
$opening_span = '<span class="page_number current_page">';
}
else
{
$opening_span = '<span class="page_number">';
}
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', $page, $base_url);
}
else
{
$target_url = $base_url . $page;
}
$return[] = self::_composeLink($target_url, $opening_span . $page . '</span>');
}
// Compose the link to the last page.
if ($last_shown < $total_pages)
{
if (strpos($base_url, '$PAGE') !== false)
{
$target_url = str_replace('$PAGE', $total_pages, $base_url);
}
else
{
$target_url = $base_url . $total_pages;
}
$return[] = '<span class="ellipsis">...</span>';
$return[] = self::_composeLink($target_url, '<span class="page_number last_page">' . $total_pages . '</span> <span class="arrow">&raquo;</span>');
}
// Close the <div> tag.
$return[] = '</div>';
// Return the completed HTML.
return implode(' ', $return);
}
/**
* Link creation subroutine.
*
* @param string $target_url
* @param string $content
* @return string
*/
protected static function _composeLink($target_url, $content)
{
return '<a href="' . escape($target_url) . '">' . $content . '</a>';
}
}

View file

@ -19,9 +19,9 @@ class ConfigParser
public static function convert()
{
// Load DB info file.
if (file_exists(RX_BASEDIR . Config::$old_db_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_db_config_filename))
{
include RX_BASEDIR . Config::$old_db_config_filename;
include \RX_BASEDIR . Config::$old_db_config_filename;
}
else
{
@ -29,16 +29,16 @@ class ConfigParser
}
// Load FTP info file.
if (file_exists(RX_BASEDIR . Config::$old_ftp_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_ftp_config_filename))
{
include RX_BASEDIR . Config::$old_ftp_config_filename;
include \RX_BASEDIR . Config::$old_ftp_config_filename;
}
// Load selected language file.
if (file_exists(RX_BASEDIR . Config::$old_lang_config_filename))
if (file_exists(\RX_BASEDIR . Config::$old_lang_config_filename))
{
$lang_selected = array();
$lang_selected_raw = file_get_contents(RX_BASEDIR . Config::$old_lang_config_filename);
$lang_selected_raw = file_get_contents(\RX_BASEDIR . Config::$old_lang_config_filename);
$lang_selected_raw = array_map('trim', explode("\n", $lang_selected_raw));
foreach ($lang_selected_raw as $lang_selected_item)
{
@ -59,7 +59,7 @@ class ConfigParser
}
// Load defaults for the new configuration.
$config = (include RX_BASEDIR . Config::$default_config_filename);
$config = (include \RX_BASEDIR . Config::$default_config_filename);
// Convert database configuration.
if (!isset($db_info->master_db))
@ -184,7 +184,7 @@ class ConfigParser
{
$default_url = \Context::decodeIdna($default_url);
}
$config['url']['default'] = $default_url ?: (RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
$config['url']['default'] = $default_url ?: (\RX_SSL ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . \RX_BASEURL;
$config['url']['http_port'] = $db_info->http_port ?: null;
$config['url']['https_port'] = $db_info->https_port ?: null;
$config['url']['ssl'] = $db_info->use_ssl ?: 'none';

View file

@ -3,6 +3,7 @@
namespace Rhymix\Framework\Parsers;
use Rhymix\Framework\Lang;
use Rhymix\Framework\Storage;
/**
* Lang parser class for XE compatibility.
@ -32,7 +33,7 @@ class LangParser
foreach ($files as $filename)
{
$new_filename = preg_replace('/\.lang\.php$/', '.php', str_replace('jp.lang', 'ja.lang', $filename));
\FileHandler::rename($filename, $new_filename);
Storage::move($filename, $new_filename);
}
}
}
@ -49,7 +50,7 @@ class LangParser
// Check if the cache file already exists.
if ($output_filename === null)
{
$output_filename = RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
$output_filename = \RX_BASEDIR . 'files/cache/lang/' . md5($filename) . '.' . $language . '.php';
if (file_exists($output_filename) && filemtime($output_filename) > filemtime($filename))
{
return $output_filename;
@ -60,7 +61,7 @@ class LangParser
$xml = @simplexml_load_file($filename);
if ($xml === false)
{
\FileHandler::writeFile($output_filename, '');
Storage::write($output_filename, '');
return false;
}
@ -95,7 +96,7 @@ class LangParser
$buff .= '$lang->' . $key . ' = ' . var_export($value, true) . ";\n";
}
}
\FileHandler::writeFile($output_filename, $buff);
Storage::write($output_filename, $buff);
return $output_filename;
}

View file

@ -7,5 +7,476 @@ namespace Rhymix\Framework;
*/
class Storage
{
/**
* Check if a path really exists.
*
* @param string $path
* @return bool
*/
public static function exists($path)
{
$path = rtrim($path, '/\\');
clearstatcache(true, $path);
return @file_exists($path);
}
/**
* Check if the given path is a file.
*
* @param string $path
* @return bool
*/
public static function isFile($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_file($path);
}
/**
* Check if the given path is an empty file.
*
* @param string $path
* @return bool
*/
public static function isEmptyFile($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_file($path) && (@filesize($path) == 0);
}
/**
* Check if the given path is a directory.
*
* @param string $path
* @return bool
*/
public static function isDirectory($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_dir($path);
}
/**
* Check if the given path is an empty directory.
*
* @param string $path
* @return bool
*/
public static function isEmptyDirectory($path)
{
$path = rtrim($path, '/\\');
if (!self::isDirectory($path))
{
return false;
}
$iterator = new \FilesystemIterator($path, \FilesystemIterator::SKIP_DOTS);
return (iterator_count($iterator)) === 0 ? true : false;
}
/**
* Check if the given path is a symbolic link.
*
* @param string $path
* @return bool
*/
public static function isSymlink($path)
{
$path = rtrim($path, '/\\');
return @is_link($path);
}
/**
* Check if the given path is a valid symbolic link.
*
* @param string $path
* @return bool
*/
public static function isValidSymlink($path)
{
$path = rtrim($path, '/\\');
return @is_link($path) && ($target = @readlink($path)) !== false && self::exists($target);
}
/**
* Check if the given path is readable.
*
* @param string $path
* @return bool
*/
public static function isReadable($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_readable($path);
}
/**
* Check if the given path is writable.
*
* @param string $path
* @return bool
*/
public static function isWritable($path)
{
$path = rtrim($path, '/\\');
return @self::exists($path) && @is_writable($path);
}
/**
* Get the size of a file.
*
* This method returns the size of a file, or false on error.
*
* @param string $filename
* @return int|false
*/
public static function getSize($filename)
{
$filename = rtrim($filename, '/\\');
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
{
return @filesize($filename);
}
else
{
return false;
}
}
/**
* Get the content of a file.
*
* This method returns the content if it exists and is readable.
* Otherwise, it returns false.
*
* @param string $filename
* @return string|false
*/
public static function read($filename)
{
$filename = rtrim($filename, '/\\');
if (self::exists($filename) && @is_file($filename) && @is_readable($filename))
{
return @file_get_contents($filename);
}
else
{
return false;
}
}
/**
* Write $content to a file.
*
* This method returns true on success and false on failure.
*
* @param string $filename
* @param string $content
* @param string $mode (optional)
* @param int $perms (optional)
* @return string|false
*/
public static function write($filename, $content, $mode = 'w', $perms = null)
{
$filename = rtrim($filename, '/\\');
$destination_dir = dirname($filename);
if (!self::exists($destination_dir))
{
$mkdir_success = self::createDirectory($destination_dir);
if (!$mkdir_success)
{
return false;
}
}
$flags = ($mode === 'a') ? (\FILE_APPEND | \LOCK_EX) : (\LOCK_EX);
$write_success = @file_put_contents($filename, $content, $flags);
$result = ($write_success === strlen($content)) ? true : false;
@chmod($filename, ($perms === null ? (0666 & ~umask()) : $perms));
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $result;
}
/**
* Copy $source to $destination.
*
* This method returns true on success and false on failure.
* If the destination permissions are not given, they will be copied from the source.
*
* @param string $source
* @param string $destination
* @param int $destination_perms
* @return bool
*/
public static function copy($source, $destination, $destination_perms = null)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::exists($source))
{
return false;
}
$destination_dir = dirname($destination);
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
{
return false;
}
elseif (self::isDirectory($destination))
{
$destination = $destination . '/' . basename($source);
}
$copy_success = @copy($source, $destination);
if (!$copy_success)
{
return false;
}
if ($destination_perms === null)
{
@chmod($destination, 0777 & @fileperms($source));
}
else
{
@chmod($destination, $destination_perms);
}
return true;
}
/**
* Move $source to $destination.
*
* This method returns true on success and false on failure.
*
* @param string $source
* @param string $destination
* @return bool
*/
public static function move($source, $destination)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::exists($source))
{
return false;
}
$destination_dir = dirname($destination);
if (!self::exists($destination_dir) && !self::createDirectory($destination_dir))
{
return false;
}
elseif (self::isDirectory($destination))
{
$destination = $destination . '/' . basename($source);
}
$result = @rename($source, $destination);
if (function_exists('opcache_invalidate') && substr($source, -4) === '.php')
{
@opcache_invalidate($source, true);
}
return $result;
}
/**
* Delete a file.
*
* This method returns true if the file exists and has been successfully
* deleted, and false on any kind of failure.
*
* @param string $filename
* @return bool
*/
public static function delete($filename)
{
$filename = rtrim($filename, '/\\');
$result = @self::exists($filename) && @is_file($filename) && @unlink($filename);
if (function_exists('opcache_invalidate') && substr($filename, -4) === '.php')
{
@opcache_invalidate($filename, true);
}
return $result;
}
/**
* Create a directory.
*
* @param string $dirname
* @return bool
*/
public static function createDirectory($dirname, $mode = null)
{
$dirname = rtrim($dirname, '/\\');
if ($mode === null)
{
$mode = 0777 & ~umask();
}
return @mkdir($dirname, $mode, true);
}
/**
* Read the list of files in a directory.
*
* @param string $dirname
* @param bool $full_path (optional)
* @param bool $skip_dotfiles (optional)
* @param bool $skip_subdirs (optional)
* @return array|false
*/
public static function readDirectory($dirname, $full_path = true, $skip_dotfiles = true, $skip_subdirs = true)
{
$dirname = rtrim($dirname, '/\\');
if (!self::isDirectory($dirname))
{
return false;
}
try
{
$iterator = new \FilesystemIterator($dirname, \FilesystemIterator::CURRENT_AS_PATHNAME);
}
catch (\UnexpectedValueException $e)
{
return false;
}
$result = array();
foreach ($iterator as $fileinfo)
{
if (!$skip_subdirs || !is_dir($fileinfo))
{
$basename = basename($fileinfo);
if (!$skip_dotfiles || $basename[0] !== '.')
{
$result[] = $full_path ? $fileinfo : $basename;
}
}
}
sort($result);
return $result;
}
/**
* Copy a directory recursively.
*
* @param string $source
* @param string $destination
* @param string $exclude_regexp (optional)
* @return bool
*/
public static function copyDirectory($source, $destination, $exclude_regexp = null)
{
$source = rtrim($source, '/\\');
$destination = rtrim($destination, '/\\');
if (!self::isDirectory($source))
{
return false;
}
if (!self::isDirectory($destination) && !self::createDirectory($destination))
{
return false;
}
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, $rdi_options), $rii_options);
foreach ($iterator as $path)
{
$path_source = $path->getPathname();
if (strpos($path_source, $source) !== 0)
{
continue;
}
if ($exclude_regexp && preg_match($exclude_regexp, $path_source))
{
continue;
}
$path_destination = $destination . substr($path_source, strlen($source));
if ($path->isDir())
{
$status = self::isDirectory($path_destination) || self::createDirectory($path_destination, $path->getPerms());
if (!$status)
{
return false;
}
}
else
{
$status = self::copy($path_source, $path_destination, $path->getPerms());
if (!$status)
{
return false;
}
}
}
return true;
}
/**
* Move a directory.
*
* @param string $source
* @param string $destination
* @return bool
*/
public static function moveDirectory($source, $destination)
{
return self::move($source, $destination);
}
/**
* Delete a directory recursively.
*
* @param string $dirname
* @param bool $delete_self (optional)
* @return bool
*/
public static function deleteDirectory($dirname, $delete_self = true)
{
$dirname = rtrim($dirname, '/\\');
if (!self::isDirectory($dirname))
{
return false;
}
$rdi_options = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::SKIP_DOTS;
$rii_options = \RecursiveIteratorIterator::CHILD_FIRST;
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dirname, $rdi_options), $rii_options);
foreach ($iterator as $path)
{
if ($path->isDir())
{
if (!@rmdir($path->getPathname()))
{
return false;
}
}
else
{
if (!@unlink($path->getPathname()))
{
return false;
}
}
}
if ($delete_self)
{
return @rmdir($dirname);
}
else
{
return true;
}
}
}

109
common/framework/timer.php Normal file
View file

@ -0,0 +1,109 @@
<?php
namespace Rhymix\Framework;
/**
* The timer class.
*/
class Timer
{
/**
* Timestamps are stored here.
*/
protected static $_timestamps = array();
/**
* Start a timer.
*
* This method returns the current microtime.
*
* @param string $name (optional)
* @return float
*/
public static function start($name = null)
{
$timestamp = microtime(true);
if ($name === null)
{
$name = 'anon-timer-' . $timestamp;
}
self::$_timestamps[$name] = $timestamp;
return $timestamp;
}
/**
* Stop a timer and return the elapsed time.
*
* If the name is not given, the most recently started timer will be stopped.
* If no timer has been started, this method returns false.
*
* @param string $name (optional)
* @return float|false
*/
public static function stop($name = null)
{
$timestamp = microtime(true);
$started_timestamp = 0;
if ($name === null)
{
if (count(self::$_timestamps))
{
$started_timestamp = array_pop(self::$_timestamps);
}
else
{
return false;
}
}
elseif (array_key_exists($name, self::$_timestamps))
{
$started_timestamp = self::$_timestamps[$name];
unset(self::$_timestamps[$name]);
}
else
{
return false;
}
return $timestamp - $started_timestamp;
}
/**
* Stop a timer and return the elapsed time in a human-readable format.
*
* If the name is not given, the most recently started timer will be stopped.
* If no timer has been started, this method returns false.
*
* @param string $name (optional)
* @return string|false
*/
public static function stopFormat($name = null)
{
$result = self::stop($name);
if ($result === false) return $result;
return number_format($result * 1000, 1, '.', ',') . 'ms';
}
/**
* This method returns how much time has elapsed since Rhymix startup.
*
* @return float
*/
public static function sinceStartup()
{
return microtime(true) - \RX_MICROTIME;
}
/**
* This method returns how much time has elapsed since startup in a human-readable format.
*
* @return string
*/
public static function sinceStartupFormat()
{
return number_format((microtime(true) - \RX_MICROTIME) * 1000, 1, '.', ',') . 'ms';
}
}

240
common/framework/ua.php Normal file
View file

@ -0,0 +1,240 @@
<?php
namespace Rhymix\Framework;
/**
* The user-agent class.
*/
class UA
{
/**
* Cache to prevent multiple lookups.
*/
protected static $_mobile_cache = array();
protected static $_tablet_cache = array();
protected static $_robot_cache = array();
/**
* Check whether the current visitor is using a mobile device.
*
* @param string $ua (optional)
* @return bool
*/
public static function isMobile($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
if ($ua === null)
{
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
$using_header = true;
}
else
{
$using_header = false;
}
// If the User-Agent header is missing, it's probably not a mobile browser.
if (is_null($ua))
{
return false;
}
// Look for headers that are only used in mobile browsers.
if ($using_header && (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA'])))
{
return true;
}
// Look up the cache.
if (isset(self::$_mobile_cache[$ua]))
{
return self::$_mobile_cache[$ua];
}
// Look for the 'mobile' keyword and common mobile platform names.
if (preg_match('/android|ip(hone|ad|od)|blackberry|nokia|palm|mobile/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// Look for common non-mobile OS names.
if (preg_match('/windows|linux|os [x9]|bsd/i', $ua))
{
return self::$_mobile_cache[$ua] = false;
}
// Look for other platform, manufacturer, and device names that are known to be mobile.
if (preg_match('/kindle|opera (mini|mobi)|polaris|netfront|fennec|motorola|symbianos|webos/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
if (preg_match('/s[pgc]h-|lgtelecom|sonyericsson|alcatel|vodafone|maemo|minimo|bada/i', $ua))
{
return self::$_mobile_cache[$ua] = true;
}
// If we're here, it's probably not a mobile device.
return self::$_mobile_cache[$ua] = false;
}
/**
* Check whether the current visitor is using a tablet.
*
* @param string $ua (optional)
* @return bool
*/
public static function isTablet($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a tablet.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_tablet_cache[$ua]))
{
return self::$_tablet_cache[$ua];
}
// Check if the user-agent is mobile.
if (!self::isMobile($ua))
{
return self::$_tablet_cache[$ua] = false;
}
// Check for Android tablets without the 'mobile' keyword.
if (stripos($ua, 'android') !== false && stripos($ua, 'mobile') === false)
{
return self::$_tablet_cache[$ua] = true;
}
// Check for common tablet identifiers.
if (preg_match('/tablet|pad\b|tab\b|\bgt-\d+|kindle|nook|playbook|webos|xoom/i', $ua))
{
return self::$_tablet_cache[$ua] = true;
}
// If we're here, it's probably not a tablet.
return self::$_tablet_cache[$ua] = false;
}
/**
* Check whether the current visitor is a robot.
*
* @param string $ua (optional)
* @return bool
*/
public static function isRobot($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// If the User-Agent header is missing, it's probably not a robot.
if (is_null($ua))
{
return false;
}
// Look up the cache.
if (isset(self::$_robot_cache[$ua]))
{
return self::$_robot_cache[$ua];
}
// Look for common search engine names and the 'bot' keyword.
if (preg_match('/bot|slurp|facebook(externalhit|scraper)|ia_archiver|ask jeeves|teoma|baidu|daumoa|lycos|pingdom/i', $ua))
{
return self::$_robot_cache[$ua] = true;
}
// If we're here, it's probably not a robot.
return self::$_robot_cache[$ua] = false;
}
/**
* This method parses the User-Agent string to guess what kind of browser it is.
*
* @param string $ua (optional)
* @return object
*/
public static function getBrowserInfo($ua = null)
{
// Get the User-Agent header if the caller did not specify $ua.
$ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null);
// Initialize the result.
$result = (object)array(
'browser' => null,
'version' => null,
'os' => null,
'is_mobile' => null,
'is_tablet' => null,
);
if (is_null($ua))
{
return $result;
}
// Try to guess the OS.
if (preg_match('#(Windows|Android|Linux|iOS|OS X|Macintosh)#i', $ua, $matches))
{
if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false)
{
$matches[1] = 'Android';
}
if ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false)
{
$matches[1] = 'OS X';
}
$result->os = $matches[1];
}
// Fill in miscellaneous fields.
$result->is_mobile = self::isMobile($ua);
$result->is_tablet = self::isTablet($ua);
// Try to match some of the most common browsers.
if (preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches) && strpos($ua, 'Chrome') === false)
{
$result->browser = 'Android';
$result->version = $matches[1];
return $result;
}
if (preg_match('#Edge/([0-9]+\\.)#', $ua, $matches))
{
$result->browser = 'Edge';
$result->version = $matches[1] . '0';
return $result;
}
if (preg_match('#Trident/([0-9]+)\\.[0-9]+#', $ua, $matches))
{
$result->browser = 'IE';
$result->version = ($matches[1] + 4) . '.0';
return $result;
}
if (preg_match('#(MSIE|Chrome|Firefox|Safari)[ /:]([0-9]+\\.[0-9]+)#', $ua, $matches))
{
$result->browser = $matches[1] === 'MSIE' ? 'IE' : $matches[1];
$result->version = $matches[2];
return $result;
}
if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Opera';
$result->version = $matches[1];
return $result;
}
if (preg_match('#(?:Konqueror|KHTML)/([0-9]+\\.[0-9]+)$#', $ua, $matches))
{
$result->browser = 'Konqueror';
$result->version = $matches[1];
return $result;
}
return $result;
}
}

View file

@ -519,6 +519,27 @@ function utf8_check($str)
}
}
/**
* Remove BOM and invalid UTF-8 sequences from file content.
*
* @param string $str
* @return string
*/
function utf8_clean($str)
{
if (strlen($str) >= 3 && substr($str, 0, 3) === "\xEF\xBB\xBF")
{
$str = substr($str, 3);
}
if (!utf8_check($str))
{
$str = @iconv('UTF-8', 'UTF-8//IGNORE', $str);
}
return $str;
}
/**
* Encode UTF-8 characters outside of the Basic Multilingual Plane in the &#xxxxxx format.
* This allows emoticons and other characters to be stored in MySQL without utf8mb4 support.

View file

@ -999,8 +999,7 @@ function json_encode2($data)
*/
function isCrawler($agent = NULL)
{
$agent = $agent ?: $_SERVER['HTTP_USER_AGENT'];
return (bool)preg_match('@bot|crawl|sp[iy]der|https?://|google|yahoo|slurp|yeti|daum|teoma|fish|hanrss|facebook|yandex|infoseek|askjeeves|stackrambler@i', $agent);
return Rhymix\Framework\UA::isRobot($agent);
}
/**

View file

@ -1,39 +1,44 @@
{
"name": "rhymix/rhymix",
"description": "Rhymix",
"homepage": "https://www.rhymix.org",
"license": "GPL-2.0+",
"type": "project",
"authors": [
{ "name": "Rhymix Developers and Contributors", "email": "devops@rhymix.org" },
{ "name": "NAVER", "email": "developers@xpressengine.com" }
],
"require": {
"php": ">=5.3.3",
"ext-curl": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-mcrypt": "*",
"ext-openssl": "*",
"ext-pcre": "*",
"ext-xml": "*",
"defuse/php-encryption": "1.2.1",
"ezyang/htmlpurifier": "4.7.*",
"firephp/firephp-core": "0.4.0",
"hautelook/phpass": "0.3.*",
"matthiasmullie/minify": "1.3.*",
"michelf/php-markdown": "1.5.*",
"rmccue/requests": "1.6.*",
"sunra/php-simple-html-dom-parser": "1.5.*",
"name": "rhymix/rhymix",
"description": "Rhymix",
"homepage": "https://www.rhymix.org",
"license": "GPL-2.0+",
"type": "project",
"authors": [
{ "name": "Rhymix Developers and Contributors", "email": "devops@rhymix.org" },
{ "name": "NAVER", "email": "developers@xpressengine.com" }
],
"require": {
"php": ">=5.3.3",
"ext-curl": "*",
"ext-gd": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-mcrypt": "*",
"ext-openssl": "*",
"ext-pcre": "*",
"ext-xml": "*",
"defuse/php-encryption": "1.2.1",
"ezyang/htmlpurifier": "4.7.*",
"firephp/firephp-core": "0.4.0",
"hautelook/phpass": "0.3.*",
"jbbcode/jbbcode": "1.3.*",
"leafo/lessphp": "0.5.*",
"leafo/scssphp": "0.4.*",
"league/html-to-markdown": "4.2.*",
"matthiasmullie/minify": "1.3.*",
"michelf/php-markdown": "1.6.*",
"michelf/php-smartypants": "1.6.0-beta1",
"rmccue/requests": "1.6.*",
"sunra/php-simple-html-dom-parser": "1.5.*",
"swiftmailer/swiftmailer": "5.4.*",
"true/punycode": "2.*"
},
"require-dev": {
"php": ">=5.4.0",
"codeception/codeception": "2.1.*",
"codeception/verify": "0.3.*",
"codeception/specify": "0.4.*"
}
},
"require-dev": {
"php": ">=5.4.0",
"codeception/codeception": "2.1.*",
"codeception/verify": "0.3.*",
"codeception/specify": "0.4.*"
}
}

288
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "9b062f27815a9b2ef1e700b4664ee864",
"content-hash": "69345a9112733f99c09fa298ba72fa22",
"hash": "61ea4cb999dbb61cd01ba1a4ede5ead6",
"content-hash": "ec214228f19d828ce0ea5feeb0a72aba",
"packages": [
{
"name": "defuse/php-encryption",
@ -176,6 +176,213 @@
],
"time": "2012-08-31 00:00:00"
},
{
"name": "jbbcode/jbbcode",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/jbowens/jBBCode.git",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jbowens/jBBCode/zipball/645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"type": "library",
"autoload": {
"psr-0": {
"JBBCode": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jackson Owens",
"email": "jackson_owens@alumni.brown.edu",
"homepage": "http://jbowens.org/",
"role": "Developer"
}
],
"description": "A lightweight but extensible BBCode parser written in PHP 5.3.",
"homepage": "http://jbbcode.com/",
"keywords": [
"BB",
"bbcode"
],
"time": "2014-07-06 05:48:20"
},
{
"name": "leafo/lessphp",
"version": "v0.5.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/lessphp.git",
"reference": "0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/lessphp/zipball/0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283",
"reference": "0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283",
"shasum": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
},
"autoload": {
"classmap": [
"lessc.inc.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT",
"GPL-3.0"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "lessphp is a compiler for LESS written in PHP.",
"homepage": "http://leafo.net/lessphp/",
"time": "2014-11-24 18:39:20"
},
{
"name": "leafo/scssphp",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/scssphp.git",
"reference": "78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/scssphp/zipball/78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a",
"reference": "78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"kherge/box": "~2.5",
"phpunit/phpunit": "~3.7",
"squizlabs/php_codesniffer": "~2.3"
},
"bin": [
"bin/pscss"
],
"type": "library",
"autoload": {
"classmap": [
"classmap.php"
],
"psr-4": {
"Leafo\\ScssPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "scssphp is a compiler for SCSS written in PHP.",
"homepage": "http://leafo.github.io/scssphp/",
"keywords": [
"css",
"less",
"sass",
"scss",
"stylesheet"
],
"time": "2015-11-09 14:44:09"
},
{
"name": "league/html-to-markdown",
"version": "4.2.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/9a5becc8c6b520920fb846afefcfd7faf4c31712",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xml": "*",
"php": ">=5.3.3"
},
"require-dev": {
"mikehaertl/php-shellcommand": "~1.1.0",
"phpunit/phpunit": "4.*",
"scrutinizer/ocular": "~1.1"
},
"bin": [
"bin/html-to-markdown"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
}
},
"autoload": {
"psr-4": {
"League\\HTMLToMarkdown\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "http://www.colinodell.com",
"role": "Lead Developer"
},
{
"name": "Nick Cernis",
"email": "nick@cern.is",
"homepage": "http://modernnerd.net",
"role": "Original Author"
}
],
"description": "An HTML-to-markdown conversion helper for PHP",
"homepage": "https://github.com/thephpleague/html-to-markdown",
"keywords": [
"html",
"markdown"
],
"time": "2016-02-01 16:49:02"
},
{
"name": "matthiasmullie/minify",
"version": "1.3.34",
@ -284,16 +491,16 @@
},
{
"name": "michelf/php-markdown",
"version": "1.5.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9"
"reference": "156e56ee036505ec637d761ee62dc425d807183c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/e1aabe18173231ebcefc90e615565742fc1c7fd9",
"reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/156e56ee036505ec637d761ee62dc425d807183c",
"reference": "156e56ee036505ec637d761ee62dc425d807183c",
"shasum": ""
},
"require": {
@ -315,15 +522,15 @@
"BSD-3-Clause"
],
"authors": [
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
},
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "https://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "https://daringfireball.net/"
}
],
"description": "PHP Markdown",
@ -331,7 +538,62 @@
"keywords": [
"markdown"
],
"time": "2015-03-01 12:03:08"
"time": "2015-12-24 01:37:31"
},
{
"name": "michelf/php-smartypants",
"version": "1.6.0-beta1",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-smartypants.git",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
}
],
"description": "PHP SmartyPants",
"homepage": "http://michelf.ca/projects/php-smartypants/",
"keywords": [
"dashes",
"quotes",
"spaces",
"typographer",
"typography"
],
"time": "2013-07-31 18:13:10"
},
{
"name": "rmccue/requests",
@ -528,7 +790,9 @@
"packages-dev": null,
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"michelf/php-smartypants": 10
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View file

@ -1098,7 +1098,11 @@ class documentController extends document
function updateReadedCount(&$oDocument)
{
// Pass if Crawler access
if(isCrawler()) return false;
if (\Rhymix\Framework\UA::isRobot())
{
return false;
}
$oDocumentModel = getModel('document');
$config = $oDocumentModel->getDocumentConfig();

View file

@ -0,0 +1,6 @@
This is the first paragraph.
It contains [b]bold[/b] [i]italic[/i] text.
[quote]This is quoted text.[/quote]
This example belongs to the test suite for [url="https://www.rhymix.org"]Rhymix[/url].

View file

@ -0,0 +1,6 @@
This is the first paragraph.
It contains <strong>bold</strong> <em>italic</em> text.
<blockquote>This is quoted text.</blockquote>
This example belongs to the test suite for <a href="https://www.rhymix.org">Rhymix</a>.

View file

@ -0,0 +1,13 @@
<p>This is a Markdown document.
These lines belong in the same paragraph.
Markdown usually ignores single line breaks.
</p>
<p>This is a <a href="foobar.html" title="title">link</a>.<br />
This is an <img src="foobar.jpg" title="image" alt="alt" />.
</p>
<ul>
<li>This is a list.</li>
<li>It has two items.</li>
</ul>

View file

@ -0,0 +1,7 @@
This is a Markdown document. These lines belong in the same paragraph. Markdown usually ignores single line breaks.
This is a [link](foobar.html "title").
This is an ![alt](foobar.jpg "image").
- This is a list.
- It has two items.

View file

@ -0,0 +1,13 @@
<p>
This is a sample text file.<br />
This is a paragraph with multiple lines.<br />
This is the third line.
</p>
<p>
This is another paragraph.
</p>
<p>
This is a <span>SPAN</span> element that will be stripped away.<br />
This is a <a href="foobar.html">link</a> that will be preserved.<br />
This is an <img src="test.jpg" alt="Image Title" /> that will be preserved.<br />
</p>

View file

@ -0,0 +1,9 @@
This is a sample text file.
This is a paragraph with multiple lines.
This is the third line.
This is another paragraph.
This is a SPAN element that will be stripped away.
This is a link <foobar.html> that will be preserved.
This is an [Image Title] <test.jpg> that will be preserved.

View file

@ -0,0 +1,3 @@
.myfunction(@size) {
margin: @size;
}

View file

@ -0,0 +1,7 @@
.rhymix {
color: @foo;
background: url('foo/bar.jpg');
span {
.myfunction(@bar);
}
}

View file

@ -0,0 +1,9 @@
@charset "UTF-8";
.rhymix {
color: #123456;
background: url("../_data/formatter/foo/bar.jpg");
}
.rhymix span {
margin: 320px;
}

View file

@ -0,0 +1,2 @@
@charset "UTF-8";
.rhymix{color:#123456;background:url("../_data/formatter/foo/bar.jpg");}.rhymix span{margin:320px;}

View file

@ -0,0 +1,17 @@
This is a Markdown document.
These lines belong in the same paragraph.
Markdown usually ignores single line breaks.
This is an indented code segment.
All Markdown variants will recognize it.
```
This is a fenced code segment.
Only Markdown Extra will recognize it.
```
This is a [link](foobar.html).
This is an ![image](foobar.jpg).
- This is a list.
- It has two items.

View file

@ -0,0 +1,17 @@
<p>This is a Markdown document.
These lines belong in the same paragraph.
Markdown usually ignores single line breaks.</p>
<pre><code>This is an indented code segment.
All Markdown variants will recognize it.
</code></pre>
<p><code>This is a fenced code segment.
Only Markdown Extra will recognize it.</code></p>
<p>This is a <a href="foobar.html">link</a>.
This is an <img src="foobar.jpg" alt="image" />.</p>
<ul><li>This is a list.</li>
<li>It has two items.</li>
</ul>

View file

@ -0,0 +1,17 @@
<p>This is a Markdown document.<br />
These lines belong in the same paragraph.<br />
Markdown usually ignores single line breaks.</p>
<pre><code>This is an indented code segment.
All Markdown variants will recognize it.
</code></pre>
<p><code>This is a fenced code segment.
Only Markdown Extra will recognize it.</code></p>
<p>This is a <a href="foobar.html">link</a>.<br />
This is an <img src="foobar.jpg" alt="image" />.</p>
<ul><li>This is a list. </li>
<li>It has two items. </li>
</ul>

View file

@ -0,0 +1,18 @@
<p>This is a Markdown document.<br />
These lines belong in the same paragraph.<br />
Markdown usually ignores single line breaks.</p>
<pre><code>This is an indented code segment.
All Markdown variants will recognize it.
</code></pre>
<pre><code>This is a fenced code segment.
Only Markdown Extra will recognize it.
</code></pre>
<p>This is a <a href="foobar.html">link</a>.<br />
This is an <img src="foobar.jpg" alt="image" />.</p>
<ul><li>This is a list. </li>
<li>It has two items. </li>
</ul>

View file

@ -0,0 +1,10 @@
@charset "UTF-8";
.rhymix {
background: url("foo/bar.jpg");
}
.wordpress {
border-radius: 4px;
}
.xpressengine {
margin: 320px;
}

View file

@ -0,0 +1,6 @@
(function($) {
$(".foo").click(function(event) {
event.preventDefault();
$(this).attr("bar", "baz");
});
})(jQuery);

View file

@ -0,0 +1 @@
@charset "UTF-8";.rhymix{background:url(../_data/formatter/foo/bar.jpg)}.wordpress{border-radius:4px}.xpressengine{margin:320px}

View file

@ -0,0 +1 @@
(function($){$(".foo").click(function(event){event.preventDefault();$(this).attr("bar","baz")})})(jQuery)

View file

@ -0,0 +1,3 @@
@mixin mymixin($size) {
margin: $size;
}

View file

@ -0,0 +1,7 @@
.rhymix {
color: $foo;
background: url('foo/bar.jpg');
span {
@include mymixin($bar);
}
}

View file

@ -0,0 +1,9 @@
@charset "UTF-8";
.rhymix {
color: #123456;
background: url("../_data/formatter/foo/bar.jpg");
}
.rhymix span {
margin: 320px;
}

View file

@ -0,0 +1,2 @@
@charset "UTF-8";
.rhymix{color:#123456;background:url("../_data/formatter/foo/bar.jpg")}.rhymix span{margin:320px}

View file

@ -0,0 +1,2 @@
<p>This paragraph contains &quot;dumb quotes&quot; and short -- dashes.</p>
<p>This paragraph contains ``backtick quotes'' and 'long' --- dashes...</p>

View file

@ -0,0 +1,2 @@
<p>This paragraph contains &#8220;dumb quotes&#8221; and short &#8212; dashes.</p>
<p>This paragraph contains &#8220;backtick quotes&#8221; and &#8217;long&#8217; &#8211; dashes&#8230;</p>

View file

@ -0,0 +1,9 @@
This is a sample text file.
Some of these lines are close together.
Other lines are separated by two newlines.
Or three newlines.
This is a <p>tag</p> that will be escaped if encoded.
Hello world!

View file

@ -0,0 +1,9 @@
This is a sample text file.<br />
Some of these lines are close together.<br />
<br />
Other lines are separated by two newlines.<br />
<br />
<br />
Or three newlines.<br />
This is a &lt;p&gt;tag&lt;/p&gt; that will be escaped if encoded.<br />
Hello world!<br />

View file

@ -0,0 +1,9 @@
<p>This is a sample text file.</p>
<p>Some of these lines are close together.</p>
<p></p>
<p>Other lines are separated by two newlines.</p>
<p></p>
<p></p>
<p>Or three newlines.</p>
<p>This is a &lt;p&gt;tag&lt;/p&gt; that will be escaped if encoded.</p>
<p>Hello world!</p>

View file

@ -0,0 +1,12 @@
<p>
This is a sample text file.<br />
Some of these lines are close together.
</p>
<p>
Other lines are separated by two newlines.
</p>
<p>
Or three newlines.<br />
This is a &lt;p&gt;tag&lt;/p&gt; that will be escaped if encoded.<br />
Hello world!
</p>

View file

@ -0,0 +1,52 @@
<?php
class CalendarTest extends \Codeception\TestCase\Test
{
public function testGetMonthName()
{
$this->assertEquals('January', Rhymix\Framework\Calendar::getMonthName('01'));
$this->assertEquals('October', Rhymix\Framework\Calendar::getMonthName('10'));
$this->assertEquals('Nov', Rhymix\Framework\Calendar::getMonthName(11, false));
$this->assertEquals('Dec', Rhymix\Framework\Calendar::getMonthName(12, false));
}
public function testGetMonthStartDayOfWeek()
{
$this->assertEquals(5, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(1, 2016));
$this->assertEquals(1, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(2, 2016));
$this->assertEquals(2, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(3, 2016));
$this->assertEquals(5, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(4, 2016));
}
public function testGetMonthDays()
{
$this->assertEquals(30, Rhymix\Framework\Calendar::getMonthDays(11, 2015));
$this->assertEquals(31, Rhymix\Framework\Calendar::getMonthDays(12, 2015));
$this->assertEquals(31, Rhymix\Framework\Calendar::getMonthDays(1, 2016));
$this->assertEquals(29, Rhymix\Framework\Calendar::getMonthDays(2, 2016));
}
public function testGetMonthCalendar()
{
$target_201508 = array(
array(null, null, null, null, null, null, 1),
array(2, 3, 4, 5, 6, 7, 8),
array(9, 10, 11, 12, 13, 14, 15),
array(16, 17, 18, 19, 20, 21, 22),
array(23, 24, 25, 26, 27, 28, 29),
array(30, 31, null, null, null, null, null),
);
$target_201603 = array(
array(null, null, 1, 2, 3, 4, 5),
array(6, 7, 8, 9, 10, 11, 12),
array(13, 14, 15, 16, 17, 18, 19),
array(20, 21, 22, 23, 24, 25, 26),
array(27, 28, 29, 30, 31, null, null),
array(null, null, null, null, null, null, null),
);
$this->assertEquals($target_201508, Rhymix\Framework\Calendar::getMonthCalendar(8, 2015));
$this->assertEquals($target_201603, Rhymix\Framework\Calendar::getMonthCalendar(3, 2016));
}
}

View file

@ -0,0 +1,134 @@
<?php
class FormatterTest extends \Codeception\TestCase\Test
{
public function testText2HTML()
{
$text = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/text2html.source.txt');
$html1 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/text2html.target1.html');
$html2 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/text2html.target2.html');
$html3 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/text2html.target3.html');
$this->assertEquals($html1, Rhymix\Framework\Formatter::text2html($text));
$this->assertEquals($html2, Rhymix\Framework\Formatter::text2html($text, Rhymix\Framework\Formatter::TEXT_NEWLINE_AS_P));
$this->assertEquals($html3, Rhymix\Framework\Formatter::text2html($text, Rhymix\Framework\Formatter::TEXT_DOUBLE_NEWLINE_AS_P));
}
public function testHTML2Text()
{
$html = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/html2text.source.html');
$text = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/html2text.target.txt');
$this->assertEquals($text, Rhymix\Framework\Formatter::html2text($html));
}
public function testMarkdown2HTML()
{
$markdown = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/markdown2html.source.md');
$html1 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/markdown2html.target1.html');
$html2 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/markdown2html.target2.html');
$html3 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/markdown2html.target3.html');
$this->assertEquals($html1, Rhymix\Framework\Formatter::markdown2html($markdown));
$this->assertEquals($html2, Rhymix\Framework\Formatter::markdown2html($markdown, Rhymix\Framework\Formatter::MD_NEWLINE_AS_BR));
$this->assertEquals($html3, Rhymix\Framework\Formatter::markdown2html($markdown, Rhymix\Framework\Formatter::MD_NEWLINE_AS_BR | Rhymix\Framework\Formatter::MD_ENABLE_EXTRA));
}
public function testHTML2Markdown()
{
$html = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/html2markdown.source.html');
$markdown = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/html2markdown.target.md');
$this->assertEquals($markdown, Rhymix\Framework\Formatter::html2markdown($html));
}
public function testBBCode()
{
$bbcode = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/bbcode.source.txt');
$html = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/bbcode.target.html');
$this->assertEquals($html, Rhymix\Framework\Formatter::bbcode($bbcode));
}
public function testApplySmartQuotes()
{
$before = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/smartypants.source.html');
$after = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/smartypants.target.html');
$this->assertEquals($after, Rhymix\Framework\Formatter::applySmartQuotes($before));
}
public function testCompileLESS()
{
$sources = array(
\RX_BASEDIR . 'tests/_data/formatter/less.source1.less',
\RX_BASEDIR . 'tests/_data/formatter/less.source2.less',
);
$variables = array(
'foo' => '#123456',
'bar' => '320px',
);
$real_target1 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/less.target1.css');
$real_target2 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/less.target2.css');
$test_target1 = \RX_BASEDIR . 'tests/_output/less.target1.css';
$test_target2 = \RX_BASEDIR . 'tests/_output/less.target2.css';
$this->assertTrue(Rhymix\Framework\Formatter::compileLESS($sources, $test_target1, $variables));
$this->assertEquals($real_target1, file_get_contents($test_target1));
$this->assertTrue(Rhymix\Framework\Formatter::compileLESS($sources, $test_target2, $variables, true));
$this->assertEquals($real_target2, file_get_contents($test_target2));
unlink($test_target1);
unlink($test_target2);
}
public function testCompileSCSS()
{
$sources = array(
\RX_BASEDIR . 'tests/_data/formatter/scss.source1.scss',
\RX_BASEDIR . 'tests/_data/formatter/scss.source2.scss',
);
$variables = array(
'foo' => '#123456',
'bar' => '320px',
);
$real_target1 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/scss.target1.css');
$real_target2 = file_get_contents(\RX_BASEDIR . 'tests/_data/formatter/scss.target2.css');
$test_target1 = \RX_BASEDIR . 'tests/_output/scss.target1.css';
$test_target2 = \RX_BASEDIR . 'tests/_output/scss.target2.css';
$this->assertTrue(Rhymix\Framework\Formatter::compileSCSS($sources, $test_target1, $variables));
$this->assertEquals($real_target1, file_get_contents($test_target1));
$this->assertTrue(Rhymix\Framework\Formatter::compileSCSS($sources, $test_target2, $variables, true));
$this->assertEquals($real_target2, file_get_contents($test_target2));
unlink($test_target1);
unlink($test_target2);
}
public function testMinifyCSS()
{
$source = \RX_BASEDIR . 'tests/_data/formatter/minify.source.css';
$real_target = \RX_BASEDIR . 'tests/_data/formatter/minify.target.css';
$test_target = \RX_BASEDIR . 'tests/_output/minify.target.css';
$this->assertTrue(Rhymix\Framework\Formatter::minifyCSS($source, $test_target));
$this->assertEquals(file_get_contents($real_target), file_get_contents($test_target));
unlink($test_target);
}
public function testMinifyJS()
{
$source = \RX_BASEDIR . 'tests/_data/formatter/minify.source.js';
$real_target = \RX_BASEDIR . 'tests/_data/formatter/minify.target.js';
$test_target = \RX_BASEDIR . 'tests/_output/minify.target.js';
$this->assertTrue(Rhymix\Framework\Formatter::minifyJS($source, $test_target));
$this->assertEquals(file_get_contents($real_target), file_get_contents($test_target));
unlink($test_target);
}
}

View file

@ -0,0 +1,107 @@
<?php
class KoreaTest extends \Codeception\TestCase\Test
{
public function testFormatPhoneNumber()
{
$this->assertEquals('1588-0000', Rhymix\Framework\Korea::formatPhoneNumber('1588-0000'));
$this->assertEquals('02-345-6789', Rhymix\Framework\Korea::formatPhoneNumber('+82 23456789'));
$this->assertEquals('02-3000-5000', Rhymix\Framework\Korea::formatPhoneNumber('0230005000'));
$this->assertEquals('031-222-3333', Rhymix\Framework\Korea::formatPhoneNumber('82-0312-2233-33'));
$this->assertEquals('031-2222-3333', Rhymix\Framework\Korea::formatPhoneNumber('03122223333'));
$this->assertEquals('011-444-5555', Rhymix\Framework\Korea::formatPhoneNumber('011 444 5555'));
$this->assertEquals('010-6666-7777', Rhymix\Framework\Korea::formatPhoneNumber('82+1066667777'));
$this->assertEquals('0303-456-7890', Rhymix\Framework\Korea::formatPhoneNumber('03034567890'));
$this->assertEquals('0505-987-6543', Rhymix\Framework\Korea::formatPhoneNumber('050-5987-6543'));
$this->assertEquals('070-7432-1000', Rhymix\Framework\Korea::formatPhoneNumber('0707-432-1000'));
}
public function testIsValidPhoneNumber()
{
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('1588-0000'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('02-345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('+82-2-345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('+82-02-2345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('053-444-5555'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('053-4444-5555'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('011-444-5555'));
$this->assertTrue(Rhymix\Framework\Korea::isValidPhoneNumber('010-4444-5555'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('010-4444-55555'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('010-1234-5678'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('02-123-4567'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('02-123456'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('03-456-7890'));
$this->assertFalse(Rhymix\Framework\Korea::isValidPhoneNumber('090-9876-5432'));
}
public function testIsValidMobilePhoneNumber()
{
$this->assertTrue(Rhymix\Framework\Korea::isValidMobilePhoneNumber('011-345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidMobilePhoneNumber('010-2345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidMobilePhoneNumber('+82-11-345-6789'));
$this->assertTrue(Rhymix\Framework\Korea::isValidMobilePhoneNumber('82 010-2345-6789'));
$this->assertFalse(Rhymix\Framework\Korea::isValidMobilePhoneNumber('010-1111-1111'));
$this->assertFalse(Rhymix\Framework\Korea::isValidMobilePhoneNumber('02-345-6789'));
$this->assertFalse(Rhymix\Framework\Korea::isValidMobilePhoneNumber('063-9876-5432'));
$this->assertFalse(Rhymix\Framework\Korea::isValidMobilePhoneNumber('070-7654-3210'));
}
public function testIsValidJuminNumber()
{
// These numbers are fake.
$this->assertTrue(Rhymix\Framework\Korea::isValidJuminNumber('123456-3456787'));
$this->assertFalse(Rhymix\Framework\Korea::isValidJuminNumber('123456-3456788'));
}
public function testIsValidCorporationNumber()
{
// These numbers are fake.
$this->assertTrue(Rhymix\Framework\Korea::isValidCorporationNumber('123456-0123453'));
$this->assertFalse(Rhymix\Framework\Korea::isValidCorporationNumber('123456-0123454'));
}
public function testIsValidBusinessNumber()
{
// These numbers are fake.
$this->assertTrue(Rhymix\Framework\Korea::isValidBusinessNumber('123-45-67891'));
$this->assertFalse(Rhymix\Framework\Korea::isValidBusinessNumber('123-45-67892'));
}
public function testIsKoreanIP()
{
// Private IP ranges.
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('10.12.34.210'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('127.0.123.45'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('192.168.10.1'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('::1'));
// Korean IP ranges.
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('115.71.233.0'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('114.207.12.3'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('2001:0320::1'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanIP('2407:B800::F'));
// Foreign IP ranges.
$this->assertFalse(Rhymix\Framework\Korea::isKoreanIP('216.58.197.0'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanIP('170.14.168.0'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanIP('2001:41d0:8:e8ad::1'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanIP('2404:6800:4005:802::200e'));
}
public function testIsKoreanEmailAddress()
{
// Test Korean portals.
$this->assertTrue(Rhymix\Framework\Korea::isKoreanEmailAddress('test@naver.com'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanEmailAddress('test@hanmail.net'));
$this->assertTrue(Rhymix\Framework\Korea::isKoreanEmailAddress('test@worksmobile.com'));
// Test foreign portals.
$this->assertFalse(Rhymix\Framework\Korea::isKoreanEmailAddress('test@gmail.com'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanEmailAddress('test@hotmail.com'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanEmailAddress('test@yahoo.com'));
// Test third-party MX services.
$this->assertTrue(Rhymix\Framework\Korea::isKoreanEmailAddress('test@woorimail.com'));
$this->assertFalse(Rhymix\Framework\Korea::isKoreanEmailAddress('test@rhymix.org'));
}
}

View file

@ -0,0 +1,22 @@
<?php
class MIMETest extends \Codeception\TestCase\Test
{
public function testMIME()
{
$this->assertEquals('audio/ogg', Rhymix\Framework\MIME::getTypeByExtension('ogg'));
$this->assertEquals('image/gif', Rhymix\Framework\MIME::getTypeByExtension('gif'));
$this->assertEquals('text/html', Rhymix\Framework\MIME::getTypeByExtension('htm'));
$this->assertEquals('application/msword', Rhymix\Framework\MIME::getTypeByFilename('attachment.doc'));
$this->assertEquals('application/pdf', Rhymix\Framework\MIME::getTypeByFilename('라이믹스.pdf'));
$this->assertEquals('application/postscript', Rhymix\Framework\MIME::getTypeByFilename('MyGraphics.v2.eps'));
$this->assertEquals('application/vnd.ms-excel', Mail::returnMIMEType('MySpreadsheet.xls'));
$this->assertEquals('application/octet-stream', Mail::returnMIMEType('Untitled File'));
$this->assertEquals('odt', Rhymix\Framework\MIME::getExtensionByType('application/vnd.oasis.opendocument.text'));
$this->assertEquals('jpg', Rhymix\Framework\MIME::getExtensionByType('image/jpeg'));
$this->assertEquals('mpeg', Rhymix\Framework\MIME::getExtensionByType('video/mpeg'));
$this->assertFalse(Rhymix\Framework\MIME::getExtensionByType('application/octet-stream'));
}
}

View file

@ -0,0 +1,48 @@
<?php
class PaginationTest extends \Codeception\TestCase\Test
{
public function testCountPages()
{
$this->assertEquals(1, Rhymix\Framework\Pagination::countPages(0, 20));
$this->assertEquals(1, Rhymix\Framework\Pagination::countPages(10, 20));
$this->assertEquals(1, Rhymix\Framework\Pagination::countPages(20, 20));
$this->assertEquals(2, Rhymix\Framework\Pagination::countPages(21, 20));
}
public function testCreateLinks()
{
$links = Rhymix\Framework\Pagination::createLinks('index.php?page=', 27, 3);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<a href="index.php?page=3">', $links);
$this->assertContains('<span class="page_number">1</span>', $links);
$this->assertContains('<span class="page_number">10</span>', $links);
$links = Rhymix\Framework\Pagination::createLinks('/foo/bar/page/', 27, 13);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<a href="/foo/bar/page/13">', $links);
$this->assertContains('<span class="page_number">11</span>', $links);
$this->assertContains('<span class="page_number">20</span>', $links);
$links = Rhymix\Framework\Pagination::createLinks('/rhymix?page=$PAGE&foo=bar', 27, 25);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<a href="/rhymix?page=27&amp;foo=bar">', $links);
$this->assertContains('<span class="page_number">21</span>', $links);
$this->assertContains('<span class="page_number">27</span>', $links);
$links = Rhymix\Framework\Pagination::createLinks('p', 27, 3, 10, Rhymix\Framework\Pagination::COUNT_STYLE_CONTINUOUS);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<span class="page_number">1</span>', $links);
$this->assertContains('<span class="page_number">10</span>', $links);
$links = Rhymix\Framework\Pagination::createLinks('p', 27, 13, 10, Rhymix\Framework\Pagination::COUNT_STYLE_CONTINUOUS);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<span class="page_number">9</span>', $links);
$this->assertContains('<span class="page_number">18</span>', $links);
$links = Rhymix\Framework\Pagination::createLinks('p', 27, 25, 10, Rhymix\Framework\Pagination::COUNT_STYLE_CONTINUOUS);
$this->assertContains('<div class="pagination">', $links);
$this->assertContains('<span class="page_number">18</span>', $links);
$this->assertContains('<span class="page_number">27</span>', $links);
}
}

View file

@ -0,0 +1,260 @@
<?php
class StorageTest extends \Codeception\TestCase\Test
{
public function _before()
{
Rhymix\Framework\Storage::deleteDirectory(\RX_BASEDIR . 'tests/_output', false);
}
public function _after()
{
Rhymix\Framework\Storage::deleteDirectory(\RX_BASEDIR . 'tests/_output', false);
}
public function _failed()
{
Rhymix\Framework\Storage::deleteDirectory(\RX_BASEDIR . 'tests/_output', false);
}
public function testExists()
{
$this->assertTrue(Rhymix\Framework\Storage::exists(__FILE__));
$this->assertTrue(Rhymix\Framework\Storage::exists(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::exists(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::exists(__DIR__ . '/nonexistent.subdirectory'));
}
public function testIsFile()
{
$this->assertTrue(Rhymix\Framework\Storage::isFile(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::isFile(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::isFile(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::isFile(__DIR__ . '/nonexistent.subdirectory'));
}
public function testIsEmptyFile()
{
$emptyfile = \RX_BASEDIR . 'tests/_output/emptyfile.txt';
file_put_contents($emptyfile, '');
$this->assertTrue(Rhymix\Framework\Storage::isEmptyFile($emptyfile));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyFile($emptyfile . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyFile(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyFile(__DIR__));
}
public function testIsDirectory()
{
$this->assertTrue(Rhymix\Framework\Storage::isDirectory(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::isDirectory(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::isDirectory(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::isDirectory(__DIR__ . '/nonexistent.subdirectory'));
}
public function testIsEmptyDirectory()
{
$emptydir = \RX_BASEDIR . 'tests/_output/emptydir';
mkdir($emptydir);
$this->assertTrue(Rhymix\Framework\Storage::isEmptyDirectory($emptydir));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyDirectory($emptydir . '/nonexistent.subdirectory'));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyDirectory(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::isEmptyDirectory(__DIR__));
}
public function testIsSymlink()
{
if (strncasecmp(\PHP_OS, 'Win', 3) === 0)
{
return;
}
$symlink_source = \RX_BASEDIR . 'tests/_output/link.source.txt';
$symlink_target = \RX_BASEDIR . 'tests/_output/link.target.txt';
file_put_contents($symlink_target, 'foobar');
symlink($symlink_target, $symlink_source);
$this->assertTrue(Rhymix\Framework\Storage::isSymlink($symlink_source));
$this->assertFalse(Rhymix\Framework\Storage::isSymlink($symlink_target));
unlink($symlink_target);
$this->assertTrue(Rhymix\Framework\Storage::isSymlink($symlink_source));
$this->assertFalse(Rhymix\Framework\Storage::isValidSymlink($symlink_source));
$this->assertFalse(Rhymix\Framework\Storage::isSymlink($symlink_target));
$this->assertFalse(Rhymix\Framework\Storage::isValidSymlink($symlink_target));
}
public function testIsReadable()
{
$this->assertTrue(Rhymix\Framework\Storage::isReadable(__FILE__));
$this->assertTrue(Rhymix\Framework\Storage::isReadable(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::isReadable(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::isReadable('/dev/nonexistent'));
}
public function testIsWritable()
{
$testfile = \RX_BASEDIR . 'tests/_output/testfile.txt';
file_put_contents($testfile, 'foobar');
$this->assertTrue(Rhymix\Framework\Storage::isWritable(__FILE__));
$this->assertTrue(Rhymix\Framework\Storage::isWritable(__DIR__));
$this->assertTrue(Rhymix\Framework\Storage::isWritable($testfile));
$this->assertTrue(Rhymix\Framework\Storage::isWritable(dirname($testfile)));
$this->assertFalse(Rhymix\Framework\Storage::isWritable($testfile . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::isWritable('/dev/nonexistent'));
}
public function testGetSize()
{
$this->assertEquals(filesize(__FILE__), Rhymix\Framework\Storage::getSize(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::getSize(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::getSize(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::getSize('/dev/nonexistent'));
}
public function testRead()
{
$this->assertEquals(file_get_contents(__FILE__), Rhymix\Framework\Storage::read(__FILE__));
$this->assertFalse(Rhymix\Framework\Storage::read(__FILE__ . '.nonexistent.suffix'));
$this->assertFalse(Rhymix\Framework\Storage::read(__DIR__));
$this->assertFalse(Rhymix\Framework\Storage::read('/dev/nonexistent'));
}
public function testWrite()
{
$testfile = \RX_BASEDIR . 'tests/_output/subdirectory/testfile.txt';
$this->assertFalse(file_exists($testfile));
$this->assertTrue(Rhymix\Framework\Storage::write($testfile, 'foobarbazzjazz'));
$this->assertTrue(file_exists($testfile));
$this->assertEquals('foobarbazzjazz', file_get_contents($testfile));
}
public function testCopy()
{
$source = \RX_BASEDIR . 'tests/_output/copy.source.txt';
$target = \RX_BASEDIR . 'tests/_output/copy.target.txt';
file_put_contents($source, 'foobarbaz');
chmod($source, 0646);
$this->assertTrue(Rhymix\Framework\Storage::copy($source, $target));
$this->assertTrue(file_exists($target));
$this->assertTrue(file_get_contents($target) === 'foobarbaz');
if (strncasecmp(\PHP_OS, 'Win', 3) !== 0)
{
$this->assertEquals(0646, fileperms($target) & 0777);
$this->assertTrue(Rhymix\Framework\Storage::copy($source, $target, 0755));
$this->assertEquals(0755, fileperms($target) & 0777);
}
}
public function testMove()
{
$source = \RX_BASEDIR . 'tests/_output/move.source.txt';
$target = \RX_BASEDIR . 'tests/_output/move.target.txt';
file_put_contents($source, 'foobarbaz');
$this->assertTrue(Rhymix\Framework\Storage::move($source, $target));
$this->assertTrue(file_exists($target));
$this->assertTrue(file_get_contents($target) === 'foobarbaz');
$this->assertFalse(file_exists($source));
}
public function testDelete()
{
$testfile = \RX_BASEDIR . 'tests/_output/testfile.txt';
file_put_contents($testfile, 'foobar');
$this->assertTrue(Rhymix\Framework\Storage::delete($testfile));
$this->assertFalse(file_exists($testfile));
}
public function testCreateDirectory()
{
$emptydir = \RX_BASEDIR . 'tests/_output/emptydir';
$this->assertTrue(Rhymix\Framework\Storage::createDirectory($emptydir));
$this->assertTrue(file_exists($emptydir) && is_dir($emptydir));
}
public function testReadDirectory()
{
$testdir = \RX_BASEDIR . 'tests/_output/testdir';
mkdir($testdir);
mkdir($testdir . '/subdir');
file_put_contents($testdir . '/.dotfile', '');
file_put_contents($testdir . '/foo', 'foo');
file_put_contents($testdir . '/bar', 'bar');
file_put_contents($testdir . '/baz', 'baz');
$this->assertEquals(array($testdir . '/bar', $testdir . '/baz', $testdir . '/foo'), Rhymix\Framework\Storage::readDirectory($testdir));
$this->assertEquals(array('bar', 'baz', 'foo'), Rhymix\Framework\Storage::readDirectory($testdir, false));
$this->assertEquals(array('bar', 'baz', 'foo', 'subdir'), Rhymix\Framework\Storage::readDirectory($testdir, false, true, false));
$this->assertEquals(array('.dotfile', 'bar', 'baz', 'foo'), Rhymix\Framework\Storage::readDirectory($testdir, false, false, true));
$this->assertEquals(array('.dotfile', 'bar', 'baz', 'foo', 'subdir'), Rhymix\Framework\Storage::readDirectory($testdir, false, false, false));
$this->assertFalse(Rhymix\Framework\Storage::readDirectory('/opt/nonexistent.foobar'));
}
public function testCopyDirectory()
{
$sourcedir = \RX_BASEDIR . 'tests/_output/sourcedir';
mkdir($sourcedir);
mkdir($sourcedir . '/subdir');
file_put_contents($sourcedir . '/bar', 'bar');
file_put_contents($sourcedir . '/subdir/baz', 'baz');
$targetdir = \RX_BASEDIR . 'tests/_output/targetdir';
$this->assertTrue(Rhymix\Framework\Storage::copyDirectory($sourcedir, $targetdir));
$this->assertTrue(file_exists($targetdir . '/bar'));
$this->assertTrue(file_exists($targetdir . '/subdir/baz'));
$this->assertFalse(Rhymix\Framework\Storage::copyDirectory($sourcedir, '/opt/nonexistent.foobar'));
}
public function testMoveDirectory()
{
$sourcedir = \RX_BASEDIR . 'tests/_output/sourcedir';
mkdir($sourcedir);
mkdir($sourcedir . '/subdir');
file_put_contents($sourcedir . '/bar', 'bar');
file_put_contents($sourcedir . '/subdir/baz', 'baz');
$targetdir = \RX_BASEDIR . 'tests/_output/targetdir';
$this->assertTrue(Rhymix\Framework\Storage::moveDirectory($sourcedir, $targetdir));
$this->assertTrue(file_exists($targetdir . '/bar'));
$this->assertTrue(file_exists($targetdir . '/subdir/baz'));
$this->assertFalse(file_exists($sourcedir));
}
public function testDeleteDirectory()
{
$sourcedir = \RX_BASEDIR . 'tests/_output/sourcedir';
mkdir($sourcedir);
mkdir($sourcedir . '/subdir');
file_put_contents($sourcedir . '/bar', 'bar');
file_put_contents($sourcedir . '/subdir/baz', 'baz');
$nonexistent = \RX_BASEDIR . 'tests/_output/targetdir';
$this->assertTrue(Rhymix\Framework\Storage::deleteDirectory($sourcedir));
$this->assertFalse(file_exists($sourcedir . '/subdir/baz'));
$this->assertFalse(file_exists($sourcedir));
$this->assertFalse(Rhymix\Framework\Storage::deleteDirectory($nonexistent));
}
public function testDeleteDirectoryKeepRoot()
{
$sourcedir = \RX_BASEDIR . 'tests/_output/sourcedir';
mkdir($sourcedir);
mkdir($sourcedir . '/subdir');
file_put_contents($sourcedir . '/bar', 'bar');
file_put_contents($sourcedir . '/subdir/baz', 'baz');
$nonexistent = \RX_BASEDIR . 'tests/_output/targetdir';
$this->assertTrue(Rhymix\Framework\Storage::deleteDirectory($sourcedir, false));
$this->assertFalse(file_exists($sourcedir . '/subdir/baz'));
$this->assertTrue(file_exists($sourcedir));
$this->assertFalse(Rhymix\Framework\Storage::deleteDirectory($nonexistent));
}
}

View file

@ -0,0 +1,71 @@
<?php
class TimerTest extends \Codeception\TestCase\Test
{
function testStartStop()
{
$t1 = microtime(true);
usleep(1000);
$started = Rhymix\Framework\Timer::start();
usleep(1000);
$t2 = microtime(true);
usleep(1000);
$elapsed = Rhymix\Framework\Timer::stop();
usleep(1000);
$t3 = microtime(true);
$this->assertGreaterThanOrEqual($t1, $started);
$this->assertLessThanOrEqual($t2, $started);
$this->assertGreaterThanOrEqual($t2 - $started, $elapsed);
$this->assertLessThanOrEqual($t3 - $t1, $elapsed);
$this->assertGreaterThan(0, $elapsed);
}
function testNestedTimers()
{
$t1 = Rhymix\Framework\Timer::start();
usleep(1000);
$t2 = Rhymix\Framework\Timer::start();
usleep(1000);
$t3 = Rhymix\Framework\Timer::stop();
usleep(1000);
$t4 = Rhymix\Framework\Timer::stop();
$this->assertGreaterThanOrEqual($t1, $t2);
$this->assertGreaterThan($t3, $t4);
}
function testMultipleTimers()
{
$t1 = Rhymix\Framework\Timer::start('timer1');
usleep(5000);
$t2 = Rhymix\Framework\Timer::start('timer2');
usleep(1000);
$t3 = Rhymix\Framework\Timer::stop('timer1');
usleep(2000);
$t4 = Rhymix\Framework\Timer::stop('timer2');
$this->assertGreaterThanOrEqual($t1, $t2);
$this->assertGreaterThanOrEqual($t4, $t3);
}
function testTimerFormat()
{
$t1 = Rhymix\Framework\Timer::start();
usleep(1000);
$t2 = Rhymix\Framework\Timer::stopFormat();
$this->assertRegexp('/^[0-9\.,]+ms$/', $t2);
}
function testTimerSinceStartup()
{
$t1 = Rhymix\Framework\Timer::sinceStartup();
$t2 = Rhymix\Framework\Timer::sinceStartup();
$this->assertGreaterThanOrEqual($t1, $t2);
$t3 = Rhymix\Framework\Timer::sinceStartupFormat();
$this->assertRegexp('/^[0-9\.,]+ms$/', $t3);
}
}

View file

@ -0,0 +1,104 @@
<?php
class UATest extends \Codeception\TestCase\Test
{
public function testIsMobile()
{
// Phones
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'));
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
// Tablets
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertTrue(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Linux; Android 4.4.2; SM-T530 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36'));
// Not mobile
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0'));
$this->assertFalse(Rhymix\Framework\UA::isMobile('Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16'));
}
public function testIsTablet()
{
// Phones
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'));
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
// Tablets
$this->assertTrue(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertTrue(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Linux; Android 4.4.2; SM-T530 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36'));
// Not mobile
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
$this->assertFalse(Rhymix\Framework\UA::isTablet('Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)'));
}
public function testIsRobot()
{
// Robot
$this->assertTrue(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'));
$this->assertTrue(Rhymix\Framework\UA::isRobot('Googlebot/2.1 (+http://www.googlebot.com/bot.html)'));
$this->assertTrue(Rhymix\Framework\UA::isRobot('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)'));
// Not robot
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25'));
$this->assertFalse(Rhymix\Framework\UA::isRobot('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko'));
}
public function testGetBrowserInfo()
{
// Android default browser
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; U; Android 4.0.3; Device Name) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30');
$this->assertEquals('Android', $browser->browser);
$this->assertEquals('4.0', $browser->version);
$this->assertEquals('Android', $browser->os);
$this->assertTrue($browser->is_mobile);
$this->assertTrue($browser->is_tablet);
// Mobile Chrome
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36');
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('38.0', $browser->version);
$this->assertEquals('Android', $browser->os);
$this->assertTrue($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
// Edge
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136');
$this->assertEquals('Edge', $browser->browser);
$this->assertEquals('12.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
$this->assertFalse($browser->is_mobile);
$this->assertFalse($browser->is_tablet);
// IE 11
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('11.0', $browser->version);
$this->assertEquals('Windows', $browser->os);
// IE 10 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (Compatible; MSIE 8.0; Windows NT 5.2; Trident/6.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('10.0', $browser->version);
// IE 8 in compatibility mode
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)');
$this->assertEquals('IE', $browser->browser);
$this->assertEquals('8.0', $browser->version);
// Linux Chrome
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36');
$this->assertEquals('Chrome', $browser->browser);
$this->assertEquals('41.0', $browser->version);
$this->assertEquals('Linux', $browser->os);
// OS X Firefox
$browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0');
$this->assertEquals('Firefox', $browser->browser);
$this->assertEquals('33.0', $browser->version);
$this->assertEquals('OS X', $browser->os);
}
}

View file

@ -0,0 +1,41 @@
<?php
class i18nTest extends \Codeception\TestCase\Test
{
public function testListCountries()
{
$sort_code_2 = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_CODE_2);
$this->assertEquals('AD', array_first($sort_code_2)->iso_3166_1_alpha2);
$this->assertEquals('AND', array_first_key($sort_code_2));
$sort_code_3 = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_CODE_3);
$this->assertEquals('ABW', array_first($sort_code_3)->iso_3166_1_alpha3);
$this->assertEquals('ABW', array_first_key($sort_code_3));
$sort_code_n = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_CODE_NUMERIC);
$this->assertEquals('004', array_first($sort_code_n)->iso_3166_1_numeric);
$this->assertEquals('AFG', array_first_key($sort_code_n));
$sort_cctld = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_CCTLD);
$this->assertEquals('zw', array_last($sort_cctld)->cctld);
$this->assertEquals('ZWE', array_last_key($sort_cctld));
$sort_english = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_NAME_ENGLISH);
$this->assertEquals('Afghanistan', array_first($sort_english)->name_english);
$this->assertEquals('AFG', array_first_key($sort_english));
$this->assertEquals('Åland Islands', array_last($sort_english)->name_english);
$this->assertEquals('ALA', array_last_key($sort_english));
$sort_korean = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_NAME_KOREAN);
$this->assertEquals('가나', array_first($sort_korean)->name_korean);
$this->assertEquals('GHA', array_first_key($sort_korean));
$this->assertEquals('홍콩', array_last($sort_korean)->name_korean);
$this->assertEquals('HKG', array_last_key($sort_korean));
$sort_native = Rhymix\Framework\i18n::listCountries(Rhymix\Framework\i18n::SORT_NAME_NATIVE);
$this->assertEquals('Amerika Sāmoa', array_first($sort_native)->name_native);
$this->assertEquals('대한민국', $sort_native['KOR']->name_korean);
$this->assertEquals('United States of America', $sort_native['USA']->name_english);
$this->assertEquals('nz', $sort_native['NZL']->cctld);
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* Do not allow execution over the web.
*/
if (PHP_SAPI !== 'cli')
{
exit 1;
}
/**
* Download the latest IPv4 data from libkrisp.
*/
$download_url = 'https://mirror.oops.org/pub/oops/libkrisp/data/v2/krisp.csv.gz';
$referer_url = 'https://mirror.oops.org/pub/oops/libkrisp/data/v2/';
$content = file_get_contents($download_url, false, stream_context_create(array(
'http' => array(
'user_agent' => 'Mozilla/5.0 (compatible; IP range generator)',
'header' => 'Referer: ' . $referer_url . "\r\n",
),
)));
$content = gzdecode($content);
if (!$content)
{
exit 2;
}
/**
* Load IP range data.
*/
$ranges = array();
$content = explode("\n", $content);
foreach ($content as $line)
{
$line = explode("\t", $line);
if (count($line) < 2) continue;
$start = trim($line[0]);
$end = trim($line[1]);
$ranges[$start] = array($start, $end);
}
/**
* Sort the ranges.
*/
ksort($ranges);
$ranges = array_values($ranges);
$count = count($ranges);
/**
* Merge adjacent ranges.
*/
for ($i = 0; $i < $count; $i++)
{
if ($i == 0) continue;
$previous_i = $i - 1;
while (true)
{
if ($ranges[$previous_i] !== null) break;
$previous_i--;
}
if ($ranges[$i][0] == $ranges[$previous_i][1] + 1)
{
$ranges[$previous_i][1] = $ranges[$i][1];
$ranges[$i] = null;
}
}
/**
* Organize into final format.
*/
$ranges_final = array();
foreach ($ranges as $range)
{
if ($range !== null) $ranges_final[] = $range;
}
/**
* Save to file.
*/
$content = '<' . '?php' . "\n\n" . '/**' . "\n" . ' * Source: ' . $referer_url . "\n";
$content .= ' * Last Updated: ' . date('Y-m-d') . "\n" . ' */' . "\n";
$content .= 'return ' . var_export($ranges_final, true) . ';' . "\n";
file_put_contents('korea.ipv4.php', $content);
/**
* Report status.
*/
echo count($ranges_final) . ' IPv4 ranges saved.' . PHP_EOL;

View file

@ -0,0 +1,92 @@
<?php
/**
* Do not allow execution over the web.
*/
if (PHP_SAPI !== 'cli')
{
exit 1;
}
/**
* Download the latest IPv6 data from KISA.
*/
$download_url = 'https://ip.kisa.or.kr/ip_cate_stat/stat_05_05_toexcel.act';
$referer_url = 'https://ip.kisa.or.kr/ip_cate_stat/stat_05_05.act';
$content = file_get_contents($download_url, false, stream_context_create(array(
'http' => array(
'user_agent' => 'Mozilla/5.0 (compatible; IP range generator)',
'header' => 'Referer: ' . $referer_url . "\r\n",
),
)));
if (!$content)
{
exit 2;
}
/**
* Parse the HTML/Excel document.
*/
$regex = '#<tr>\\s*<td [^>]+>([0-9a-f:]+::)</td>\\s*<td [^>]+>(/[0-9]+)</td>#iU';
preg_match_all($regex, $content, $matches, PREG_SET_ORDER);
/**
* Extract the address and netmask for each range.
*/
$ranges = array();
foreach ($matches as $match)
{
$start = str_pad(str_replace(':', '', strtolower($match[1])), 16, '0', STR_PAD_RIGHT);
$mask = str_repeat('f', ((64 - trim($match[2], '/')) / 4));
$end = substr($start, 0, 16 - strlen($mask)) . $mask;
$ranges[$start] = array($start, $end);
}
/**
* Sort the ranges.
*/
ksort($ranges);
$ranges = array_values($ranges);
$count = count($ranges);
/**
* Merge adjacent ranges.
*/
for ($i = 0; $i < $count; $i++)
{
if ($i == 0) continue;
$previous_i = $i - 1;
while (true)
{
if ($ranges[$previous_i] !== null) break;
$previous_i--;
}
if (hexdec($ranges[$i][0]) == hexdec($ranges[$previous_i][1]) + 1)
{
$ranges[$previous_i][1] = $ranges[$i][1];
$ranges[$i] = null;
}
}
/**
* Organize into final format.
*/
$ranges_final = array();
foreach ($ranges as $range)
{
if ($range !== null) $ranges_final[] = $range;
}
/**
* Save to file.
*/
$content = '<' . '?php' . "\n\n" . '/**' . "\n" . ' * Source: ' . $referer_url . "\n";
$content .= ' * Last Updated: ' . date('Y-m-d') . "\n" . ' */' . "\n";
$content .= 'return ' . var_export($ranges_final, true) . ';' . "\n";
file_put_contents('korea.ipv6.php', $content);
/**
* Report status.
*/
echo count($ranges_final) . ' IPv6 ranges saved.' . PHP_EOL;

1
vendor/bin/html-to-markdown vendored Symbolic link
View file

@ -0,0 +1 @@
../league/html-to-markdown/bin/html-to-markdown

1
vendor/bin/pscss vendored Symbolic link
View file

@ -0,0 +1 @@
../leafo/scssphp/bin/pscss

View file

@ -238,6 +238,67 @@ return array(
'HTMLPurifier_VarParser_Native' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php',
'HTMLPurifier_Zipper' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php',
'Hautelook\\Phpass\\PasswordHash' => $vendorDir . '/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php',
'JBBCode\\CodeDefinition' => $vendorDir . '/jbbcode/jbbcode/JBBCode/CodeDefinition.php',
'JBBCode\\CodeDefinitionBuilder' => $vendorDir . '/jbbcode/jbbcode/JBBCode/CodeDefinitionBuilder.php',
'JBBCode\\CodeDefinitionSet' => $vendorDir . '/jbbcode/jbbcode/JBBCode/CodeDefinitionSet.php',
'JBBCode\\DefaultCodeDefinitionSet' => $vendorDir . '/jbbcode/jbbcode/JBBCode/DefaultCodeDefinitionSet.php',
'JBBCode\\DocumentElement' => $vendorDir . '/jbbcode/jbbcode/JBBCode/DocumentElement.php',
'JBBCode\\ElementNode' => $vendorDir . '/jbbcode/jbbcode/JBBCode/ElementNode.php',
'JBBCode\\InputValidator' => $vendorDir . '/jbbcode/jbbcode/JBBCode/InputValidator.php',
'JBBCode\\Node' => $vendorDir . '/jbbcode/jbbcode/JBBCode/Node.php',
'JBBCode\\NodeVisitor' => $vendorDir . '/jbbcode/jbbcode/JBBCode/NodeVisitor.php',
'JBBCode\\Parser' => $vendorDir . '/jbbcode/jbbcode/JBBCode/Parser.php',
'JBBCode\\ParserException' => $vendorDir . '/jbbcode/jbbcode/JBBCode/ParserException.php',
'JBBCode\\TextNode' => $vendorDir . '/jbbcode/jbbcode/JBBCode/TextNode.php',
'JBBCode\\Tokenizer' => $vendorDir . '/jbbcode/jbbcode/JBBCode/Tokenizer.php',
'JBBCode\\validators\\CssColorValidator' => $vendorDir . '/jbbcode/jbbcode/JBBCode/validators/CssColorValidator.php',
'JBBCode\\validators\\UrlValidator' => $vendorDir . '/jbbcode/jbbcode/JBBCode/validators/UrlValidator.php',
'JBBCode\\visitors\\HTMLSafeVisitor' => $vendorDir . '/jbbcode/jbbcode/JBBCode/visitors/HTMLSafeVisitor.php',
'JBBCode\\visitors\\NestLimitVisitor' => $vendorDir . '/jbbcode/jbbcode/JBBCode/visitors/NestLimitVisitor.php',
'JBBCode\\visitors\\SmileyVisitor' => $vendorDir . '/jbbcode/jbbcode/JBBCode/visitors/SmileyVisitor.php',
'JBBCode\\visitors\\TagCountingVisitor' => $vendorDir . '/jbbcode/jbbcode/JBBCode/visitors/TagCountingVisitor.php',
'Leafo\\ScssPhp\\Base\\Range' => $vendorDir . '/leafo/scssphp/src/Base/Range.php',
'Leafo\\ScssPhp\\Block' => $vendorDir . '/leafo/scssphp/src/Block.php',
'Leafo\\ScssPhp\\Colors' => $vendorDir . '/leafo/scssphp/src/Colors.php',
'Leafo\\ScssPhp\\Compiler' => $vendorDir . '/leafo/scssphp/src/Compiler.php',
'Leafo\\ScssPhp\\Compiler\\Environment' => $vendorDir . '/leafo/scssphp/src/Compiler/Environment.php',
'Leafo\\ScssPhp\\Formatter' => $vendorDir . '/leafo/scssphp/src/Formatter.php',
'Leafo\\ScssPhp\\Formatter\\Compact' => $vendorDir . '/leafo/scssphp/src/Formatter/Compact.php',
'Leafo\\ScssPhp\\Formatter\\Compressed' => $vendorDir . '/leafo/scssphp/src/Formatter/Compressed.php',
'Leafo\\ScssPhp\\Formatter\\Crunched' => $vendorDir . '/leafo/scssphp/src/Formatter/Crunched.php',
'Leafo\\ScssPhp\\Formatter\\Debug' => $vendorDir . '/leafo/scssphp/src/Formatter/Debug.php',
'Leafo\\ScssPhp\\Formatter\\Expanded' => $vendorDir . '/leafo/scssphp/src/Formatter/Expanded.php',
'Leafo\\ScssPhp\\Formatter\\Nested' => $vendorDir . '/leafo/scssphp/src/Formatter/Nested.php',
'Leafo\\ScssPhp\\Formatter\\OutputBlock' => $vendorDir . '/leafo/scssphp/src/Formatter/OutputBlock.php',
'Leafo\\ScssPhp\\Node' => $vendorDir . '/leafo/scssphp/src/Node.php',
'Leafo\\ScssPhp\\Node\\Number' => $vendorDir . '/leafo/scssphp/src/Node/Number.php',
'Leafo\\ScssPhp\\Parser' => $vendorDir . '/leafo/scssphp/src/Parser.php',
'Leafo\\ScssPhp\\Server' => $vendorDir . '/leafo/scssphp/src/Server.php',
'Leafo\\ScssPhp\\Type' => $vendorDir . '/leafo/scssphp/src/Type.php',
'Leafo\\ScssPhp\\Util' => $vendorDir . '/leafo/scssphp/src/Util.php',
'Leafo\\ScssPhp\\Version' => $vendorDir . '/leafo/scssphp/src/Version.php',
'League\\HTMLToMarkdown\\Configuration' => $vendorDir . '/league/html-to-markdown/src/Configuration.php',
'League\\HTMLToMarkdown\\ConfigurationAwareInterface' => $vendorDir . '/league/html-to-markdown/src/ConfigurationAwareInterface.php',
'League\\HTMLToMarkdown\\Converter\\BlockquoteConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/BlockquoteConverter.php',
'League\\HTMLToMarkdown\\Converter\\CommentConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/CommentConverter.php',
'League\\HTMLToMarkdown\\Converter\\ConverterInterface' => $vendorDir . '/league/html-to-markdown/src/Converter/ConverterInterface.php',
'League\\HTMLToMarkdown\\Converter\\DefaultConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/DefaultConverter.php',
'League\\HTMLToMarkdown\\Converter\\DivConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/DivConverter.php',
'League\\HTMLToMarkdown\\Converter\\EmphasisConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/EmphasisConverter.php',
'League\\HTMLToMarkdown\\Converter\\HardBreakConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/HardBreakConverter.php',
'League\\HTMLToMarkdown\\Converter\\HeaderConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/HeaderConverter.php',
'League\\HTMLToMarkdown\\Converter\\HorizontalRuleConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/HorizontalRuleConverter.php',
'League\\HTMLToMarkdown\\Converter\\ImageConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/ImageConverter.php',
'League\\HTMLToMarkdown\\Converter\\LinkConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/LinkConverter.php',
'League\\HTMLToMarkdown\\Converter\\ListBlockConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/ListBlockConverter.php',
'League\\HTMLToMarkdown\\Converter\\ListItemConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/ListItemConverter.php',
'League\\HTMLToMarkdown\\Converter\\ParagraphConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/ParagraphConverter.php',
'League\\HTMLToMarkdown\\Converter\\PreformattedConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/PreformattedConverter.php',
'League\\HTMLToMarkdown\\Converter\\TextConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/TextConverter.php',
'League\\HTMLToMarkdown\\Element' => $vendorDir . '/league/html-to-markdown/src/Element.php',
'League\\HTMLToMarkdown\\ElementInterface' => $vendorDir . '/league/html-to-markdown/src/ElementInterface.php',
'League\\HTMLToMarkdown\\Environment' => $vendorDir . '/league/html-to-markdown/src/Environment.php',
'League\\HTMLToMarkdown\\HtmlConverter' => $vendorDir . '/league/html-to-markdown/src/HtmlConverter.php',
'MatthiasMullie\\Minify\\CSS' => $vendorDir . '/matthiasmullie/minify/src/CSS.php',
'MatthiasMullie\\Minify\\Exception' => $vendorDir . '/matthiasmullie/minify/src/Exception.php',
'MatthiasMullie\\Minify\\Exceptions\\BasicException' => $vendorDir . '/matthiasmullie/minify/src/Exceptions/BasicException.php',
@ -304,4 +365,16 @@ return array(
'Requests_Utility_FilteredIterator' => $vendorDir . '/rmccue/requests/library/Requests/Utility/FilteredIterator.php',
'Sunra\\PhpSimple\\HtmlDomParser' => $vendorDir . '/sunra/php-simple-html-dom-parser/Src/Sunra/PhpSimple/HtmlDomParser.php',
'TrueBV\\Punycode' => $vendorDir . '/true/punycode/src/Punycode.php',
'lessc' => $vendorDir . '/leafo/lessphp/lessc.inc.php',
'lessc_formatter_classic' => $vendorDir . '/leafo/lessphp/lessc.inc.php',
'lessc_formatter_compressed' => $vendorDir . '/leafo/lessphp/lessc.inc.php',
'lessc_formatter_lessjs' => $vendorDir . '/leafo/lessphp/lessc.inc.php',
'lessc_parser' => $vendorDir . '/leafo/lessphp/lessc.inc.php',
'scss_formatter' => $vendorDir . '/leafo/scssphp/classmap.php',
'scss_formatter_compressed' => $vendorDir . '/leafo/scssphp/classmap.php',
'scss_formatter_crunched' => $vendorDir . '/leafo/scssphp/classmap.php',
'scss_formatter_nested' => $vendorDir . '/leafo/scssphp/classmap.php',
'scss_parser' => $vendorDir . '/leafo/scssphp/classmap.php',
'scss_server' => $vendorDir . '/leafo/scssphp/classmap.php',
'scssc' => $vendorDir . '/leafo/scssphp/classmap.php',
);

View file

@ -8,7 +8,8 @@ $baseDir = dirname($vendorDir);
return array(
'Sunra\\PhpSimple\\HtmlDomParser' => array($vendorDir . '/sunra/php-simple-html-dom-parser/Src'),
'Requests' => array($vendorDir . '/rmccue/requests/library'),
'Michelf' => array($vendorDir . '/michelf/php-markdown'),
'Michelf' => array($vendorDir . '/michelf/php-markdown', $vendorDir . '/michelf/php-smartypants'),
'JBBCode' => array($vendorDir . '/jbbcode/jbbcode'),
'Hautelook' => array($vendorDir . '/hautelook/phpass/src'),
'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'),
);

View file

@ -9,4 +9,6 @@ return array(
'TrueBV\\' => array($vendorDir . '/true/punycode/src'),
'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),
'League\\HTMLToMarkdown\\' => array($vendorDir . '/league/html-to-markdown/src'),
'Leafo\\ScssPhp\\' => array($vendorDir . '/leafo/scssphp/src'),
);

View file

@ -92,59 +92,6 @@
"html"
]
},
{
"name": "michelf/php-markdown",
"version": "1.5.0",
"version_normalized": "1.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/e1aabe18173231ebcefc90e615565742fc1c7fd9",
"reference": "e1aabe18173231ebcefc90e615565742fc1c7fd9",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-03-01 12:03:08",
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.4.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
},
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "https://michelf.ca/",
"role": "Developer"
}
],
"description": "PHP Markdown",
"homepage": "https://michelf.ca/projects/php-markdown/",
"keywords": [
"markdown"
]
},
{
"name": "sunra/php-simple-html-dom-parser",
"version": "v1.5.0",
@ -537,5 +484,330 @@
"password",
"security"
]
},
{
"name": "michelf/php-markdown",
"version": "1.6.0",
"version_normalized": "1.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "156e56ee036505ec637d761ee62dc425d807183c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/156e56ee036505ec637d761ee62dc425d807183c",
"reference": "156e56ee036505ec637d761ee62dc425d807183c",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-12-24 01:37:31",
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.4.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "https://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "https://daringfireball.net/"
}
],
"description": "PHP Markdown",
"homepage": "https://michelf.ca/projects/php-markdown/",
"keywords": [
"markdown"
]
},
{
"name": "jbbcode/jbbcode",
"version": "v1.3.0",
"version_normalized": "1.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/jbowens/jBBCode.git",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jbowens/jBBCode/zipball/645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"reference": "645b6a1c0afa92b7d029d3417ebd8b60a5c578b3",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"time": "2014-07-06 05:48:20",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"JBBCode": "."
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jackson Owens",
"email": "jackson_owens@alumni.brown.edu",
"homepage": "http://jbowens.org/",
"role": "Developer"
}
],
"description": "A lightweight but extensible BBCode parser written in PHP 5.3.",
"homepage": "http://jbbcode.com/",
"keywords": [
"BB",
"bbcode"
]
},
{
"name": "leafo/lessphp",
"version": "v0.5.0",
"version_normalized": "0.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/lessphp.git",
"reference": "0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/lessphp/zipball/0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283",
"reference": "0f5a7f5545d2bcf4e9fad9a228c8ad89cc9aa283",
"shasum": ""
},
"time": "2014-11-24 18:39:20",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"classmap": [
"lessc.inc.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT",
"GPL-3.0"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "lessphp is a compiler for LESS written in PHP.",
"homepage": "http://leafo.net/lessphp/"
},
{
"name": "leafo/scssphp",
"version": "v0.4.0",
"version_normalized": "0.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/leafo/scssphp.git",
"reference": "78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/scssphp/zipball/78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a",
"reference": "78a6f27aa4eaf70bb3ff4d13b639bab71fdaf47a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"kherge/box": "~2.5",
"phpunit/phpunit": "~3.7",
"squizlabs/php_codesniffer": "~2.3"
},
"time": "2015-11-09 14:44:09",
"bin": [
"bin/pscss"
],
"type": "library",
"installation-source": "dist",
"autoload": {
"classmap": [
"classmap.php"
],
"psr-4": {
"Leafo\\ScssPhp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "scssphp is a compiler for SCSS written in PHP.",
"homepage": "http://leafo.github.io/scssphp/",
"keywords": [
"css",
"less",
"sass",
"scss",
"stylesheet"
]
},
{
"name": "league/html-to-markdown",
"version": "4.2.0",
"version_normalized": "4.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/9a5becc8c6b520920fb846afefcfd7faf4c31712",
"reference": "9a5becc8c6b520920fb846afefcfd7faf4c31712",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-xml": "*",
"php": ">=5.3.3"
},
"require-dev": {
"mikehaertl/php-shellcommand": "~1.1.0",
"phpunit/phpunit": "4.*",
"scrutinizer/ocular": "~1.1"
},
"time": "2016-02-01 16:49:02",
"bin": [
"bin/html-to-markdown"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"League\\HTMLToMarkdown\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Colin O'Dell",
"email": "colinodell@gmail.com",
"homepage": "http://www.colinodell.com",
"role": "Lead Developer"
},
{
"name": "Nick Cernis",
"email": "nick@cern.is",
"homepage": "http://modernnerd.net",
"role": "Original Author"
}
],
"description": "An HTML-to-markdown conversion helper for PHP",
"homepage": "https://github.com/thephpleague/html-to-markdown",
"keywords": [
"html",
"markdown"
]
},
{
"name": "michelf/php-smartypants",
"version": "1.6.0-beta1",
"version_normalized": "1.6.0.0-beta1",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-smartypants.git",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2013-07-31 18:13:10",
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.6.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
}
],
"description": "PHP SmartyPants",
"homepage": "http://michelf.ca/projects/php-smartypants/",
"keywords": [
"dashes",
"quotes",
"spaces",
"typographer",
"typography"
]
}
]

2
vendor/jbbcode/jbbcode/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
composer.lock
vendor

View file

@ -0,0 +1,328 @@
<?php
namespace JBBCode;
/**
* This class represents a BBCode Definition. You may construct instances of this class directly,
* usually through the CodeDefinitionBuilder class, to create text replacement bbcodes, or you
* may subclass it to create more complex bbcode definitions.
*
* @author jbowens
*/
class CodeDefinition
{
/* NOTE: THIS PROPERTY SHOULD ALWAYS BE LOWERCASE; USE setTagName() TO ENSURE THIS */
protected $tagName;
/* Whether or not this CodeDefinition uses an option parameter. */
protected $useOption;
/* The replacement text to be used for simple CodeDefinitions */
protected $replacementText;
/* Whether or not to parse elements of this definition's contents */
protected $parseContent;
/* How many of this element type may be nested within each other */
protected $nestLimit;
/* How many of this element type have been seen */
protected $elCounter;
/* The input validator to run options through */
protected $optionValidator;
/* The input validator to run the body ({param}) through */
protected $bodyValidator;
/**
* Constructs a new CodeDefinition.
*/
public static function construct($tagName, $replacementText, $useOption = false,
$parseContent = true, $nestLimit = -1, $optionValidator = array(),
$bodyValidator = null)
{
$def = new CodeDefinition();
$def->elCounter = 0;
$def->setTagName($tagName);
$def->setReplacementText($replacementText);
$def->useOption = $useOption;
$def->parseContent = $parseContent;
$def->nestLimit = $nestLimit;
$def->optionValidator = $optionValidator;
$def->bodyValidator = $bodyValidator;
return $def;
}
/**
* Constructs a new CodeDefinition.
*
* This constructor is deprecated. You should use the static construct() method or the
* CodeDefinitionBuilder class to construct a new CodeDefiniton.
*
* @deprecated
*/
public function __construct()
{
/* WARNING: This function is deprecated and will be made protected in a future
* version of jBBCode. */
$this->parseContent = true;
$this->useOption = false;
$this->nestLimit = -1;
$this->elCounter = 0;
$this->optionValidator = array();
$this->bodyValidator = null;
}
/**
* Determines if the arguments to the given element are valid based on
* any validators attached to this CodeDefinition.
*
* @param $el the ElementNode to validate
* @return true if the ElementNode's {option} and {param} are OK, false if they're not
*/
public function hasValidInputs(ElementNode $el)
{
if ($this->usesOption() && $this->optionValidator) {
$att = $el->getAttribute();
foreach($att as $name => $value){
if(isset($this->optionValidator[$name]) && !$this->optionValidator[$name]->validate($value)){
return false;
}
}
}
if (!$this->parseContent() && $this->bodyValidator) {
/* We only evaluate the content if we're not parsing the content. */
$content = "";
foreach ($el->getChildren() as $child) {
$content .= $child->getAsBBCode();
}
if (!$this->bodyValidator->validate($content)) {
/* The content of the element is not valid. */
return false;
}
}
return true;
}
/**
* Accepts an ElementNode that is defined by this CodeDefinition and returns the HTML
* markup of the element. This is a commonly overridden class for custom CodeDefinitions
* so that the content can be directly manipulated.
*
* @param $el the element to return an html representation of
*
* @return the parsed html of this element (INCLUDING ITS CHILDREN)
*/
public function asHtml(ElementNode $el)
{
if (!$this->hasValidInputs($el)) {
return $el->getAsBBCode();
}
$html = $this->getReplacementText();
if ($this->usesOption()) {
$options = $el->getAttribute();
if(count($options)==1){
$vals = array_values($options);
$html = str_ireplace('{option}', reset($vals), $html);
}
else{
foreach($options as $key => $val){
$html = str_ireplace('{' . $key . '}', $val, $html);
}
}
}
$content = $this->getContent($el);
$html = str_ireplace('{param}', $content, $html);
return $html;
}
protected function getContent(ElementNode $el){
if ($this->parseContent()) {
$content = "";
foreach ($el->getChildren() as $child)
$content .= $child->getAsHTML();
} else {
$content = "";
foreach ($el->getChildren() as $child)
$content .= $child->getAsBBCode();
}
return $content;
}
/**
* Accepts an ElementNode that is defined by this CodeDefinition and returns the text
* representation of the element. This may be overridden by a custom CodeDefinition.
*
* @param $el the element to return a text representation of
*
* @return the text representation of $el
*/
public function asText(ElementNode $el)
{
if (!$this->hasValidInputs($el)) {
return $el->getAsBBCode();
}
$s = "";
foreach ($el->getChildren() as $child)
$s .= $child->getAsText();
return $s;
}
/**
* Returns the tag name of this code definition
*
* @return this definition's associated tag name
*/
public function getTagName()
{
return $this->tagName;
}
/**
* Returns the replacement text of this code definition. This usually has little, if any meaning if the
* CodeDefinition class was extended. For default, html replacement CodeDefinitions this returns the html
* markup for the definition.
*
* @return the replacement text of this CodeDefinition
*/
public function getReplacementText()
{
return $this->replacementText;
}
/**
* Returns whether or not this CodeDefinition uses the optional {option}
*
* @return true if this CodeDefinition uses the option, false otherwise
*/
public function usesOption()
{
return $this->useOption;
}
/**
* Returns whether or not this CodeDefnition parses elements contained within it,
* or just treats its children as text.
*
* @return true if this CodeDefinition parses elements contained within itself
*/
public function parseContent()
{
return $this->parseContent;
}
/**
* Returns the limit of how many elements defined by this CodeDefinition may be
* nested together. If after parsing elements are nested beyond this limit, the
* subtrees formed by those nodes will be removed from the parse tree. A nest
* limit of -1 signifies no limit.
*/
public function getNestLimit()
{
return $this->nestLimit;
}
/**
* Sets the tag name of this CodeDefinition
*
* @deprecated
*
* @param the new tag name of this definition
*/
public function setTagName($tagName)
{
$this->tagName = strtolower($tagName);
}
/**
* Sets the html replacement text of this CodeDefinition
*
* @deprecated
*
* @param the new replacement text
*/
public function setReplacementText($txt)
{
$this->replacementText = $txt;
}
/**
* Sets whether or not this CodeDefinition uses the {option}
*
* @deprecated
*
* @param boolean $bool
*/
public function setUseOption($bool)
{
$this->useOption = $bool;
}
/**
* Sets whether or not this CodeDefinition allows its children to be parsed as html
*
* @deprecated
*
* @param boolean $bool
*/
public function setParseContent($bool)
{
$this->parseContent = $bool;
}
/**
* Increments the element counter. This is used for tracking depth of elements of the same type for next limits.
*
* @deprecated
*
* @return void
*/
public function incrementCounter()
{
$this->elCounter++;
}
/**
* Decrements the element counter.
*
* @deprecated
*
* @return void
*/
public function decrementCounter()
{
$this->elCounter--;
}
/**
* Resets the element counter.
*
* @deprecated
*/
public function resetCounter()
{
$this->elCounter = 0;
}
/**
* Returns the current value of the element counter.
*
* @deprecated
*
* @return int
*/
public function getCounter()
{
return $this->elCounter;
}
}

View file

@ -0,0 +1,160 @@
<?php
namespace JBBCode;
require_once "CodeDefinition.php";
/**
* Implements the builder pattern for the CodeDefinition class. A builder
* is the recommended way of constructing CodeDefinition objects.
*
* @author jbowens
*/
class CodeDefinitionBuilder
{
protected $tagName;
protected $useOption = false;
protected $replacementText;
protected $parseContent = true;
protected $nestLimit = -1;
protected $optionValidator = array();
protected $bodyValidator = null;
/**
* Construct a CodeDefinitionBuilder.
*
* @param $tagName the tag name of the definition to build
* @param $replacementText the replacement text of the definition to build
*/
public function __construct($tagName, $replacementText)
{
$this->tagName = $tagName;
$this->replacementText = $replacementText;
}
/**
* Sets the tag name the CodeDefinition should be built with.
*
* @param $tagName the tag name for the new CodeDefinition
*/
public function setTagName($tagName)
{
$this->tagName = $tagName;
return $this;
}
/**
* Sets the replacement text that the new CodeDefinition should be
* built with.
*
* @param $replacementText the replacement text for the new CodeDefinition
*/
public function setReplacementText($replacementText)
{
$this->replacementText = $replacementText;
return $this;
}
/**
* Set whether or not the built CodeDefinition should use the {option} bbcode
* argument.
*
* @param $option ture iff the definition includes an option
*/
public function setUseOption($option)
{
$this->useOption = $option;
return $this;
}
/**
* Set whether or not the built CodeDefinition should allow its content
* to be parsed and evaluated as bbcode.
*
* @param $parseContent true iff the content should be parsed
*/
public function setParseContent($parseContent)
{
$this->parseContent = $parseContent;
return $this;
}
/**
* Sets the nest limit for this code definition.
*
* @param $nestLimit a positive integer, or -1 if there is no limit.
* @throws \InvalidArgumentException if the nest limit is invalid
*/
public function setNestLimit($limit)
{
if(!is_int($limit) || ($limit <= 0 && -1 != $limit)) {
throw new \InvalidArgumentException("A nest limit must be a positive integer " .
"or -1.");
}
$this->nestLimit = $limit;
return $this;
}
/**
* Sets the InputValidator that option arguments should be validated with.
*
* @param $validator the InputValidator instance to use
*/
public function setOptionValidator(\JBBCode\InputValidator $validator, $option=null)
{
if(empty($option)){
$option = $this->tagName;
}
$this->optionValidator[$option] = $validator;
return $this;
}
/**
* Sets the InputValidator that body ({param}) text should be validated with.
*
* @param $validator the InputValidator instance to use
*/
public function setBodyValidator(\JBBCode\InputValidator $validator)
{
$this->bodyValidator = $validator;
return $this;
}
/**
* Removes the attached option validator if one is attached.
*/
public function removeOptionValidator()
{
$this->optionValidator = array();
return $this;
}
/**
* Removes the attached body validator if one is attached.
*/
public function removeBodyValidator()
{
$this->bodyValidator = null;
return $this;
}
/**
* Builds a CodeDefinition with the current state of the builder.
*
* @return a new CodeDefinition instance
*/
public function build()
{
$definition = CodeDefinition::construct($this->tagName,
$this->replacementText,
$this->useOption,
$this->parseContent,
$this->nestLimit,
$this->optionValidator,
$this->bodyValidator);
return $definition;
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace JBBCode;
require_once 'CodeDefinition.php';
use JBBCode\CodeDefinition;
/**
* An interface for sets of code definitons.
*
* @author jbowens
*/
interface CodeDefinitionSet
{
/**
* Retrieves the CodeDefinitions within this set as an array.
*/
public function getCodeDefinitions();
}

View file

@ -0,0 +1,75 @@
<?php
namespace JBBCode;
require_once 'CodeDefinition.php';
require_once 'CodeDefinitionBuilder.php';
require_once 'CodeDefinitionSet.php';
require_once 'validators/CssColorValidator.php';
require_once 'validators/UrlValidator.php';
/**
* Provides a default set of common bbcode definitions.
*
* @author jbowens
*/
class DefaultCodeDefinitionSet implements CodeDefinitionSet
{
/* The default code definitions in this set. */
protected $definitions = array();
/**
* Constructs the default code definitions.
*/
public function __construct()
{
/* [b] bold tag */
$builder = new CodeDefinitionBuilder('b', '<strong>{param}</strong>');
array_push($this->definitions, $builder->build());
/* [i] italics tag */
$builder = new CodeDefinitionBuilder('i', '<em>{param}</em>');
array_push($this->definitions, $builder->build());
/* [u] underline tag */
$builder = new CodeDefinitionBuilder('u', '<u>{param}</u>');
array_push($this->definitions, $builder->build());
$urlValidator = new \JBBCode\validators\UrlValidator();
/* [url] link tag */
$builder = new CodeDefinitionBuilder('url', '<a href="{param}">{param}</a>');
$builder->setParseContent(false)->setBodyValidator($urlValidator);
array_push($this->definitions, $builder->build());
/* [url=http://example.com] link tag */
$builder = new CodeDefinitionBuilder('url', '<a href="{option}">{param}</a>');
$builder->setUseOption(true)->setParseContent(true)->setOptionValidator($urlValidator);
array_push($this->definitions, $builder->build());
/* [img] image tag */
$builder = new CodeDefinitionBuilder('img', '<img src="{param}" />');
$builder->setUseOption(false)->setParseContent(false)->setBodyValidator($urlValidator);
array_push($this->definitions, $builder->build());
/* [img=alt text] image tag */
$builder = new CodeDefinitionBuilder('img', '<img src="{param}" alt="{option}" />');
$builder->setUseOption(true)->setParseContent(false)->setBodyValidator($urlValidator);
array_push($this->definitions, $builder->build());
/* [color] color tag */
$builder = new CodeDefinitionBuilder('color', '<span style="color: {option}">{param}</span>');
$builder->setUseOption(true)->setOptionValidator(new \JBBCode\validators\CssColorValidator());
array_push($this->definitions, $builder->build());
}
/**
* Returns an array of the default code definitions.
*/
public function getCodeDefinitions()
{
return $this->definitions;
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace JBBCode;
require_once 'ElementNode.php';
/**
* A DocumentElement object represents the root of a document tree. All
* documents represented by this document model should have one as its root.
*
* @author jbowens
*/
class DocumentElement extends ElementNode
{
/**
* Constructs the document element node
*/
public function __construct()
{
parent::__construct();
$this->setTagName("Document");
$this->setNodeId(0);
}
/**
* (non-PHPdoc)
* @see JBBCode.ElementNode::getAsBBCode()
*
* Returns the BBCode representation of this document
*
* @return this document's bbcode representation
*/
public function getAsBBCode()
{
$s = "";
foreach($this->getChildren() as $child){
$s .= $child->getAsBBCode();
}
return $s;
}
/**
* (non-PHPdoc)
* @see JBBCode.ElementNode::getAsHTML()
*
* Documents don't add any html. They only exist as a container for their
* children, so getAsHTML() simply iterates through the document's children,
* returning their html.
*
* @return the HTML representation of this document
*/
public function getAsHTML()
{
$s = "";
foreach($this->getChildren() as $child)
$s .= $child->getAsHTML();
return $s;
}
public function accept(NodeVisitor $visitor)
{
$visitor->visitDocumentElement($this);
}
}

View file

@ -0,0 +1,241 @@
<?php
namespace JBBCode;
require_once 'Node.php';
/**
* An element within the tree. Consists of a tag name which defines the type of the
* element and any number of Node children. It also contains a CodeDefinition matching
* the tag name of the element.
*
* @author jbowens
*/
class ElementNode extends Node
{
/* The tagname of this element, for i.e. "b" in [b]bold[/b] */
protected $tagName;
/* The attribute, if any, of this element node */
protected $attribute;
/* The child nodes contained within this element */
protected $children;
/* The code definition that defines this element's behavior */
protected $codeDefinition;
/* How deeply this node is nested */
protected $nestDepth;
/**
* Constructs the element node
*/
public function __construct()
{
$this->children = array();
$this->nestDepth = 0;
}
/**
* Accepts the given NodeVisitor. This is part of an implementation
* of the Visitor pattern.
*
* @param $nodeVisitor the visitor attempting to visit this node
*/
public function accept(NodeVisitor $nodeVisitor)
{
$nodeVisitor->visitElementNode($this);
}
/**
* Gets the CodeDefinition that defines this element.
*
* @return this element's code definition
*/
public function getCodeDefinition()
{
return $this->codeDefinition;
}
/**
* Sets the CodeDefinition that defines this element.
*
* @param codeDef the code definition that defines this element node
*/
public function setCodeDefinition(CodeDefinition $codeDef)
{
$this->codeDefinition = $codeDef;
$this->setTagName($codeDef->getTagName());
}
/**
* Returns the tag name of this element.
*
* @return the element's tag name
*/
public function getTagName()
{
return $this->tagName;
}
/**
* Returns the attribute (used as the option in bbcode definitions) of this element.
*
* @return the attribute of this element
*/
public function getAttribute()
{
return $this->attribute;
}
/**
* Returns all the children of this element.
*
* @return an array of this node's child nodes
*/
public function getChildren()
{
return $this->children;
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsText()
*
* Returns the element as text (not including any bbcode markup)
*
* @return the plain text representation of this node
*/
public function getAsText()
{
if ($this->codeDefinition) {
return $this->codeDefinition->asText($this);
} else {
$s = "";
foreach ($this->getChildren() as $child)
$s .= $child->getAsText();
return $s;
}
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsBBCode()
*
* Returns the element as bbcode (with all unclosed tags closed)
*
* @return the bbcode representation of this element
*/
public function getAsBBCode()
{
$str = "[".$this->tagName;
if (!empty($this->attribute)) {
foreach($this->attribute as $key => $value){
if($key == $this->tagName){
$str .= "=".$value;
}
else{
$str .= " ".$key."=" . $value;
}
}
}
$str .= "]";
foreach ($this->getChildren() as $child) {
$str .= $child->getAsBBCode();
}
$str .= "[/".$this->tagName."]";
return $str;
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsHTML()
*
* Returns the element as html with all replacements made
*
* @return the html representation of this node
*/
public function getAsHTML()
{
if($this->codeDefinition) {
return $this->codeDefinition->asHtml($this);
} else {
return "";
}
}
/**
* Adds a child to this node's content. A child may be a TextNode, or
* another ElementNode... or anything else that may extend the
* abstract Node class.
*
* @param child the node to add as a child
*/
public function addChild(Node $child)
{
array_push($this->children, $child);
$child->setParent($this);
}
/**
* Removes a child from this node's contnet.
*
* @param child the child node to remove
*/
public function removeChild(Node $child)
{
foreach ($this->children as $key => $value) {
if ($value == $child)
unset($this->children[$key]);
}
}
/**
* Sets the tag name of this element node.
*
* @param tagName the element's new tag name
*/
public function setTagName($tagName)
{
$this->tagName = $tagName;
}
/**
* Sets the attribute (option) of this element node.
*
* @param attribute the attribute of this element node
*/
public function setAttribute($attribute)
{
$this->attribute = $attribute;
}
/**
* Traverses the parse tree upwards, going from parent to parent, until it finds a
* parent who has the given tag name. Returns the parent with the matching tag name
* if it exists, otherwise returns null.
*
* @param str the tag name to search for
*
* @return the closest parent with the given tag name
*/
public function closestParentOfType($str)
{
$str = strtolower($str);
$currentEl = $this;
while (strtolower($currentEl->getTagName()) != $str && $currentEl->hasParent()) {
$currentEl = $currentEl->getParent();
}
if (strtolower($currentEl->getTagName()) != $str) {
return null;
} else {
return $currentEl;
}
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace JBBCode;
/**
* Defines an interface for validation filters for bbcode options and
* parameters.
*
* @author jbowens
* @since May 2013
*/
interface InputValidator
{
/**
* Returns true iff the given input is valid, false otherwise.
*/
public function validate($input);
}

109
vendor/jbbcode/jbbcode/JBBCode/Node.php vendored Normal file
View file

@ -0,0 +1,109 @@
<?php
namespace JBBCode;
/**
* A node within the document tree.
*
* Known subclasses: TextNode, ElementNode
*
* @author jbowens
*/
abstract class Node
{
/* Pointer to the parent node of this node */
protected $parent;
/* The node id of this node */
protected $nodeid;
/**
* Returns the node id of this node. (Not really ever used. Dependent upon the parse tree the node exists within.)
*
* @return this node's id
*/
public function getNodeId()
{
return $this->nodeid;
}
/**
* Returns this node's immediate parent.
*
* @return the node's parent
*/
public function getParent()
{
return $this->parent;
}
/**
* Determines if this node has a parent.
*
* @return true if this node has a parent, false otherwise
*/
public function hasParent()
{
return $this->parent != null;
}
/**
* Returns true if this is a text node. Returns false otherwise.
* (Overridden by TextNode to return true)
*
* @return true if this node is a text node
*/
public function isTextNode()
{
return false;
}
/**
* Accepts a NodeVisitor
*
* @param nodeVisitor the NodeVisitor traversing the graph
*/
abstract public function accept(NodeVisitor $nodeVisitor);
/**
* Returns this node as text (without any bbcode markup)
*
* @return the plain text representation of this node
*/
abstract public function getAsText();
/**
* Returns this node as bbcode
*
* @return the bbcode representation of this node
*/
abstract public function getAsBBCode();
/**
* Returns this node as HTML
*
* @return the html representation of this node
*/
abstract public function getAsHTML();
/**
* Sets this node's parent to be the given node.
*
* @param parent the node to set as this node's parent
*/
public function setParent(Node $parent)
{
$this->parent = $parent;
}
/**
* Sets this node's nodeid
*
* @param nodeid this node's node id
*/
public function setNodeId($nodeid)
{
$this->nodeid = $nodeid;
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace JBBCode;
/**
* Defines an interface for a visitor to traverse the node graph.
*
* @author jbowens
* @since January 2013
*/
interface NodeVisitor
{
public function visitDocumentElement(DocumentElement $documentElement);
public function visitTextNode(TextNode $textNode);
public function visitElementNode(ElementNode $elementNode);
}

View file

@ -0,0 +1,662 @@
<?php
namespace JBBCode;
require_once 'ElementNode.php';
require_once 'TextNode.php';
require_once 'DefaultCodeDefinitionSet.php';
require_once 'DocumentElement.php';
require_once 'CodeDefinition.php';
require_once 'CodeDefinitionBuilder.php';
require_once 'CodeDefinitionSet.php';
require_once 'NodeVisitor.php';
require_once 'ParserException.php';
require_once 'Tokenizer.php';
require_once 'visitors/NestLimitVisitor.php';
require_once 'InputValidator.php';
use JBBCode\CodeDefinition;
/**
* BBCodeParser is the main parser class that constructs and stores the parse tree. Through this class
* new bbcode definitions can be added, and documents may be parsed and converted to html/bbcode/plaintext, etc.
*
* @author jbowens
*/
class Parser
{
const OPTION_STATE_DEFAULT = 0;
const OPTION_STATE_TAGNAME = 1;
const OPTION_STATE_KEY = 2;
const OPTION_STATE_VALUE = 3;
const OPTION_STATE_QUOTED_VALUE = 4;
const OPTION_STATE_JAVASCRIPT = 5;
/* The root element of the parse tree */
protected $treeRoot;
/* The list of bbcodes to be used by the parser. */
protected $bbcodes;
/* The next node id to use. This is used while parsing. */
protected $nextNodeid;
/**
* Constructs an instance of the BBCode parser
*/
public function __construct()
{
$this->reset();
$this->bbcodes = array();
}
/**
* Adds a simple (text-replacement only) bbcode definition
*
* @param string $tagName the tag name of the code (for example the b in [b])
* @param string $replace the html to use, with {param} and optionally {option} for replacements
* @param boolean $useOption whether or not this bbcode uses the secondary {option} replacement
* @param boolean $parseContent whether or not to parse the content within these elements
* @param integer $nestLimit an optional limit of the number of elements of this kind that can be nested within
* each other before the parser stops parsing them.
* @param InputValidator $optionValidator the validator to run {option} through
* @param BodyValidator $bodyValidator the validator to run {param} through (only used if $parseContent == false)
*
* @return Parser
*/
public function addBBCode($tagName, $replace, $useOption = false, $parseContent = true, $nestLimit = -1,
InputValidator $optionValidator = null, InputValidator $bodyValidator = null)
{
$builder = new CodeDefinitionBuilder($tagName, $replace);
$builder->setUseOption($useOption);
$builder->setParseContent($parseContent);
$builder->setNestLimit($nestLimit);
if ($optionValidator) {
$builder->setOptionValidator($optionValidator);
}
if ($bodyValidator) {
$builder->setBodyValidator($bodyValidator);
}
$this->addCodeDefinition($builder->build());
return $this;
}
/**
* Adds a complex bbcode definition. You may subclass the CodeDefinition class, instantiate a definition of your new
* class and add it to the parser through this method.
*
* @param CodeDefinition $definition the bbcode definition to add
*
* @return Parser
*/
public function addCodeDefinition(CodeDefinition $definition)
{
array_push($this->bbcodes, $definition);
return $this;
}
/**
* Adds a set of CodeDefinitions.
*
* @param CodeDefinitionSet $set the set of definitions to add
*
* @return Parser
*/
public function addCodeDefinitionSet(CodeDefinitionSet $set) {
foreach ($set->getCodeDefinitions() as $def) {
$this->addCodeDefinition($def);
}
return $this;
}
/**
* Returns the entire parse tree as text. Only {param} content is returned. BBCode markup will be ignored.
*
* @return string a text representation of the parse tree
*/
public function getAsText()
{
return $this->treeRoot->getAsText();
}
/**
* Returns the entire parse tree as bbcode. This will be identical to the inputted string, except unclosed tags
* will be closed.
*
* @return string a bbcode representation of the parse tree
*/
public function getAsBBCode()
{
return $this->treeRoot->getAsBBCode();
}
/**
* Returns the entire parse tree as HTML. All BBCode replacements will be made. This is generally the method
* you will want to use to retrieve the parsed bbcode.
*
* @return string a parsed html string
*/
public function getAsHTML()
{
return $this->treeRoot->getAsHTML();
}
/**
* Accepts the given NodeVisitor at the root.
*
* @param NodeVisitor a NodeVisitor
*
* @return Parser
*/
public function accept(NodeVisitor $nodeVisitor)
{
$this->treeRoot->accept($nodeVisitor);
return $this;
}
/**
* Constructs the parse tree from a string of bbcode markup.
*
* @param string $str the bbcode markup to parse
*
* @return Parser
*/
public function parse($str)
{
/* Set the tree root back to a fresh DocumentElement. */
$this->reset();
$parent = $this->treeRoot;
$tokenizer = new Tokenizer($str);
while ($tokenizer->hasNext()) {
$parent = $this->parseStartState($parent, $tokenizer);
if ($parent->getCodeDefinition() && false ===
$parent->getCodeDefinition()->parseContent()) {
/* We're inside an element that does not allow its contents to be parseable. */
$this->parseAsTextUntilClose($parent, $tokenizer);
$parent = $parent->getParent();
}
}
/* We parsed ignoring nest limits. Do an O(n) traversal to remove any elements that
* are nested beyond their CodeDefinition's nest limit. */
$this->removeOverNestedElements();
return $this;
}
/**
* Removes any elements that are nested beyond their nest limit from the parse tree. This
* method is now deprecated. In a future release its access privileges will be made
* protected.
*
* @deprecated
*/
public function removeOverNestedElements()
{
$nestLimitVisitor = new \JBBCode\visitors\NestLimitVisitor();
$this->accept($nestLimitVisitor);
}
/**
* Removes the old parse tree if one exists.
*/
protected function reset()
{
// remove any old tree information
$this->treeRoot = new DocumentElement();
/* The document element is created with nodeid 0. */
$this->nextNodeid = 1;
}
/**
* Determines whether a bbcode exists based on its tag name and whether or not it uses an option
*
* @param string $tagName the bbcode tag name to check
* @param boolean $usesOption whether or not the bbcode accepts an option
*
* @return bool true if the code exists, false otherwise
*/
public function codeExists($tagName, $usesOption = false)
{
foreach ($this->bbcodes as $code) {
if (strtolower($tagName) == $code->getTagName() && $usesOption == $code->usesOption()) {
return true;
}
}
return false;
}
/**
* Returns the CodeDefinition of a bbcode with the matching tag name and usesOption parameter
*
* @param string $tagName the tag name of the bbcode being searched for
* @param boolean $usesOption whether or not the bbcode accepts an option
*
* @return CodeDefinition if the bbcode exists, null otherwise
*/
public function getCode($tagName, $usesOption = false)
{
foreach ($this->bbcodes as $code) {
if (strtolower($tagName) == $code->getTagName() && $code->usesOption() == $usesOption) {
return $code;
}
}
return null;
}
/**
* Adds a set of default, standard bbcode definitions commonly used across the web.
*
* This method is now deprecated. Please use DefaultCodeDefinitionSet and
* addCodeDefinitionSet() instead.
*
* @deprecated
*/
public function loadDefaultCodes()
{
$defaultSet = new DefaultCodeDefinitionSet();
$this->addCodeDefinitionSet($defaultSet);
}
/**
* Creates a new text node with the given parent and text string.
*
* @param $parent the parent of the text node
* @param $string the text of the text node
*
* @return TextNode the newly created TextNode
*/
protected function createTextNode(ElementNode $parent, $string)
{
if (count($parent->getChildren())) {
$children = $parent->getChildren();
$lastElement = end($children);
reset($children);
if ($lastElement->isTextNode()) {
$lastElement->setValue($lastElement->getValue() . $string);
return $lastElement;
}
}
$textNode = new TextNode($string);
$textNode->setNodeId(++$this->nextNodeid);
$parent->addChild($textNode);
return $textNode;
}
/**
* jBBCode parsing logic is loosely modelled after a FSM. While not every function maps
* to a unique DFSM state, each function handles the logic of one or more FSM states.
* This function handles the beginning parse state when we're not currently in a tag
* name.
*
* @param ElementNode $parent the current parent node we're under
* @param Tokenizer $tokenizer the tokenizer we're using
*
* @return ElementNode the new parent we should use for the next iteration.
*/
protected function parseStartState(ElementNode $parent, Tokenizer $tokenizer)
{
$next = $tokenizer->next();
if ('[' == $next) {
return $this->parseTagOpen($parent, $tokenizer);
}
else {
$this->createTextNode($parent, $next);
/* Drop back into the main parse loop which will call this
* same method again. */
return $parent;
}
}
/**
* This function handles parsing the beginnings of an open tag. When we see a [
* at an appropriate time, this function is entered.
*
* @param ElementNode $parent the current parent node
* @param Tokenizer $tokenizer the tokenizer we're using
*
* @return ElementNode the new parent node
*/
protected function parseTagOpen(ElementNode $parent, Tokenizer $tokenizer)
{
if (!$tokenizer->hasNext()) {
/* The [ that sent us to this state was just a trailing [, not the
* opening for a new tag. Treat it as such. */
$this->createTextNode($parent, '[');
return $parent;
}
$next = $tokenizer->next();
/* This while loop could be replaced by a recursive call to this same method,
* which would likely be a lot clearer but I decided to use a while loop to
* prevent stack overflow with a string like [[[[[[[[[...[[[.
*/
while ('[' == $next) {
/* The previous [ was just a random bracket that should be treated as text.
* Continue until we get a non open bracket. */
$this->createTextNode($parent, '[');
if (!$tokenizer->hasNext()) {
$this->createTextNode($parent, '[');
return $parent;
}
$next = $tokenizer->next();
}
if (!$tokenizer->hasNext()) {
$this->createTextNode($parent, '['.$next);
return $parent;
}
$after_next = $tokenizer->next();
$tokenizer->stepBack();
if ($after_next != ']')
{
$this->createTextNode($parent, '['.$next);
return $parent;
}
/* At this point $next is either ']' or plain text. */
if (']' == $next) {
$this->createTextNode($parent, '[');
$this->createTextNode($parent, ']');
return $parent;
} else {
/* $next is plain text... likely a tag name. */
return $this->parseTag($parent, $tokenizer, $next);
}
}
protected function parseOptions($tagContent)
{
$buffer = "";
$tagName = "";
$state = static::OPTION_STATE_TAGNAME;
$keys = array();
$values = array();
$options = array();
$len = strlen($tagContent);
$done = false;
$idx = 0;
try{
while(!$done){
$char = $idx < $len ? $tagContent[$idx]:null;
switch($state){
case static::OPTION_STATE_TAGNAME:
switch($char){
case '=':
$state = static::OPTION_STATE_VALUE;
$tagName = $buffer;
$keys[] = $tagName;
$buffer = "";
break;
case ' ':
$state = static::OPTION_STATE_DEFAULT;
$tagName = $buffer;
$buffer = '';
$keys[] = $tagName;
break;
case null:
$tagName = $buffer;
$buffer = '';
$keys[] = $tagName;
break;
default:
$buffer .= $char;
}
break;
case static::OPTION_STATE_DEFAULT:
switch($char){
case ' ':
// do nothing
default:
$state = static::OPTION_STATE_KEY;
$buffer .= $char;
}
break;
case static::OPTION_STATE_VALUE:
switch($char){
case '"':
$state = static::OPTION_STATE_QUOTED_VALUE;
break;
case null: // intentional fall-through
case ' ': // key=value<space> delimits to next key
$values[] = $buffer;
$buffer = "";
$state = static::OPTION_STATE_KEY;
break;
case ":":
if($buffer=="javascript"){
$state = static::OPTION_STATE_JAVASCRIPT;
}
$buffer .= $char;
break;
default:
$buffer .= $char;
}
break;
case static::OPTION_STATE_JAVASCRIPT:
switch($char){
case ";":
$buffer .= $char;
$values[] = $buffer;
$buffer = "";
$state = static::OPTION_STATE_KEY;
break;
default:
$buffer .= $char;
}
break;
case static::OPTION_STATE_KEY:
switch($char){
case '=':
$state = static::OPTION_STATE_VALUE;
$keys[] = $buffer;
$buffer = '';
break;
case ' ': // ignore <space>key=value
break;
default:
$buffer .= $char;
break;
}
break;
case static::OPTION_STATE_QUOTED_VALUE:
switch($char){
case null:
case '"':
$state = static::OPTION_STATE_KEY;
$values[] = $buffer;
$buffer = '';
// peek ahead. If the next character is not a space or a closing brace, we have a bad tag and need to abort
if(isset($tagContent[$idx+1]) && $tagContent[$idx+1]!=" " && $tagContent[$idx+1]!="]" ){
throw new ParserException("Badly formed attribute: $tagContent");
}
break;
default:
$buffer .= $char;
break;
}
break;
default:
if(!empty($char)){
$state = static::OPTION_STATE_KEY;
}
}
if($idx >= $len){
$done = true;
}
$idx++;
}
if(count($keys) && count($values)){
if(count($keys)==(count($values)+1)){
array_unshift($values, "");
}
$options = array_combine($keys, $values);
}
}
catch(ParserException $e){
// if we're in this state, then something evidently went wrong. We'll consider everything that came after the tagname to be the attribute for that keyname
$options[$tagName]= substr($tagContent, strpos($tagContent, "=")+1);
}
return array($tagName, $options);
}
/**
* This is the next step in parsing a tag. It's possible for it to still be invalid at this
* point but many of the basic invalid tag name conditions have already been handled.
*
* @param ElementNode $parent the current parent element
* @param Tokenizer $tokenizer the tokenizer we're using
* @param string $tagContent the text between the [ and the ], assuming there is actually a ]
*
* @return ElementNode the new parent element
*/
protected function parseTag(ElementNode $parent, Tokenizer $tokenizer, $tagContent)
{
$next;
if (!$tokenizer->hasNext() || ($next = $tokenizer->next()) != ']') {
/* This is a malformed tag. Both the previous [ and the tagContent
* is really just plain text. */
$this->createTextNode($parent, '[');
$this->createTextNode($parent, $tagContent);
return $parent;
}
/* This is a well-formed tag consisting of [something] or [/something], but
* we still need to ensure that 'something' is a valid tag name. Additionally,
* if it's a closing tag, we need to ensure that there was a previous matching
* opening tag.
*/
/* There could be attributes. */
list($tmpTagName, $options) = $this->parseOptions($tagContent);
// $tagPieces = explode('=', $tagContent);
// $tmpTagName = $tagPieces[0];
$actualTagName;
if ('' != $tmpTagName && '/' == $tmpTagName[0]) {
/* This is a closing tag name. */
$actualTagName = substr($tmpTagName, 1);
} else {
$actualTagName = $tmpTagName;
}
if ('' != $tmpTagName && '/' == $tmpTagName[0]) {
/* This is attempting to close an open tag. We must verify that there exists an
* open tag of the same type and that there is no option (options on closing
* tags don't make any sense). */
$elToClose = $parent->closestParentOfType($actualTagName);
if (null == $elToClose || count($options) > 1) {
/* Closing an unopened tag or has an option. Treat everything as plain text. */
$this->createTextNode($parent, '[');
$this->createTextNode($parent, $tagContent);
$this->createTextNode($parent, ']');
return $parent;
} else {
/* We're closing $elToClose. In order to do that, we just need to return
* $elToClose's parent, since that will change our effective parent to be
* elToClose's parent. */
return $elToClose->getParent();
}
}
/* Verify that this is a known bbcode tag name. */
if ('' == $actualTagName || !$this->codeExists($actualTagName, !empty($options))) {
/* This is an invalid tag name! Treat everything we've seen as plain text. */
$this->createTextNode($parent, '[');
$this->createTextNode($parent, $tagContent);
$this->createTextNode($parent, ']');
return $parent;
}
/* If we're here, this is a valid opening tag. Let's make a new node for it. */
$el = new ElementNode();
$el->setNodeId(++$this->nextNodeid);
$code = $this->getCode($actualTagName, !empty($options));
$el->setCodeDefinition($code);
if (!empty($options)) {
/* We have an attribute we should save. */
$el->setAttribute($options);
}
$parent->addChild($el);
return $el;
}
/**
* Handles parsing elements whose CodeDefinitions disable parsing of element
* contents. This function uses a rolling window of 3 tokens until it finds the
* appropriate closing tag or reaches the end of the token stream.
*
* @param ElementNode $parent the current parent element
* @param Tokenizer $tokenizer the tokenizer we're using
*
* @return ElementNode the new parent element
*/
protected function parseAsTextUntilClose(ElementNode $parent, Tokenizer $tokenizer)
{
/* $parent's code definition doesn't allow its contents to be parsed. Here we use
* a sliding window of three tokens until we find [ /tagname ], signifying the
* end of the parent. */
if (!$tokenizer->hasNext()) {
return $parent;
}
$prevPrev = $tokenizer->next();
if (!$tokenizer->hasNext()) {
$this->createTextNode($parent, $prevPrev);
return $parent;
}
$prev = $tokenizer->next();
if (!$tokenizer->hasNext()) {
$this->createTextNode($parent, $prevPrev);
$this->createTextNode($parent, $prev);
return $parent;
}
$curr = $tokenizer->next();
while ('[' != $prevPrev || '/'.$parent->getTagName() != strtolower($prev) ||
']' != $curr) {
$this->createTextNode($parent, $prevPrev);
$prevPrev = $prev;
$prev = $curr;
if (!$tokenizer->hasNext()) {
$this->createTextNode($parent, $prevPrev);
$this->createTextNode($parent, $prev);
return $parent;
}
$curr = $tokenizer->next();
}
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace JBBCode;
use Exception;
class ParserException extends Exception{
}

View file

@ -0,0 +1,102 @@
<?php
namespace JBBCode;
require_once 'Node.php';
/**
* Represents a piece of text data. TextNodes never have children.
*
* @author jbowens
*/
class TextNode extends Node
{
/* The value of this text node */
protected $value;
/**
* Constructs a text node from its text string
*
* @param string $val
*/
public function __construct($val)
{
$this->value = $val;
}
public function accept(NodeVisitor $visitor)
{
$visitor->visitTextNode($this);
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::isTextNode()
*
* returns true
*/
public function isTextNode()
{
return true;
}
/**
* Returns the text string value of this text node.
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsText()
*
* Returns the text representation of this node.
*
* @return this node represented as text
*/
public function getAsText()
{
return $this->getValue();
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsBBCode()
*
* Returns the bbcode representation of this node. (Just its value)
*
* @return this node represented as bbcode
*/
public function getAsBBCode()
{
return $this->getValue();
}
/**
* (non-PHPdoc)
* @see JBBCode.Node::getAsHTML()
*
* Returns the html representation of this node. (Just its value)
*
* @return this node represented as HTML
*/
public function getAsHTML()
{
return $this->getValue();
}
/**
* Edits the text value contained within this text node.
*
* @param newValue the new text value of the text node
*/
public function setValue($newValue)
{
$this->value = $newValue;
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace JBBCode;
/**
* This Tokenizer is used while constructing the parse tree. The tokenizer
* handles splitting the input into brackets and miscellaneous text. The
* parser is then built as a FSM ontop of these possible inputs.
*
* @author jbowens
*/
class Tokenizer
{
protected $tokens = array();
protected $i = -1;
/**
* Constructs a tokenizer from the given string. The string will be tokenized
* upon construction.
*
* @param $str the string to tokenize
*/
public function __construct($str)
{
$strStart = 0;
for ($index = 0; $index < strlen($str); ++$index) {
if (']' == $str[$index] || '[' == $str[$index]) {
/* Are there characters in the buffer from a previous string? */
if ($strStart < $index) {
array_push($this->tokens, substr($str, $strStart, $index - $strStart));
$strStart = $index;
}
/* Add the [ or ] to the tokens array. */
array_push($this->tokens, $str[$index]);
$strStart = $index+1;
}
}
if ($strStart < strlen($str)) {
/* There are still characters in the buffer. Add them to the tokens. */
array_push($this->tokens, substr($str, $strStart, strlen($str) - $strStart));
}
}
/**
* Returns true if there is another token in the token stream.
*/
public function hasNext()
{
return count($this->tokens) > 1 + $this->i;
}
/**
* Advances the token stream to the next token and returns the new token.
*/
public function next()
{
if (!$this->hasNext()) {
return null;
} else {
return $this->tokens[++$this->i];
}
}
/**
* Retrieves the current token.
*/
public function current()
{
if ($this->i < 0) {
return null;
} else {
return $this->tokens[$this->i];
}
}
/**
* Moves the token stream back a token.
*/
public function stepBack()
{
if ($this->i > -1) {
$this->i--;
}
}
/**
* Restarts the tokenizer, returning to the beginning of the token stream.
*/
public function restart()
{
$this->i = -1;
}
/**
* toString method that returns the entire string from the current index on.
*/
public function toString()
{
return implode('', array_slice($this->tokens, $this->i + 1));
}
}

View file

@ -0,0 +1,12 @@
<?php
require_once "/path/to/jbbcode/Parser.php";
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$text = "The default codes include: [b]bold[/b], [i]italics[/i], [u]underlining[/u], ";
$text .= "[url=http://jbbcode.com]links[/url], [color=red]color![/color] and more.";
$parser->parse($text);
print $parser->getAsHtml();

View file

@ -0,0 +1,10 @@
<?php
require_once "/path/to/jbbcode/Parser.php";
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$text = "The bbcode in here [b]is never closed!";
$parser->parse($text);
print $parser->getAsBBCode();

View file

@ -0,0 +1,11 @@
<?php
require_once "/path/to/jbbcode/Parser.php";
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$text = "[b][u]There is [i]a lot[/i] of [url=http://en.wikipedia.org/wiki/Markup_language]markup[/url] in this";
$text .= "[color=#333333]text[/color]![/u][/b]";
$parser->parse($text);
print $parser->getAsText();

View file

@ -0,0 +1,7 @@
<?php
require_once "/path/to/jbbcode/Parser.php";
$parser = new JBBCode\Parser();
$parser->addBBCode("quote", '<div class="quote">{param}</div>');
$parser->addBBCode("code", '<pre class="code">{param}</pre>', false, false, 1);

View file

@ -0,0 +1,22 @@
<?php
require_once("../Parser.php");
require_once("../visitors/SmileyVisitor.php");
error_reporting(E_ALL);
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
if (count($argv) < 2) {
die("Usage: " . $argv[0] . " \"bbcode string\"\n");
}
$inputText = $argv[1];
$parser->parse($inputText);
$smileyVisitor = new \JBBCode\visitors\SmileyVisitor();
$parser->accept($smileyVisitor);
echo $parser->getAsHTML() . "\n";

View file

@ -0,0 +1,23 @@
<?php
require_once("../Parser.php");
require_once("../visitors/TagCountingVisitor.php");
error_reporting(E_ALL);
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
if (count($argv) < 3) {
die("Usage: " . $argv[0] . " \"bbcode string\" <tag name to check>\n");
}
$inputText = $argv[1];
$tagName = $argv[2];
$parser->parse($inputText);
$tagCountingVisitor = new \JBBCode\visitors\TagCountingVisitor();
$parser->accept($tagCountingVisitor);
echo $tagCountingVisitor->getFrequency($tagName) . "\n";

View file

@ -0,0 +1,85 @@
<?php
require_once(dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'Parser.php');
/**
* Test cases testing the functionality of parsing bbcode and
* retrieving a bbcode well-formed bbcode representation.
*
* @author jbowens
*/
class BBCodeToBBCodeTest extends PHPUnit_Framework_TestCase
{
/**
* A utility method for these tests that will evaluate its arguments as bbcode with
* a fresh parser loaded with only the default bbcodes. It returns the
* bbcode output, which in most cases should be in the input itself.
*/
private function defaultBBCodeParse($bbcode)
{
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$parser->parse($bbcode);
return $parser->getAsBBCode();
}
/**
* Asserts that the given bbcode matches the given text when
* the bbcode is run through defaultBBCodeParse
*/
private function assertBBCodeOutput($bbcode, $text)
{
$this->assertEquals($this->defaultBBCodeParse($bbcode), $text);
}
public function testEmptyString()
{
$this->assertBBCodeOutput('', '');
}
public function testOneTag()
{
$this->assertBBCodeOutput('[b]this is bold[/b]', '[b]this is bold[/b]');
}
public function testOneTagWithSurroundingText()
{
$this->assertBBCodeOutput('buffer text [b]this is bold[/b] buffer text',
'buffer text [b]this is bold[/b] buffer text');
}
public function testMultipleTags()
{
$bbcode = 'this is some text with [b]bold tags[/b] and [i]italics[/i] and ' .
'things like [u]that[/u].';
$bbcodeOutput = 'this is some text with [b]bold tags[/b] and [i]italics[/i] and ' .
'things like [u]that[/u].';
$this->assertBBCodeOutput($bbcode, $bbcodeOutput);
}
public function testCodeOptions()
{
$code = 'This contains a [url=http://jbbcode.com]url[/url] which uses an option.';
$codeOutput = 'This contains a [url=http://jbbcode.com]url[/url] which uses an option.';
$this->assertBBCodeOutput($code, $codeOutput);
}
/**
* @depends testCodeOptions
*/
public function testOmittedOption()
{
$code = 'This doesn\'t use the url option [url]http://jbbcode.com[/url].';
$codeOutput = 'This doesn\'t use the url option [url]http://jbbcode.com[/url].';
$this->assertBBCodeOutput($code, $codeOutput);
}
public function testUnclosedTags()
{
$code = '[b]bold';
$codeOutput = '[b]bold[/b]';
$this->assertBBCodeOutput($code, $codeOutput);
}
}

View file

@ -0,0 +1,78 @@
<?php
require_once(dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'Parser.php');
/**
* Test cases testing the ability to parse bbcode and retrieve a
* plain text representation without any markup.
*
* @author jbowens
*/
class BBCodeToTextTest extends PHPUnit_Framework_TestCase
{
/**
* A utility method for these tests that will evaluate
* its arguments as bbcode with a fresh parser loaded
* with only the default bbcodes. It returns the
* text output.
*/
private function defaultTextParse($bbcode)
{
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$parser->parse($bbcode);
return $parser->getAsText();
}
/**
* Asserts that the given bbcode matches the given text when
* the bbcode is run through defaultTextParse
*/
private function assertTextOutput($bbcode, $text)
{
$this->assertEquals($text, $this->defaultTextParse($bbcode));
}
public function testEmptyString()
{
$this->assertTextOutput('', '');
}
public function testOneTag()
{
$this->assertTextOutput('[b]this is bold[/b]', 'this is bold');
}
public function testOneTagWithSurroundingText()
{
$this->assertTextOutput('buffer text [b]this is bold[/b] buffer text',
'buffer text this is bold buffer text');
}
public function testMultipleTags()
{
$bbcode = 'this is some text with [b]bold tags[/b] and [i]italics[/i] and ' .
'things like [u]that[/u].';
$text = 'this is some text with bold tags and italics and things like that.';
$this->assertTextOutput($bbcode, $text);
}
public function testCodeOptions()
{
$code = 'This contains a [url=http://jbbcode.com]url[/url] which uses an option.';
$text = 'This contains a url which uses an option.';
$this->assertTextOutput($code, $text);
}
/**
* @depends testCodeOptions
*/
public function testOmittedOption()
{
$code = 'This doesn\'t use the url option [url]http://jbbcode.com[/url].';
$text = 'This doesn\'t use the url option http://jbbcode.com.';
$this->assertTextOutput($code, $text);
}
}

View file

@ -0,0 +1,54 @@
<?php
require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'Parser.php';
/**
* Test cases for the default bbcode set.
*
* @author jbowens
* @since May 2013
*/
class DefaultCodesTest extends PHPUnit_Framework_TestCase
{
/**
* Asserts that the given bbcode string produces the given html string
* when parsed with the default bbcodes.
*/
public function assertProduces($bbcode, $html)
{
$parser = new \JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$parser->parse($bbcode);
$this->assertEquals($html, $parser->getAsHtml());
}
/**
* Tests the [b] bbcode.
*/
public function testBold()
{
$this->assertProduces('[b]this should be bold[/b]', '<strong>this should be bold</strong>');
}
/**
* Tests the [color] bbcode.
*/
public function testColor()
{
$this->assertProduces('[color=red]red[/color]', '<span style="color: red">red</span>');
}
/**
* Tests the example from the documentation.
*/
public function testExample()
{
$text = "The default codes include: [b]bold[/b], [i]italics[/i], [u]underlining[/u], ";
$text .= "[url=http://jbbcode.com]links[/url], [color=red]color![/color] and more.";
$html = 'The default codes include: <strong>bold</strong>, <em>italics</em>, <u>underlining</u>, ';
$html .= '<a href="http://jbbcode.com">links</a>, <span style="color: red">color!</span> and more.';
$this->assertProduces($text, $html);
}
}

View file

@ -0,0 +1,77 @@
<?php
require_once(dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'Parser.php');
/**
* Test cases testing the HTMLSafe visitor, which escapes all html characters in the source text
*
* @author astax-t
*/
class HTMLSafeTest extends PHPUnit_Framework_TestCase
{
/**
* Asserts that the given bbcode string produces the given html string
* when parsed with the default bbcodes.
*/
public function assertProduces($bbcode, $html)
{
$parser = new \JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$parser->parse($bbcode);
$htmlsafer = new JBBCode\visitors\HTMLSafeVisitor();
$parser->accept($htmlsafer);
$this->assertEquals($html, $parser->getAsHtml());
}
/**
* Tests escaping quotes and ampersands in simple text
*/
public function testQuoteAndAmp()
{
$this->assertProduces('te"xt te&xt', 'te&quot;xt te&amp;xt');
}
/**
* Tests escaping quotes and ampersands inside a BBCode tag
*/
public function testQuoteAndAmpInTag()
{
$this->assertProduces('[b]te"xt te&xt[/b]', '<strong>te&quot;xt te&amp;xt</strong>');
}
/**
* Tests escaping HTML tags
*/
public function testHtmlTag()
{
$this->assertProduces('<b>not bold</b>', '&lt;b&gt;not bold&lt;/b&gt;');
$this->assertProduces('[b]<b>bold</b>[/b] <hr>', '<strong>&lt;b&gt;bold&lt;/b&gt;</strong> &lt;hr&gt;');
}
/**
* Tests escaping ampersands in URL using [url]...[/url]
*/
public function testUrlParam()
{
$this->assertProduces('text [url]http://example.com/?a=b&c=d[/url] more text', 'text <a href="http://example.com/?a=b&amp;c=d">http://example.com/?a=b&amp;c=d</a> more text');
}
/**
* Tests escaping ampersands in URL using [url=...] tag
*/
public function testUrlOption()
{
$this->assertProduces('text [url=http://example.com/?a=b&c=d]this is a "link"[/url]', 'text <a href="http://example.com/?a=b&amp;c=d">this is a &quot;link&quot;</a>');
}
/**
* Tests escaping ampersands in URL using [url=...] tag when URL is in quotes
*/
public function testUrlOptionQuotes()
{
$this->assertProduces('text [url="http://example.com/?a=b&c=d"]this is a "link"[/url]', 'text <a href="http://example.com/?a=b&amp;c=d">this is a &quot;link&quot;</a>');
}
}

View file

@ -0,0 +1,46 @@
<?php
require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'Parser.php';
/**
* Test cases for CodeDefinition nest limits. If an element is nested beyond
* its CodeDefinition's nest limit, it should be removed from the parse tree.
*
* @author jbowens
* @since May 2013
*/
class NestLimitTest extends PHPUnit_Framework_TestCase
{
/**
* Tests that when elements have no nest limits they may be
* nested indefinitely.
*/
public function testIndefiniteNesting()
{
$parser = new JBBCode\Parser();
$parser->addBBCode('b', '<strong>{param}</strong>', false, true, -1);
$parser->parse('[b][b][b][b][b][b][b][b]bold text[/b][/b][/b][/b][/b][/b][/b][/b]');
$this->assertEquals('<strong><strong><strong><strong><strong><strong><strong><strong>' .
'bold text' .
'</strong></strong></strong></strong></strong></strong></strong></strong>',
$parser->getAsHtml());
}
/**
* Test over nesting.
*/
public function testOverNesting()
{
$parser = new JBBCode\Parser();
$parser->addCodeDefinitionSet(new JBBCode\DefaultCodeDefinitionSet());
$parser->addBBCode('quote', '<blockquote>{param}</blockquote>', false, true, 2);
$bbcode = '[quote][quote][quote]wut[/quote] huh?[/quote] i don\'t know[/quote]';
$parser->parse($bbcode);
$expectedBbcode = '[quote][quote] huh?[/quote] i don\'t know[/quote]';
$expectedHtml = '<blockquote><blockquote> huh?</blockquote> i don\'t know</blockquote>';
$this->assertEquals($expectedBbcode, $parser->getAsBBCode());
$this->assertEquals($expectedHtml, $parser->getAsHtml());
}
}

Some files were not shown because too many files have changed in this diff Show more