Smarty Forum Index Smarty
The discussions here are for Smarty, a template engine for the PHP programming language.
Switch control structures
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Smarty Forum Index -> Feature Requests
View previous topic :: View next topic  
Author Message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sun Aug 20, 2006 3:01 am    Post subject: Switch control structures Reply with quote

Not essential of course, but it might be nice to have a switch control structure added...

e.g., with {/case} being optionally insertable as equivalent to "break;"

Code:

{switch $var}
{case 'greeting'}
Hello
{/case}
{case 'farewell'}
Goodbye and
{case 'night'}
Good night
{/case}
{default}
Hi
{/switch}


I think switch may be even more useful for Smarty than for regular PHP in that Smarty is supposed to generally be accepting simpler input....So this might also be useful as an alternative to setting a number of independent flags (i.e., one flag could be given a different value within the script---e.g., usertype_flag which could have the value of admin, user, etc., and then tested within the template via switch).

This would also simplify the PHP side as one did not need to think of new flag names--just the values.
Back to top
View user's profile Send private message
cowboystyle
Smarty Rookie


Joined: 02 Oct 2006
Posts: 5
Location: New York, NY

PostPosted: Mon Oct 02, 2006 4:07 pm    Post subject: Reply with quote

throwing my support for this feature, wondering whether mulitple {if}{elseif}{/if} statements are slower than an implementation of {switch}.
Back to top
View user's profile Send private message Visit poster's website AIM Address
boots
Administrator


Joined: 16 Apr 2003
Posts: 5613
Location: Toronto, Canada

PostPosted: Wed Oct 04, 2006 7:31 pm    Post subject: Reply with quote

Long ago, messju posted a "page" plugin that does this. Maybe search the forums to find it.
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Thu Oct 05, 2006 5:04 am    Post subject: Reply with quote

Thanks... I found it here: http://www.phpinsider.com/smarty-forum/viewtopic.php?t=296&highlight=switch+plugin

I presume tigger_error in the code should be trigger_error.

I haven't tested it yet for omitting the closing {/page} tags to see if one can nest them and thus allow brevity in the code.

However, it seems that by having to repeat the variable, some of the point of using the structure (brevity) is rendered moot.
Back to top
View user's profile Send private message
boots
Administrator


Joined: 16 Apr 2003
Posts: 5613
Location: Toronto, Canada

PostPosted: Thu Oct 05, 2006 2:11 pm    Post subject: Reply with quote

You can probably rewrite it as a compiler plugin to avoid the "brevity" issue -- but I'm not much a fun of plainly replicating every control structure found in GP languages.
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Fri Feb 09, 2007 10:49 am    Post subject: Reply with quote

Howdy,

In response to boots' last post (now a good while ago), I should say that I've seen several requests in the forums for a switch structure (including one in German too). I think it's not just having it to have one...A switch can not only be shorter but also make things easier to read, and I think it really is suitable for Smarty where the often simple logic only would require a 'case' statement (e.g., going through flags depending on access).

One idea I was thinking about (which is not in any language that I'm familiar with--what's GP by the way?) is to allow an if-else to work like a switch-case in regards to allowing the code to continue into the next loop whenever there is not a break of sorts. Although this would be nice in PHP too (e.g., if you let out a '}'), I would imagine it could also conceivably be done in Smarty too...

e.g.,
Code:

{if $num < 2}
That's a little number!
{elseif $num < 10}
Your number is too small.
{/elseif}
{else}
Ok
{/else}

If the number were "1", it would thus print both "That's a little number!" and "Your number is too small" (and then stop).

I think this could make less redundant code. If people find switches useful, why not this too? (Though I'm pretty sure it would mess people up to use the same names and suddenly change on them.) Just sharing the idea (and I bet someone somewhere's already done this in some language or other), but it is the switch that I'm really interested in (and don't think I'll find enough time to try to hash it out on my own). Smile
Back to top
View user's profile Send private message
pynej
Smarty Rookie


