diff --git a/.gitignore b/.gitignore index 4711ea7..2b1a93b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,2 @@ -# ---> Yii -assets/* -!assets/.gitignore -protected/runtime/* -!protected/runtime/.gitignore -protected/data/*.db -themes/classic/views/ - +# ---> filesite.io +runtime/* diff --git a/LICENSE b/LICENSE index 2071b23..472ac23 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,4 @@ MIT License - Copyright (c) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Nginx.conf.md b/Nginx.conf.md new file mode 100644 index 0000000..4c80e09 --- /dev/null +++ b/Nginx.conf.md @@ -0,0 +1,39 @@ +# Nginx配置参考 + +请根据你的实际情况修改域名和代码所在目录; +* 域名:`fsc.org` +* 目录:`/var/www/fscphp/www/` + + +``` + server { + listen 80; + server_name fsc.org; + + root /var/www/fscphp/www/; + index index.php index.html index.htm; + + location / { + try_files $uri $uri/ /index.php?$args; + } + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + location ~ /(index)\.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /var/www/fscphp/www$fastcgi_script_name; + include fastcgi_params; + } + + # deny all other php + # + location ~ \.php { + deny all; + } + + # deny all hidden files + location ~ /\. { + deny all; + } + } +``` diff --git a/README.md b/README.md index 9f30b71..6bd16ad 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,73 @@ -# fsc +# Filesite.io core lib -Core lib for filesite.io \ No newline at end of file +FSC is the core lib of filesite.io. + + +## Controllers and Actions + +Please put controller files in the directory **controller/**, +which are extends from the base **Controller**. + +Please name public actions like **actionIndex** in your controller file, +which are using the prefix **action**. + +Examples: +``` +controller/SiteController.php +controller/CommandController.php +``` + + +## Layout and Views + +Please put layout files in the directory **views/layout/**, +and create diretories for your views, +such as **views/site/**. + + +## App config + +Please set configs in the file **conf/app.php**. + +You can use ```FSC::$app['config']``` in controller and views. + +Example in views: + +``` +print_r(FSC::$app['config']); +``` + + +## View data + +You can pass parameters to view with an array, like: + +``` +$viewName = 'index'; +$params = compact('foo', 'bar'); +return $this->render($viewName, $params); +``` + +And you can visit parameters with the variable ```$viewData``` in views. + +Example in view: + +``` +echo "I'm foo {$viewData['foo']}."; +print_r($viewData); +``` + + +## Commands + +You can add functions in the command controller ```controller/CommandController.php```, +and run the command to execute it: + +``` +php bin/command.php "action" "foo=bar" +``` + + +## Nginx config example + +Please check the file [Nginx.conf.md](./Nginx.conf.md) diff --git a/bin/command.php b/bin/command.php new file mode 100644 index 0000000..7102c96 --- /dev/null +++ b/bin/command.php @@ -0,0 +1,25 @@ + 'Asia/HonKong', //timezone + + 'content_directory' => 'content', //directory of contents + 'theme' => '', //name of theme which is enabled + //when it's empty, use layout and views in directory views/ + 'default_layout' => 'main', //default layout + 'error_layout' => 'error', //exception layout, show error title and content + + //for debug, log directory: ../runtime/logs/ + 'debug' => false, +); diff --git a/content/README.md b/content/README.md new file mode 100644 index 0000000..441754e --- /dev/null +++ b/content/README.md @@ -0,0 +1 @@ +# 内容目录 diff --git a/controller/CommandController.php b/controller/CommandController.php new file mode 100644 index 0000000..dac7f3f --- /dev/null +++ b/controller/CommandController.php @@ -0,0 +1,26 @@ +layout = FSC::$app['config']['default_layout']; + } + } + + function __destruct() { + $this->logTimeCost(); + } + + //redirect url + protected function redirect($url, $code = 302) { //--{{{ + header("Location: {$url}", true, $code); + exit; + } //--}}} + + //render view + protected function render($viewName, $viewData = array(), $pageTitle = '') { //--{{{ + $layoutFile = __DIR__ . '/../views/layout/' . $this->layout . '.php'; + if (!empty(FSC::$app['config']['theme'])) { + $layoutFile = __DIR__ . '/../themes/' . FSC::$app['config']['theme'] . '/views/layout/' . $this->layout . '.php'; + } + + //include layout and view + if (file_exists($layoutFile)) { + //show time cost + $end_time = microtime(true); + $page_time_cost = ceil( ($end_time - FSC::$app['start_time']) * 1000 ); //ms + + ob_start(); + include_once $layoutFile; + + $htmlCode = ob_get_contents(); + ob_end_clean(); + + //enable gzip + ob_start('ob_gzhandler'); + echo $htmlCode; + ob_end_flush(); + }else { + throw Exception("Layout file not exist: {$layoutFile}", 500); + } + } //--}}} + + //render json data + protected function renderJson($data) { //--{{{ + header("Content-Type: application/json; charset=utf-8"); + echo json_encode($data); + exit; + } //--}}} + + //render m3u8 file + protected function renderM3u8($content) { //--{{{ + header("Content-Type: application/x-mpegURL; charset=utf-8"); + echo $content; + exit; + } //--}}} + + //get params by key + protected function get($key = '', $defaultValue = '') { //--{{{ + if (empty($key)) { + return $_GET; + } + return !empty($_GET[$key]) ? $_GET[$key] : $defaultValue; + } //--}}} + + //post params by key + protected function post($key = '', $defaultValue = '') { //--{{{ + if (empty($key)) { + return $_POST; + } + return !empty($_POST[$key]) ? $_POST[$key] : $defaultValue; + } //--}}} + + //debug log + protected function logTimeCost() { //--{{{ + if (!empty(FSC::$app['config']['debug'])) { + $end_time = microtime(true); + $timeCost = ceil( ($end_time - FSC::$app['start_time']) * 1000 ); //ms + $thisUrl = FSC::$app['requestUrl']; + $logTime = date('Y-m-d H:i:s'); + $logDir = __DIR__ . '/../runtime/logs/'; + $logOk = @error_log("{$logTime}\t{$thisUrl}\ttime cost: {$timeCost} ms\n", 3, "{$logDir}debug.log"); + if (!$logOk) { //try to mkdir + @mkdir($logDir, 0700, true); + @error_log("{$logTime}\t{$thisUrl}\ttime cost: {$timeCost} ms\n", 3, "{$logDir}debug.log"); + } + } + } //--}}} + + //get user real ip + protected function getUserIp() { //--{{{ + $ip = false; + + if (!empty($_SERVER["HTTP_CLIENT_IP"])) { + $ip = $_SERVER["HTTP_CLIENT_IP"]; + } + + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ips = explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']); + if (!empty($ip)) { + array_unshift($ips, $ip); + $ip = false; + } + + if (!empty($ips)) { + for ($i = 0; $i < count($ips); $i++) { + if (!preg_match("/^(10│172\.16│192\.168)\./", $ips[$i])) { + $ip = $ips[$i]; + break; + } + } + } + } + + return !empty($ip) ? $ip : $_SERVER['REMOTE_ADDR']; + } //--}}} + + //request url via curl + protected function request($url, $postFields = array(), $timeout = 10, $pc = false) { //--{{{ + $s = curl_init(); + + curl_setopt($s, CURLOPT_URL, $url); + curl_setopt($s, CURLOPT_TIMEOUT, $timeout); + curl_setopt($s, CURLOPT_RETURNTRANSFER, true); + + if (!empty($postFields)) { + curl_setopt($s, CURLOPT_POST, true); + curl_setopt($s, CURLOPT_POSTFIELDS, $postFields); + } + + //iphone client + $user_agent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'; + //mac os client + if ($pc) { + $user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'; + } + curl_setopt($s, CURLOPT_USERAGENT, $user_agent); + curl_setopt($s, CURLOPT_REFERER, '-'); + + $curlResult = curl_exec($s); + $curlStatus = curl_getinfo($s, CURLINFO_HTTP_CODE); + curl_close($s); + + return array( + 'status' => $curlStatus, + 'result' => $curlResult, + ); + } //--}}} + + //set cookie for message show + //type: info, warning, danger, success + protected function sendMsgToClient($msg, $type = 'info') { //--{{{ + $cookieKey = "alert_msg_{$type}"; + $expires = time() + 15; + $path = '/'; + + //empty character replace to "%20" + $encoded = urlencode($msg); + $noempty = str_replace('+', '%20', $encoded); + $val = base64_encode( $noempty ); + + setcookie($cookieKey, $val, $expires, $path); + } //--}}} + +} diff --git a/controller/SiteController.php b/controller/SiteController.php new file mode 100644 index 0000000..dc21138 --- /dev/null +++ b/controller/SiteController.php @@ -0,0 +1,23 @@ +sendMsgToClient('Alert message - info', 'info'); + $this->sendMsgToClient('Alert message - warning', 'warning'); + $this->sendMsgToClient('Alert message - danger', 'danger'); + $this->sendMsgToClient('Alert message - success', 'success'); + + $pageTitle = "Welcome to FSC PHP Framework"; + $viewName = 'index'; + $params = array( + 'foo' => 'bar', + ); + return $this->render($viewName, $params, $pageTitle); + } + +} diff --git a/lib/FSC.php b/lib/FSC.php new file mode 100644 index 0000000..021c1fb --- /dev/null +++ b/lib/FSC.php @@ -0,0 +1,94 @@ +getMessage(), ENT_QUOTES); + $code = $e->getCode(); + if (empty($code)) {$code = 500;} + $title = "HTTP/1.0 {$code} Internal Server Error"; + header($title, true, $code); + + //render error layout + $errors = compact('code', 'title', 'content'); + $layoutName = !empty($config['error_layout']) ? $config['error_layout'] : 'error'; + $error_layout = __DIR__ . "/../views/layout/{$layoutName}.php"; + if (!empty($config['theme'])) { + $error_layout = __DIR__ . "/../themes/{$config['theme']}/views/layout/{$layoutName}.php"; + } + + ob_start(); + include_once $error_layout; + + $htmlCode = ob_get_contents(); + ob_end_clean(); + + //enable gzip + ob_start('ob_gzhandler'); + echo $htmlCode; + ob_end_flush(); + } + } //--}}} + + //parse url to controller and action name + protected static function getControllerAndAction($url, $config) { //--{{{ + $arr = parse_url($url); + $path = !empty($arr['path']) ? $arr['path'] : '/'; + + @list(, $controller, $action) = explode('/', $path); + if (empty($controller)) { + $controller = !empty($config['defaultController']) ? $config['defaultController'] : 'site'; + }else if(preg_match('/\w+\.\w+/i', $controller)) { //not controller but some file not exist + throw new Exception("File {$controller} not exist.", 404); + }else { + $controller = preg_replace('/\W/', '', $controller); + } + + if (empty($action)) { + $action = 'index'; + }else { + $action = preg_replace('/\W/', '', $action); + } + + return compact('controller', 'action'); + } //--}}} + + protected static function loadController($config) { //--{{{ + //parse url to controller and action + $requestUrl = $_SERVER['REQUEST_URI']; + $arr = self::getControllerAndAction($requestUrl, $config); + $controller = $arr['controller']; + $action = $arr['action']; + $start_time = self::$start_time; + + //set parameters + self::$app = compact('config', 'controller', 'action', 'requestUrl', 'start_time'); + + //call class and function + $className = ucfirst($controller) . 'Controller'; + $funName = 'action' . ucfirst($action); + $controllerFile = __DIR__ . "/../controller/{$className}.php"; + if (file_exists($controllerFile)) { + require_once $controllerFile; + $cls = new $className(); + if (method_exists($className, $funName)) { + $cls->$funName(); + }else { + throw new Exception("Function {$funName} not exist in class {$className}.", 500); + } + }else { + throw new Exception("Controller {$className} not exist.", 500); + } + } //--}}} + +} diff --git a/runtime/index.html b/runtime/index.html new file mode 100644 index 0000000..122250c --- /dev/null +++ b/runtime/index.html @@ -0,0 +1 @@ +

