2018.2.5 PHP如何写好一个程序用框架

随着PHP标准和Composer包管理工具的面世,普通开发者撸一个框架已经再也不是什么难事了。

不管是路由管理、ORM管理、仍是视图渲染都有许许多多优秀的包可使用。咱们就像堆积木同样把这些包用composer一个个堆积起来。

接下来咱们即是简单地实现一个MVC框架,来加深咱们对框架的理解。

composer

建立一个空的 composer.json 文件。
{
  
}
或者在空目录下执行:
composer init
则能够生成一个类型以下的文件:
{
    "name": "charlie/my_first_framework",
    "description": "My First Framework",
    "type": "project",
    "license": "MIT",
    "authors": [
        {
            "name": "Charlie",
            "email": "demo@qq.com"
        }
    ],
    "minimum-stability": "dev",
    "require": {}
}

安装第一个包

咱们接下来安装一个管理路由的包: noahbuscher/macaw。 功能比这个更增强大的路由包有不少,可是为了简单起见,咱们选择安装这个包。

composer require noahbuscher/macaw

当前目录结构为:
➜  demo tree
.
├── composer.json
├── composer.lock
└── vendor
    ├── autoload.php
    ├── composer
    │   ├── ClassLoader.php
    │   ├── LICENSE
    │   ├── autoload_classmap.php
    │   ├── autoload_namespaces.php
    │   ├── autoload_psr4.php
    │   ├── autoload_real.php
    │   ├── autoload_static.php
    │   └── installed.json
    └── noahbuscher
        └── macaw
            ├── LICENSE.md
            ├── Macaw.php
            ├── README.md
            ├── composer.json
            ├── composer.lock
            └── config
                ├── Web.config
                └── nginx.conf

public/index.phpphp

咱们在根目录下新建一个public文件夹,并在该文件夹下新建 index.php。index.php 文件相似于一个大厦的入口,咱们全部的请求都运行 index.php。
下面是 index.php 文件的代码:
// 自动加载vendor目录下的包require '../vendor/autoload.php';
routes.php
此时咱们观察 index.php,除了把vendor下面的包都 require 进来了外,其余啥都没干。那么如何响应各类各样的请求呢?
咱们须要定义路由。路由就有点像快递分拣站,把写着不一样地址的请求分拨给不一样的控制器处理。
那么咱们在根目录下建立一个 routes 文件夹,并在该文件夹下建立 web.php 文件。

文件内容:

<?php
use NoahBuscher\Macaw\Macaw;

Macaw::get('hello', function () {
    echo "hello world";
});

Macaw::dispatch();

而后咱们启动php内置的开发服务器:

> cd public
> php -S localhost:8001

咱们访问 http://localhost:8001/hello 就能看到咱们预期的 "hello world".

上面咱们仅仅实现了访问一个地址,返回一个字符串。html

下面咱们来真正搭建MVC框架。MVC其实就是Model、View、Controller三个的简称。

无论怎么样,咱们先新建三个文件夹再说,即 views、models、controllers。mysql

新建 controllersHomeController.php 文件,代码以下:nginx

<?php
namespace App\Controllers;

use App\Models\Article;
class HomeController extends BaseController{
    public function home()
    {
      echo "<h1>This is Home Page</h1>";
    }

}

另外咱们在 routes/web.php 中添加一条路由:

Macaw::get('', 'App\\Controllers\\HomeController@home');
总体代码为:
<?php
use NoahBuscher\Macaw\Macaw;

Macaw::get('hello', function () {
    echo "hello world";
});

Macaw::get('', 'App\\Controllers\\HomeController@home');

Macaw::dispatch();

此时已经绑定了一个路由至咱们一个控制器的方法,可是咱们去访问 http://localhost:8001 会出现 Uncaught Error: Class 'App\Controllers\HomeController' not found in 的错误。为何呢?

由于此时咱们还并无将控制器加载进来,程序并不知道控制器在哪儿。咱们能够用 composer 的 classmap 方法加载进来。

修改 composer.json 中添加:git

"autoload": {
    "classmap": [
      "app/controllers",
      "app/models"
    ]
  }

咱们顺便把models也加载进来。web

composer dump-autoloadsql

刷新自动加载
Model链接数据库
咱们建立一个Article Model,这个 Model 对应数据库一张表。此时咱们先用mysql 命令行工具新建一个 demo_database 的数据库,里面有一张表 articles , 表的结构大体以下:数据库

mysql> desc articles;json

+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int(11)      | NO   | PRI | NULL    | auto_increment |
| title   | varchar(256) | YES  |     | NULL    |                |
| content | varchar(256) | YES  |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

咱们再在表里面填入数据:浏览器

mysql> select * from articles;

