twigphp / Twig

Twig, the flexible, fast, and secure template language for PHP
https://twig.symfony.com/
BSD 3-Clause "New" or "Revised" License
8.17k stars 1.25k forks source link

hash literals (feature request) #62

Closed literal closed 13 years ago

literal commented 14 years ago

Twig's datatype support looks strangely incomplete to me.

So far Twig supports literals for null, booleans, numbers, strings and (scalar/true) arrays.

While it is possible to access hashes (or associative arrays if you want to stick to the ugly PHP type system and terminology) as foo.bar or foo[bar] or through a for key, value in foo loop, there is no way to create them.

Now, why would one want hash literals? Mere symmetry (if you can use, you should also be able to create it) might not be strong enough a motive.

The real use in my view are hash arguments for calling filters, macros or methods of objects in the template context.

Look at this example from Twig documentation:

{% macro input(name, value, type, size) %}
    <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}

What if you also wanted to also allow for id, class, style, onmouseover, onmouseout etc.? The parameter signature of the macro would become excessive and you might end up with macro calls like this:

{{ form.input('foo', none, none, none, none, none, 'color: red;') }}

...and the input tag generator is only a very simplistic example. Think of macros/filters/methods for more complex HTML constructs.

The above example would be so much easier if one could do it like this:

{{ form.input({'name': 'foo', 'style': 'color: red;'}) }}

Or think of another common use case, a macro/filter to dynamically create a query string for a link URL. If hash literals existed, it could be as easy as this:

