목차
1 MVC란 무엇입니까" >1 MVC란 무엇입니까
객체지향 프로그래밍과 " >인터넷에는 우수한 MVC 프레임워크가 많이 있습니다. 이 튜토리얼은 포괄적이고 궁극적인 MVC 프레임워크 솔루션을 개발하기 위한 것이 아닙니다. 우리는 이것이 내부에서 PHP를 배울 수 있는 좋은 기회라고 생각했습니다. 그 과정에서 객체지향 프로그래밍
3.1 환경 준비" >3.1 환경 준비
1. MySQL 테이블 이름은 " >디렉토리가 설정된 후 다음으로 코드 사양을 규정합니다. 1. MySQL 테이블 이름은
3.3 目录准备" >3.3 目录准备
3.4 重定向" >3.4 重定向
4 PHP MVC核心文件" >4 PHP MVC核心文件
4.1 入口文件" >4.1 入口文件
4.2 配置文件" >4.2 配置文件
4.3 框架核心类" >4.3 框架核心类
4.4 Controller基类" >4.4 Controller基类
4.5 Model基类" >4.5 Model基类
4.6 View基类" >4.6 View基类
无法找到视图文件
5 应用" >5 应用
5.1 数据库部署" >5.1 数据库部署
5.2 部署模型" >5.2 部署模型
5.3 部署控制器" >5.3 部署控制器
5.4 部署视图" >5.4 部署视图
6 应用测试" >6 应用测试
백엔드 개발 PHP 튜토리얼 PHP MVC 프레임워크 작성 [보모 수준 권장 튜토리얼]

PHP MVC 프레임워크 작성 [보모 수준 권장 튜토리얼]

Jan 07, 2022 pm 01:58 PM

1 MVC란 무엇입니까

MVC 패턴(Model-View-Controller)은 소프트웨어 공학의 소프트웨어 아키텍처 패턴입니다. MVC는 소프트웨어 시스템을 Model(모델), View(보기) 및 Controller(컨트롤러)의 세 가지 기본 부분으로 나눕니다. PHP의 MVC 패턴은 Web MVC라고도 하며 1970년대부터 발전했습니다. MVC의 목적은 프로그램의 후속 수정, 확장 및 단순화를 용이하게 하고 프로그램의 특정 부분을 재사용할 수 있도록 하는 동적 프로그래밍 설계를 구현하는 것입니다. 또한 이 모드는 복잡성을 단순화하여 프로그램 구조를 보다 직관적으로 만듭니다. MVC 각 부분의 기능:

    Model
  • 대부분의 비즈니스 로직모든 데이터베이스 로직을 관리합니다. 모델은 데이터베이스에 연결하고 조작하기 위한 추상화 계층을 제공합니다.
  • Controller
  • 사용자 요청에 응답, 데이터 준비 및 데이터 표시 방법 결정을 담당합니다.
  • View
  • - 데이터 렌더링을 담당하고 이를 HTML을 통해 사용자에게 표시합니다.

PHP MVC 프레임워크 작성 [보모 수준 권장 튜토리얼]일반적인 웹 MVC 프로세스:

1. 컨트롤러는 사용자가 보낸 요청을 가로챕니다.

2. 컨트롤러는 상태 읽기 및 쓰기 작업을 완료합니다. 컨트롤러는 데이터를 뷰에 전달합니다.

4. 뷰는 최종 결과를 렌더링하여 사용자에게 제공합니다.

2 자체 MVC 프레임워크를 개발해야 하는 이유

인터넷에는 우수한 MVC 프레임워크가 많이 있습니다. 이 튜토리얼은 포괄적이고 궁극적인 MVC 프레임워크 솔루션을 개발하기 위한 것이 아닙니다. 우리는 이것이 내부에서 PHP를 배울 수 있는 좋은 기회라고 생각했습니다. 그 과정에서 객체지향 프로그래밍

MVC 디자인 패턴

을 배우고, 개발 시 고려해야 할 사항도 배우게 됩니다. 또한 자체 제작한 MVC 프레임워크를 통해 누구나 자신의 프레임워크를 완벽하게 제어하고 아이디어를 프레임워크에 통합할 수 있습니다. 정말 대단하지 않나요~~3 준비

3.1 환경 준비

여기서 가장 기본적인 PHP 환경이 필요합니다:

Nginx 또는 Apache

PHP5.4+
  • MySQL
  • 한 번의 클릭으로 이러한 LNMP 환경을 설치하려면 phpStudy 또는 docker를 사용하는 것이 좋습니다.
3.2 코드 사양

디렉토리가 설정된 후 다음으로 코드 사양을 규정합니다. 1. MySQL 테이블 이름은

소문자

또는

소문자와 밑줄

이어야 합니다. 예: item, <code>car_orders. 2. 모듈 이름(모델)은 CamelCase 명명법, 즉 첫 글자 itemcar_orders

2、模块名(Models)需用大驼峰命名法,即首字母大写,并在名称后添加Model,如:ItemModelCarModel

3、控制器(Controllers)需用大驼峰命名法,即首字母大写,并在名称后添加Controller,如:ItemControllerCarController

4、方法名(Action)需用小驼峰命名法,即首字母小写,如:indexindexPost

