这篇就晒晒代码,从入口文件开始。
index.php内容如下:
<?php //入口文件 $arr=explode('?',$_SERVER['REQUEST_URI']); //区分是实例化控制器还是渲染页面加载css,js文件 if(count($arr)==1)//加载css,js文件 { $src_js_css=explode('index.php/',$arr[0]); include $src_js_css[1]; exit(); } //导入需要加载的文件 require_once 'start/define.php'; require_once 'start/Autoload.php'; require_once 'myclass/MyLog.php'; require_once 'start/search.php'; 依次下面展示导入的文件:
defined.php:
<?php //$module是app下的模块名,即index或admin $arr=explode('?',$_SERVER['REQUEST_URI']); $arr2=explode('/',$arr[0]); $module=$arr2[count($arr2)-1]; //定义文件夹常量 define('MVC_PATH',str_replace('\\','/',dirname($_SERVER["SCRIPT_FILENAME"])));//$_SERVER['SCRIPT_FILENAME']引用当前脚本的路径,这里是入口文件 define('APP_PATH',MVC_PATH.'/app'); define('MD_PATH',APP_PATH.'/'.$module); define('APP_CONTROLLER',MD_PATH.'/controller'); define('APP_VIEW',MD_PATH.'/view'); define('APP_MODEL',MD_PATH.'/model'); define('CLASS_PATH',MVC_PATH.'/myclass'); define('START_PATH',MVC_PATH.'/start'); define('SRC_PATH',MVC_PATH.'/src'); define('LOG_PATH',MVC_PATH.'/log'); define('TEMPORARY_PATH',MVC_PATH.'/temporary'); define('CONFIG_PATH',MVC_PATH.'/config'); define('IS_POST',($_SERVER['REQUEST_METHOD']=='POST')?true:false); define('FRAME_VERSION', '1.1.0'); //框架版本 date_default_timezone_set('Asia/Shanghai'); //设置默认时区
AutoLoad.php:
<?php /**自动加载类,该类的作用是找不到的类会通过该类找到,但要先实例化自身*/ namespace start; class Autoload { private static $instance; static function getInstance() { if(!self::$instance) { self::$instance=new self(); } return self::$instance; } private function __construct() { spl_autoload_register([$this,'autoload']); } function autoload($className) {//echo $className; $pos=strrpos($className,'\\');//查找最后一个符合条件的对象 $spaceName=substr($className,0,$pos); $name=substr($className,$pos+1); $this->mapLoad($spaceName,$name); } protected function mapLoad($spaceName,$name) { $path_pre=rtrim(str_replace("\\","/",$spaceName),"/")."/"; $path_shu=$name.'.php'; $path=$path_pre.$path_shu; if(file_exists($path)) { include $path; echo 'o.o'; }else{ include CLASS_PATH.'/EmptyController.php'; \myclass\EmptyController::tellPeople(); } } }
Mylog.php:
<?php /** * Created by PhpStorm. * User: 15961 * Date: 2018/10/15 * Time: 18:58 */ namespace myclass; class Log { static function write_log($msg) { $log_name=MVC_PATH.'/log/'.date('Y-m-d').'.txt'; $mylog=fopen($log_name,'a') or die('日记写入失败'); fwrite($mylog,$msg); fclose($mylog); } }
search/php:
<?php //实例化自动加载类 $auto=start\Autoload::getInstance(); //获取当前访问的url问号之后的部分 $param=$_SERVER['QUERY_STRING']; $params=explode('&',$param); $_SESSION['url_now']=$params[0].'&'.$params[1]; //判断是否登陆 if(empty($_SESSION['user']['id']) && $_SESSION['url_now']!='m=login&a=index' && $_SESSION['url_now']!='m=login&a=dologin'){ echo '<script>location.href ="'.$module.'?m=login&a=index"</script>'; exit(); } //获取控制器和方法 !$_REQUEST['m']?$m='login':$m=$_REQUEST['m']; $m=ucfirst(strtolower($m));//首字母大写 !$_REQUEST['a']? $a='index':$a=$_REQUEST['a']; $controller='app\\'.$module.'\controller'.'\\'.$m;//echo $controller; //日志 $frameConfig=CONFIG_PATH.'/frameConfig.php'; $mdConfig=CONFIG_PATH.'/'.$module.'Config.php'; file_exists($mdConfig)?$config=array_merge(require_once $frameConfig,require_once $mdConfig):$config=require_once $frameConfig; if($config['DO_LOG']) myclass\Log::write_log(date('Y-m-d H:i:s').':'.'m='.$m.'&a='.$a."\r\n"); //调用控制器和方法 $obj=new $controller(); call_user_func([$obj,$a]);//调用一个类里面的方法
有了以上这些,框架就可以通过url找到对应的控制器和函数了。要想执行相应的操作,就需要控制器类和视图类了。
Controller.php:
<?php /** * Created by PhpStorm. * User: 15961 * Date: 2018/10/10 * Time: 14:16 */ namespace myclass; use myclass\Log; use myclass\View; class Controller { protected $data=[]; protected static $model; protected static $view; function __construct() { $this->controllerInit(); $this->beforeAction(); self::$view=View::getInstance(); } //初始化 public function controllerInit() { } //前置操作 public function beforeAction() { } public function write_log($msg) { Log::write_log($msg); } function p($var)//打印函数 { if(is_bool($var) || is_null($var) || is_array($var)) { var_dump($var); }else{ echo $var; } } //跳转函数,可以带参param,time为暂缓跳转时间 function go($controller=null,$action=null,$param=[],$time=0) { if(!$controller || !$action) { echo '控制器或页面不存在'; exit(); } $par=''; if(count($param)>0) { foreach ($param as $key=>$val) { $par.='&'.$key.'='.$val; } } if($time>0) sleep($time); header('location:?m='.$controller.'&a='.$action.$par); } protected function fetch($view=null) { self::$view->fetch($view); } protected function assign($key,$val) { self::$view->assign($key,$val); } }
其他类可以通过继承来继承属性和方法。
View.php:
<?php /** * Created by PhpStorm. * User: 15961 * Date: 2018/10/18 * Time: 11:55 */ namespace myclass; class View { //模板实例 protected static $instance; // 模板变量 protected $data = []; // 视图输出替换 protected $replace = []; protected function __construct() { } public static function getInstance() { if(!isset(self::$instance)) { return self::$instance=new self(); } return self::$instance; } protected function display() { } //模板渲染 function fetch($view=null) { if(isset($_SESSION['user'])) {//判断访问权限 $id=$_SESSION['user']['id']; $arr_url=['m=page&a=login','m=page&a=home','m=page&a=index','m=login&a=loginout','m=menu&a=elements','m=menu&a=chart','m=menu&a=index','m=index&a=index','m=login&a=index','m=user&a=add','m=user&a=edit']; if((!in_array($_SESSION['url_now'],$_SESSION['url'])) && (!in_array($_SESSION['url_now'],$arr_url))) { header('location:admin?m=index&a=index&uid='.$id); } }/*else if(!($_REQUEST['m']=='login' && $_REQUEST['a']=='index')){ header('location:admin?m=login&a=index'); }*/ //视图模板加载变量 extract($this->data); //找到路径 $controller=$_REQUEST['m']; $action=$_REQUEST['a']; if(!isset($view)) { $file=APP_VIEW.'/'.strtolower($controller).'/'.strtolower($action).'.html'; }else{ $arr=explode('.',$view); if(count($arr)>1) { $file=APP_VIEW.'/'.strtolower($controller).'/'.strtolower($view); }else{ $file=APP_VIEW.'/'.strtolower($controller).'/'.strtolower($view).'.html'; } } //获取模板源文件,用来替换 $template_content = file_get_contents($file); $pattern = array( '/\{\s*\$([a-zA-Z][a-zA-Z0-9_]*)\s*\}/i' ); $replace = array( '<?php echo $this->data["${1}"]; ?>' ); //用正则去替换模板源文件中的变量符号 $res = preg_replace($pattern,$replace,$template_content); //编译后文件写入某个目录 $template_c_path=TEMPORARY_PATH.'/temporary.txt'; file_put_contents($template_c_path,$res); //加载新模板 include $template_c_path; } function assign($key,$val) { if(is_string($key)) { $this->data[$key]=$val; }else if(is_array($key)){ $this->data=array_merge($this->data,$key); } } protected function config() { } public function __set($name, $value) { $this->data[$name]=$value; } public function __get($name) { return $this->data[$name]; } public function __isset($name) { return isset($this->data[$name]); } }
已经可以展示登陆页面了,为了登录,还需要连接数据库,模型基类DbModel.php如下:
<?php /** * Created by PhpStorm. * User: 15961 * Date: 2018/10/11 * Time: 18:25 */ namespace myclass; class DbModel { protected $host; protected $username; protected $password; protected $dbname; protected $charset; protected $prefix; protected static $config; public $link;//连接资源 protected $tableName;//默认表名,不带前缀 protected $sql; protected $options;//操作数组 所有的查询条件 protected static $instance; static function getInstance() { self::$config=include 'config/dbConfig.php'; if(!(self::$instance instanceof self)) { return self::$instance=new self(self::$config); } return self::$instance; } private function __construct($config) { $this->host = $config['host']; $this->username = $config['username']; $this->password = $config['password']; $this->dbname = $config['dbname']; $this->charset = $config['charset']; $this->prefix = $config['prefix']; //连接数据库 $this->link = $this->connect(); //初始化操作数组 $this->initOptions(); } protected function connect() { $link=mysqli_connect($this->host,$this->username,$this->password); if(!$link) { die('数据库连接失败!'); } //设置数据库名 $link->select_db($this->dbname); //设置字符 $link->set_charset($this->charset); return $link; } protected function initOptions() { $arr=['table','field','where','limit','order','having','group']; foreach ($arr as $value) { $this->options[$value]=''; if($value=='table') { $this->options['table']=$this->tableName; } } } public function table($table) { if($table) { $this->options['table']=$this->prefix.$table; $this->tableName=$this->prefix.$table; } return $this; } function field($field) { if($field) { if(is_string($field)) { $this->options['field']=$field; }else if(is_array($field)) { $this->options['field']=join(',',$field); } } return $this; } function where($where) { if($where) { if(is_string($where)) { $this->options['where']=' where '.$where; } } return $this; } function limit($limit) { if($limit) { if(is_string($limit)) { $this->options['limit']=' limit '.$limit; }else if(is_array($limit)) { $this->options['limit']=' limit '.join(',',$limit); } } return $this; } function group($group) { if(!empty($group)) { $this->options['group']=' group by '.$group; } return $this; } function order($order) { if(is_string($order)) { $this->options['order']=' order by '.$order; }else if(is_array($order)) { $this->options['order']=' order by '.$order[0].' '.$order[1]; } return $this; } function having($having) { if(!empty($having)) { $this->options['having']=' having '.$having; } return $this; } function select() { $sql="select ".$this->options['field']." from ".$this->options['table'].$this->options['where']; $sql.=$this->options['group'].$this->options['having'].$this->options['order']; $sql.=$this->options['limit']; $this->sql=$sql; $this->initOptions(); $result=mysqli_query($this->link,$sql); if($result && mysqli_affected_rows($this->link)) { $data=[]; while($res=mysqli_fetch_assoc($result)) { $data[]=$res; } return $data; } } function __get($name) { if($name=='sql') { return $this->sql; } } function query($sql) { $this->initOptions(); $result=mysqli_query($this->link,$sql); if($result && mysqli_affected_rows($this->link)) { $data=[]; while($res=mysqli_fetch_assoc($result)) { $data[]=$res; } return json_encode($data); } } function exec($sql,$isInsert=false) { $this->initOptions(); $this->sql=$sql; $result=mysqli_query($this->link,$sql); if($result && mysqli_affected_rows($this->link)) { if($isInsert) { return mysqli_insert_id($this->link); }else { return mysqli_affected_rows($this->link); } }else{ return false; } } function insert($data) { if(is_array($data)) { //处理数组中的数值为字符串的字段 $data=$this->parseValue($data); $keys=array_keys($data); $values=array_values($data); $sql="insert into ".$this->tableName." (".join(',',$keys)." ) values(".join(',',$values).")"; return $this->exec($sql,true); } } protected function parseValue($data) { foreach($data as $key=>$val) { if(is_string($val)) { $data[$key]="'".$val."'"; } } return $data; } function update($data) { if(is_array($data)) { $data=$this->parseValue($data); $new_data=[]; foreach ($data as $k=>$v) { if($k=='id') { $id=$v; }else{ $new_data[]=$k.'='.$v; } } $sql="update ".$this->tableName." set ".join(',',$new_data)." where id=".$id; $this->exec($sql); } } function delete($id) { $sql="delete from ".$this->tableName." where id=".$id; $this->exec($sql); } function max($field) { if($field) { $this->options['field']=" max(".$field.") as max "; } return $this; } function __destruct() { $this->link=null; } function __call($name, $arguments) { $str=substr(strtolower($name),0,5); $field=substr(strtolower($name),5); if($str=='getby') { $result=$this->query("select ".$field." from ".$this->tableName); return json_encode($result); } } }
如此,就实现了基本的框架结构。