Joined: 07 Mar 2008
Posts: 8

PostPosted: Fri Mar 07, 2008 5:21 pm    Post subject: Working Smarty Switch Plugin: UPDATED Reply with quote

There is a complete and working switch plugin. It works exactly as a php switch and you can even use variables and modifiers for the conditions. Feel free to use it and let me know if you have and problems.

03/08/2008 - Updated to version 2
This update changes the break attribute to work how you would expect. That is, setting the break attribute will cause smarty to automatically render a {break} tag before it starts the NEXT case or default section. I strongly recommend updating to this version of the switch plugin, as the older version was very counter intuitive in this aspect.

02/09/2010 - Updated to version 3
An updated version of my switch plugin for Smarty 3 is available here. (The Smarty 3 version is not backward compatible so I will leave this post in-tacked. The new version can be downloaded from the above link.)

(Also, this does work with nested switches, witch is why you see it accessing the _switchData variable. Be sure to not alter this variable in smarty code, as it will make the switches behave incorrectly.)

* Both of these blocks will produce identical switch logic.
Code:

{case 1 break}
   Code 1
{case 2}
   Code 2
{default break}
   Code 3

Code:

{case 1}
   Code 1
   {break}
{case 2}
   Code 2
{default}
   Code 3
   {break}



03/08/2008 - Updated to version 2.1
Added {/case} tag, this is identical to {break}.

compiler.switch.php
Code:

<?php
/**
 * Switch statement plugin for smarty.
 *    This smarty plugin provides php switch statement functionality in smarty tags.
 *    To install this plugin just drop it into your smarty plugins folder.
 *
 * @author Jeremy Pyne <jeremy.pyne@gmail.com>
 * - Donations accepted via PayPal at the above address.
 * - Updated: 03/08/2008 - Version 2
 * - File: smarty/plugins/compiler.switch.php
 * - Updates
 *    Version 2:
 *       Changed the break attribute to cause a break to be printed before the next case, instead of before this
 *          case.  This way makes more sense and simplifies the code.  This change in incompatible with code in
 *          from version one.  This is written to support nested switches and will work as expected.
 *    Version 2.1:
 *       Added {/case} tag, this is identical to {break}.
 * - Bugs/Notes:
 *       If you are using the short form, you must case condition before the break option.  In long hand this is
 *          not necessary.
 *
 * @package Smarty
 * @subpackage plugins
 *
 * Sample usage:
 * <code>
 * {foreach item=$debugItem from=$debugData}
 *  // Switch on $debugItem.type
 *    {switch $debugItem.type}
 *       {case 1}
 *       {case "invalid_field"}
 *          // Case checks for string and numbers.
 *       {/case}
 *       {case $postError}
 *       {case $getError|cat:"_ajax"|lower}
 *          // Case checks can also use variables and modifiers.
 *          {break}
 *       {default}
 *          // Default case is supported.
 *    {/switch}
 * {/foreach}
 * </code>
 *
 * Note in the above example that the break statements work exactly as expected.  Also the switch and default
 *    tags can take the break attribute. If set they will break automatically before the next case is printed.
 *
 * Both blocks produce the same switch logic:
 * <code>
 *    {case 1 break}
 *       Code 1
 *    {case 2}
 *       Code 2
 *    {default break}
 *       Code 3
 * </code>
 *
 * <code>
 *    {case 1}
 *     Code 1
 *       {break}
 *    {case 2}
 *       Code 2
 *    {default}
 *       Code 3
 *       {break}
 * </code>
 *
 * Finally, there is an alternate long hand style for the switch statments that you may need to use in some cases.
 *
 * <code>
 * {switch var=$type}
 *    {case value="box" break=true}
 *    {case value="line"}
 *       {break}
 *    {default}
 * {/switch}
 */

