php 一步步实现mvc架构——view篇

100人浏览   2024-09-04 08:03:23

bigsmoker 51future 技术宅

同今天给同学们带了得是"php 一步步实现mvc架构——view篇"。如。掌握设计模式,有利于理解mvc中各种模式得实现。

好了,言归正传。首先先了解下,什么是view 层。还记得 在 路由篇 中得那张图吗?为了更直观一些,我还是将这张图引入

看见那个view层了吧。对得,就是视图层。这个层就是今天得主角。

一 先说说实现思路,来看个view层代码

index.php

======================================================================

<html>
<head>{{$test}}</head>
<body>
<div>{{$myname}}</div>
{{foreach($memners as $k=>$v)}}
<div>{{$v}}</div>
{{endforeach}}
</body>
</html>  

这些个奇葩得类似 {{$test}}的变量,就是模板变量,可能有同学说 里面不光是模板变量,还有

{{foreach}}之流,没做。在这个模板里,它们统统都叫模板变量。

再来看下controller层代码

Test.php

======================================================================

function mytestAction()
{
    //echo "hello";
    //var_dump(func_get_args());
    //  $this->loadModel('http\models\Test');
    $this->assign("test","mytestAction");
    $this->assign("myname","hhh");
    $this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));
    $this->display('index');
}

以上代码代码通过display,去调用 templates.php中的display方法来引用模板。那么这个display是什么鬼?哈哈,其实这个display就做两件事。

1 将index.php中的模板变量解析成 原生php语法

解析前的模板

======================================================================

<html>
<head>{{$test}}</head>
<body>
<div>{{$myname}}</div>
{{foreach($memners as $k=>$v)}}
<div>{{$v}}</div>
{{endforeach}}
</body>
</html>

======================================================================

解析后的模板

======================================================================

<?php $test ='mytestAction'; ?><?php $myname ='hhh'; ?><?php $memners =array (
    'a' => 1,
    'b' => '2',
    'c' => 3,
); ?><html>
<head><?php echo $test?></head>
<body>
<div><?php echo $myname?></div>
<?php foreach($memners as $k=>$v) {?>
    <div><?php echo $v?></div>
<?php  }?>
</body>
</html>

======================================================================

没错,你可以用任何你能想得到的方法去实现。这里的实现思路就是用正则匹配,替换模板变量

具体实现步骤:

1通过template.php 中的display方法读取模板流

2将模板流中的循环{{foreach($memners as $k=>$v)}}替换成 <?php foreach($memners as $k=>$v) {?>,{{endforeach}}替换成<?php }?>,{{$test}}替换成<?php echo $test?>

3将controller方法mytestAction中的要赋值给模板的值,通过assign($key,$value)的方法赋值给templates.php中的varArr数组。这个vArr形如:vArr["test"]="mytestAction"。

以下是具体操作

给模板赋值

$this->assign("test","mytestAction");

$this->assign("myname","hhh");

$this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));

4将varArr中的值注入模板流

注意

模板流 指用file_get_content,获取的字符串

二 讲完思路,我们上代码,具体看下如何实现

先看下架构图

======================================================================

======================================================================

上图cache 层就是最终引入的php文件(最终的视图模板)

======================================================================

<?php $test ='mytestAction'; ?><?php $myname ='hhh'; ?><?php $memners =array (
    'a' => 1,
    'b' => '2',
    'c' => 3,
); ?><html>
<head><?php echo $test?></head>
<body>
<div><?php echo $myname?></div>
<?php foreach($memners as $k=>$v) {?>
    <div><?php echo $v?></div>
<?php  }?>
</body>
</html>

======================================================================

再看下http\controller\Test.php

<?php
/**
 * Created by PhpStorm.
 * User: mario
 * Date: 2021/1/26
 * Time: 0:39
 */
namespace http\controllers;
class Test extends controller
{
    function mytestAction()
    {
        $this->assign("test","mytestAction");//模板赋值
        $this->assign("myname","hhh");
        $this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));
        $this->display('index');//调用templates/Test/mytest/index.php
    }
    function mytest3Action()
    {
        $this->assign("test","mytest3Action");
        $this->assign("myname","hello");
        $this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));
        $this->display('index');//调用templates/Test/mytest3/index.php
    }
}

======================================================================

再看下 controller类,调用了Vendor\lib\templates.php的assign和display方法

======================================================================

<?php
namespace http\controllers;
use Vendor\lib\templates;
class controller {
    protected  $model = null;
    private $templatesObj = null;
    function __construct()
    {
        $this->templatesObj = (new templates());
    }
    function display($name, ...$arguments)
    {
        $this->templatesObj->display($name, $arguments);
    }
    function assign($k,$v)
    {
        $this->templatesObj->assign($k,$v);
    }
}

======================================================================

继续查看Vendor\lib\templates.php

======================================================================

