资讯专栏INFORMATION COLUMN

从头开始撸一个 PHP 框架

nanfeiyan / 2705人阅读

摘要:本文仅仅是用于自我学习记录,参考了的原创文章随着标准和包管理工具的面世,普通开发者撸一个框架已经不再是什么难事了。

本文仅仅是用于自我学习记录,参考了JohnLui的原创文章: https://segmentfault.com/a/11...
随着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.php

我们在根目录下新建一个public文件夹,并在该文件夹下新建 index.phpindex.php 文件类似于一个大厦的入口,我们所有的请求都运行 index.php

下面是 index.php 文件的代码:

// 自动加载vendor目录下的包
require "../vendor/autoload.php";
routes.php

此时我们观察 index.php,除了把vendor下面的包都 require 进来了外,其他啥都没干。那么如何响应各种各样的请求呢?

我们需要定义路由。路由就有点像快递分拣站,把写着不同地址的请求分拨给不同的控制器处理。
那么我们在根目录下创建一个 routes 文件夹,并在该文件夹下创建 web.php 文件。文件内容:


然后我们启动php内置的开发服务器:

> cd public

> php -S localhost:8001

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

MVC

上面我们仅仅实现了访问一个地址,返回一个字符串。下面我们来真正搭建MVC框架。MVC其实就是Model、View、Controller三个的简称。
不管怎么样,我们先新建三个文件夹再说,即 viewsmodelscontrollers

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

This is Home Page";
    }

}

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

Macaw::get("", "AppControllersHomeController@home");

整体代码为:


此时已经绑定了一个路由至我们一个控制器的方法,但是我们去访问 http://localhost:8001 会出现 Uncaught Error: Class "AppControllersHomeController" not found in 的错误。

为什么呢?

因为此时我们还并没有将控制器加载进来,程序并不知道控制器在哪儿。我们可以用 composer 的 classmap 方法加载进来。修改 composer.json 中添加:

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

我们顺便把models也加载进来。

composer dump-autoload

刷新自动加载

Model连接数据库

我们创建一个Article Model,这个 Model 对应数据库一张表。此时我们先用mysql 命令行工具新建一个 demo_database 的数据库,里面有一张表 articles , 表的结构大致如下:

mysql> desc articles;
+---------+--------------+------+-----+---------+----------------+
| 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 来连接数据库查询数据:

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 "

" . $article["title"] . "

"; echo "

" . $article["content"] . "

"; } $mysqli->close(); } }

这么一来我们就可以在控制器里面使用
`

Article::first();

`
来查询 articles 表里面的第一条article数据,然后我们再通过 echo 返回给浏览器。


View层

看上面的代码,我们在 Article Model 中连续写了两条 echo 语句来格式化输出。如果后续我们的页面十分复杂的时候,我们把所有的格式化输出的代码都写在 Model 里面感觉是个灾难。我们应该把这些格式化输出的代码分离出来,这便是我们说的 MVC 层的 View 层。

我们在 views 目录下新建 home.php:

" . $article["title"] . "

"; echo "

" . $article["content"] . "

";

我们再改写一下 Article.php,删除echo 那两行,直接

return article;

然后我们在 HomeController 中指定要使用的 view:


我们这里的 view 层仅仅是用 PHP 拼接了 html,后续我们需要实现更加复杂的视图的时候,我们可以引入模版引擎。

ORM

我们一路从一个空文件夹开始,打造一个自己的一个框架,感觉并没有写多少代码,唯一写了好几行代码感觉比较麻烦的就是连接数据库来查询数据了。我们我们有很多 Model,要实现 增删改查的话,我们岂不是要重复 连接,查询、插入、删除、更新,然后关闭连接?我们应该把这些功能分装一下。

怎么封装?有其他人写好的包了,直接拿来用吧~ 当然如果你想自己造轮子的话,也可以自己实现一下。

我们这里使用 illuminate/database:

composer require illuminate/database

然后我们在 public/index.php 中引入:

use IlluminateDatabaseCapsuleManager 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 的数据库配置文件。

 "mysql",
    "host" => "localhost",
    "database" => "demo_database",
    "username" => "root",
    "password" => "secret",
    "charset" => "utf8",
    "collation" => "utf8_general_ci",
    "prefix" => ""
];

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


更多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 ...

本文用到的示例代码已经上传到github:https://github.com/charliex2/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28470.html

相关文章

  • grpc - 使用 golang 带你从头一套 RPC 服务(二)

    摘要:紧接上一篇,下面用作为客户端调用的服务端。安装插件插件可以帮助我们自动生成客户端封装了的服务接口,方便我们直接引入调用,否则只生成服务请求响应的实体类,用起来不太方便。打包解包客户端发送至服务端服务端接收数据后使用对应的实体解包服务端 紧接上一篇,下面用PHP作为客户端调用Go的服务端。 安装 grpc_php_plugin 插件 grpc_php_plugin插件可以帮助我们自动生成...

    raise_yang 评论0 收藏0
  • 来!我们一起来从头开始构建自己的JavaScript模块化工具

    摘要:能达到一个积木块模块的要求。接下来我们将改进它。结尾到此,我们自己构建了一个很实用的模块化工具,项目的源码在这里,喜欢的话,给个。 前言 希望编写程序能像玩积木一样,首先规划要产出怎样的作品,然后在积木堆中挑选合适的积木块,最后一组合就完工了。 于是JavaScript需要类似这样模块化,每个模块都隐藏内部细节并且对外暴露接口,再处理好模块之间的依赖关系,就可以达到玩积木的效果了。 虽...

    AaronYuan 评论0 收藏0
  • 那是我夕阳下的奔跑--一个萌新的觉醒

    摘要:晚上又不想看书屋里光线不好,最近正好在跟着权哥学习和,不如自己写一个以后能用到的后台模板来玩玩练练手。把重写成不太确定,明天去问问权哥,哎呀写文档是真的有用把这个文件和文件都拖到网站根目录下,然后把中的加载框架引导文件改成正确读取的路径。 今天下班回来走在路上,刚下过雨,又出了太阳。 步行从公司到租的房子里,路过人民公园,空气一度让人觉的这根本不是郑州的样子。公司里有些让人不快的事情也...

    MiracleWong 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<