之前我们实现了最简单的echo命令的模版替换,就是将{{ $name }}这样一段内容替换成<?php echo $name ?>

现在我们来说下其他的命令,先来回顾下之前的定义

  • 输出变量值

{{ }} 表达式的返回值将被自动传递给 PHPhtmlentities 函数进行处理,以防止 XSS 攻击。

Hello, {{ $name }}!
  • 输出未转义的变量值
Hello, {!! $name !!}!
  • If 表达式

通过 @if@elseif@else@endif 指令可以创建 if 表达式。

@if (count($records) === 1)
I have one record!
@elseif (count($records) > 1)
I have multiple records!
@else
I don't have any records!
@endif
  • 循环
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor @foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach @while (true)
<p>I'm looping forever.</p>
@endwhile
  • 引入其他视图
@include('view.name', ['some' => 'data'])

要匹配这些定义,我们要写出相应的正则表达式,关于@开头的命令直接拿了laravel中的使用。

我们先在src下创建view文件夹,再创建Compiler类文件。

我们将compiler的方式氛围两种,一种是@开头的命令(Statements),一种是输出(Echos)。这两种的正则是不一样的。

首先定义变量compliers,定义如下:

protected $compilers = [
'Statements',
'Echos',
];

然后按着见原来Controllerrender方法的内容迁移到Complier类中,按这两种依次匹配,代码如下:

public function compile($path = null)
{
$fileContent = file_get_contents($path);
$result = '';
foreach (token_get_all($fileContent) as $token) {
if (is_array($token)) {
list($id, $content) = $token;
if ($id == T_INLINE_HTML) {
foreach ($this->compilers as $type) {
$content = $this->{"compile{$type}"}($content);
}
}
$result .= $content;
} else {
$result .= $token;
}
}
$generatedFile = '../runtime/cache/' . md5($path);
file_put_contents($generatedFile, $result);
require_once $generatedFile;
} protected function compileStatements($content)
{
return $content;
} protected function compileEchos($content)
{
return preg_replace('/{{(.*)}}/', '<?php echo $1 ?>', $content);
}

其中的Statements完全没有处理,Echos则还是跟之前一样。

先来调整下Echos中的处理,添加变量记录{{ }}{!! !!}的名称

protected $echoCompilers = [
'RawEchos',
'EscapedEchos'
];

处理的时候可以添加一下存在的判断,默认值是null,内容可以调整如下:

protected function compileEchos($content)
{
foreach ($this->echoCompilers as $type) {
$content = $this->{"compile{$type}"}($content);
}
return $content;
} protected function compileEscapedEchos($content)
{
return preg_replace('/{{(.*)}}/', '<?php echo htmlentities(isset($1) ? $1 : null) ?>', $content);
} protected function compileRawEchos($content)
{
return preg_replace('/{!!(.*)!!}/', '<?php echo isset($1) ? $1 : null ?>', $content);
}

EscapedEchosRawEchos的区别在于,第一个会做html转义。

我们再来看Statements命令的处理,其原理也一样,匹配到相应的命令,如ifforeach,调用相应的方法做替换。

代码如下:

protected function compileStatements($content)
{
return preg_replace_callback(
'/\B@(@?\w+(?:::\w+)?)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', function ($match) {
return $this->compileStatement($match);
}, $content
);
} protected function compileStatement($match)
{
if (strpos($match[1], '@') !== false) {
$match[0] = isset($match[3]) ? $match[1].$match[3] : $match[1];
} elseif (method_exists($this, $method = 'compile'.ucfirst($match[1]))) {
$match[0] = $this->$method(isset($match[3]) ? $match[3] : null);
} return isset($match[3]) ? $match[0] : $match[0].$match[2];
} protected function compileIf($expression)
{
return "<?php if{$expression}: ?>";
} protected function compileElseif($expression)
{
return "<?php elseif{$expression}: ?>";
} protected function compileElse($expression)
{
return "<?php else{$expression}: ?>";
} protected function compileEndif($expression)
{
return '<?php endif; ?>';
} protected function compileFor($expression)
{
return "<?php for{$expression}: ?>";
} protected function compileEndfor($expression)
{
return '<?php endfor; ?>';
} protected function compileForeach($expression)
{
return "<?php foreach{$expression}: ?>";
} protected function compileEndforeach($expression)
{
return '<?php endforeach; ?>';
} protected function compileWhile($expression)
{
return "<?php while{$expression}: ?>";
} protected function compileEndwhile($expression)
{
return '<?php endwhile; ?>';
} protected function compileContinue($expression)
{
return '<?php continue; ?>';
} protected function compileBreak($expression)
{
return '<?php break; ?>';
}

其中的include实现比较麻烦,就没有做,留给大家思考啦。

然后,我们再考虑一下,不可能每次都去操作文件重新生成,我应该要判断文件改变,如果没改变直接使用缓存就可以了。

调整代码如下:

public function isExpired($path)
{
$compiled = $this->getCompiledPath($path);
if (!file_exists($compiled)) {
return true;
} return filemtime($path) >= filemtime($compiled);
} protected function getCompiledPath($path)
{
return '../runtime/cache/' . md5($path);
} public function compile($file = null, $params = [])
{
$path = '../views/' . $file . '.sf';
extract($params);
if (!$this->isExpired($path)) {
$compiled = $this->getCompiledPath($path);
require_once $compiled;
return;
}
$fileContent = file_get_contents($path);
$result = '';
foreach (token_get_all($fileContent) as $token) {
if (is_array($token)) {
list($id, $content) = $token;
if ($id == T_INLINE_HTML) {
foreach ($this->compilers as $type) {
$content = $this->{"compile{$type}"}($content);
}
}
$result .= $content;
} else {
$result .= $token;
}
}
$compiled = $this->getCompiledPath($path);
file_put_contents($compiled, $result);
require_once $compiled;
}

