php框架

从零开始搭建php框架

.htaccess文件 ‘/.htaccess’

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

入口文件 /index.php

<?php
// 整个框架所在的根目录
define ('UEK', realpath('./'));

// 核心类所有在目录
define ('CORE', UEK.'/core');

// APP开发目录
define ('APP', UEK.'/app');

// 是否开启调试模式
define ('DEBUG', true);

if( DEBUG ){
  ini_set('display_errors', 'On');
}else{
  ini_set('display_errors', 'Off');
}

// 引入公共函数库
include CORE . '/common/function.php';

// 引入框架核心类
include CORE . '/uek.php';

// 如果new一个不存在的类,则调用\core\uek类中的load方法
spl_autoload_register('\core\uek::load');

// 调用uek类中的run方法启动整个框架
\core\uek::run();

公共函数库 /core/common/function.php

<?php
function p($var)
{
  if( is_bool($var) ){
    var_dump($var);
  }else if( is_null ($var) ){
    var_dump($var);
  }else{
    echo '<pre>' . print_r($var, true) . '</pre>';
  }
}

框架核心类 /core/uek.php

<?php
namespace core;
class uek
{
  // 缓存已经加载过的类
  public static $class_map = array();

  // 保存要输出到视图中的变量
  public $assign;

  // 框架启动方法 调用路由类管理路由
  static public function run()
  {
    // 当前并没有include route类文件 会触发spl_autoload_register() 函数中指定的方法
    // 也就是本类中的 load() 方法
    // spl_autoload_register函数会把 \core\lib\route 作为字符串传递给load()

    $route = new \core\lib\route();

    $controller_class = $route->controller;
    $action = $route->action;

    // 根据路由规则找到app下对应的类文件, 根据路由规则运行其中的方法
    $controller_file = APP . '/controller/' . $controller_class . 'Controller.php';
    if( is_file($controller_file) ){
      include $controller_file;
      $class_name =  '\\app\\controller\\'.$controller_class.'Controller';
      $controller = new $class_name();
      $controller->$action();
    }else{
      throw new \Exception("can't find module");
    }

  }

  // 自动加载一个类文件
  public static function load($class)
  {
    // 存在于缓存中
    if( isset($class_map[$class])){
      return true;
    }else{
      // 根据命名空间规则找到对应的类文件
      $file  = UEK . '/' . str_replace('\\', '/', $class) . '.php';

      if( is_file($file) ){
        include $file;
        // 如果已经include过,利用类名做一次缓存
        self::$class_map[$class] = $class;
      }else{
        return false;
      }
    }
  }

  public function assign($name, $value)
  {
    $this->assign[$name] = $value;
  }

  public function display($file)
  {
    $view_path = APP . '/' . $file;
    if(is_file($view_path)){
      extract($this->assign);
      include($view_path);
    }
  }
}

框架路由类 /core/lib/route.php

<?php
namespace core\lib;
class route
{
  public $controller;
  public $action;

  // new 本类时会自动运行的方法
  public function __constructor()
  {
    // 根据用户访问的路径确定调用APP下哪个php文件中的哪个方法
    // 这里返回的只是$controller的名字和对应方法的名字
    // 这里还要处理用户的get传参
    if( isset( $_SERVER['REQUEST_URI'] ) &&  $_SERVER['REQUEST_URI'] !== '/' ){
      $path = $_SERVER['REQUEST_URI'];
      $params = explode('/', trim($path, '/'));

      if(isset($params[0])){
        $this->controller  = $params[0];
        unset($params[0]);
      }
      if(isset($params[1])){
        $this->action = $params[1];
        unset($params[1]);
      }else{
        $this->action = 'index';
      }

      // 将多余的参数收入$_GET
      // index/index/id/1/str/2/test/3
      for ($i = 2; $i < count($params) + 2; $i += 2) {
        if (isset($params[$i + 1])) {
          $_GET[$params[$i]] = $params[$i + 1];
        }
      }

    }else{
      // 用户访问 / 时候默认调用app下的 indexController.php 下的 index() 方法

      $this->controller = 'index';
      $this->action = 'index';
    }
  }
}

框架model类 利用pdo实现连接数据库 /core/lib/model.php

<?php
namespace core\lib;
class model extends \PDO
{
  public function __construct()
  {
    $database_info = 'mysql:host=localhost;dbname=yarn';
    $username = 'root';
    $password = 'root';

    try{
      parent::__construct($database_info, $username, $password);
    } catch (\PDOException $e){
      p($e->getMessage());
    }
  }
}

用户首页类 /app/controller/indexController.php

<?php
namespace app\controller;
// 继承框架核心文件
// 利用核心文件中的 assign 和 display 方法来管理视图

class indexController extends \core\uek
{
  public function index()
  {
    $model = new \core\lib\model();
    $sql = 'select * from todos';
    $result = $model->query($sql);
    p($result->fetchAll());
    p($_GET);

    $this->assign('data','hello world');
    $this->assign('title','facebook');
    $this->display('view/index.html');
  }
}

用户视图文件 ‘/app/view/index.html’

<html>
<head>
  <meta charset='utf-8'>
  <title><?php echo $title ?></title>
</head>
<body>
  <div><?php echo $data ?></div>
</body>
</html>

db文件

SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";

--
-- Database: `yarn`
--
CREATE DATABASE IF NOT EXISTS `yarn` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `yarn`;

-- --------------------------------------------------------

--
-- 表的结构 `todos`
--

DROP TABLE IF EXISTS `todos`;
CREATE TABLE IF NOT EXISTS `todos` (
  `id` int(12) NOT NULL,
  `title` varchar(255) NOT NULL,
  `is_done` tinyint(1) NOT NULL,
  `is_del` tinyint(1) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

--
-- 转存表中的数据 `todos`
--

INSERT INTO `todos` (`id`, `title`, `is_done`, `is_del`) VALUES
(1, 'buy a car', 0, 0),
(2, 'buy a mao', 0, 0);

--
-- Indexes for dumped tables
--

--
-- Indexes for table `todos`
--
ALTER TABLE `todos`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT for dumped tables
--

--
-- AUTO_INCREMENT for table `todos`
--
ALTER TABLE `todos`
  MODIFY `id` int(12) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;

访问 http://localhost/index/index