// Register the other smarty methods in this file.
// smarty_compiler_switch is automatically registered.
$this->register_compiler_function('case', 'smarty_compiler_case');
$this->register_compiler_function('default', 'smarty_compiler_default');
$this->register_compiler_function('break', 'smarty_compiler_break');
$this->register_compiler_function('/case', 'smarty_compiler_break');
$this->register_compiler_function('/switch', 'smarty_compiler_endswitch');
$this->register_postfilter('smarty_postfilter_switch');

/**
 * Start a new switch statement.
 *    A variable must be passed to switch on.
 *  Also, the switch can only directly contain {case} and {default} tags.
 *
 * @param string $tag_arg
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_compiler_switch($tag_arg, &$smarty) {
   // Add var= if needed.
   if(strpos($tag_arg, 'var=') === false)
      $tag_arg = 'var='.$tag_arg;
   
   // Run the smarty code passed in.
   $_params = $smarty->_parse_attrs($tag_arg);
   
   // Make sure we have a var.
    if (!isset($_params['var'])) {
        $smarty->_syntax_error("switch: missing 'var' parameter", E_USER_WARNING);
        return;
    }
   
    // Get the switch data.
    $switchData =& $smarty->get_template_vars("_switchData");
    // Add a new switch data array.
    if(is_null($switchData)) {
       $switchData = array();
       $smarty->assign_by_ref("_switchData", $switchData);
    }
    // Turn auto-break off.
   array_unshift($switchData, false);
   
   // Return the switch.
   return "switch ({$_params['var']}) {";
}

/**
 * Print out a case line for this switch.
 *    A condition must be passed to match on.
 *    This can only go in {switch} tags.
 *    If break is passed, a {break} will be rendered before the next case.
 *
 * @param string $tag_arg
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_compiler_case($tag_arg, &$smarty) {
   // Add value= if needed.
   if(strpos($tag_arg, 'value=') === false)
      $tag_arg = 'value='.$tag_arg;
   
   // Change break to break=true
   $tag_arg = preg_replace('/ break$/', ' break=true', $tag_arg);
   
   // Run the smarty code passed in.
   $_params = $smarty->_parse_attrs($tag_arg);
   
   // Make sure we have a value.
   if (!isset($_params['value'])) {
      $smarty->_syntax_error("switch: missing 'value' parameter", E_USER_WARNING);
      return;
   }
   
   // Get the switch data, and fetch the current auto-break value.
   $switchData =& $smarty->get_template_vars("_switchData");
   $break =& $switchData[0];
   
   // If auto-break is on, break before the new case.
   $return = ($break ? 'break; ' :'') . "case {$_params['value']}:";
   
   // If the break attribute was passed, set the auto-break for the next case.
   $break = (array_key_exists('break', $_params) && $_params['break'] == 'true');
   
   return $return;
}

/**
 * Print out a default line for this switch.
 *    This can only go in {switch} tags.
 *    If break is passed, a {break} will be rendered before the next case.
 *
 * @param string $tag_arg
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_compiler_default($tag_arg, &$smarty) {
   // Change break to break=true
   $tag_arg = preg_replace('/break$/', ' break=true', $tag_arg);
   
   // Run the smarty code passed in.
   $_params = $smarty->_parse_attrs($tag_arg);
   
   // Get the switch data, and fetch the current auto-break value.
   $switchData =& $smarty->get_template_vars("_switchData");
   $break =& $switchData[0];
   
   // If auto-break is on, break before the new case.
   $return = ($break ? 'break; ' :'') . 'default;';
   
   // If the break attribute was passed, set the auto-break for the next case.
   $break = (array_key_exists('break', $_params) && $_params['break'] == 'true');
   
   return $return;
}

/**
 * Print out a break command for the switch.
 *    This can only go inside of {case} tags.
 *
 * @param string $tag_arg
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_compiler_break($tag_arg, &$smarty) {
   return "break;";
}

/**
 * End a switch statement.
 *
 * @param string $tag_arg
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_compiler_endswitch($tag_arg, &$smarty) {
   $switchData =& $smarty->get_template_vars("_switchData");
   array_shift($switchData);
   
   return "}";
}

/**
 * Filter the template after it is generated to fix switch bugs.
 *    Remove any spaces after the 'switch () {' code and before the first switch.  Any tabs or spaces
 *       for layout would cause php errors witch this reged will fix. 
 *
 * @param string $compiled
 * @param Smarty_Compiler $smarty
 * @return string
 */
