Browse Source

init

master
filesite 3 years ago
parent
commit
1df26ffa28
  1. 10
      .gitignore
  2. 1
      LICENSE
  3. 39
      Nginx.conf.md
  4. 74
      README.md
  5. 25
      bin/command.php
  6. 16
      conf/app.php
  7. 1
      content/README.md
  8. 26
      controller/CommandController.php
  9. 174
      controller/Controller.php
  10. 23
      controller/SiteController.php
  11. 94
      lib/FSC.php
  12. 1
      runtime/index.html
  13. 1
      themes/README.md
  14. 18
      views/layout/error.php
  15. 26
      views/layout/main.php
  16. 18
      views/site/index.php
  17. 5
      www/css/main.css
  18. 0
      www/favicon.ico
  19. 12
      www/index.php
  20. 2
      www/js/js.cookie.min.js
  21. 40
      www/js/main.js

10
.gitignore vendored

@ -1,8 +1,2 @@ @@ -1,8 +1,2 @@
# ---> Yii
assets/*
!assets/.gitignore
protected/runtime/*
!protected/runtime/.gitignore
protected/data/*.db
themes/classic/views/
# ---> filesite.io
runtime/*

1
LICENSE

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
MIT License
Copyright (c) <year> <copyright holders>
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:

39
Nginx.conf.md

@ -0,0 +1,39 @@ @@ -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;
}
}
```

74
README.md

@ -1,3 +1,73 @@ @@ -1,3 +1,73 @@
# fsc
# Filesite.io core lib
Core lib for filesite.io
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)

25
bin/command.php

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/../lib/FSC.php';
require_once __DIR__ . '/../controller/Controller.php';
$config = require_once __DIR__ . '/../conf/app.php';
$default_timezone = !empty($config['default_timezone']) ? $config['default_timezone'] : 'Asia/HongKong';
date_default_timezone_set($default_timezone);
//set variables
$action = isset($argv[1]) ? $argv[1] : 'index';
$_SERVER['REQUEST_URI'] = "/command/{$action}";
//GET parameters, format: foo=bar&hello=world
if (!empty($argv[2])) {
$_SERVER['QUERY_STRING'] = $argv[2];
$arr = explode('&', $argv[2]);
if (!isset($_GET)) {$_GET = array();}
foreach ($arr as $item) {
$ar = explode('=', $item);
$_GET[$ar[0]] = $ar[1];
}
}
//run app
FSC::run($config);

16
conf/app.php

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?php
/**
* Config
*/
return array(
'default_timezone' => '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,
);

1
content/README.md

@ -0,0 +1 @@ @@ -0,0 +1 @@
# 内容目录

26
controller/CommandController.php

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<?php
/**
* Command Controller
*/
Class CommandController extends Controller {
public function actionIndex() {
$commands = <<<eof
Actions:
- test
Usage:
php command.php action
eof;
echo $commands;
exit;
}
public function actionTest() {
print_r(FSC::$app);
exit;
}
}

174
controller/Controller.php

@ -0,0 +1,174 @@ @@ -0,0 +1,174 @@
<?php
/**
* Controller
*/
Class Controller {
protected $layout = 'main';
function __construct() {
//set default layout
if (!empty(FSC::$app['config']['default_layout'])) {
$this->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);
} //--}}}
}

23
controller/SiteController.php

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
<?php
/**
* Site Controller
*/
Class SiteController extends Controller {
public function actionIndex() {
//alert message test
$this->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);
}
}

94
lib/FSC.php

@ -0,0 +1,94 @@ @@ -0,0 +1,94 @@
<?php
/**
* Class App
*/
Class FSC {
public static $app = array();
protected static $start_time = 0;
//call function in controller
public static function run($config = []) { //--{{{
self::$start_time = microtime(true);
try {
self::loadController($config);
}catch(Exception $e) {
$content = htmlspecialchars($e->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);
}
} //--}}}
}

1
runtime/index.html

@ -0,0 +1 @@ @@ -0,0 +1 @@
<h1>403 Forbidden</h1>

1
themes/README.md