5、视图(Views)部署结构为控制器名/行为名,如:item/view.phpcar/buy.php

上述规则是为了程序能更好地相互调用。 接下来就开始真正的PHP MVC编程了。

3.3 目录准备

在开始开发前,我们给这个框架先起个名字吧,就叫:Fastphp框架。 然后根据需要来把项目的目录创建。 假设我们建立的项目为 project,目录结构就这样:

project                 WEB部署根目录
├─app                   应用目录
│  ├─controllers        控制器目录
│  ├─models             模块目录
│  ├─views              视图目录
├─config                配置文件目录
├─fastphp               框架核心目录
│ ├─base                MVC基类目录
│ ├─db                  数据库操作类目录
│ ├─Fastphp.php         内核文件  
├─static                静态文件目录
├─index.php             入口文件
로그인 후 복사

然后按照下一步,把Nginx或者Apache的站点根目录配置到project目录,。

3.4 重定向

重定向的目的有两个:设置根目录为project所在位置,以及将所有请求都发送给 index.php 文件。 如果是Apache服务器,在 project 目录下新建一个 .htaccess 文件,内容为:

<IfModule mod_rewrite.c>
# 打开Rerite功能
RewriteEngine On

    # 如果请求的是真实存在的文件或目录,直接访问
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d

    # 如果访问的文件或目录不是真事存在,分发请求至 index.php
    RewriteRule . index.php
</IfModule>
로그인 후 복사

如果是Nginx服务器,修改配置文件,在servercapital

을 사용해야 하며 이름 뒤에 Model을 추가해야 합니다. 예: ItemModel</ 코드>, <code>자동차 모델.

