Files
fc/server/web/framework/App.php
T
2026-05-23 22:10:14 +08:00

263 lines
8.2 KiB
PHP

<?php
/*-----------------------------------------------------+
* PHP简易框架
* 目的是提供一种简单灵活易扩展的PHP使用方式
* @author yeahoo2000@gmail.com
*
* 基本使用流程:
* 1.梆定自定义函数和类到App,构建适合业务逻辑的运行环境
* 2.映射url到对应的处理函数或方法
* 3.运行App::start()处理url路由
* 4.由router调用相应的函数或方法
*
* 文件和目录约定:
* ROOT/init.php 自定义系统初始化
* ROOT/env.php 配置文件
* ROOT/lib/ 自定义类库存储目录
* ROOT/mod/ 业务逻辑模块目录
* ROOT/tpl/ 模板文件目录
* ROOT/var/ 动态数据临时存储目录
* ROOT/var/sess php session存储目录
* ROOT/var/error.log 系统日志,包括错误日志
*
* 系统内置基础组件:
* App::cfg() 访问配置文件
* App::map() 梆定自定义函数
* App::register() 注册自定义类库
* App::route() 映射url到指定的callback
* App::request() 请求处理
* App::response() 响应处理
* App::url() 生成一个url路径
* App::redirect() 重定向
* App::debug() 输出一条调试信息到日志文件
* App::start() 开始处理url路由
* Util.php 工具包
* OS.php 操作系统相关类库
* View.php 视图模板处理类库
+-----------------------------------------------------*/
define('FRAMEWORK_ROOT', __DIR__);
App::bootup();
class App{
private static $kernel = null;
private $baseUrl = '';
private $funcs = [];
private $classes = [];
private $instances = [];
private $routes = [];
public static function _classLoader($class){
require "$class.php";
// 载入脚本时自动调用__awake()方法
if(method_exists($class, '__awake')){
$class::__awake();
}
}
private function __construct(){
// 设置autoload,如果有新的目录想要加入autoload搜索路径中,
// 可以自行调用set_include_path()指定
set_include_path(
ROOT.'/mod'.PATH_SEPARATOR.
ROOT.'/lib'.PATH_SEPARATOR.
dirname(__FILE__).PATH_SEPARATOR.
get_include_path()
);
spl_autoload_register([__CLASS__, '_classLoader']);
}
public static function bootup(){
if(!defined('ROOT')) exit("没有定义ROOT常量,系统无法工作");
require 'errorhandler.php';
if(is_null(self::$kernel)){
self::$kernel = new self();
}
// 注册默认的cfg, request, response
self::register('cfg', 'Cfg');
self::register('request', 'Request');
self::register('response', 'Response');
self::register('sess', 'Session');
if(self::cfg()->ini_set->session->auto_start){
self::sess()->start();
}
}
// TODO:目前只支持简单匹配
public static function start(){
$targetUrl = self::$kernel->request()->url;
$params = self::$kernel->request()->params;
$matched = false;
foreach(self::$kernel->routes as $url => $callback){
if($url == $targetUrl || $url == '*'){
// 返回true则继续下一个匹配
if(true == call_user_func_array($callback, $params)){
$matched = true;
continue;
}else{
return;
}
}
}
if(!$matched){
self::$kernel->response(false)
->status(404)
->write('404 找不到此页面')
->send();
}
}
public static function route($pattern, $callback){
self::$kernel->routes[$pattern] = $callback;
}
public static function map($name, $callback){
if(method_exists(__CLASS__, $name)) throw new ErrorException("不能覆盖内部方法: ${name}");
self::$kernel->funcs[$name] = $callback;
}
public static function register($name, $class, $params = [], $callback = null){
// if(!class_exists($class)) throw new ErrorException("无法注册不存在的类: ${class}");
if(method_exists(__CLASS__, $name)) throw new ErrorException("不能覆盖内部方法: ${name}");
self::$kernel->classes[$name] = [$class, $params, $callback];
}
public static function unregister($name, $class, $params = [], $callback = null){
unset(self::$kernel->classes[$name]);
}
public static function setBaseUrl($baseUrl){
self::$kernel->baseUrl = $baseUrl;
}
public static function getBaseUrl(){
return self::$kernel->baseUrl;
}
public static function url($path, $params = []){
if(empty($params)){
return self::$kernel->baseUrl.$path;
}else{
$p = '?';
foreach($params as $k => $v){
$p .= "$k=$v";
}
return self::$kernel->baseUrl.$path.urlencode($p);
}
}
public static function redirect($url){
self::$kernel->response()
->status(303)
->header('Location', $url)
->send();
}
public static function error($msg){
self::log('ERROR', $msg);
exit('发生了系统内部错误'); // 不要显示详细错误到页面上
}
public static function debug($msg){
self::log('DEBUG', $msg);
}
public static function info($msg){
self::log('INFO', $msg);
}
private static function log($tag, $msg){
$caller = debug_backtrace()[1]; // 第1层的栈包含了调用者信息
$msg = sprintf("[%s][%s %s:%d] %s\n", $tag, date('y/m/d H:i:s'), basename($caller['file']), $caller['line'], $msg);
error_log($msg, 3, ROOT."/var/error.log");
}
public static function __callStatic($name, $params){
return self::$kernel->invoke(self::$kernel, $name, $params);
}
public function __call($name, $params){
return $this->invoke($this, $name, $params);
}
private function invoke($kernel, $name, $params){
if(isset($kernel->funcs[$name])){
return call_user_func_array($kernel->funcs[$name], $params);
}
if(isset($kernel->instances[$name]) && empty($params)){
return $kernel->instances[$name];
}
if(isset($kernel->classes[$name])){
list($class, $initParams, $callback) = $kernel->classes[$name];
if(!empty($params)){
// 如果带有参数则说明是需要创建新的对象
// 并且把第一个参数丢弃进行实例化新对象
array_shift($params);
$ref = new ReflectionClass($class);
$obj = $ref->newInstanceArgs($params);
return $obj;
}
$ref = new ReflectionClass($class);
$obj = $ref->newInstanceArgs($initParams);
$kernel->instances[$name] = $obj;
return $obj;
}
throw new ErrorException("无法调用未梆定的函数或方法: App::${name}()");
}
}
class ObjectBaseArray{
protected $__vars;
public function __construct($vars = [], $allToObject = false){
$this->setArray($vars, $allToObject);
}
public function setArray($vars, $allToObject = false){
if($allToObject){
$this->__vars = $this->allToObject($vars);
}else{
$this->__vars = &$vars;
}
}
public function getArray(){
return $this->__vars;
}
public function __set($k, $v){
$this->__vars[$k] = $v;
}
public function __get($k){
if(isset($this->__vars[$k])) return $this->__vars[$k];
return false; // 注意:访问不存在的变量时返回的默认值是false,如果想知道是不是存在该变量请使用isset()
}
public function __isset($k){
return isset($this->__vars[$k]);
}
public function __unset($k){
unset($this->__vars[$k]);
}
public function __call($name, $params){
throw new ErrorException("无法调用不存在的方法: $name()");
}
// 将数组中所有子项类型为array的数据转为ObjectBaseArray
private function allToObject($arr){
foreach($arr as $k => $v){
if(is_array($v)){
$arr[$k] = new self($v, true);
}else{
$arr[$k] = $v;
}
}
return $arr;
}
}