zeptech / code-templates
PHP 代码生成支持
Requires
- php: >=5.3.0
Requires (Dev)
- mockery/mockery: dev-master
This package is not auto-updated.
Last update: 2024-09-14 14:36:05 UTC
README
PHP 代码模板(pct)是一个 PHP 库,它可以解析并执行 PHP 类模板的值替换。
创建模板
定义一个模板涉及到创建一个文件,该文件封装了一个常见的结构,并用标记表示应该替换值的位置,以便创建一个可以执行(在代码的情况下)或提供有用内容的具体文件。PCT 支持简单的标签替换以及条件和重复部分。
替换标签
为了指定模板中要替换值的点,添加一个具有以下语法的标签
SUBSTITUTION_TAG <- '/*#' SUBSTITUTION_EXPRESSION '#*/'
SUBSTITUTION_EXPRESSION <- (FILTER_EXPRESSION ('-' FILTER_EXPRESSION)* ':')?VARIABLE_NAME
FILTER_EXPRESSION <- [a-zA-Z]+ ( '(' FILTER_PARAMETER (',' FILTER_PARAMETER)* ')' )?
FILTER_PARAMETER <- .+
VARIABLE_NAME <- [a-zA-Z]+ ('[' VARIABLE_INDEX ']')*
VARIABLE_INDEX <- [a-zA-Z]+
注意,元素之间包含的空白仅用于清晰,不应包含在编写标签时。唯一例外的是,可以在 /*#
开放和 #*/
关闭之间出现任意数量的空白。
示例
/*# var #*/
-- 输出替换值var
/*# php:var #*/
-- 使用 php 过滤器输出替换值var
/*# join(,):var #*/
-- 使用逗号作为粘合剂使用 join 过滤器输出替换值var
/*# each(php)-join(,):var #*/
-- 将var
的每个值通过 php 过滤器传递,然后使用逗号连接结果值/*# var[idx] #*/
-- 输出数组值var
的idx
值/*# var[idx][subidx] #*/
-- 也支持嵌套索引
过滤器
有一些预定义的过滤器用于输出替换值。
- each(filter) 将过滤器应用于数组替换值的每个值。
- json 将替换值编码为 JSON 格式输出。
- join(glue) 使用
glue
连接数组的所有值,并输出结果。 - php 使用 var_export 输出替换值。
- xml 使用编码的 XML 实体输出替换值。注意,这不会将数组或对象序列化为 XML。
可以使用 '-' 字符连接多个过滤器表达式来堆叠过滤器。以这种方式定义的过滤器将从左到右评估,每个过滤器接收前一个过滤器的结果作为输入。
条件部分
模板支持条件部分。只有在替换值的集合与条件表达式匹配时,这些部分才会输出。条件部分有以下的语法
IF_EXPRESSION <- '#{ if' CONDITIONAL '\n' BLOCK
(('#{' / '#}{') ' elseif' CONDITIONAL '\n' BLOCK)*
(('#{' / '#}{') ' else' '\n' BLOCK)?
'#}'
CONDITIONAL <- COMPARISON (('and' / 'or') COMPARISON)*
COMPARISON <- OPERAND (UNARY_OPERATOR / BINARY_OPERATOR OPERAND)?
OPERAND <- VARIABLE_NAME / NUMBER / STRING
VARIABLE_NAME <- [a-zA-Z]+ ('[' VARIABLE_INDEX ']')*
VARIABLE_INDEX <- [a-zA-Z]+
NUMBER <- Any numeric string*
STRING <- ('\'' / '"') .* ('\'' / '"')
BINARY_OPERATOR <- '=' / '!=' / '<' / '>' / '<=' / '>=' /
UNARY_OPERATOR <- 'ISSET' / 'ISNOTSET'
BLOCK <- PHP code with addition template syntax
当比较定义为没有操作符的单一 OPERAND
时,解析的操作数的布尔值将用于解决 IF_EXPRESSION
。
由 is_numeric 定义。
示例
#{ if var[type] = 'list'
// Handle a list
#}{ elseif var[type] = 'map'
// Handle a map
#}{ else
// Handle non-collection type
#}
switch 块
除了 if 风格的条件部分外,还可以使用 switch 部分为相同的替换变量的不同值替换不同的内容。
示例
#{ switch var
#| case 0
// Handle case when var = 0
#| case > 0
// Handle case when var > 0
#| case < 0
// Handle case when var < 0
#}
if 块的示例可以重写为
#{ switch var[type]
#| case 'list'
// Handle a list
#| case 'map'
// Handle a map
#| default
/ Handle non-collection type
#}
switch 情况不会 穿透。
重复部分
可以按照以下方式指定重复部分
#{ each <var> as <name> [<status>]
<section>
#}
<var>
必须引用一个数组替换值。在重复部分中,可以使用具有名称 <name>
的替换值来使用数组替换值的当前值。
如果为 <status>
变量提供了名称,则它将被填充为一个包含以下信息的数组
- index:当前迭代的索引。
- first:迭代是否在第一个元素上。
- last:迭代是否在最后一个元素上。
- has_next:迭代是否在当前元素之后还有其他元素。
示例
此示例是用于在 Clarinet ORM 项目 中为模型类创建持久化对象的模板的一部分。完整的模板可以在 https://github.com/pgraham/Clarinet/blob/master/src/persister/persister.tmlp.php 找到。
class /*# actor #*/ { // ... public function create(\/*# class #*/ $model) { if (!$this->validator->validate($model, $e)) { throw $e; } if ($model->get/*# id_property */() !== null) { return $model->get/*# id_property */(); } #{ if beforeCreate $model->beforeCreate(); #} try { $startTransaction = $this->_pdo->beginTransaction(); $model->set/*# id_property */(self::CREATE_MARKER); $params = Array(); #{ each properties as prop #{ if prop[type] = boolean $params['/*# prop[col] #*/'] = $model->get/*# prop[name] #*/() ? 1 : 0; #{ else $params['/*# prop[col] #*/'] = $model->get/*# prop[name] #*/(); #} #} #{ each relationships as rel #{ if rel[type] = many-to-one // Populate /*# rel[rhs] #*/ parameter -------------------------------- // ... // ------------------------------------------------------------------- #} #} $this->_create->execute($params); $id = $this->transformer->idFromDb($this->_pdo->lastInsertId()); $model->set/*# id_property */($id); $this->_cache[$id] = $model; #{ each collections as col $this->insertCollection_/*# col[property] #*/( $id, $model->get/*# col[property] #*/() ); #} #{ each relationships as rel $related = $model->get/*# rel[lhsProperty] #*/(); #{ if rel[type] = many-to-many // ... #{ elseif rel[type] = one-to-many // ... #} #} if ($startTransaction) { $this->_pdo->commit(); } #{ if onCreate $model->onCreate(); #} return $id; } catch (PDOException $e) { $this->_pdo->rollback(); $model->set/*# id_property */(null); $e = new PdoExceptionWrapper($e, '/*# class #*/'); $e->setSql($sql, $params); throw $e; } } // ... }
值替换
值替换,在此之后称为模板解析(或更简单地说,解析)是指用值替换所有模板标记的过程,以便代码模板成为实际有用的代码片段。
可以使用 zpt\pct\TemplateResolver
实例解析模板,并调用其 resolve($templatePath, $outputpath, $values);
方法。