|
Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon. |
|
View previous topic :: View next topic |
Author |
Message |
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Thu Oct 06, 2011 12:50 pm Post subject: Better control of outputting of newlines / line breaks |
|
|
There has been some recurring discussion on how to handle the extra newlines that are introduced by function blocks (for example here: http://groups.google.com/group/smarty-developers/browse_thread/thread/d1b244df61e407b7) In that thread, it is said "This discussion comes up every few years, and we always circle around to the same conclusion. " but IMNSHO that is simply because not enough options are considered
I'm porting an application from Python to PHP. The Python version used the Jinja2 template engine. It uses a construct that IMO would be perfect for Smarty to adopt.
To have something concrete to illustrate, consider the following:
Code: |
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS
{if isset($preprocessor_flags.dll)};{$preprocessor_flags.dll|implode:';'}{/if}
{if isset($preprocessor_flags.all)};{$preprocessor_flags.all|implode:';'}{/if}
"
|
Space at start of first line is important. This is a snippet from an XML file I generate. When the .dll and .all array of $preprocessor_flags are empty, I want the output to be
Code: |
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
|
If one has contents, I want the output to be (e.g)
Code: |
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;SOME_FLAG;ANOTHER"
|
At the moment (using Smarty 3.1.2, latest release), with the given code, this won't work - there will be extra newlines, causing the final quote to be on its own line, and if the arrays have content them to be on a different line too. The only way not to get that, is to put it all on one big line, which makes it very hard to read.
The way Jinja solved this is by allowing a modifier on the tag opener, that removed a preceding or following newline. For example:
Code: |
<an_element attr_1="
{-if isset($somevar)-}hello{/if-}
">
|
would generate, if $somevar is not set
Code: |
<an_element attr_1="">
|
and if it is set
Code: |
<an_element attr_1="hello">
|
The minus sign at the begin or end of a resp. open- or close tag makes the engine remove the newline immediately before resp after it.
This is very useful when generating things that are whitespace sensitive or need to be human-readable, which I find myself doing a lot (I'm not in web dev anymore and now use Smarty mostly to generate source code). Additionally, it could be extended to also eat whitespace up until the first preceding newline. That way, you could write
Code: |
<an_element attr_1="
{-if isset($somevar)-}hello{/if-}
">
|
and it would still generate the same output. This is useful often to keep your smarty code indentation consistent, otherwise you have to left-align all smarty code, making it hard to read. Maybe this whitespace-eating could be optional by using a different modifier - not {-if} but {--if} or whatever.
Thanks for reading this far |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Thu Oct 06, 2011 1:30 pm Post subject: |
|
|
Code: | {strip}
so much space as you
want
on as
{if true} many {/if} lines
{if true}
as you
{/if}
like
{strip} |
would produce Code: | so much space as youwanton as many linesas youlike |
(yeah, newlines are not replaced by a space…)
you can easily roll your own plugin to remove linebreaks, duplicate spaces, whatever: Code: | $smarty->registerPlugin('block', 'stripws' 'smarty_block_stripws');
function smarty_block_stripws($params, $content, $template, &$repeat) {
return preg_replace( '#\s+#', ' ', $content);
} |
_________________ Twitter |
|
Back to top |
|
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Tue Mar 06, 2012 10:51 am Post subject: |
|
|
I have run into this issue a few times more since I posted my original question, but the situation I have now is egregious enough to compel me to post about this again. I may just be missing something very obvious, but in my current understanding of things, white space control remains a weak point in Smarty.
I have the following, which is a snippet from a template that generates member variables of a C++ class declaration:
Code: |
{foreach $element->children as $c}
{if $c->max_count == 1}
{$c->getClassName()} {$c->getMemberVariableName()};
{else}
{$c->getClassName()} {$c->getMemberVariableName()};
std::vector<{$c->getClassName()}> {$c->getMemberVariableName()}s;
{/if}
{/foreach}
|
This will output spurious white space, namely the spaces in front of the {foreach} and {if} blocks. Using {strip}, it seems I would have to fix it thusly:
Code: |
{strip}
{foreach $element->children as $c}
{if $c->max_count == 1}
{/strip}
{$c->getClassName()} {$c->getMemberVariableName()};
{strip}
{else}
{/strip}
{$c->getClassName()} {$c->getMemberVariableName()};
std::vector<{$c->getClassName()}> {$c->getMemberVariableName()}s;
{strip}
{/if}
{/foreach}
{/strip}
|
Causing the template to become unwieldy and unreadable, especially since I'm basically writing C++ code in the template and all the {strip} tags add a whole new dimension of reading obstacles.
The obvious proper solution is again to add a way to ignore white space before and/or after Smarty constructs, because that's the fundamental problem here: to keep the template readable I add spacing to indent Smarty constructs, but that spacing is not part of the template output itself.
For generating HTML, there are easy solutions to simply strip ALL white space, since it doesn't matter for browser rendering engines anyway. In other use cases, a more fine grained control of generated white space is required.
I hope this example illustrates the shortcomings of {trim} or a trim_whitespace output filter. Of course if there is something I'm not understanding about Smarty that provides a way to solve this problem, I'd be happy to hear. Thanks. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Tue Mar 06, 2012 11:15 am Post subject: |
|
|
How about using a pre-filter to remove the indentation?
Code: | function trimLines($string, $template) {
return preg_replace('#^\s*(\{/?(foreach|foreachelse|if|else|elseif))#m', "\\1", $string);
}
$smarty->registerFilter('pre', 'trimLines'); |
above filter would turn Code: | {foreach $element->children as $c}
{if $c->max_count == 1}
{$c->getClassName()} {$c->getMemberVariableName()};
{else}
{$c->getClassName()} {$c->getMemberVariableName()};
std::vector<{$c->getClassName()}> {$c->getMemberVariableName()}s;
{/if}
{/foreach} | into Code: | {foreach $element->children as $c}
{if $c->max_count == 1}
{$c->getClassName()} {$c->getMemberVariableName()};
{else}
{$c->getClassName()} {$c->getMemberVariableName()};
std::vector<{$c->getClassName()}> {$c->getMemberVariableName()}s;
{/if}
{/foreach} |
_________________ Twitter |
|
Back to top |
|
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Wed Mar 07, 2012 9:24 am Post subject: |
|
|
Thanks for taking the time to reflect about my issue.
Your suggestion works (after some modifications) and makes my template look a lot tidier already, so thanks for that too. The multiline regex strips empty lines before Smarty constructs too, so I split the template content into lines and then apply the regex on each line separately. While it feels a bit brittle, on my current test data set it seems to work, and if I discover corner cases it's not that big of a deal to modify the regex to cope.
This said, from a library design point of view, I still feel like this is a workaround for something that is a deeper issue, an issue I hope will be considered in a next round of updating. If I run into more situations where I struggle with white space that can't be elegantly solved with {strip} or the prefilter, I will post in this thread to keep a complete record of use cases where a more fine-grained build-it control of white space in Smarty could be of use. |
|
Back to top |
|
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Wed Mar 07, 2012 9:28 am Post subject: |
|
|
To follow up further, looking over my comments on this forum I see that I posted about another use case back in 2010: http://www.smarty.net/forums/viewtopic.php?p=68606&highlight=#68606 .
This is disguised as an issue with 'indent', but it boils down to the same issue. In this case, the prefilter approach wouldn't work, and the block in question would have to be surrounded by {strip} tags (I think - or would the {strip} remove the spaces put in by {indent}? I'm not sure, I'd have to test).
Either way, in that case too, having a built-in way to remove the leading space of the text tag would be the fix to the root cause. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Wed Mar 07, 2012 9:44 am Post subject: |
|
|
There is no generic solution to the "whitespace problem". Consider the following snippen: Code: | <pre>
{foreach $foo as $bar}{$bar}, {/foreach}
</pre> |
You'd like the spaces before {foreach} removed, while I'd want them preserved. How do you think this could be handled? _________________ Twitter |
|
Back to top |
|
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Wed Mar 07, 2012 10:14 am Post subject: |
|
|
In the way other template engines (such as Jinja for Python) do, by having a modifier in the tag format that would indicate whether to remove leading white space or not. My first post in this thread contains an example, basically it could be something like
Code: |
{foreach $foo as $bar}{$bar}, {/foreach}
|
where no stripping is done (the default), and
Code: |
{-foreach $foo as $bar}{$bar}, {/foreach}
|
(notice the minus sign on the opening brace of the foreach) where it is stripped. The '-' basically means 'strip all whitespace before this'. One could debate if it should only work on whitespace at the beginning of a line, or if
Code: |
{-foreach $foo as $bar}{$bar}, {-/foreach}
|
should remove the white space after the commas. Also for consistency, having {-foreach-} strip trailing white space would be the logical thing to do, although I'm not sure of in which cases that would be useful.
Either way, my point is that other template engines have features to address this, and I won't claim to know everything about every template engine, so there may be other ways or syntaxes to do this. I'm also not claiming that Smarty should copy every feature from every other template engine. It's just that I run into this quite regularly (as evidenced by my posts spanning at least 3 years), and I imagine that if a casual Smarty user like myself runs into it, there must be others, too. Or maybe not and I'm just an outlier, I don't have data. Still with my posts here I'm just trying to illustrate the issue, and hopefully I can make a solid point about it, and maybe contribute to a discussion on other use cases where an addition like the proposed one would cause problems; since I won't claim that I have thought through every possible angle of adding new syntax. I just hope that my examples can convince you/the smarty devs/people in general that this would be worthwhile issue to address, or alternatively, that I can be convinced that other solutions are superior to introducing new syntax. As you can tell from my rambling, that last thing hasn't happened yet [/quote] |
|
Back to top |
|
roel_v Smarty Rookie
Joined: 17 Nov 2010 Posts: 11
|
Posted: Wed Mar 07, 2012 10:17 am Post subject: |
|
|
For reference, here is the Jinja documentation on the white space stripping syntax: http://jinja.pocoo.org/docs/templates/#whitespace-control
It also has something called 'line statements' (http://jinja.pocoo.org/docs/templates/#line-statements), which solves the same problem in a different way, but I'm no fan of that syntax. |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
Posted: Wed Mar 07, 2012 11:18 am Post subject: |
|
|
Looks like it might be handled with a prefilter as well. So (for now) you could implement that "whitespace control" plugin yourself. I'm not sure if this can be done directly in the compiler without some serious changes... so a regex-based prefilter seems like the thing to do.
put this in …/plugins/prefilter.whitespace_control.php: Code: | <?php
function smarty_prefilter_whitespace_control($string, Smarty_Internal_Template $template) {
$ldelim = $template->smarty->left_delimiter;
$rdelim = $template->smarty->right_delimiter;
$ldelim = '{';
$rdelim = '}';
$_ldelim = preg_quote($ldelim);
$_rdelim = preg_quote($rdelim);
// strip whitespace to previous non-space (including line breaks)
$string = preg_replace('#\s*'. $_ldelim .'--#', $ldelim, $string);
// strip whitespace to next non-space (including line breaks)
$string = preg_replace('#--'. $_rdelim .'\s*#', $rdelim, $string);
// strip whitespace to previous non-space (excluding line breaks)
$string = preg_replace('#[^\S\r\n]*'. $_ldelim .'-#', $ldelim, $string);
// strip whitespace to next non-space (excluding line breaks)
$string = preg_replace('#-'. $_rdelim .'[^\S\r\n]*#', $rdelim, $string);
return $string;
} |
and make your smarty autoload it: Code: | $smarty->autoload_filters['pre'] => array('whitespace_control'); |
now you can pre/append - to any tag (variable-output as well) to remove the whitespace to the next non-whitespace character or line-break. pre/append -- to also remove line-breaks.
about what you had in mind? _________________ Twitter |
|
Back to top |
|
rodneyrehm Administrator
Joined: 30 Mar 2007 Posts: 674 Location: Germany, border to Switzerland
|
|
Back to top |
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|
|