一步一步学PHP MVC 框架--一个简单例子

前言:MVC模式是PHP下的默认开发模式。不懂MVC模式的请出门左转,有很多PHP在MVC的开发的教程。本教程仅仅以一个比较简单的例子进行讲解。
环境:XAMPP(PHP 7.4.10) + Ubuntu 20.04。安装略过。

Step 0:创建目录
在/opt/htdocs/tutorials/创建基本目录如下,这里没有什么好说的。
app是传统的PHP MVC框架下用来放置框架源代码的,其内部又进一步细分了一些文件夹。
在这里插入图片描述
Step 1: 初始化程序
这里把.htaccess的部分放在后面,把初始化的部分提前。许多写程序的人都会有一个基本思路:先把初始化的部分准备好,然后其余的部分随后跟进,这里也把这部分提前。
public/index.php

<?php
require_once '../app/init.php';

$app = new App;

app/core/Controller.php

<?php

class Controller{
    
    

}

app/init.php

<?php

require_once 'core/App.php';
require_once 'core/Controller.php';

app/core/App.php

<?php

class App{
    
    
    public function __construct(){
    
    
        echo 'OK';
    }
}

这里,进行一次测试
http://localhost:9999/tutorials/mvc/public/
显示:OK。

Step 2: 完善Controller
MVC的一个核心功能再与把url的地址进行分段解析,为后续程序作个铺垫,这里在App.php中简单得把这里的内容实现一下简单的接收功能。
app/core/App.php

<?php

class App{
    
    
    protected $controller = 'home';//
    protected $method = 'index';//
    public function __construct(){
    
    
        // echo 'OK';
        $this->parseUrl();
    }
    
    public function parseUrl(){
    
    
        if(isset($_GET['url'])){
    
    
            echo $_GET['url'];//把接受到的东西在浏览器中输出出来
        }
    }
}

http://localhost:9999/tutorials/mvc/public/?url=Iamnewurl 尝试一下。

到现在为止,可以把url的解析部分展现出来。
MVC的另外一部分url相关的重定向功能则需要.htaccess文件进行。这里,我试着慢慢讲解一下。

Step 3: 重定向与.htaccess
这里,先做个尝试,mvc/public/.htaccess

Options -Indexes

此时,再尝试http://localhost:9999/tutorials/mvc/public/css/ 会显示禁止访问。

在这个尝试之后,你会发现,也需要在app文件夹下放一份,因为PHP的MVC框架大多会把app里面放源代码的,必然不希望外界访问到。

进一步扩展mvc/public/.htaccess

Options -Indexes
RewriteEngine On

RewriteBase /tutorials/mvc/public
RewriteCond %{
    
    REQUEST_FILENAME} !-d
RewriteCond %{
    
    REQUEST_FILENAME} !-f

RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]

RewriteEngine On 是通知apache服务器要启动重定向功能了。
RewriteBase /tutorials/mvc/public 是告诉重定向的根目录。为什么是这里?因为MVC框架下默认要让index.php负责处理url,而index.php在public文件夹下,所以就在这里。
RewriteCond %{REQUEST_FILENAME} !-d 与RewriteCond %{REQUEST_FILENAME} !-f分别对应的目录与文件。
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L] 是核心,会把url传给index.php

http://localhost:9999/tutorials/mvc/public/home/good/night/thanks
尝试一下,浏览器会返回home/good/night/thanks。 这里的这个串是从index.php那里过来的。执行顺序是:url丢给index.php,解析, 执行到$app = new App; 然后,在App中的构造器调用parseUrl()进行的处理,从而实现屏幕输出。

Step 4: 进一步完善parseUrl
上面的例子是个含有/的字符串,这里对其进行分割处理。
core/App.php

<?php

class App{
    
    
    protected $controller = 'home';//
    protected $method = 'index';//
    public function __construct(){
    
    
        // echo 'OK';
        // $this->parseUrl();
        print_r($this->parseUrl());
    }
    
    public function parseUrl(){
    
    
        if(isset($_GET['url'])){
    
    
            //echo $_GET['url'];
            return $url = explode('/',filter_var(rtrim($_GET['url'],'/'), FILTER_SANITIZE_URL));
        }
    }
}

http://localhost:9999/tutorials/mvc/public/home/good/night/thanks
浏览器会显示Array ( [0] => home [1] => good [2] => night [3] => thanks )
修改后parseUrl基本功能就是去除右侧的空格,然后依照url的格式进行切割。
这里,经过分割后,就会进一步把参数传给对应的class, function, argument等,具体顺序如何安排,看个人的喜好,一般地,[0]对应的是controller,[1]对应该controller的方法,[2]之后的是方法的参数,后面的也是按照这个顺序进行。

Step 5: 调用对应的controller
这里,先创建app/controllers/home.php

<?php

class Home{
    
    

    protected $controller = 'home';
    protected $method = 'index';
    protected $params = [];
    public function index(){
    
    
        echo 'home/index';
    }
}