+----+--------+--------------+| id | title  | content      |
+----+--------+--------------+
|  1 | hhhhh  | heheheheheh  ||  2 | hhhhh2 | heheheheh2eh |
+----+--------+--------------+
2 rows in set (0.00 sec)

固然了,咱们如今是直接经过 MySQL 的 insert 命令直接填入数据,后续咱们能够经过咱们的框架新建 model 。
接下来咱们要作的就是怎么在 Model 中链接数据库取到数据库里面的数据啦! 本文使用的 php 7.1,咱们使用 mysqli 来链接数据库查询数据:

<?php
namespace App\Models;
class Article {
    public static function first()
    {
        //mysql_connect is deprecated
        $mysqli = new \mysqli('localhost', 'root', 'w.x.c.123', 'demo_database');
        if ($mysqli->connect_errno) {
            echo "Failed to connect to Mysql: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error();
        }
        $mysqli->set_charset('utf-8');
        $result = $mysqli->query("SELECT * FROM articles limit 0,1");

        $article = $result->fetch_array();
        if ($article) {
            echo "<p>" . $article['title'] . "</p>";
            echo "<p>" . $article['content'] . "</p>";
        }
        $mysqli->close();
    }
}

这么一来咱们就能够在控制器里面使用
Article::first();
来查询 articles 表里面的第一条article数据,而后咱们再经过 echo 返回给浏览器。

<?php
namespace App\Controllers;

use App\Models\Article;
class HomeController extends BaseController{
    public function home()
    {
        Article::first();
    }

}

View层
看上面的代码,咱们在 Article Model 中连续写了两条 echo 语句来格式化输出。若是后续咱们的页面十分复杂的时候,咱们把全部的格式化输出的代码都写在 Model 里面感受是个灾难。咱们应该把这些格式化输出的代码分离出来,这即是咱们说的 MVC 层的 View 层。
咱们在 views 目录下新建 home.php:

<?php
echo "<p>" . $article['title'] . "</p>";echo "<p>" . $article['content'] . "</p>";

咱们再改写一下 Article.php,删除echo 那两行,直接
return article;
而后咱们在 HomeController 中指定要使用的 view:

<?php
namespace App\Controllers;

use App\Models\Article;
class HomeController extends BaseController{
    public function home()
    {
        $article = Article::first();
        require dirname(__FILE__) . "/../views/home.php";
    }
}

咱们这里的 view 层仅仅是用 PHP 拼接了 html,后续咱们须要实现更加复杂的视图的时候,咱们能够引入模版引擎。
ORM
咱们一路从一个空文件夹开始,打造一个本身的一个框架,感受并无写多少代码,惟一写了好几行代码感受比较麻烦的就是链接数据库来查询数据了。咱们咱们有不少 Model,要实现 增删改查的话,咱们岂不是要重复 链接,查询、插入、删除、更新,而后关闭链接?咱们应该把这些功能分装一下。
怎么封装?有其余人写好的包了,直接拿来用吧~ 固然若是你想本身造轮子的话,也能够本身实现一下。
咱们这里使用 illuminate/database:
composer require illuminate/database
而后咱们在 public/index.php 中引入:
use Illuminate\Database\Capsule\Manager as Capsule;
require '../vendor/autoload.php';
// Eloquent ORM
$capsule = new Capsule();
$capsule->addConnection(require '../config/database.php');
$capsule->bootEloquent();
//路由配置require '../routes/web.php';
咱们在链接数据的时候,使用了 config/database.php 的数据库配置文件。

<?php
return [
    'driver' => 'mysql',
    'host' => 'localhost',
    'database' => 'demo_database',
    'username' => 'root',
    'password' => 'secret',
    'charset' => 'utf8',
    'collation' => 'utf8_general_ci',
    'prefix' => ''
];

接下来咱们就能够删掉 models/Article.php 文件中咱们写的大部分代码,而仅仅须要继承IlluminateDatabaseEloquentModel 来使用 Eloquent ORM 的功能:

<?php/**
 * Created by PhpStorm.
 * User: W
 * Date: 24/03/2018
 * Time: 22:23
 */
namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Article extends Model{
    public $timestamps = false;
}

更多Eloquent ORM的功能,您也能够本身查阅文档。

总结

好了,咱们一个 MVC 框架基本上就搭建完了,咱们回头看一下整个框架目录结构,是否是跟 Laravel 有点像呢?
➜ myFirstFramework git:(master) ✗ tree

.
├── README.md
├── app
│   ├── controllers
│   │   ├── BaseController.php
│   │   └── HomeController.php
│   ├── models
│   │   └── Article.php
│   └── views
│       └── home.php
├── composer.json
├── composer.lock
├── config
│   └── database.php
├── public
│   └── index.php
├── routes
│   └── web.php
└── vendor ...
相关文章
相关标签/搜索