{% macro querystring(params) %}{#
    #}{% for name, value in params %}{#
        #}{{ (loop.first ? '' : '&') ~ name|urlencode ~ '=' ~ value|urlencode }}{#
    #}{% endfor %}{#
#}{% endmacro %}

[Note: The strange looking comments are necessary to avoid unwanted whitespace inside the result.]

... and it would be called like this:

<a href="someurl?{{ url.querystring({'search': search_string, 'page': page_index + 1}) | e }}">next search results page</a>

I feel hash literals are a really essential feature and although my request comes in rather late I propose to schedule it for the 1.0 release.

I'd recommend JSON notation for hash literals as shown in the examples above. Array literals are written in JSON notation already ([foo, bar, ...]), and many template designers will be familiar with the {foo: bar, boom: baz, ... } syntax from JavaScript.

I haven't taken too deep a look into the Twig source code yet, but I assume it shouldn't be very hard to implement hash literals.

gwilym commented 14 years ago

are you looking for...

{{ macros.blah([ 'key': 'value', ... ]) }}

?

fabpot commented 14 years ago

Hashes are already supported:

{{ form.input(['name': 'foo', 'style': 'color: red']) }}
literal commented 14 years ago

Um, sorry, my bad.

I didn't get that because it is not mentioned under "Literals" in "Twig for Template Designers". But I see now, that the [foo: bar] syntax is used in two examples.

I probably missed that also because it was not what I expected. I saw "[...]", thought "all right, that's arrays, where are the hashes?". Arrays and hashes are different concepts and I see it as one of the fundamental design weaknesses of PHP not to make a clear distinction between them. (Just imagine how much easier those built-in PHP array_* functions could be if you didn't have to remember how they act on hashes and how on real arrays - or even worse, a mix of both).

So I think it's a pity that Twig mimics this PHP flaw. But I understand it's probably too late to change the syntax from [foo: bar] to {foo: bar}...

fabpot commented 14 years ago

No, it's not too late. As Twig 1.0 is not released yet, we can still change things like that if that makes sense.

So, that's something we can still discuss. The reasoning behind the decision is to ease the learning curve for web designers who don't know any programming language. For them, it's better to have one unified construct like PHP has. On the other hand, supporting a more standard syntax like JSON can also make sense.

literal commented 14 years ago

Well I disagree with the PHP-style "unified construct" being easier.

If you follow my basic statement that arrays and hases are different structures for different use cases, you might also see that there is a certain WTF-factor to arrays sometimes having explicit keys and sometimes not.

Arrays store an arbitrary number of similar things. You'd normally loop over the elements and not care for the keys. A non-programmer-Twig-user might not even be aware there are keys and fortunately doesn't have to.

Hashes/collections/dictionaries have named elements with different meanings. You'd normally access them directly by their keys. And if you loop, you'd actually use the keys in some way.

In my view it's not easy but instead confusing to have different concepts share the same notation.

And by the way all other programming languages I have used besides PHP either make a clear distinction between arrays and hashes (Python, Ruby, Javascript etc.) or have no native hash constructs (like Java were you use e.g. a java.util.HashMap object).

Another point:

The typical template desiner I have in mind (and which I have met many times) has had contact with one programming language: JavaScript. He/she might not have a concept of OOP, prototype based inheritance or even functions, but uses frameworks and other off-the-shelf stuff like TinyMCE . Mostly he/she copies and adapts examples from tutorials, forums etc. which requires a basic knowledge of JS syntax. Thus [foo, bar] and {foo: bar, boom: baz} are familiar.

gwilym commented 14 years ago

Personally I think if it's a PHP template engine, then stick to the syntax provided by the underlying language?

Having worked on and with both sides of site creation - design and development - I would say any designer who is capable of writing javascript stands out above 90% of other designers as it is, and it shouldn't be too hard for them to understand that they just use [ square brackets ] for server stuff, instead of { curly braces }.

Of course, if support for curly was added while keeping square as it is then, whatever, I just don't think it would be wise to remove square bracket combined arrays + hashes since it's a staple of PHP (whether anyone thinks it's for better or for worse, it just is).

fabpot commented 14 years ago

A quick suggestion: what about allowing both {} and [] in the parser? Which would mean that all the following expressions would be valid:

[foo, bar]
{foo, bar}
[foo: bar]
{foo: bar}
[foo: bar, bar]
{foo: bar, bar}
henrikbjorn commented 14 years ago

+1 for only allowing hashes which makes more sense even for a designer or frontender since they most likely already know some javascript.

fabpot commented 14 years ago

I'm not sure I understand. You don't want support for "simple" arrays?

henrikbjorn commented 14 years ago

when i see [] i assume its like a array() with numeric keys and {} here keys have meaning if that makes sense :)

lmammino commented 14 years ago

I do agree with supporting both the square and curly brackets syntaxes! I think it will be definitely a more flexible syntax because every developer can select its own conventions based on its preferences and habits.

literal commented 14 years ago

Every "there is more than one way to do it"-descision also has negative aspects:

Let's assume the docs use one style in the examples and the alternative style is only mentioned in some more formal reference part or a footnote.

Now if some template designer who is not aware of the alternative syntax edits a Twig template that someone else wrote using the alternative style. What is s/he going to think? "Is it an error?", "What does it mean?". "Is {foo: bar} the same as [foo: bar] or is there a subtle difference?" (e.g. like between single an double quotes in PHP) It would make the template designer feel unsure about his/her understanding of Twig.

markstory commented 14 years ago

Only having one way to do something is valuable, and adding redundant ways of doing things, should be avoided unless there is going to be a deprecation over time imo. Either {} or [] should work, but not both. The points that literal raised are very valid, confusion and documentation bloat are real concerns for me.

fabpot commented 13 years ago

ok, we need to make a decision here. If I understand all arguments, we basically have two choices:

Which one do we want?

lmammino commented 13 years ago

I prefer the first choice... By Changing we could break all the templates that we've already written... Furthermore I prefer a syntax closer to PHP, because we're using PHP and not JS...

krurkrit commented 13 years ago

I vote for the first choice. I have a question - Is possible to pass hash in hash? such as css have key 'color' and 'padding', value of 'padding' is hash with key 'top' and 'bottom'. {{ css(['color': 'blue', 'padding': ['top': 10px, 'bottom': 5px]]) }}

if we choose the second choice, it will be {{ css({'color': 'blue', 'padding': {'top': 10px, 'bottom': 5px}}) }}

I do not care if new syntax will break the old template that I've written. If it make good performance, easy for designer and clean code (easy to read).

henrikbjorn commented 13 years ago

Yes it should be possible to do that...

I votes for the [] as it is now to keep it simple and as immamino said we are using php anyways.

lmammino commented 13 years ago

@henrikbjorn ... Lmammino :P

esgy commented 13 years ago

I'm for the [] construct. It's not confusing because PHP programmers know how arrays work in PHP and don't care how they work in other languages. And from my small 7 years experience with web development (3 of them with php) the programmer that writes the models and the controllers is also the one that writes the logic of the view. Small or large firms.. same stuff. And interface designers/developers only play around with html and javascript. Please stop thinking for those guys and think for the one that actually use this template engine and it's us the php programmers.

henrikbjorn commented 13 years ago

@Imammino hard to write on an iPad.

markstory commented 13 years ago

I think that switching to {} would make the Twig templates look more like other templates, and provide some consistency with other languages like javascript, python, and ruby. I don't really but into the argument, that php developers will be confused by {}. I don't think you can be a front end web developer and avoid Javascript, also you don't declare arrays in php with [] either. While I'm not happy about having to change all my existing templates when this feature comes, I think separating hashes and arrays makes sense even if PHP decided to conflate the two concepts.

henrikbjorn commented 13 years ago

But the underlying language is still PHP and not javascript and not python. If you want that kind of syntax why arent you all using Python? :)

markstory commented 13 years ago

But PHP doesn't use [] to construct arrays either. So its just as disconnected from the underlying language in either case.

henrikbjorn commented 13 years ago

But there is an RFC for adding the [] array shortcut into core.

Seldaek commented 13 years ago

Just one little thing, everyone mentions we're using php so [] makes more sense, which is not even true, but assuming it would be. What about using foo.bar? How is that close to PHP?

The template engine is written in PHP, but templates are not PHP. Much like PHP is written in C and yet we use classes and such, who cares about the underlying implementation's language. The fact is, using dot for properties/method access instead of concatenation makes the Twig syntax look almost 100% like JS already. Aside from the filter, if we changed to the second proposal ([] = array, {} = hash), Twig would look like JS, which imo is a big win for a typically frontend-oriented technology.

That being said, if we support {} for hashes, I think we need to revisit the syntax, atm I think Twig forces you to quote the keys, while JS does not take quotes, but JSON takes double quotes. I'd go for no quotes. Which means you can't define a hash with a variable key, unless we also allow stuff like {% set foo[var] = val %} - I'm not sure if this is possible atm, but I don't think so?

@henrikbjorn: having an RFC doesn't mean it's gonna happen.

henrikbjorn commented 13 years ago

@Seldaek i know that having an RFC dosent mean it will happen. And i dont think i will.

+1 for [] - 1 for {}

If we are going to look at inconsistensies in the templating language also, we should look at the {% block %} tag which takes one parameter that is not a quoted string.

Seldaek commented 13 years ago

Well, I think we should look at inconsistencies, and fix them before it's too late.

nikic commented 13 years ago

There should be a decision in here, this is a change that should be made asap. Later it will never be made, as "many programs rely on it" (call-time pass by reference is deprecated for decades, but it still isn't removed from PHP).

I definitely approve of the variant with different syntaxes for arrays and hashes. There are basically two reasons:

fabpot commented 13 years ago

After thinking about the issue for quite a long time now, and based on all the arguments from this thread, I have finally implemented the [] and {} syntax.

https://github.com/fabpot/Twig/commit/4505200d9f5481839738421eed0bde3ae546e0a7