403 Forbidden

diff --git a/themes/README.md b/themes/README.md new file mode 100644 index 0000000..7ac4eef --- /dev/null +++ b/themes/README.md @@ -0,0 +1 @@ +# 皮肤目录 diff --git a/views/layout/error.php b/views/layout/error.php new file mode 100644 index 0000000..a62d59b --- /dev/null +++ b/views/layout/error.php @@ -0,0 +1,18 @@ + + + +<?php echo $errors['title']; ?> + + + + + +

+

Exception :

+
+ + diff --git a/views/layout/main.php b/views/layout/main.php new file mode 100644 index 0000000..2e590b3 --- /dev/null +++ b/views/layout/main.php @@ -0,0 +1,26 @@ + + + + +<?php echo $pageTitle;?> + + + + + + + + + + + + + + + diff --git a/views/site/index.php b/views/site/index.php new file mode 100644 index 0000000..565c945 --- /dev/null +++ b/views/site/index.php @@ -0,0 +1,18 @@ + +

Welcome to FSC PHP Framework

+
+ +

App parameters

+
+
+
+ +

View parameters

+
+
+
diff --git a/www/css/main.css b/www/css/main.css new file mode 100644 index 0000000..b162396 --- /dev/null +++ b/www/css/main.css @@ -0,0 +1,5 @@ +.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px} +.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6} +.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1} +.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc} +.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1} diff --git a/www/favicon.ico b/www/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/www/index.php b/www/index.php new file mode 100644 index 0000000..77128c1 --- /dev/null +++ b/www/index.php @@ -0,0 +1,12 @@ + 0) { + var h1 = els_h1[0]; + h1.parentNode.insertBefore(alertDiv, h1.nextSibling); + }else { + var els_header = document.getElementsByClassName('header'); + if (els_header.length > 0) { + els_header[0].appendChild(alertDiv); + } + } + + setTimeout(function() { + alertDiv.remove(); + }, 5000); + }; + + //get alert message from cookie + var cookieKeys = { + 'info': 'alert_msg_info', + 'success': 'alert_msg_success', + 'warning': 'alert_msg_warning', + 'danger': 'alert_msg_danger' + }; + + var alert_msg = ''; + for (var key in cookieKeys) { + try { + alert_msg = decodeURIComponent( atob( Cookies.get(cookieKeys[key]) ) ); + if (alert_msg) { + showAlertMsg(key, alert_msg); + Cookies.remove(cookieKeys[key]); + } + }catch(e) {} + } +})();