从零开始写一个PHP框架

相关代码在这里:github.com/zakokun/myPHP

个人觉得徒手写一个PHP框架是帮助我们了解MVC架构的好办法,同时对PHP的资源调度以及代码组织都是一个很好的锻炼。

开始

首先我们建立基本的文件夹结构,如下:

application
——controller
——model
——view
system
index.php

application是项目的目录,而system不用说就是框架文件的目录了。 index.php 是我们所有请求的入口,我们规定通过如下的url来调用controllerlocalhost/index.php?c=controller&a=action&param=123

设置index.php 入口以及路由

因为所有的请求都是通过index.php发起的,因此我们需要在index.php里面进行一些初始化的设置:

define('BASE_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
define('APP_ROOT', BASE_ROOT . 'application' .  DIRECTORY_SEPARATOR);

因为我们规定url参数中的c是控制器名,a是方法名,因此index.php中需要一个路由函数去活取这两个参数:

define('BASE_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
define('APP_ROOT', BASE_ROOT . 'application' .  DIRECTORY_SEPARATOR);

function route(){
    $c = isset($_GET['c'])?$_GET['c']:'index';
    $a = isset($_GET['a'])?$_GET['a']:'index';

    $r['c']=ucfirst(strtolower(trim($c)));
    $r['a']=strtolower(trim($a));
    return $r;
}
$routeArr = route();
$controller=$routeArr['c'];
$action=$routeArr['a'];

获取到了需要实例化的controller以后,就可以进行相关操作了:

define('BASE_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
define('APP_ROOT', BASE_ROOT . 'application' .  DIRECTORY_SEPARATOR);

function route(){
    $c = isset($_GET['c'])?$_GET['c']:'index';
    $a = isset($_GET['a'])?$_GET['a']:'index';

    $r['c']=ucfirst(strtolower(trim($c)));
    $r['a']=strtolower(trim($a));
    return $r;
}
$routeArr = route();
$controller=$routeArr['c'];
$action=$routeArr['a'];

//根据route的参数加载所需的文件
if (file_exists(APP_ROOT . 'controller/' . $controller.'Controller.php' )){
    require_once APP_ROOT . 'controller/' . $controller.'Controller.php';
    $obj=new $controller();
    if (method_exists($obj,$action)){
        $obj->$action();
    }else{
        die('func not exist');
    }
}else{
    die('file not exist');
}

创建测试控制器

我们在controller文件夹里面创建一个 IndexController.php 文件:

class IndexController{

    public function test(){
        echo 'test data';
    }
}

试一试 localhost/index.php?c=index&a=test ,不出意外就能得到test data 了。

提取父类控制器

光是这样肯定是不够的,因为我们需要在controller里面连接model,调用模板,加载各种工具类等。因此我们需要一个带一些公用方法的 父类,这个父类可以被所有的controller继承。

我们创建一个controller.php文件:

class controller
{
    //打印的方法
    public function dump($data){
        echo '<p>';
        print_r($data);
        echo '</p>';
        die;
    } 
}

这样一个简单的父类就完成了,里面带了一个很常用的格式化打印数据的方法。那么这个父类我们放在哪里呢? 最简单的方法就是放在 application/controller文件夹里面了,但是一般来说application文件夹是用来放我们项目本身的代码的,框架的代码我们最好放在 system 文件夹里面,然后通过__autoload() 函数自动调用。

我们可以在index.php 里面添加这个函数:

function __autoload($className){
    if(file_exists(BASE_ROOT . 'system/' . $className . '.php')){
        require_once BASE_ROOT . 'system/' . $className . '.php';
    }
}

接下来我们就能在项目的controller继承这个父类了:

class IndexController extends Controller
{
    public function test(){
        $this->dump('test data');
    }
}

Model

接下来我们在application文件夹下面创建model文件夹,用来存放模型。那怎么通过controller来调用model?自然就是通过父类自带 的方法了。我们在controller.php里面添加一个方法:

public function model($modelName){
    $modelFile = $modelName . 'Model';
    return new $modelFile();
}

单是这样还不行,因为php并不知道我们试图实例化的这个model文件在哪里,因此我们需要通过__autoload()函数告诉PHP:

function __autoload($className){
    if(file_exists(BASE_ROOT . 'system/' . $className . '.php')){
        require_once BASE_ROOT . 'system/' . $className . '.php';
    }elseif(file_exists(APP_ROOT . 'model/' . $className . '.php')){
        require_once APP_ROOT . 'model/' . $className . '.php';
    }
}

然后我们就可以写一个测试用的TestModel.php 了:

class TestModel
{
    public function findData(){
        return 'model data';
    }
}

试着在TestController.php 里面调用看看:

class IndexController extends Controller
{
    public function test(){
        $testModel = $this->model('Test');
        echo $testModel->findData();   //输出 model data;
    }
}

连接数据库

但是现在的model是无法连接数据库的,这样的model就是个废物。但是在每一个model里面写连接数据库的方法是很蠢的,最好的 方法就是通过父类的model连接数据库,其他model继承父类就好了。

和controller.php相同,我们在sysytem文件夹下创建一个model.php

class model
{
    protected $_db;

    public function __construct(){
        $this->_db=new mysqli('localhost', 'root','123456', 'myphp');
        if(mysqli_connect_errno()){
            echo 'error...'.mysqli_connect_errno();
        }
    }

    public function query($sql){
        $result=$this->_db->query($sql);
        if($result->num_rows){
            while ($row = $result->fetch_assoc()) {
                $myRow[] = $row;
            }
            return $myRow;
        }
    }
}

自然我们所有的model都继承这个父类model:

class TestModel extends Model
{
    // other code......
}

让我们在本地建立一个myphp数据库,再新建一个user表,格式如下:

id name age
1 zhang 20
2 shao 21
3 wang 22

让我们写一个UserController连接UserModel查找数据,所以我们要这么做:

application/controller 文件夹下面创建UserController.php,内容如下:

class UserController extends Controller
{
    public function user(){
        $id = $_GET['id'];
        $userModel = $this->model('User');
        $user = $userModel->getUser($id);
        $this->dump($user);
    }
}

application/model 文件夹下面创建UserModel.php,内容如下:

class UserModel extends Model
{
    public function getUser($id){
        $sql = 'select * from user where id ='.$id;
        $data = $this->query($sql);
        return $data;
    }
}

试着通过localhost/index.php?c=user&a=user&id=1 请求看看吧!

待续~~~

Comments

comments powered by Disqus