oxid-esales / smarty-to-twig-converter
将 smarty 模板引擎转换为 twig 的脚本
Requires
- php: ^7.1
- ext-dom: *
- doctrine/dbal: ^2.9
- phpunit/dbunit: 3.*
- phpunit/phpunit: ^6
- sebastian/diff: 2.*
- symfony/console: ~2.1
- symfony/filesystem: ~2.1
- symfony/finder: ~2.1
Requires (Dev)
- ext-pdo: *
- oxid-esales/coding-standards: ^v3.0.5
This package is auto-updated.
Last update: 2024-09-19 22:37:50 UTC
README
位于 GitHub 的转换工具可以将现有的 Smarty 模板文件转换为 Twig 语法。此工具除了标准 Smarty 语法外,还调整以处理定制的 OXID 修改和扩展。
安装
克隆仓库
git clone https://github.com/OXID-eSales/smarty-to-twig-converter.git
安装依赖项
cd smarty-to-twig-converter
composer install
使用
convert 命令尝试在一个给定的文件、目录或数据库上修复尽可能多的编码标准问题。
路径和扩展名参数
转换器可以与文件和目录一起工作
php toTwig convert --path=/path/to/dir
php toTwig convert --path=/path/to/file
默认情况下,将创建扩展名为 .html.twig
的文件。要指定不同的扩展名,请使用 --ext
参数
php toTwig convert --path=/path/to/dir --ext=.js.twig
数据库和数据库列参数
它还可以与数据库一起工作
php toTwig convert --database="mysql://user:password@localhost/db"
--database
参数获取 数据库 doctrine-like URL。默认情况下,转换器将以下表列转换为
- oxactions.OXLONGDESC
- oxactions.OXLONGDESC_1
- oxactions.OXLONGDESC_2
- oxactions.OXLONGDESC_3
- oxcontents.OXCONTENT
- oxcontents.OXCONTENT_1
- oxcontents.OXCONTENT_2
- oxcontents.OXCONTENT_3
- oxartextends.OXLONGDESC
- oxartextends.OXLONGDESC_1
- oxartextends.OXLONGDESC_2
- oxartextends.OXLONGDESC_3
- oxcategories.OXLONGDESC
- oxcategories.OXLONGDESC
- oxcategories.OXLONGDESC_2
- oxcategories.OXLONGDESC_3
使用 --database-columns
选项可以让你选择要转换的表列(表列名必须以 table_a.column_b 格式指定,并用逗号分隔)
php toTwig convert --database="..." --database-columns=oxactions.OXLONGDESC,oxcontents.OXCONTENT
您还可以通过 -table_a.column_b 使用黑名单排除不需要的表列
php toTwig convert --database="..." --database-columns=-oxactions.OXLONGDESC_1,-oxcontents.OXCONTENT_1
转换器参数
--converters
选项允许您选择要应用的确切转换器(转换器名称必须用逗号分隔)
php toTwig convert --path=/path/to/dir --ext=.html.twig --converters=for,if,misc
如果您觉得这样做更方便,还可以使用 -name 黑名单排除不需要的转换器
php toTwig convert --path=/path/to/dir --ext=.html.twig --converters=-for,-if
dry-run、verbose 和 diff 参数
--dry-run
、--verbose
和 --diff
的组合将显示提议的更改摘要,但不会修改您的文件。
默认情况下,所有转换器都会应用。
--dry-run
选项显示需要修复的文件,但实际上不会修改它们
php toTwig convert --path=/path/to/code --ext=.html.twig --dry-run
config-path 参数
除了构建长的命令行之外,还可以注入 PHP 配置代码。主目录中包含两个示例文件:config_file.php
和 config_database.php
。要包含配置文件,请使用 --config-path 参数
php toTwig convert --config-path=config_file.php
配置脚本应返回 toTwig\Config\ConfigInterface
实例。可以使用 toTwig\Config\Config::create()
静态方法创建。
已知问题
-
在Twig中默认所有变量都会被转义。一些变量应该使用
|raw
过滤器进行过滤以避免这种情况。这意味着所有模板、HTML代码和包含不安全字符(如< > $ &
)的字符串都应该在输出之前使用|raw
进行过滤。您可以使用网页浏览器的检查工具来检查是否所有必要的变量都被转义。如果您想输出包含模板的变量,而不是使用raw
过滤器,可以使用template_from_string
函数。更多相关信息请参阅文档。Smarty
[{$product->oxarticles__oxtitle->value}]
转换后的Twig
{{ product.oxarticles__oxtitle.value }}
修复后的Twig
{{ product.oxarticles__oxtitle.value|raw }}
-
变量作用域。在Twig中,模板中声明的变量作用域由块(如
{% block %}
、{% for %}
等)限制。如果变量在块之外使用,则应在块之外声明。Smarty
[{foreach $myColors as $color}] <li>[{$color}]</li> [{/foreach}] [{$color}]
转换后的Twig
{% for color in myColors %} <li>{{ color }}</li> {% endfor %} {{ color }}
修复后的Twig
{% for color in myColors %} <li>{{ color }}</li> {% endfor %} {{ myColors|last }}
-
重新声明块在Twig中是不允许的。您必须在每个模板中为每个块使用唯一的名称。
Smarty
[{block name="foo"}] ... [{/block}] [{block name="foo"}] ... [{/block}]
转换后的Twig
{% block foo %} ... {% endblock %} {% block foo %} ... {% endblock %}
修复后的Twig
{% block foo_A %} ... {% endblock %} {% block foo_B %} ... {% endblock %}
-
访问数组项
$myArray.$itemIndex
应手动转换为myArray[itemIndex]
Smarty
[{$myArray.$itemIndex}]
转换后的Twig
{{ myArray.$itemIndex }}
修复后的Twig
{{ myArray[itemIndex] }}
-
在模板中使用正则表达式字符串——在如此复杂的案例中,工具可能会中断或工作不正确,因此手动复制粘贴正则表达式更安全。
Smarty
[{birthDate|regex_replace:"/^([0-9]{4})[-]/":""|regex_replace:"/[-]([0-9]{1,2})$/":""}]
转换后的Twig
{{ birthDate|regex_replace("/^([0-9]{4)})[-]/":""|regex_replace("/[-]([0-9]{1,) 2})$/":"" }}
修复后的Twig
{{ birthDate|regex_replace("/^([0-9]{4})[-]/","")|regex_replace("/[-]([0-9]{1,2})$/","") }}
-
[{section}]
-loop
是触发不同行为的数组或整数。工具无法检测变量类型,因此您需要检查每个loop
中使用的内容。Smarty
[{section name="month" start=1 loop=13}] [{$smarty.section.month.index}] [{/section}] [{section name=customer loop=$custid}] id: [{$custid[customer]}]<br /> [{/section}]
转换后的Twig
{% for month in 1..13 %} {{ loop.index0 }} {% endfor %} {% for customer in 0..$custid %} id: {{ custid[customer] }}<br /> {% endfor %}
修复后的Twig
{% for month in 1..12 %} {{ loop.index0 }} {% endfor %} {% for customer in custid %} id: {{ customer }}<br /> {% endfor %}
-
字符串连接——工具在处理字符串的开启和关闭时存在问题。在字符串中使用Smarty变量可能会导致转换器失败。Twig不支持这种类型的连接。您应该在拼接变量的地方检查并使用Twig的
~
而不是字符串中的变量。在转换后的模板中,您应该寻找类似$var_name
的模式。Smarty
[{assign var="sUrl" value="http://www.example.com?aid=`$sAccountId`&wid=`$sWidgetId`&csize=20&start=0"}] [{assign var="divId" value=oxStateDiv_$stateSelectName}]
转换后的Twig
{% set sUrl = "http://www.example.com?aid=`$sAccountId`&wid=`$sWidgetId`&csize=20&start=0" %} {% set divId = oxStateDiv_$stateSelectName %}
修复后的Twig
{% set sUrl = "http://www.example.com?aid=" ~ sAccountId ~ "&wid=" ~ sWidgetId ~ "&csize=20&start=0" %} {% set divId = "oxStateDiv_" ~ stateSelectName %}
-
$
符号并不总是从变量中移除。有时当表达式过于复杂时,转换器将不会从变量名中移除$
符号。转换后,您应该检查模板中的$
符号。Smarty
[{$oViewConf->getImageUrl($sEmailLogo, false)}]
转换后的Twig
{{ oViewConf.getImageUrl($sEmailLogo, false) }}
修复后的Twig
{{ oViewConf.getImageUrl(sEmailLogo, false) }}
-
Twig提供了轻松访问循环的第一个元素的途径。您可以使用
loop.index0
或当前的迭代loop.index
来代替变量的索引元素。转换器不处理如$smarty.section.arg
之类的结构。更多相关信息请参阅Twig 'for' 文档。Smarty
[{if $review->getRating() >= $smarty.section.starRatings.iteration}]
转换后的Twig
{% if review.getRating() >= smarty.section.starRatings.iteration %}
修复后的Twig
{% if review.getRating() >= loop.index %}
-
在某些地方,访问全局变量的方式需要调整。在转换后的代码中寻找单词
smarty
并将其替换为twig
。Smarty
[{$smarty.capture.loginErrors}]
转换后的Twig
{{ smarty.capture.loginErrors }}
修复后的Twig
{{ twig.capture.loginErrors }}
-
在Smarty和Twig中,属性访问方式不同,有时需要手动修复。如果没有定义魔术isset,则必须显式调用魔术getter。此外,如果您想访问类属性而不调用getter,则必须使用类似数组的语法。
Smarty
[{foreach from=$cattree->aList item=pcat}] [{pcat.val}]
转换后的Twig
{% for pcat in cattree.aList %} {{ pcat.val }}
修复后的Twig
{% for pcat in cattree.__get('aList') %} {{ pcat['val'] }}
-
转换器并不总是转换逻辑运算符如
||
和&&
,如果它们没有用空格分隔。需要手动将||
改为or
,将&&
改为and
。Smarty
[{if $product->isNotBuyable()||($aVariantSelections&&$aVariantSelections.selections)||$product->hasMdVariants()}]
转换后的Twig
{% if product.isNotBuyable()||(aVariantSelections&&$aVariantSelections.selections) or product.hasMdVariants() %}
修复后的Twig
{% if product.isNotBuyable() or (aVariantSelections and aVariantSelections.selections) or product.hasMdVariants() %}
-
如果您从模板中访问请求变量,请考虑重构任何进行此操作的模板。如果不可能,请使用与Twig引擎一起提供的函数
get_global_cookie
或get_global_get
。如果您需要访问其他请求变量,您将需要自行扩展这些函数之一。Smarty
[{if $smarty.get.plain == '1'}] popup[{/if}]
转换后的Twig
{% if smarty.get.plain == '1' %} popup{% endif %}
修复后的Twig
{% if get_global_get('plain') == '1' %} popup{% endif %}
-
您可能需要手动检查模板文件中的逻辑。某些地方将需要使用
is same as
比较,它使用PHP的===
而不是==
。这可能是在检查变量是否设置、是否包含信息、是否为0
或是否为null
时必要的。检查不存在的(null)属性存在问题。例如,我们想检查不存在属性oxarticles__oxunitname
的值。Twig使用isset
来检查此属性是否存在,如果不存在,则Twig会假设属性名是函数名,并尝试调用它。Smarty
[{if $_sSelectionHashCollection}] [{assign var="_sSelectionHashCollection" value=$_sSelectionHashCollection|cat:","}] [{/if}]
转换后的Twig
{% if _sSelectionHashCollection %} {% set _sSelectionHashCollection = _sSelectionHashCollection|cat(",") %} {% endif %}
修复后的Twig
{% if _sSelectionHashCollection is not same as("") %} {% set _sSelectionHashCollection = _sSelectionHashCollection|cat(",") %} {% endif %}
转换的插件和语法片段
以下是插件和语法片段的列表,以及基本示例如何进行转换。请注意,这些示例仅用于展示转换方式,并不涵盖所有可能的情况,如附加参数、块嵌套、重复调用(如计数器和循环函数)等。
核心Smarty
assign > set
转换器名称:assign
Smarty
[{assign var="name" value="Bob"}]
Twig
{% set name = "Bob" %}
block > block
转换器名称:block
Smarty
[{block name="title"}]默认标题[{/block}]
Twig
{% block title %}默认标题{% endblock %}
capture > set
转换器名称:CaptureConverter
Smarty
[{capture name="foo" append="var"}] bar [{/capture}]
Twig
{% set foo %}{{ var }} bar {% endset %}
注释
转换器名称:comment
Smarty
[{* foo *}]
Twig
{# foo #}
counter > set
转换器名称:counter
Smarty
[{counter}]
Twig
{% set defaultCounter = ( defaultCounter|default(0) ) + 1 %}
cycle > smarty_cycle
转换器名称:cycle
Smarty
[{cycle values="val1,val2,val3"}]
Twig
{{ smarty_cycle(["val1", "val2", "val3"]) }}
foreach > for
转换器名称:for
Smarty
[{foreach $myColors as $color}]foo[{/foreach}]
Twig
{% for color in myColors %}foo{% endfor %}
if > if
转换器名称:if
Smarty
[{if !$foo or $foo->bar or $foo|bar:foo["hello"]}]foo[{/if}]
Twig
{% if not foo or foo.bar or foo|bar(foo["hello"]) %}foo{% endif %}
include > include
转换器名称:include
Smarty
[{include file='page_header.tpl'}]
Twig
{% include 'page_header.tpl' %}
insert > include
转换器名称:insert
Smarty
[{insert name="oxid_tracker" title="PRODUCT_DETAILS"|oxmultilangassign product=$oDetailsProduct cpath=$oView->getCatTreePath()}]
Twig
{% include "oxid_tracker" with {title: "PRODUCT_DETAILS"|oxmultilangassign, product: oDetailsProduct, cpath: oView.getCatTreePath()} %}
mailto > mailto
转换器名称:mailto
Smarty
[{mailto address='me@example.com'}]
Twig
{{ mailto('me@example.com') }}
math > core Twig math syntax
转换器名称:math
Smarty
[{math equation="x + y" x=1 y=2}]
Twig
{{ 1 + 2 }}
变量转换
转换器名称:variable
其他
转换器名称:misc
OXID自定义扩展
oxcontent > include_content
转换器名称:oxcontent
Smarty
[{oxcontent ident='oxregisteremail'}]
Twig
{% include_content 'oxregisteremail' %}
oxeval > include(template_from_string())
转换器名称:OxevalConverter
Smarty
[{oxeval var=$variable}]
Twig
{{ include(template_from_string(variable)) }}
oxgetseourl > seo_url
转换器名称:oxgetseourl
Smarty
[{oxgetseourl ident=$oViewConf->getSelfLink()|cat:"cl=basket"}]
Twig
{{ seo_url({ ident: oViewConf.getSelfLink()|cat("cl=basket") }) }}
oxhasrights > hasrights
转换器名称:oxhasrights
Smarty
[{oxhasrights object=$edit readonly=$readonly}]foo[{/oxhasrights}]
Twig
{% hasrights { "object": "edit", "readonly": "readonly", } %}foo{% endhasrights %}
oxid_include_dynamic > include_dynamic
转换器名称:oxid_include_dynamic
Smarty
[{oxid_include_dynamic file="form/formparams.tpl"}]
Twig
{% include_dynamic "form/formparams.tpl" %}
oxid_include_widget > include_widget
转换器名称:oxid_include_widget
Smarty
[[oxid_include_widget cl="oxwCategoryTree" cnid=$oView->getCategoryId() deepLevel=0 noscript=1 nocookie=1]]
Twig
{{ include_widget({ cl: "oxwCategoryTree", cnid: oView.getCategoryId(), deepLevel: 0, noscript: 1, nocookie: 1 }) }}
oxifcontent => ifcontent
转换器名称: oxifcontent
Smarty
[[oxifcontent ident="TOBASKET" object="aObject"]]foo[[/oxifcontent]]
Twig
{% ifcontent ident "TOBASKET" set aObject %}foo{% endifcontent %}
oxinputhelp => include "inputhelp.tpl"
转换器名称: oxinputhelp
Smarty
[[oxinputhelp ident="foo"]]
Twig
{% include "inputhelp.tpl" with {'sHelpId': getSHelpId(foo), 'sHelpText': getSHelpText(foo)} %}
oxmailto => oxmailto
转换器名称: oxmailto
Smarty
[[oxmailto address='me@example.com']]
Twig
{{ mailto('me@example.com') }}
oxmultilang => translate
转换器名称: oxmultilang
Smarty
[[oxmultilang ident="ERROR_404"]]
Twig
{{ translate({ ident: "ERROR_404" }) }}
oxprice => format_price
转换器名称: oxprice
Smarty
[[oxprice price=$basketitem->getUnitPrice() currency=$currency]]
Twig
{{ format_price(basketitem.getUnitPrice(), { currency: currency }) }}
oxscript => script
转换器名称: oxscript
Smarty
[[oxscript include="js/pages/details.min.js" priority=10]]
Twig
{{ script({ include: "js/pages/details.min.js", priority: 10, dynamic: __oxid_include_dynamic }) }}
oxstyle => style
转换器名称: oxstyle
Smarty
[[oxstyle include="css/libs/chosen/chosen.min.css"]]
Twig
{{ style({ include: "css/libs/chosen/chosen.min.css" }) }}
section => for
转换器名称: section
Smarty
[[section name=picRow start=1 loop=10]]foo[[/section]]
Twig
{% for picRow in 1..10 %}foo{% endfor %}
过滤器
运行数据库转换 PHPUnit 测试
CI 注意:要运行数据库转换 PHPUnit 测试,需要 sqlite。您可以通过运行以下命令来安装它
$ sudo apt-get install sqlite3
$ sudo apt-get install php7.2-sqlite
错误和问题
如果您遇到任何错误或问题,请在 https://bugs.oxid-esales.com 的 OXID eShop(所有版本) 部分 Twig 引擎 类别下报告