这里,仅仅是为了下面的例子中能找到home.php,其内容还不完善。
app/core/App.php

<?php

class App{
    
    
    protected $controller = 'home';//
    protected $method = 'index';//
    public function __construct(){
    
    
        // echo 'OK';
        // $this->parseUrl();
        print_r($this->parseUrl());
        $url = $this->parseUrl();
        if(file_exists('../app/controllers/'.$url[0].'.php')){
    
    
            $this->controller = $url[0];
            unset($url[0]);
        }
        require_once '../app/controllers/'.$this->controller.'.php';
        $this->controller = new $this->controller;
        var_dump($this->controller);
    }
    
    public function parseUrl(){
    
    
        if(isset($_GET['url'])){
    
    
            //echo $_GET['url'];
            return $url = explode('/',filter_var(rtrim($_GET['url'],'/'), FILTER_SANITIZE_URL));

        }
    }
}

尝试http://localhost:9999/tutorials/mvc/public/home 返回
object(Home)#2 (3) { [“controller”:protected]=> string(4) “home” [“method”:protected]=> string(5) “index” [“params”:protected]=> array(0) { } }
意思是找到了home.php。如果换成个别的不存在的文件,file_exists会找不到文件,甚至会报错。

Step 6 : 调用对应的controller 的相应的方法
app/core/App.php

<?php

class App{
    
    
    protected $controller = 'home';//
    protected $method = 'index';//
    public function __construct(){
    
    
        // echo 'OK';
        // $this->parseUrl();
        print_r($this->parseUrl());
        $url = $this->parseUrl();
        if(file_exists('../app/controllers/'.$url[0].'.php')){
    
    
            $this->controller = $url[0];
            unset($url[0]);
        }
        require_once '../app/controllers/'.$this->controller.'.php';
        $this->controller = new $this->controller;
        // var_dump($this->controller);
        if(null !== [$url[1]]){
    
    
            if(method_exists($this->controller, $url[1])){
    
    
                $this->method = $url[1];
                unset($url[1]);
            }
        }
        
        $this->params = $url?array_values($url):[];
        // print_r($this->params);
        call_user_func_array([$this->controller, $this->method], $this->params);
    }
    
    public function parseUrl(){
    
    
        if(isset($_GET['url'])){
    
    
            //echo $_GET['url'];
            return $url = explode('/',filter_var(rtrim($_GET['url'],'/'), FILTER_SANITIZE_URL));

        }else{
    
    

        }
    }

}

call_user_func_array相当于java中的反射,用以调用对应的class,function,以及传递进去参数。更好的方式是使用php的自动加载功能,这里就省略了。
http://localhost:9999/tutorials/mvc/public/home/index/one,试试看看,
能看到关键的home/index。读者可以重新建立一个app/controllers/contact.php

<?php

class Contact{
    
    
    public function index($name="", $name2=""){
    
    
        echo 'Good night '.$name.' and '.$name2;
    }
}

http://localhost:9999/tutorials/mvc/public/contact/index/mark/john返回
Good night mark and john
至此,接收url,调用controller的方法部分完成,下面的是完成model的部分。

Step 7 :model部分
在此之前,把controller的涉及的class略作修改。
app/core/Controller.php

<?php

class Controller{
    
    
    public function model($model){
    
    
        require_once '../app/models/'.$model.'.php';
        return new $model();
    }
}

app/controller/home.php

<?php

class Home extends Controller{
    
    

    protected $controller = 'home';
    protected $method = 'index';
    protected $params = [];
    public function index($name=""){
    
    
        // echo 'home/index';
        $user = $this->model('user');
        $user->name = 'Mark';
        echo $user->name;
    }
}

为了演示,创建一个user.php
app/models/user.php

<?php

class User{
    
    
    public $user;
}

http://localhost:9999/tutorials/mvc/public/home/index/Billy 返回的是Billy
这背后的逻辑是:由home.php继承自controller.php的实例化model的方法,在接受到url传递而来的function名字后,调用该方法(上面的例子中调用了user.php)。

Step 8 :view部分
app/core/controller.php

<?php

class Controller{
    
    
    public function model($model){
    
    
        require_once '../app/models/'.$model.'.php';
        return new $model();
    }
    public function view($view, $data = []){
    
    
        require_once '../app/views/'.$view.'.php';
    }
}

app/views/home/index.php

Hello <?php $data['name'];?>

app/controllers/home.php

<?php

class Home extends Controller{
    
    

    protected $controller = 'home';
    protected $method = 'index';
    protected $params = [];
    public function index($name=""){
    
    
        // echo 'home/index';
        $user = $this->model('user');
        $user->name = 'Mark';
        // echo $user->name;
        $this->view('home/index',['name'=>$user->name]);
    }
}

http://localhost:9999/tutorials/mvc/public/home/index/Billy
这里会调用到app/views/home/index.php的内容,返回一个Hello Billy.

齐活。

这之后,就需要ORM的内容。

猜你喜欢

转载自blog.csdn.net/yaoguoxing/article/details/109403811