3. 컨트롤러는 🎜CamelCase 명명법🎜, 즉 첫 글자 🎜capital🎜을 사용해야 하며 이름 뒤에 Controller를 추가해야 합니다. 예: ItemController, < 코드>자동차 컨트롤러. 🎜🎜4 메소드 이름(Action)은 🎜little camel Case naming method🎜, 즉 첫 글자 🎜소문자🎜를 사용해야 합니다. 예: index, indexPost</code >. 🎜🎜5 뷰의 배포 구조는 🎜컨트롤러 이름/동작 이름🎜입니다(예: <code>item/view.php, car/buy.php). 🎜🎜위 규칙은 프로그램이 서로 더 잘 호출하기 위한 규칙입니다. 다음으로 실제 PHP MVC 프로그래밍이 시작됩니다. 🎜🎜3.3 디렉토리 준비🎜🎜🎜개발을 시작하기 전에 이 프레임워크에 이름을 지정하고 🎜Fastphp 프레임워크🎜라고 부르겠습니다. 그런 다음 필요에 따라 프로젝트 디렉터리를 만듭니다. 우리가 빌드하는 프로젝트가 🎜project🎜이고 디렉터리 구조가 다음과 같다고 가정합니다. 🎜
<span class="hljs-title">location</span> / {
<span class="hljs-comment">    # 重新向所有非真是存在的请求到index.php
</span><span class="hljs-title">    try_files</span> <span class="hljs-variable">$uri</span> <span class="hljs-variable">$uri</span>/ /index.php<span class="hljs-variable">$args</span>;
}
로그인 후 복사
🎜 그런 다음 다음 단계에 따라 Nginx 또는 Apache 사이트 루트 디렉터리를 project 디렉터리로 구성합니다. 🎜🎜3.4 Redirect🎜🎜🎜리디렉션의 목적은 두 가지입니다. 루트 디렉터리를 project 위치로 설정하고 모든 요청을 index.php 파일로 보내는 것입니다. 🎜Apache 서버🎜인 경우 프로젝트 디렉터리에 다음 콘텐츠로 새 🎜.htaccess🎜 파일을 만듭니다. 🎜
<?php
// 应用目录为当前目录
define(&#39;APP_PATH&#39;, __DIR__ . &#39;/&#39;);

// 开启调试模式
define(&#39;APP_DEBUG&#39;, true);

// 加载框架文件
require(APP_PATH . &#39;fastphp/Fastphp.php&#39;);

// 加载配置文件
$config = require(APP_PATH . &#39;config/config.php&#39;);

// 实例化框架类
(new fastphp\Fastphp($config))->run();
로그인 후 복사
로그인 후 복사
🎜Nginx 서버인 경우 구성 파일을 수정하고 서버에 다음 리디렉션을 추가합니다. block :🎜
<?php

// 数据库配置
$config[&#39;db&#39;][&#39;host&#39;] = &#39;localhost&#39;;
$config[&#39;db&#39;][&#39;username&#39;] = &#39;root&#39;;
$config[&#39;db&#39;][&#39;password&#39;] = &#39;123456&#39;;
$config[&#39;db&#39;][&#39;dbname&#39;] = &#39;project&#39;;

// 默认控制器和操作名
$config[&#39;defaultController&#39;] = &#39;Item&#39;;
$config[&#39;defaultAction&#39;] = &#39;index&#39;;

return $config;
로그인 후 복사
로그인 후 복사
🎜이 작업을 수행하는 주된 이유는 다음과 같습니다.🎜

(1)静态文件能直接访问。如果文件或者目录真实存在,则直接访问存在的文件/目录。 比如,静态文件static/css/main.css真实存在,就可以直接访问它。 (2)程序有单一的入口。 这种情况是请求地址不是真实存在的文件或目录,这样请求就会传到 index.php 上。 例如,访问地址:localhost/item/detail/1,在文件系统中并不存在这样的文件或目录。 那么,Apache或Nginx服务器会把请求发给index.php,并且把域名之后的字符串赋值给REQUEST_URI变量。 这样在PHP中用$_SERVER[&#39;REQUEST_URI&#39;]就能拿到/item/detail/1; (3)可以用来生成美化的URL,利于SEO

4 PHP MVC核心文件

4.1 入口文件

接下来,在 project 目录下新建 index.php 入口文件,文件内容为:

<?php
// 应用目录为当前目录
define(&#39;APP_PATH&#39;, __DIR__ . &#39;/&#39;);

// 开启调试模式
define(&#39;APP_DEBUG&#39;, true);

// 加载框架文件
require(APP_PATH . &#39;fastphp/Fastphp.php&#39;);

// 加载配置文件
$config = require(APP_PATH . &#39;config/config.php&#39;);

// 实例化框架类
(new fastphp\Fastphp($config))->run();
로그인 후 복사
로그인 후 복사

注意,上面的PHP代码中,并没有添加PHP结束符号?>

这么做的主要原因是:

对于只有 PHP 代码的文件,最好没有结束标志?>

PHP自身并不需要结束符号,不加结束符让程序更加安全,很大程度防止了末尾被注入额外的内容。

4.2 配置文件

在入口文件中,我们加载了config.php文件的内容,那它有何作用呢? 从名称不难看出,它的作用是保存一些常用配置config.php 文件内容如下,作用是定义数据库连接参数参数,以及配置默认控制器名和操作名:

<?php

// 数据库配置
$config[&#39;db&#39;][&#39;host&#39;] = &#39;localhost&#39;;
$config[&#39;db&#39;][&#39;username&#39;] = &#39;root&#39;;
$config[&#39;db&#39;][&#39;password&#39;] = &#39;123456&#39;;
$config[&#39;db&#39;][&#39;dbname&#39;] = &#39;project&#39;;

// 默认控制器和操作名
$config[&#39;defaultController&#39;] = &#39;Item&#39;;
$config[&#39;defaultAction&#39;] = &#39;index&#39;;

return $config;
로그인 후 복사
로그인 후 복사

入口中的$config变量接收到配置参数后,再传给框架的核心类,也就是Fastphp类。

4.3 框架核心类

入口文件对框架类做了两步操作:实例化调用run()方法

实例化操作接受$config参数配置,并保存到对象属性中。

run()方法则调用用类自身方法,完成下面几个操作:

  • 类自动加载

  • 环境检查

  • 过滤敏感字符

  • 移除全局变量的老用法

  • 路由处理

fastphp目录下新建核心类文件,名称Fastphp.php,代码:

<?php
namespace fastphp;

// 框架根目录
defined(&#39;CORE_PATH&#39;) or define(&#39;CORE_PATH&#39;, __DIR__);

/**
 * fastphp框架核心
 */
class Fastphp
{
    // 配置内容
    protected $config = [];

    public function __construct($config)
    {
        $this->config = $config;
    }

    // 运行程序
    public function run()
    {
        spl_autoload_register(array($this, &#39;loadClass&#39;));
        $this->setReporting();
        $this->removeMagicQuotes();
        $this->unregisterGlobals();
        $this->setDbConfig();
        $this->route();
    }

    // 路由处理
    public function route()
    {
        $controllerName = $this->config[&#39;defaultController&#39;];
        $actionName = $this->config[&#39;defaultAction&#39;];
        $param = array();

        $url = $_SERVER[&#39;REQUEST_URI&#39;];
        // 清除?之后的内容
        $position = strpos($url, &#39;?&#39;);
        $url = $position === false ? $url : substr($url, 0, $position);
        // 删除前后的“/”
        $url = trim($url, &#39;/&#39;);

        if ($url) {
            // 使用“/”分割字符串,并保存在数组中
            $urlArray = explode(&#39;/&#39;, $url);
            // 删除空的数组元素
            $urlArray = array_filter($urlArray);

            // 获取控制器名
            $controllerName = ucfirst($urlArray[0]);

            // 获取动作名
            array_shift($urlArray);
            $actionName = $urlArray ? $urlArray[0] : $actionName;

            // 获取URL参数
            array_shift($urlArray);
            $param = $urlArray ? $urlArray : array();
        }

        // 判断控制器和操作是否存在
        $controller = &#39;app\\controllers\\&#39;. $controllerName . &#39;Controller&#39;;
        if (!class_exists($controller)) {
            exit($controller . &#39;控制器不存在&#39;);
        }
        if (!method_exists($controller, $actionName)) {
            exit($actionName . &#39;方法不存在&#39;);
        }

        // 如果控制器和操作名存在,则实例化控制器,因为控制器对象里面
        // 还会用到控制器名和操作名,所以实例化的时候把他们俩的名称也
        // 传进去。结合Controller基类一起看
        $dispatch = new $controller($controllerName, $actionName);

        // $dispatch保存控制器实例化后的对象,我们就可以调用它的方法,
        // 也可以像方法中传入参数,以下等同于:$dispatch->$actionName($param)
        call_user_func_array(array($dispatch, $actionName), $param);
    }

    // 检测开发环境
    public function setReporting()
    {
        if (APP_DEBUG === true) {
            error_reporting(E_ALL);
            ini_set(&#39;display_errors&#39;,&#39;On&#39;);
        } else {
            error_reporting(E_ALL);
            ini_set(&#39;display_errors&#39;,&#39;Off&#39;);
            ini_set(&#39;log_errors&#39;, &#39;On&#39;);
        }
    }

    // 删除敏感字符
    public function stripSlashesDeep($value)
    {
        $value = is_array($value) ? array_map(array($this, &#39;stripSlashesDeep&#39;), $value) : stripslashes($value);
        return $value;
    }

    // 检测敏感字符并删除
    public function removeMagicQuotes()
    {
        if (get_magic_quotes_gpc()) {
            $_GET = isset($_GET) ? $this->stripSlashesDeep($_GET ) : &#39;&#39;;
            $_POST = isset($_POST) ? $this->stripSlashesDeep($_POST ) : &#39;&#39;;
            $_COOKIE = isset($_COOKIE) ? $this->stripSlashesDeep($_COOKIE) : &#39;&#39;;
            $_SESSION = isset($_SESSION) ? $this->stripSlashesDeep($_SESSION) : &#39;&#39;;
        }
    }

    // 检测自定义全局变量并移除。因为 register_globals 已经弃用,如果
    // 已经弃用的 register_globals 指令被设置为 on,那么局部变量也将
    // 在脚本的全局作用域中可用。 例如, $_POST[&#39;foo&#39;] 也将以 $foo 的
    // 形式存在,这样写是不好的实现,会影响代码中的其他变量。 相关信息,
    // 参考: http://php.net/manual/zh/faq.using.php#faq.register-globals
    public function unregisterGlobals()
    {
        if (ini_get(&#39;register_globals&#39;)) {
            $array = array(&#39;_SESSION&#39;, &#39;_POST&#39;, &#39;_GET&#39;, &#39;_COOKIE&#39;, &#39;_REQUEST&#39;, &#39;_SERVER&#39;, &#39;_ENV&#39;, &#39;_FILES&#39;);
            foreach ($array as $value) {
                foreach ($GLOBALS[$value] as $key => $var) {
                    if ($var === $GLOBALS[$key]) {
                        unset($GLOBALS[$key]);
                    }
                }
            }
        }
    }

    // 配置数据库信息
    public function setDbConfig()
    {
        if ($this->config[&#39;db&#39;]) {
            define(&#39;DB_HOST&#39;, $this->config[&#39;db&#39;][&#39;host&#39;]);
            define(&#39;DB_NAME&#39;, $this->config[&#39;db&#39;][&#39;dbname&#39;]);
            define(&#39;DB_USER&#39;, $this->config[&#39;db&#39;][&#39;username&#39;]);
            define(&#39;DB_PASS&#39;, $this->config[&#39;db&#39;][&#39;password&#39;]);
        }
    }

    // 自动加载类
    public function loadClass($className)
    {
        $classMap = $this->classMap();

        if (isset($classMap[$className])) {
            // 包含内核文件
            $file = $classMap[$className];
        } elseif (strpos($className, &#39;\\&#39;) !== false) {
            // 包含应用(application目录)文件
            $file = APP_PATH . str_replace(&#39;\\&#39;, &#39;/&#39;, $className) . &#39;.php&#39;;
            if (!is_file($file)) {
                return;
            }
        } else {
            return;
        }

        include $file;

        // 这里可以加入判断,如果名为$className的类、接口或者性状不存在,则在调试模式下抛出错误
    }

    // 内核文件命名空间映射关系
    protected function classMap()
    {
        return [
            &#39;fastphp\base\Controller&#39; => CORE_PATH . &#39;/base/Controller.php&#39;,
            &#39;fastphp\base\Model&#39; => CORE_PATH . &#39;/base/Model.php&#39;,
            &#39;fastphp\base\View&#39; => CORE_PATH . &#39;/base/View.php&#39;,
            &#39;fastphp\db\Db&#39; => CORE_PATH . &#39;/db/Db.php&#39;,
            &#39;fastphp\db\Sql&#39; => CORE_PATH . &#39;/db/Sql.php&#39;,
        ];
    }
}
로그인 후 복사

下面重点讲解主请求方法 route(),它也称路由方法。 路由方法的主要作用是:截取URL,并解析出控制器名、方法名和URL参数。 假设我们的 URL 是这样:

yoursite.com/controllerName/actionName/queryString
로그인 후 복사

当浏览器访问上面的URL,route()从全局变量 $_SERVER[&#39;REQUEST_URI&#39;]中获取到字符串/controllerName/actionName/queryString。 然后,会将这个字符串分割成三部分:controllerNameactionNamequeryString。 例如,URL链接为:yoursite.com/item/detail/1/hello,那么route()分割之后,

  • ControllerName名就是:item
  • actionName名就是:detail
  • URL参数就是:array(1, hello)

分割完成后,路由方法再实例化控制器:itemController,并调用其中的detail方法 。

4.4 Controller基类

接下来,就是在 fastphp 中创建MVC基类,包括控制器模型视图三个基类。 在fastphp/base/目录下新建控制器基类,文件名 Controller.php,功能就是总调度,内容如下:

<?php
namespace fastphp\base;

/**
 * 控制器基类
 */
class Controller
{
    protected $_controller;
    protected $_action;
    protected $_view;

    // 构造函数,初始化属性,并实例化对应模型
    public function __construct($controller, $action)
    {
        $this->_controller = $controller;
        $this->_action = $action;
        $this->_view = new View($controller, $action);
    }

    // 分配变量
    public function assign($name, $value)
    {
        $this->_view->assign($name, $value);
    }

    // 渲染视图
    public function render()
    {
        $this->_view->render();
    }
}
로그인 후 복사

Controller 类用assign()方法实现把变量保存到View对象中。 这样,在调用$this->render() 后视图文件就能显示这些变量。

4.5 Model基类

新建模型基类,继承自数据库操作类Sql类。 因为数据库操作比较复杂,所以SQL操作我们单独创建一个类。 Model基类涉及到3个类:Model基类本身,它的父类SQL,以及提供数据库连接句柄的Db类。 在fastphp/base/目录下新建模型基类文件,名为 Model.php,代码如下:

<?php
namespace fastphp\base;

use fastphp\db\Sql;

class Model extends Sql
{
    protected $model;

    public function __construct()
    {
        // 获取数据库表名
        if (!$this->table) {

            // 获取模型类名称
            $this->model = get_class($this);

            // 删除类名最后的 Model 字符
            $this->model = substr($this->model, 0, -5);

            // 数据库表名与类名一致
            $this->table = strtolower($this->model);
        }
    }
}
로그인 후 복사

fastphp/db/目录下建立一个数据库基类 Sql.php,代码如下:

<?php
namespace fastphp\db;

use \PDOStatement;

class Sql
{
    // 数据库表名
    protected $table;

    // 数据库主键
    protected $primary = &#39;id&#39;;

    // WHERE和ORDER拼装后的条件
    private $filter = &#39;&#39;;

    // Pdo bindParam()绑定的参数集合
    private $param = array();

    /**
     * 查询条件拼接,使用方式:
     *
     * $this->where([&#39;id = 1&#39;,&#39;and title=Web&#39;, ...])->fetch();
     * 为防止注入,建议通过$param方式传入参数:
     * $this->where([&#39;id = :id&#39;], [&#39;:id&#39; => $id])->fetch();
     *
     * @param array $where 条件
     * @return $this 当前对象
     */
    public function where($where = array(), $param = array())
    {
        if ($where) {
            $this->filter .= &#39; WHERE &#39;;
            $this->filter .= implode(&#39; &#39;, $where);

            $this->param = $param;
        }

        return $this;
    }

    /**
     * 拼装排序条件,使用方式:
     *
     * $this->order([&#39;id DESC&#39;, &#39;title ASC&#39;, ...])->fetch();
     *
     * @param array $order 排序条件
     * @return $this
     */
    public function order($order = array())
    {
        if($order) {
            $this->filter .= &#39; ORDER BY &#39;;
            $this->filter .= implode(&#39;,&#39;, $order);
        }

        return $this;
    }

    // 查询所有
    public function fetchAll()
    {
        $sql = sprintf(select * from `%s` %s, $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->fetchAll();
    }

    // 查询一条
    public function fetch()
    {
        $sql = sprintf(select * from `%s` %s, $this->table, $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->fetch();
    }

    // 根据条件 (id) 删除
    public function delete($id)
    {
        $sql = sprintf(delete from `%s` where `%s` = :%s, $this->table, $this->primary, $this->primary);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, [$this->primary => $id]);
        $sth->execute();

        return $sth->rowCount();
    }

    // 新增数据
    public function add($data)
    {
        $sql = sprintf(insert into `%s` %s, $this->table, $this->formatInsert($data));
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->rowCount();
    }

    // 修改数据
    public function update($data)
    {
        $sql = sprintf(update `%s` set %s %s, $this->table, $this->formatUpdate($data), $this->filter);
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, $data);
        $sth = $this->formatParam($sth, $this->param);
        $sth->execute();

        return $sth->rowCount();
    }

    /**
     * 占位符绑定具体的变量值
     * @param PDOStatement $sth 要绑定的PDOStatement对象
     * @param array $params 参数,有三种类型:
     * 1)如果SQL语句用问号?占位符,那么$params应该为
     *    [$a, $b, $c]
     * 2)如果SQL语句用冒号:占位符,那么$params应该为
     *    [&#39;a&#39; => $a, &#39;b&#39; => $b, &#39;c&#39; => $c]
     *    或者
     *    [&#39;:a&#39; => $a, &#39;:b&#39; => $b, &#39;:c&#39; => $c]
     *
     * @return PDOStatement
     */
    public function formatParam(PDOStatement $sth, $params = array())
    {
        foreach ($params as $param => &$value) {
            $param = is_int($param) ? $param + 1 : &#39;:&#39; . ltrim($param, &#39;:&#39;);
            $sth->bindParam($param, $value);
        }

        return $sth;
    }

    // 将数组转换成插入格式的sql语句
    private function formatInsert($data)
    {
        $fields = array();
        $names = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf(`%s`, $key);
            $names[] = sprintf(:%s, $key);
        }

        $field = implode(&#39;,&#39;, $fields);
        $name = implode(&#39;,&#39;, $names);

        return sprintf((%s) values (%s), $field, $name);
    }

    // 将数组转换成更新格式的sql语句
    private function formatUpdate($data)
    {
        $fields = array();
        foreach ($data as $key => $value) {
            $fields[] = sprintf(`%s` = :%s, $key, $key);
        }

        return implode(&#39;,&#39;, $fields);
    }
}
로그인 후 복사

应该说,Sql基类是框架的核心部分。为什么? 因为通过它,我们创建了一个 SQL 抽象层,可以大大减少了数据库的编程工作。 虽然 PDO 接口本来已经很简洁,但是抽象之后框架的可灵活性更高。 Sql类里面有用到Db:pdo()方法,这是我们创建的Db类,它提供一个PDO单例。 在fastphp/db/目录下创建Db.php文件,内容:

<?php
namespace fastphp\db;

use PDO;
use PDOException;

/**
 * 数据库操作类。
 * 其$pdo属性为静态属性,所以在页面执行周期内,
 * 只要一次赋值,以后的获取还是首次赋值的内容。
 * 这里就是PDO对象,这样可以确保运行期间只有一个
 * 数据库连接对象,这是一种简单的单例模式
 * Class Db
 */
class Db
{
    private static $pdo = null;

    public static function pdo()
    {
        if (self::$pdo !== null) {
            return self::$pdo;
        }

        try {
            $dsn    = sprintf(&#39;mysql:host=%s;dbname=%s;charset=utf8&#39;, DB_HOST, DB_NAME);
            $option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);

            return self::$pdo = new PDO($dsn, DB_USER, DB_PASS, $option);
        } catch (PDOException $e) {
            exit($e->getMessage());
        }
    }
}
로그인 후 복사

4.6 View基类

fastphp/base/目录下新建视图基类 View.php 内容如下:

<?php
namespace fastphp\base;

/**
 * 视图基类
 */
class View
{
    protected $variables = array();
    protected $_controller;
    protected $_action;

    function __construct($controller, $action)
    {
        $this->_controller = strtolower($controller);
        $this->_action = strtolower($action);
    }

    // 分配变量
    public function assign($name, $value)
    {
        $this->variables[$name] = $value;
    }

    // 渲染显示
    public function render()
    {
        extract($this->variables);
        $defaultHeader = APP_PATH . &#39;app/views/header.php&#39;;
        $defaultFooter = APP_PATH . &#39;app/views/footer.php&#39;;

        $controllerHeader = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/header.php&#39;;
        $controllerFooter = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/footer.php&#39;;
        $controllerLayout = APP_PATH . &#39;app/views/&#39; . $this->_controller . &#39;/&#39; . $this->_action . &#39;.php&#39;;

        // 页头文件
        if (is_file($controllerHeader)) {
            include ($controllerHeader);
        } else {
            include ($defaultHeader);
        }

        //判断视图文件是否存在
        if (is_file($controllerLayout)) {
            include ($controllerLayout);
        } else {
            echo <h1 id="无法找到视图文件">无法找到视图文件</h1>;
        }

        // 页脚文件
        if (is_file($controllerFooter)) {
            include ($controllerFooter);
        } else {
            include ($defaultFooter);
        }
    }
}
로그인 후 복사

这样,核心的PHP MVC框架核心就完成了。 下面我们编写应用来测试框架功能。

5 应用

5.1 数据库部署

在 SQL 中新建一个 project 数据库,增加一个item 表、并插入两条记录,命令如下:

CREATE DATABASE `project` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `project`;

CREATE TABLE `item` (
    `id` int(11) NOT NULL auto_increment,
    `item_name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT INTO `item` VALUES(1, &#39;Hello World.&#39;);
INSERT INTO `item` VALUES(2, &#39;Lets go!&#39;);
로그인 후 복사

5.2 部署模型

然后,我们还需要在app/models/目录中创建一个 ItemModel.php 模型,内容如下:

<?php
namespace app\models;

use fastphp\base\Model;
use fastphp\db\Db;

/**
 * 用户Model
 */
class ItemModel extends Model
{
    /**
     * 自定义当前模型操作的数据库表名称,
     * 如果不指定,默认为类名称的小写字符串,
     * 这里就是 item 表
     * @var string
     */
    protected $table = &#39;item&#39;;

    /**
     * 搜索功能,因为Sql父类里面没有现成的like搜索,
     * 所以需要自己写SQL语句,对数据库的操作应该都放
     * 在Model里面,然后提供给Controller直接调用
     * @param $title string 查询的关键词
     * @return array 返回的数据
     */
    public function search($keyword)
    {
        $sql = select * from `$this->table` where `item_name` like :keyword;
        $sth = Db::pdo()->prepare($sql);
        $sth = $this->formatParam($sth, [&#39;:keyword&#39; => %$keyword%]);
        $sth->execute();

        return $sth->fetchAll();
    }
}
로그인 후 복사

因为 Item 模型继承了 Model基类,所以它拥有 Model 类的所有功能。

5.3 部署控制器

app/controllers/ 目录下创建一个 ItemController.php 控制器,内容如下:

<?php
namespace app\controllers;

use fastphp\base\Controller;
use app\models\ItemModel;

class ItemController extends Controller
{
    // 首页方法,测试框架自定义DB查询
    public function index()
    {
        $keyword = isset($_GET[&#39;keyword&#39;]) ? $_GET[&#39;keyword&#39;] : &#39;&#39;;

        if ($keyword) {
            $items = (new ItemModel())->search($keyword);
        } else {
            // 查询所有内容,并按倒序排列输出
            // where()方法可不传入参数,或者省略
            $items = (new ItemModel)->where()->order([&#39;id DESC&#39;])->fetchAll();
        }

        $this->assign(&#39;title&#39;, &#39;全部条目&#39;);
        $this->assign(&#39;keyword&#39;, $keyword);
        $this->assign(&#39;items&#39;, $items);
        $this->render();
    }

    // 查看单条记录详情
    public function detail($id)
    {
        // 通过?占位符传入$id参数
        $item = (new ItemModel())->where([id = ?], [$id])->fetch();

        $this->assign(&#39;title&#39;, &#39;条目详情&#39;);
        $this->assign(&#39;item&#39;, $item);
        $this->render();
    }

    // 添加记录,测试框架DB记录创建(Create)
    public function add()
    {
        $data[&#39;item_name&#39;] = $_POST[&#39;value&#39;];
        $count = (new ItemModel)->add($data);

        $this->assign(&#39;title&#39;, &#39;添加成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }

    // 操作管理
    public function manage($id = 0)
    {
        $item = array();
        if ($id) {
            // 通过名称占位符传入参数
            $item = (new ItemModel())->where([id = :id], [&#39;:id&#39; => $id])->fetch();
        }

        $this->assign(&#39;title&#39;, &#39;管理条目&#39;);
        $this->assign(&#39;item&#39;, $item);
        $this->render();
    }

    // 更新记录,测试框架DB记录更新(Update)
    public function update()
    {
        $data = array(&#39;id&#39; => $_POST[&#39;id&#39;], &#39;item_name&#39; => $_POST[&#39;value&#39;]);
        $count = (new ItemModel)->where([&#39;id = :id&#39;], [&#39;:id&#39; => $data[&#39;id&#39;]])->update($data);

        $this->assign(&#39;title&#39;, &#39;修改成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }

    // 删除记录,测试框架DB记录删除(Delete)
    public function delete($id = null)
    {
        $count = (new ItemModel)->delete($id);

        $this->assign(&#39;title&#39;, &#39;删除成功&#39;);
        $this->assign(&#39;count&#39;, $count);
        $this->render();
    }
}
로그인 후 복사

5.4 部署视图

app/views/目录下新建 header.php 和 footer.php 两个页头页脚模板,如下。 header.php 内容:

<html>
<head>
    <meta http-equiv=Content-Type content=text/html; charset=utf-8 />
    <title><?php echo $title ?></title>
    <link rel=stylesheet href=/static/css/main.css type=text/css />
</head>
<body>
    <h1><?php echo $title ?></h1>
로그인 후 복사

footer.php 内容:

</body>
</html>
로그인 후 복사

页头文件用到了main.css样式文件,内容:

html, body {
margin: 0;
padding: 10px;
font-size: 20px;
}

input {
font-family:georgia,times;
font-size:24px;
line-height:1.2em;
}

a {
color:blue;
font-family:georgia,times;
line-height:1.2em;
text-decoration:none;
}

a:hover {
text-decoration:underline;
}

h1 {
color:#000000;
font-size:41px;
border-bottom:1px dotted #cccccc;
}

td {padding: 1px 30px 1px 0;}
로그인 후 복사

然后,在 app/views/item 目录下创建以下几个视图文件。 index.php,浏览数据库内 item 表的所有记录,内容:

<form action= method=get>
    <input type=text value=<?php echo $keyword ?> name=keyword>
    <input type=submit value=搜索>
</form>

<p><a href=/item/manage>新建</a></p>

<table>
    <tr>
        <th>ID</th>
        <th>内容</th>
        <th>操作</th>
    </tr>
    <?php foreach ($items as $item): ?>
        <tr>
            <td><?php echo $item[&#39;id&#39;] ?></td>
            <td><?php echo $item[&#39;item_name&#39;] ?></td>
            <td>
                <a href=/item/manage/<?php echo $item[&#39;id&#39;] ?>>编辑</a>
                <a href=/item/delete/<?php echo $item[&#39;id&#39;] ?>>删除</a>
            </td>
        </tr>
    <?php endforeach ?>
</table>
로그인 후 복사

add.php,添加记录后的提示,内容:

<a class=big href=/item/index>成功添加<?php echo $count ?>条记录,点击返回</a>
로그인 후 복사

manage.php,管理记录,内容:

<form action=<?php echo $postUrl; ?> method=post>
    <?php if (isset($item[&#39;id&#39;])): ?>
        <input type=hidden name=id value=<?php echo $item[&#39;id&#39;] ?>>
    <?php endif; ?>
    <input type=text name=value value=<?php echo isset($item[&#39;item_name&#39;]) ? $item[&#39;item_name&#39;] : &#39;&#39; ?>>
    <input type=submit value=提交>
</form>

<a class=big href=/item/index>返回</a>
로그인 후 복사

update.php,更改记录后的提示,内容:

<a class=big href=/item/index>成功修改<?php echo $count ?>项,点击返回</a>
로그인 후 복사

delete.php,删除记录后的提示,内容:

<a href=/item/index>成功删除<?php echo $count ?>项,点击返回</a>
로그인 후 복사

6 应用测试

这样,在浏览器中访问 project程序:http://localhost/item/index/,就可以看到效果了。
PHP MVC 프레임워크 작성 [보모 수준 권장 튜토리얼]

以上代码已经全部发布到 github 上,关键部分加了注释:

  • 仓库地址:https://github.com/yeszao/fastphp
  • 源码打包:https://github.com/yeszao/fastphp/archive/master.zip

위 내용은 PHP MVC 프레임워크 작성 [보모 수준 권장 튜토리얼]의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP의 컬 : REST API에서 PHP Curl Extension 사용 방법 PHP의 컬 : REST API에서 PHP Curl Extension 사용 방법 Mar 14, 2025 am 11:42 AM

PHP 클라이언트 URL (CURL) 확장자는 개발자를위한 강력한 도구이며 원격 서버 및 REST API와의 원활한 상호 작용을 가능하게합니다. PHP CURL은 존경받는 다중 프로모토콜 파일 전송 라이브러리 인 Libcurl을 활용하여 효율적인 execu를 용이하게합니다.

Codecanyon에서 12 개의 최고의 PHP 채팅 스크립트 Codecanyon에서 12 개의 최고의 PHP 채팅 스크립트 Mar 13, 2025 pm 12:08 PM

고객의 가장 긴급한 문제에 실시간 인스턴트 솔루션을 제공하고 싶습니까? 라이브 채팅을 통해 고객과 실시간 대화를 나누고 문제를 즉시 해결할 수 있습니다. 그것은 당신이 당신의 관습에 더 빠른 서비스를 제공 할 수 있도록합니다.

PHP에서 늦은 정적 결합의 개념을 설명하십시오. PHP에서 늦은 정적 결합의 개념을 설명하십시오. Mar 21, 2025 pm 01:33 PM

기사는 PHP 5.3에 도입 된 PHP의 LSB (Late STATIC BING)에 대해 논의하여 정적 방법의 런타임 해상도가보다 유연한 상속을 요구할 수있게한다. LSB의 실제 응용 프로그램 및 잠재적 성능

JWT (JSON Web Tokens) 및 PHP API의 사용 사례를 설명하십시오. JWT (JSON Web Tokens) 및 PHP API의 사용 사례를 설명하십시오. Apr 05, 2025 am 12:04 AM

JWT는 주로 신분증 인증 및 정보 교환을 위해 당사자간에 정보를 안전하게 전송하는 데 사용되는 JSON을 기반으로 한 개방형 표준입니다. 1. JWT는 헤더, 페이로드 및 서명의 세 부분으로 구성됩니다. 2. JWT의 작업 원칙에는 세 가지 단계가 포함됩니다. JWT 생성, JWT 확인 및 Parsing Payload. 3. PHP에서 인증에 JWT를 사용하면 JWT를 생성하고 확인할 수 있으며 사용자 역할 및 권한 정보가 고급 사용에 포함될 수 있습니다. 4. 일반적인 오류에는 서명 검증 실패, 토큰 만료 및 대형 페이로드가 포함됩니다. 디버깅 기술에는 디버깅 도구 및 로깅 사용이 포함됩니다. 5. 성능 최적화 및 모범 사례에는 적절한 시그니처 알고리즘 사용, 타당성 기간 설정 합리적,

프레임 워크 보안 기능 : 취약점 보호. 프레임 워크 보안 기능 : 취약점 보호. Mar 28, 2025 pm 05:11 PM

기사는 입력 유효성 검사, 인증 및 정기 업데이트를 포함한 취약점을 방지하기 위해 프레임 워크의 필수 보안 기능을 논의합니다.

프레임 워크 사용자 정의/확장 : 사용자 정의 기능을 추가하는 방법. 프레임 워크 사용자 정의/확장 : 사용자 정의 기능을 추가하는 방법. Mar 28, 2025 pm 05:12 PM

이 기사에서는 프레임 워크에 사용자 정의 기능 추가, 아키텍처 이해, 확장 지점 식별 및 통합 및 디버깅을위한 모범 사례에 중점을 둡니다.

PHP의 CURL 라이브러리를 사용하여 JSON 데이터가 포함 된 게시물 요청을 보내는 방법은 무엇입니까? PHP의 CURL 라이브러리를 사용하여 JSON 데이터가 포함 된 게시물 요청을 보내는 방법은 무엇입니까? Apr 01, 2025 pm 03:12 PM

PHP 개발에서 PHP의 CURL 라이브러리를 사용하여 JSON 데이터를 보내면 종종 외부 API와 상호 작용해야합니다. 일반적인 방법 중 하나는 컬 라이브러리를 사용하여 게시물을 보내는 것입니다 ...

See all articles