这个系列的博客到这里就暂时告一段落了~

项目内容和博客内容也都会放到Github上,欢迎大家提建议。

code:https://github.com/CraryPrimitiveMan/simple-framework/tree/1.2

blog project:https://github.com/CraryPrimitiveMan/create-your-own-php-framework

构建自己的PHP框架--构建模版引擎(3)的更多相关文章

  1. 构建自己的PHP框架--构建模版引擎(1)

    前段时间太忙,导致好久都没有更新博客了,今天抽出点时间来写一篇. 其实这个系列的博客很久没有更新了,之前想好好规划一下,再继续写,然后就放下了,今天再捡起来继续更新. 今天我们来说一下,如何构建自己的 ...

  2. 构建自己的PHP框架--构建模版引擎(2)

    自从来到新公司就一直很忙,最近这段时间终于稍微闲了一点,赶紧接着写这个系列,感觉再不写就烂尾了. 之前我们说到,拿到{{ $name }}这样一段内容时,我们只需要将它转化成<?php echo ...

  3. 构建自己的PHP框架--构建缓存组件(1)

    作为一个框架,我们还没有相应的缓存组件,下面我们就来构建我们的缓存组件. 先来定义一下接口,在 src 文件夹下创建 cache 文件夹,在cache文件夹下创建 CacheInterface.php ...

  4. 构建自己的PHP框架--构建缓存组件(2)

    上一篇博客中使用文件实现了缓存组件,这一篇我们就使用Redis来实现一下,剩下的如何使用memcache.mysql等去实现缓存我就不一一去做了. 首先我们需要安装一下 redis 和 phpredi ...

  5. 构建自己的PHP框架(Twig模板引擎)

    完整项目地址:https://github.com/Evai/Aier Twig 模板引擎 模版引擎 twig 的模板就是普通的文本文件,也不需要特别的扩展名,.html .htm .twig 都可以 ...

  6. 基于laravel框架构建最小内容管理系统

    校园失物招领平台开发 --基于laravel框架构建最小内容管理系统 摘要 ​ 针对目前大学校园人口密度大.人群活动频繁.师生学习生活等物品容易遗失的基本现状,在分析传统失物招领过程中的工作效率低下. ...

  7. net 和Mono 构建的HTTP服务框架

    Nancy是一个基于.net 和Mono 构建的HTTP服务框架,是一个非常轻量级的web框架. 设计用于处理 DELETE, GET, HEAD, OPTIONS, POST, PUT 和 PATC ...

  8. 基于Dubbo框架构建分布式服务(一)

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  9. 使用 SailingEase WinForm 框架构建复合式应用程序(插件式应用程序)

    对于一些较小的项目,具备一定经验的开发人员应该能够设计和构建出便于进行维护和扩展的应用程序.但是,随着功能模块数量(以及开发维护这些部件的人员)的不断增加,对项目实施控制的难度开始呈指数级增长. Sa ...

随机推荐

  1. 【2016北京集训测试赛】azelso

    [吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难 ...

  2. Xampp相关命令

    http://www.upwqy.com/details/5.html 1 启动关闭 需要切换目录: # cd /opt/lampp/ 启动 XAMPP # ./lampp start 停止 XAMP ...

  3. MyBatis的三层级联和二层缓存

           我们这里说的三层级联和二级缓存其实也是MyBatis映射器的知识点,只是因为比较难理解,所以单独拿出来讲解,下面是三层级联的内容:        我们知道,在数据库中包含着一对一,一对多 ...

  4. 读 《 Web 研发模式的演变 》与《Javascript:世纪机器语言》

       读了两篇文章,内心还是很震撼的,在这之前,我学习知识都是直接找教程,翻阅资料,写几个小demo,没有去了解我所学的东西的发展历程,<Web研发模式的演变>这篇文章讲述了web的前世今 ...

  5. SQL Server将自己的查询结果作为待查询数据子列之二

    嵌套子查询是SQL语句中比较常用的一种查询方法,开发过程中遇到查询需要将自己的某列作为待查询的数据,在参考别人的SQL语句的写法终于实现了自己需要的功能. 不太和谐查询语句如下: SELECT DIS ...

  6. bzoj3142 luogu3228 HNOI2013 数列

    这题好没意思啊,怀疑拉不开区分度. 题意:求一个递增序列,每两个相邻数字之间的差值不超过m,最后一个值不能大于n. 分析:网上好多人用了差分,我没想到.然后YY了一发生成函数. 考虑构造生成函数G(x ...

  7. ELK重难点总结和整体优化配置

    本文收录在Linux运维企业架构实战系列 做了几周的测试,踩了无数的坑,总结一下,全是干货,给大家分享~ 一.elk 实用知识点总结 1.编码转换问题(主要就是中文乱码) (1)input 中的cod ...

  8. node 自动重启 nodemon

    最近在玩node,可是每次修改完js,总要重启下服务,还要杀掉端口,感觉可麻烦了,最近发现nodemon这个东西很好,直接解决了我的烦恼. npm install -g nodemon 以后直接启动n ...

  9. RTC实时时间系统学习笔记(一)---------------UART串口

    临近研三了,自己倾向于要找数字IC方面的工作,苦于教研室的项目一直都是调板子调板子调板子,真正用到FPGA的很少,,本着"工欲善其事必先利其器"的原则,在网上搜寻如何自学FPGA, ...

  10. Docker 网络管理及容器跨主机通信

    1.网络模式 docker支持四种网络模式,使用--net选项指定: host,--net=host,如果指定此模式,容器将不会获得一个独立的network namespace,而是和宿主机共用一个. ...