function smarty_postfilter_switch($compiled, &$smarty) {
   // Remove the extra spaces after the start of the switch tag and before the first case statement.
   return preg_replace('/({ \?>)\s+(<\?php case)/', "$1\n$2", $compiled);
}

?>


Last edited by pynej on Tue Feb 09, 2010 4:59 pm; edited 6 times in total
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sat Mar 08, 2008 12:17 am    Post subject: Reply with quote

I'm eager to check it out, thanks!

Brett
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sat Mar 08, 2008 12:32 am    Post subject: Reply with quote

In looking at your comments, you had the following which you said were identical:
Code:

* {case 1}
* {case 2 break}
* {default break}

* {case 1}
* {break}
* {case 2}
* {break}
* {default}


To really be the same, wouldn't the first
Code:
{case 1}
need to have "break" inside of it too?


Also, there is this in one example:

Code:
* {case value="line" break=true}
* {break}


Would it really be necessary to have
Code:
break=true
and also
Code:
{break}
?

thanks,
Brett
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sat Mar 08, 2008 12:54 am    Post subject: Reply with quote

Also, I'm not clear on how dumping the file in the plugins directory will cause the file to be found if plugins are only called on demand?
Back to top
View user's profile Send private message
pynej
Smarty Rookie


Joined: 07 Mar 2008
Posts: 8

PostPosted: Sat Mar 08, 2008 8:52 pm    Post subject: Notes: Reply with quote

As for the break attribute. Due to how the smarty syntax works, passing the break attribute DOESN'T cause the current case to automatically break, Rather it prints a break tag BEFORE the case take. That is, calling {case X break} will acutely render as the following.
Code:

break;
case X:


This is something I can look into changing to work in the way you are thinking of, IE to break after this tag but before the next case tag, but I will have to update the code.


A for plugins, I wasn't clear in the initial post You have to save this code into a file named compiler.switch.php in you plugins directory. It will automatically be loaded the first time smarty runs a switch tag.
Back to top
View user's profile Send private message
pynej
Smarty Rookie


Joined: 07 Mar 2008
Posts: 8

PostPosted: Sat Mar 08, 2008 10:13 pm    Post subject: Reply with quote

Ignore the last message. I have updated the code so that
Code:

{case logic break}
   Code

will render as
Code:

{case logic}
   Code
   {break}


The changes can be found in my original code, and I strongly recommend upgrading.
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sun Mar 09, 2008 3:00 am    Post subject: Reply with quote

Now, that is awesome, thank you! I hope that can make it into a new release for sure... (and messju's "macro" for hierarchical recursion as well)

Brett
Back to top
View user's profile Send private message
pynej
Smarty Rookie


Joined: 07 Mar 2008
Posts: 8

PostPosted: Sun Mar 09, 2008 4:53 am    Post subject: Reply with quote

Added a {/case} tag. This is the same as {break}
Back to top
View user's profile Send private message
brettz9
Smarty Regular


Joined: 07 Jul 2006
Posts: 93

PostPosted: Sun Mar 30, 2008 3:04 am    Post subject: Reply with quote

Just letting you know I've been using the switch and it works like a charm... Nice and elegant...

One other convenience feature I can think of is to allow the switch to specify a default behavior for its cases (break or not to break)

thanks,
Brett
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Smarty Forum Index -> Feature Requests All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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
Protected by Anti-Spam ACP