@ -0,0 +1 @@ @@ -0,0 +1 @@
# 皮肤目录

18
views/layout/error.php

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<!DocType html>
<head>
<meta charset="utf-8">
<title><?php echo $errors['title']; ?></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<style>
body{max-width:768px;margin:0 auto}
h1{color:#FF0000}
.error{padding:4px;color:yellow;background-color:gray;min-height:40px}
</style>
</head>
<body>
<h1><?php echo $errors['title']; ?></h1>
<h3>Exception <?php echo $errors['code']; ?>:</h3>
<div class="error"><?php echo $errors['content']; ?></div>
</body>
</html>

26
views/layout/main.php

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<!DocType html>
<html>
<head>
<meta charset="utf-8">
<title><?php echo $pageTitle;?></title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link href="/css/main.css?v.1.0" rel="stylesheet">
</head>
<body>
<?php
//### Render view file
$viewFile = __DIR__ . '/../' . FSC::$app['controller'] . '/' . $viewName . '.php';
include_once $viewFile;
?>
<div class="footer">
&copy;FSCPHP 2020 - execute time: <?php echo $page_time_cost;?> ms
</div>
<script src="/js/js.cookie.min.js"></script>
<script src="/js/main.js?v.1.0"></script>
</body>
</html>

18
views/site/index.php

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<style>
body{padding:10px;max-width:480px;margin:0 auto}
code{background-color:#EEE}
pre{padding:10px;background-color:#DDD}
.footer{text-align:center}
</style>
<h1>Welcome to FSC PHP Framework</h1>
<hr>
<h3>App parameters</h3>
<pre>
<?php print_r(FSC::$app); ?>
</pre>
<h3>View parameters</h3>
<pre>
<?php print_r($viewData); ?>
</pre>

5
www/css/main.css

@ -0,0 +1,5 @@ @@ -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}

0
www/favicon.ico

12
www/index.php

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
<?php
/* All php controller enter from here */
require_once __DIR__ . '/../lib/FSC.php';
require_once __DIR__ . '/../controller/Controller.php';
//run app
$config = require_once __DIR__ . '/../conf/app.php';
$default_timezone = !empty($config['default_timezone']) ? $config['default_timezone'] : 'Asia/HongKong';
date_default_timezone_set($default_timezone);
FSC::run($config);

2
www/js/js.cookie.min.js vendored

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
/*! js-cookie v3.0.0-rc.1 | MIT */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self,function(){var n=e.Cookies,r=e.Cookies=t();r.noConflict=function(){return e.Cookies=n,r}}())}(this,function(){"use strict";function e(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)e[r]=n[r]}return e}var t={read:function(e){return e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};return function n(r,o){function i(t,n,i){if("undefined"!=typeof document){"number"==typeof(i=e({},o,i)).expires&&(i.expires=new Date(Date.now()+864e5*i.expires)),i.expires&&(i.expires=i.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape),n=r.write(n,t);var c="";for(var u in i)i[u]&&(c+="; "+u,!0!==i[u]&&(c+="="+i[u].split(";")[0]));return document.cookie=t+"="+n+c}}return Object.create({set:i,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var n=document.cookie?document.cookie.split("; "):[],o={},i=0;i<n.length;i++){var c=n[i].split("="),u=c.slice(1).join("=");'"'===u[0]&&(u=u.slice(1,-1));try{var f=t.read(c[0]);if(o[f]=r.read(u,f),e===f)break}catch(e){}}return e?o[e]:o}},remove:function(t,n){i(t,"",e({},n,{expires:-1}))},withAttributes:function(t){return n(this.converter,e({},this.attributes,t))},withConverter:function(t){return n(e({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(o)},converter:{value:Object.freeze(r)}})}(t,{path:"/"})});

40
www/js/main.js

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
(function() {
var showAlertMsg = function(msgType, msgContent) {
var els_h1 = document.getElementsByTagName('h1');
var alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${msgType}`;
alertDiv.innerHTML = msgContent;
if (els_h1.length > 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) {}
}
})();
Loading…
Cancel
Save