<?php
namespace Vendor\lib;
use IFS\Template\Template;
class templates implements Template
{
    private $tpl=[];
    private $vArr = [];
    private $controller;
    private $method;
    private $suffix;
    private $cacheFile;
    function __construct()
    {
        $this->controller=basename(str_replace("\\","/",$GLOBALS['controller']))??'index';
        $this->method=str_replace("Action","",$GLOBALS['method'])??'index';
        $this->suffix='.php';
    }
    function display($tpl,$compileDir='')
    {
        $this->tpl[$this->controller][$this->method] = TPLROOT.DIRECTORY_SEPARATOR.$this->controller.DIRECTORY_SEPARATOR.$this->method.DIRECTORY_SEPARATOR.$tpl.$this->suffix??"";
        $pattern = "#{{(.*?)}}#i";
        $content = file_get_contents($this->tpl[$this->controller][$this->method]);
        $res = $this->replacePattern($pattern,$content);
            $compileDir =COMPILEPATH.DIRECTORY_SEPARATOR.$this->controller.DIRECTORY_SEPARATOR.$this->method.DIRECTORY_SEPARATOR;
        $this->compile($res,$compileDir,$tpl);
        require $this->cacheFile;
    }
    function assign($k,$v)
    {
        $this->vArr[$k] = $v;
    }
    function compile($content,$complieDir,$tplname)
    {
        $this->cacheFile = $complieDir.$tplname.'.php';
        if(is_dir($complieDir))
        {
            return file_put_contents($this->cacheFile,$content);
        } else {
            mkdir($complieDir,0777,true);
            return file_put_contents($this->cacheFile,$content);
        }
    }
    function setPatternFlag($left,$right)
    {}
    function parseVar($pattern,$content)
    {
        preg_match_all($pattern,$content,$matches);
        $replacements = [];
        foreach ($matches[1] as $k=>$v) {
            $matches[0][$k] = '/'.addcslashes($matches[0][$k],'(,$,)').'/';
            if(preg_match("#(foreach.*\((.*?)\))#i",$v))
            {
                $replacements [$k] = "<?php ".$v." {?>";
            } else if(preg_match("#(endforeach)#i",$v)){
                $replacements [$k] = "<?php  }?>";
            }else {
                $replacements [$k] = "<?php echo ".$v."?>";
            }
        }
        $content = preg_replace($matches[0],$replacements,$content);
        $phpVar = '';
        foreach ($this->vArr as $k=>$v)
        {
            if(is_array($v))
            {
                $phpVar .= "<?php \$$k =".var_export($v,true)."; ?>";
            } else {
                $phpVar .= "<?php \$$k ='".$v."'; ?>";
            }
        }
        return $phpVar.$content;
    }
    function replacePattern($pattern,$content)
    {
        return $this->parseVar($pattern,$content);
    }
}

======================================================================

先来看下assign(),很简单,就是将值放入数组,以便将值注入模板流

======================================================================

function assign($k,$v)
{
    $this->vArr[$k] = $v;
}

======================================================================

display 函数的作用是将模板变成模板流(file_get_contents($this->tpl[$this->controller][$this->method])),然后调用replacePattern方法将模板流中的模板变量替换成 <?php ?>的形式。用file_put_content方法及将其写入cache/Test/mytest/index.php中。再 通过 require cache/Test/mytest/index.php 加载视图

======================================================================

function display($tpl,$compileDir='')
{
    $this->tpl[$this->controller][$this->method] = TPLROOT.DIRECTORY_SEPARATOR.$this->controller.DIRECTORY_SEPARATOR.$this->method.DIRECTORY_SEPARATOR.$tpl.$this->suffix??"";
    $pattern = "#{{(.*?)}}#i";
    $content = file_get_contents($this->tpl[$this->controller][$this->method]);
    $res = $this->replacePattern($pattern,$content);
        $compileDir =COMPILEPATH.DIRECTORY_SEPARATOR.$this->controller.DIRECTORY_SEPARATOR.$this->method.DIRECTORY_SEPARATOR;
    $this->compile($res,$compileDir,$tpl);
    require $this->cacheFile;//引入模板流生成的php文件

}

======================================================================

再来看下最重要的replacePattern方法,这个方法的作用就是替换模板变量{{}}为<?php ?>

然后用compile 方法读取replacePattern出来的流,写入cache对应的目录,如cache/Test/mytest/index.php

效果图

function mytest3Action()
{
    $this->assign("test","mytest3Action");
    $this->assign("myname","hello");
    $this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));
    $this->display('index');
}


function mytestAction()
{
    //echo "hello";
    //var_dump(func_get_args());
    //  $this->loadModel('http\models\Test');
    $this->assign("test","mytestAction");
    $this->assign("myname","hhh");
    $this->assign('memners',array('a'=>1,'b'=>'2','c'=>3));
    $this->display('index');
}

视图层的简单实现今天就讲到这,有兴趣的同学不妨自己试试怎么实现。

相关推荐