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

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);
}
/**