aidenknight / frame_dome
简单的PHP初学者框架
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:project
Requires
- filp/whoops: ^2.0@dev
This package is not auto-updated.
Last update: 2025-03-30 06:24:50 UTC
README
目的
- 熟悉什么是框架
- 熟悉框架的核心运行原理
- 熟悉与掌握框架的使用
要求
- 框架的运行原理以及全部流程
- 每一行代码需要加上注释
- 不要求默打,这个难度比较大,尽量做到
需要使用的知识点
- php
- mysql
- composer
项目提交composer的packagist
- git简单知识
项目提交至github
准备工作
github注册账号
创建一个项目
克隆下来到www目录
git clone https://github.com/aidenknight/frame_demo.git
安装composer
实现步骤
###1.本地创建框架的目录,使用composer init
初始化项目
composer init初始化之后会自动声场vendor目录以及composer.json文件
###2.构建框架文件以及目录(目录名全部小写规范)
|--app 应用目录
| |--home home模块
| | |--controller 控制器
| | |--view 视图
|--houdunwang 框架核心
| |--core 核心文件
| |--model 模型
| |--view 视图
|--public
| |--static 静态资源
| |--view 视图(成功提示模板)
|--system
| |--config 配置文件
| |--model 模型
###3.创建框架的启动类houdunwang\core\Boot.php类类名和文件名首字母大写
<?php
//命名空间
namespace houdunwang\core;
class Boot
{
public static function run ()
{
echo 'run';
}
}
<?php
namespace houdunwang\core;
class Boot
{
public static function run ()
{
//错误处理
self::handler();
//初始化框架
self::init();
//执行应用
self::appRun ();
}
public static function handler(){
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
}
public static function init()
{
session_id () || session_start ();
date_default_timezone_set ('PRC');
}
public static function appRun ()
{
if ( isset( $_GET[ 's' ] ) ) {
$info = explode ('/',$_GET['s']);
$info[1] = ucfirst ($info[1]);
$class = "\app\\{$info[0]}\controller\\{$info[1]}";
$action = $info[2];
define ('MODULE',strtolower ($info[0]));
define ('CONTROLLER',lcfirst ($info[1]));
define ('ACTION',$info[2]);
} else {
$class = "\app\home\controller\Entry";
$action = "index";
define ('MODULE','home');
define ('CONTROLLER','entry');
define ('ACTION',$action);
}
echo call_user_func_array ( [ new $class , $action ] , [] );
exit;
}
}
###4.在public目录中创建index.php单一入口文件
<?php
require_once '../vendor/autoload.php';
\houdunwang\core\Boot::run ();
###5.访问框架入口:http://localhost:8080/frame/public/ 这个时候报错:
Fatal error: Class 'houdunwang\core\Boot' not found in /Applications/MAMP/htdocs/frame/public/index.php on line 3
因为类加载使用时需满足:include加载文件和导入命名空间
继续往下解决这个问题
###6.配置composer配置文件
修改composer.json文件。注意autoload
该项需自己增加
{
"name": "chaorenwubin/frame",
"description": "后盾网c86php框架系统",
"type": "project",
"licence":"MIT",
"authors": [
{
"name": "武斌",
"email": "wubin.mail@foxmail.com"
}
],
"minimum-stability": "dev",
"require": {},
"autoload":{
"files":[],
"psr-4":{
"houdunwang\\":"houdunwang\\",
"app\\":"app\\"
}
}
}
PSR-4
1.是制定的代码规范,简称PSR,是代码开发的事实标准
2.PSR-4使代码更加规范,能够满足面向package的自动加载,它规范了如何从文件路径自动加载类,同时规范了自动加载文件的位置
3.如果是: "houdunwang\\":"houdunwang\\",当实例化\houdunwang\core\Boot类时,会加载houdunwang/core/Boot.php
4.如果是: "houdunwang\\":"hdphp\\",当实例化\houdunwang\core\Boot类时,hdphp/core/Boot.php
5.如果是: "houdunwang\\":"",当实例化\houdunwang\core\Boot类时,Boot.php
5.反斜线是转义
生成composer自动载入文件,在Terminal中进入项目跟目录执行:
composer dump
这时,访问项目入口public文件输出run
###7.Boot.php中书写appRun方法:
引入助手函数
1.将以前我们写的helper.php文件放入system/helper.php
2.修改composer.json文件中autoload项的files
"autoload":{
"files":[
"system/helper.php"
],
"psr-4":{
"houdunwang\\":"houdunwang\\",
"app\\":"app\\"
}
}
3.在Teminal终端执行composer dump
appRun方法具体代码
public static function appRun ()
{
if(isset($_GET['s'])){
//通过?s=模块/控制器/方法(?s=home/entry/index)进行访问
//接受get参数s,并将其转为数组
$info = explode ('/',$_GET['s']);
//dd($info);
$info[1] = ucfirst ($info[1]);
//dd($info);
$c = "app\\{$info[0]}\controller\\{$info[1]}";
$a = $info[2];
define ('MODULE',strtolower ($info[0]));
define ('CONTROLLER',strtolower ($c));
define ('ACTION',$a);
}else{
$c = "\app\home\controller\\entry";//控制器类
$a = 'index';//方法
define ('MODULE','home');
define ('CONTROLLER','entry');
define ('ACTION',$a);
}
return call_user_func_array ([new $c,$a],[]);
}
###8.测试访问app中我们构建的程序 在app/home/controller中创建Entry.php
<?php
namespace app\home\controller;
class Entry
{
public function index(){
echo 'Welcome';
}
}
通过地址栏访问public
或public/index.php
或public/index.php?s=home/entry/index
也可通过尝试其他地址访问,如:public/index.php?s=home/entry/store
浏览器正常输出Welcome
###9.构建控制器层 C
public/view放入message.php模板文件
自己参考代码
在 core 目录中创建Controller.php,并让app\home\controller\Entry.php及以后类继承Controller
<?php
namespace houdunwang\core;
class Controller
{
private $url;
protected function message($msg){
include './view/message.php';
exit;
}
protected function setRedirect($url = ''){
if($url){
$this->url = "location.href='$url'";
}else{
$this->url = "window.history.back()";
}
return $this
}
}
app/controller/Entry.php,中增加store方法进行测试,增加store方法进行测试
通过地址栏访问store方法:public/index.php?s=home/entry/store
可看到提示页面,3秒之后自动跳转
<?php
namespace app\home\controller;
use houdunwang\core\Controller;
class Entry extends Controller
{
public function index(){
echo 'Welcome';
}
public function store(){
$this->setRedirect ('?s=home/entry/index')->message ('添加成功');
}
}
###10.构建视图层 V
在core/view目录中建立View.php和Base.php
View.php
<?php
namespace houdunwang\view;
class View
{
public function __call ( $name , $arguments )
{
// TODO: Implement __call() method.
return self::parseAction ( $name , $arguments );
}
public static function __callStatic ( $name , $arguments )
{
// TODO: Implement __callStatic() method.
return self::parseAction ( $name , $arguments );
}
public static function parseAction ( $name , $arguments )
{
return call_user_func_array ( [ new Base , $name ] , $arguments );
}
}
Base.php
<?php
namespace houdunwang\view;
class Base
{
private $file;
private $data;
public function make ()
{
$this->file = "../app/" . MODULE . '/view/' . CONTROLLER . '/' . ACTION . '.php';
return $this;
}
public function with ( $var )
{
$this->data = $var;
return $this;
}
//当输出对象的时候出发,比如说echo $obj->make();
public function __toString ()
{
// TODO: Implement __toString() method.
extract ($this->data);
include $this->file;
return '';
}
}
###11构建模型层和数据库ORM数据对象关系映射
数据库配置项文件system/config/database.php
<?php
return [
'driver'=>'mysql',
'host' => '127.0.0.1',
'user' => 'root',
'password' => 'root',
'dbname' => 'c86',
];
system/helper.php助手函数中增加加载配置项的c函数
function c ( $path )
{
$info = explode ( '.' , $path );
$config = include "../system/config/" . $info[ 0 ] . '.php';
return isset( $config[ $info[ 1 ] ] ) ? $config[ $info[ 1 ] ] : null;
}
houdunwang/model/Model.php
<?php
namespace houdunwang\model;
class Model
{
/**
* @param $name
* @param $arguments
*/
public function __call ( $name , $arguments )
{
// TODO: Implement __call() method.
return self::parseAction ( $name , $arguments );
}
/**
* @param $name
* @param $arguments
*/
public static function __callStatic ( $name , $arguments )
{
// TODO: Implement __callStatic() method.
return self::parseAction ( $name , $arguments );
}
/**
* @param $name
* @param $arguments
*
* @return mixed
*/
public static function parseAction ( $name , $arguments )
{
$class = get_called_class ();
return call_user_func_array ( [ new Base($class) , $name ] , $arguments );
}
}
base.php
<?php
namespace houdunwang\model;
use PDO;
use PDOException;
use Exception;
/**
* 模型基础类
* Class Base
*
* @package houdunwang\model
*/
class Base
{
protected $table;
protected $pdo;
private $where = '';
private $field = '';
public function __construct ( $class )
{
//1.链接数据库
$this->connect ();
//$class打印结果:system\model\Article
$class = explode ( '\\' , $class );
$this->table = strtolower ( $class[ 2 ] );
}
private function connect ()
{
$dsn = c ( 'database.driver' ) . ":host=" . c ( 'database.host' ) . ";dbname=" . c ( 'database.dbname' );
$user = c ( 'database.user' );
$password = c ( 'database.password' );
try {
$this->pdo = new PDO( $dsn , $user , $password );//链接数据库
$this->pdo->query ( 'SET NAMES UTF8' );//设置字符集
//设置错误属性
$this->pdo->setAttribute ( PDO::ATTR_ERRMODE , PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $e ) {
//输出错误
exit( $e->getMessage () );
}
}
/**
* 获取单一数据
*
* @param $pk 主键值
*/
public function find ( $pk )
{
//获得主键
$priKey = $this->getPk ();
//组合where条件,调用where方法
$this->where ( "{$priKey}={$pk}" );
//组合sql语句
$field = empty( $this->field ) ? '*' : $this->field;
$sql = "select {$field} from {$this->table} {$this->where}";
//执行查询
$data = $this->query ( $sql );
if ( $data ) {
//存储到data属性,使用toarray方法获得数据
$this->data = current ( $data );
return $this;
}
return [];
}
/**
* 获取全部数据
*
* @return $this|array
*/
public function get ()
{
$field = empty( $this->field ) ? '*' : $this->field;
$sql = "select {$field} from {$this->table}";
$data = $this->query ( $sql );
if ( $data ) {
$this->data = $data;
return $this;
}
return [];
}
/**
* 删除操作
*
* @return bool
*/
public function destory ()
{
//没有where条件不能执行删除
if ( empty( $this->where ) )
return false;
$sql = "delete from {$this->table} {$this->where}";
return $this->exec ( $sql );
}
/**
* 更新数据
*
* @param $data
*
* @return bool
*/
public function update ( array $data )
{
//更新的时候没有where条件不允许更新
if ( empty( $this->where ) )
return false;
$fields = '';
foreach ( $data as $k => $v ) {
$fields .= "{$k}='{$v}',";
}
//将最后,去掉
$fields = rtrim ( $fields , ',' );
$sql = "update {$this->table} set {$fields} {$this->where}";
//执行sql语句
return $this->exec ( $sql );
}
/**
* 数据写入
*
* @param array $data
*
* @return mixed 返回自增id
*/
public function insert ( array $data )
{
$fields = '';
$values = '';
foreach ( $data as $field => $value ) {
$fields .= "$field,";
$values .= is_int ( $value ) ? "$value," : "'$value',";
}
//将最后侧的,去掉
$fields = rtrim ( $fields , ',' );
$values = rtrim ( $values , ',' );
//insert into article (atitle,time) values ('hello',180);
$sql = "insert into {$this->table} ($fields) values ($values)";
return $this->exec ( $sql );
}
/**
* 查询数据获取制定字段
*
* @param $data
*
* @return $this
*/
public function field ( $data )
{
$this->field = $data;
return $this;
}
/**
* 统计
* @return mixed
*/
public function count ()
{
$sql = "select count(*) as total from {$this->table} {$this->where}";
$data = $this->query ( $sql );
return $data[ 0 ][ 'total' ];
}
/**
* 将对象转为数组
*
* @return array
*/
public function toArray ()
{
if ( isset( $this->data ) ) {
return $this->data;
}
return [];
}
/**
* 组合where条件
*
* @param $where 条件语句
*
* @return $this
*/
public function where ( $where )
{
//将wher条件存储到where属性中
$this->where = " where $where";
return $this;
}
/**
* 获得主键
*/
private function getPk ()
{
$sql = "desc " . $this->table;// desc article
$data = $this->query ( $sql );//执行sql语句,调用自定义的query方法
$pk = '';
foreach ( $data as $d ) {
if ( $d[ 'Key' ] == 'PRI' ) {
//说明该数据中的Field是主键
$pk = $d[ 'Field' ];
break;//终止循环
}
}
return $pk;
}
/**
* 执行有结果集的查询
*
* @param $sql 查询的sql语句
*
* @return mixed 查询结果结
*/
public function query ( $sql )
{
try {
$res = $this->pdo->query ( $sql );
return $res->fetchAll ( PDO::FETCH_ASSOC );
} catch ( PDOException $e ) {
throw new Exception( "sql:{$sql} | " . $e->getMessage () );
}
}
public function exec ( $sql )
{
try {
$affectedRows = $this->pdo->exec ( $sql );
if ( $lastInsertId = $this->pdo->lastInsertId () ) {
//返回自增id
return $lastInsertId;
} else {//否则返回受影响的条数
return $affectedRows;
}
} catch ( PDOException $e ) {
throw new Exception( "sql:{$sql} | " . $e->getMessage () );
}
}
}
需要在system/model建立Article.php进行协助测试测试,Article.php名字不是固定的
<?php
namespace system\model;
use houdunwang\model\Model;
class Article extends Model
{
}
在Entry.php中进行测试,注意system中的类不会自动进行加载,需要修改composer.json配置项
"autoload":{
"files":[
"system/helper.php"
],
"psr-4":{
"houdunwang\\":"houdunwang",
"app\\":"app\\",
"system\\":"system\\"
}
}
然后执行
composer dump
###错误处理方法
composer的packagist(https://packagist.org/)搜索:`whoop`
选择第一个:filp/whoops
进入到项目根目录执行:
composer require filp/whoops
在houdunwang/core/Boot.php中加入
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
推送上到composer仓库之后即可以下载了
composer create-project chaorenwubin/frame c86frame dev-master --prefer-dist