Smarty Forum Index Smarty
WARNING: All discussion is moving to https://reddit.com/r/smarty, please go there! This forum will be closing soon.

Native PHP 'math' drop-in replacement or 'no more eval()!'
Goto page 1, 2  Next
 
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Plugins
View previous topic :: View next topic  
Author Message
aloner
Smarty Rookie


Joined: 24 Apr 2003
Posts: 24

PostPosted: Mon May 05, 2003 8:16 am    Post subject: Native PHP 'math' drop-in replacement or 'no more eval()!' Reply with quote

I've cooked up some code to replace 'math' function with native PHP code. Use pseudo-function 's_math' insted of 'math'.

Code:

[php:1:31b7e80fa2]<?php
/*
* Smarty plugin
* -------------------------------------------------------------
* File: compiler.s_math.php
* Type: compiler
* Name: s_math
* Author: Pavel 'aloner' Prishivalko,
* error/security check code by Smarty developers
* -------------------------------------------------------------
*/
function smarty_compiler_s_math($tag_arg, &$smarty)
{
$attrs = $smarty->_parse_attrs($tag_arg);

// be sure equation parameter is present
if (empty($attrs['equation'])) {
$smarty->trigger_error('s_math: missing equation parameter');
return;
}
$equation = $smarty->_dequote($attrs['equation']);
$err = "\n equation: '$equation'";

// make sure parenthesis are balanced
if (substr_count($equation,'(') != substr_count($equation,')')) {
$smarty->trigger_error('s_math: unbalanced parenthesis,'.$err);
return;
}

// match all vars in equation, make sure all are passed
preg_match_all('!([a-zA-Z][a-zA-Z0-9_]*)!', $equation, $match);

$allowed_funcs = array('int','abs','ceil','cos','exp','floor','log','log10',
'max','min','pi','pow','rand','round','sin','sqrt','srand','tan');
foreach($match[1] as $curr_var) {
if (!in_array($curr_var, array_keys($attrs)) && !in_array($curr_var, $allowed_funcs)) {
$smarty->trigger_error("s_math: parameter '$curr_var' not passed as argument or is unknown function,".$err);
return;
}
}

$PHP = '';

$pats = array();
$reps = array();

foreach ($attrs as $param_name => $param_value) {
if (!in_array($param_name, array('equation', 'assign', 'format'))) {
if (substr($param_value,0,1) == '$') {
$have_var = true;
// Is variable, need runtime checking.
$PHP .= "if(strlen($param_value)==0){\$this->trigger_error('s_math: parameter \'$param_name\' is empty');\$__smok=0;}else{if(\$__smok && !is_numeric($param_value)) \$this->trigger_error('s_math: parameter \'$param_name\' is not numeric');\$__smok=0;}\n";
}
$pats[] = '!\b'.preg_quote($param_name).'\b!';
$reps[] = ' '.$smarty->_dequote($param_value).' ';
}
}
$equation = preg_replace($pats, $reps, ' '.$equation.' ');

if ($have_var) {
$PHP = '$__smok=true;'.$PHP.'if($__smok)';
}
if ($attrs['assign']) {
$PHP .= '$this->_tpl_vars[\''.$smarty->_dequote($attrs['assign']).'\'] = ( ';
} else {
$PHP .= 'echo( ';
}

if ($attrs['format']) $equation = 'sprintf( \''.$smarty->_dequote($attrs['format']).'\' , ( '.$equation.' ) )';

$PHP .= $equation . ');';
return $PHP;
}
?>[/php:1:31b7e80fa2]

Example:
Code:
{s_math equation='(a + b)*round(c)/abs(y)' a=1 b=$b c=$something y=-5}


Will parse to:
[php:1:31b7e80fa2]<?php $__smok=true;if(strlen($this->_tpl_vars['b'])==0){$this->trigger_error('s_math: parameter \'b\' is empty');$__smok=0;}else{if($__smok && !is_numeric($this->_tpl_vars['b'])) $this->trigger_error('s_math: parameter \'b\' is not numeric');$__smok=0;}
if(strlen($this->_tpl_vars['something'])==0){$this->trigger_error('s_math: parameter \'c\' is empty');$__smok=0;}else{if($__smok && !is_numeric($this->_tpl_vars['something'])) $this->trigger_error('s_math: parameter \'c\' is not numeric');$__smok=0;}
if($__smok)echo( ( 1 + $this->_tpl_vars['b'] )*round( $this->_tpl_vars['something'] )/abs( -5 ) ); ?>[/php:1:31b7e80fa2]

Hope you enjoy it. Should work fast.

Usage is the same as 'math' function. You can use any valid PHP expressions in equation.
_________________
Your ad here.


Last edited by aloner on Sat May 10, 2003 11:16 am; edited 10 times in total
Back to top
View user's profile Send private message
boots
Administrator


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

PostPosted: Wed May 07, 2003 1:22 am    Post subject: Reply with quote

I'm going to try this out. Have you considered using a compiler function for this instead of a filter?
Back to top
View user's profile Send private message
aloner
Smarty Rookie


Joined: 24 Apr 2003
Posts: 24

PostPosted: Thu May 08, 2003 9:32 am    Post subject: Reply with quote

Boots, thanks for suggestion. I've turned it into compiler function.

Well, my first compiler function then. Smile
_________________
Your ad here.
Back to top
View user's profile Send private message
boots
Administrator


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

PostPosted: Fri May 09, 2003 6:53 pm    Post subject: Reply with quote

Just thought I'd report how much faster aloner's plugin is (especially in the compiled form!):

I ran the following extremely simple section using both the distribution {math} plugin and then with aloner's compiler version.

Code:
{section name=test loop=$iterations}
    {math equation="y + 1" y=$x assign=x}
{/section}


Obviously a contrived case, but illustrative.

I setup my environment to not use cache and to force recompiles. PHP caching is not enabled. The following lists the actual results on my machine and the average % speed-ups for various iteration counts:

100 0.08s vs 0.067s 19%
1000 0.185s vs 0.075s 147%
10000 1.31s vs 0.235s 457%

Nice! Even on short lists you get an appreciable speed-up and for longer iterations its a hands down win.

I can't think of a single case where using the standard distribution version is preferable to using aloner's plugin.

Good show, aloner!

NB:

For comparison purposes, my lovely calc modifier, while considerably faster than the distribution math plugin, can not compete with a compiler function based plugin like the one above. Modifiers--forced to runtime execution only--don't come close, especially for large iterations. Never-the-less, the modifier version does offer a more convenient syntax and if you are patching modifiers to allow for $this, they can be useful in situations where you want in-line calculations.


Last edited by boots on Fri May 09, 2003 11:52 pm; edited 1 time in total
Back to top
View user's profile Send private message
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Fri May 09, 2003 8:47 pm    Post subject: Reply with quote

boots wrote:
I can't think of a single case where using the standard distribution version is preferable to using aloner's plugin.


think of $smarty->security = true Smile

okay, i must admit there may be cases inside smarty to circumvent security=true and emit php-code into the compiled tpl to break security.
i don't know of such case OTOH, but if so, please report these to render my above statement valid. i'm willing to take performance- and feature-penalties to make $smarty->security deserve it's name.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
boots
Administrator


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

PostPosted: Fri May 09, 2003 9:26 pm    Post subject: Reply with quote

I love a challenge!

I've don't use the security feature, so it didn't occur to me Embarassed. I also found that security is not (yet) very well documented.

I haven't tried this yet, but I don't *think* it breaks security: it doesn't pass a <?php .. ?> back to Smarty -- only the included code.

Certainly, the manual does not indicate that compiled functions are a no-no with security on. Does it??

Nor do I think this SHOULD be considered a security hole!

xo boots

ps. I was hinting at the fact that with some checks put into aloner's code, it would be a good candidate to replace the distributions current math function plugin. Wink

pps. I'm not sure why a compiler function is a hole -- it is not something that template users have control over and they can't simply inject code by putting it one of the parameters.
Back to top
View user's profile Send private message
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Fri May 09, 2003 10:26 pm    Post subject: Reply with quote

it was not my point to state that a compiler-function is more a security hole than any other function. administrators have to restrict plugin_dir to sth. containing "harmless" plugins when using $smarty->security. me, being paranoid, i wouldn't consider the plugins directory of a smarty-distribution harmless, i would pick only the plugins absolutely needed out of it and put it into safe-plugins-dir when $security=true.

at first sight i consider the current implementation of math less harmful, but i must admit, i didn't test if s_math above breaks security. i just presumed, because it dumps $equation into the template.

this doesn't render the approach above useless in any way, i just thought we have to keep an option open for people needing {math} *and* security=true.

be sure, i check the benefits of s_match, but please understand, that this doesn't enjoy high-priority, math can be optimized best by being done in php in the first place.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
boots
Administrator


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

PostPosted: Fri May 09, 2003 10:57 pm    Post subject: Reply with quote

Quote:
be sure, i check the benefits of s_match, but please understand, that this doesn't enjoy high-priority, math can be optimized best by being done in php in the first place.


Agreed. But you have to admit that this beats the pants off of the distribution's math plugin Wink Also, as a compiler function that evaluates to PHP code, it can't be very much slower than doing the math elsewhere--and sometimes, it is untenable to do the math prior to calling a template.

I think its worthy of consideration.

ps. I've now tried injecting arbitrary code using this plugin and have so far failed Smile

EDIT:

pps. lastly, it means less regex support to support math internally, which I think is important.
Back to top
View user's profile Send private message
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Fri May 09, 2003 11:16 pm    Post subject: Reply with quote

if have no problem in admitting the beating of any pants of it since i didn't wear them at any time when a beating occured Smile.
i don't use {math}.
i never considered anything computational expensive inside a smarty-template. i have no problem if people are rendering the mandelbrot-set as bgcolors in a n-times-m table. or some glass-spheres over a checker-board for bigger ns and ms. but i think the more we optimize {math} the less we make people think about tweaking their data *before* the template gets hands on it.
just a thought.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
boots
Administrator


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

PostPosted: Fri May 09, 2003 11:41 pm    Post subject: Reply with quote

you a funny guy, messju Smile

Okay, I would agree that anyone calculating sets in template should step back and think about what they are trying to do. On the other hand, people DO a lot of math in template to handle simple things like keeping track of position in sections or determining depth, etc. When done in loops, the provided {math} really shows its inability.

Application logic should be separate from template logic , but the reverse is true as well: things that you need to calculate only for the template execution should not be performed in your PHP code, if possible. It makes things simpler that way.

Just my 2c.

I also want to thank you for all of your input--I very much respect your opinion!
Back to top
View user's profile Send private message
messju
Administrator


Joined: 16 Apr 2003
Posts: 3336
Location: Oldenburg, Germany

PostPosted: Sat May 10, 2003 12:15 am    Post subject: Reply with quote

my argumentation is a bad excuse for a sub-optimal implemention of math. smarty should be as good/optimized/lean as possible. in a smarty distribution this includes each and every plugin has to be hand-picked checked and put back if worth or replaced/omitted if dubious. sorry for my lazy argumentation against a faster math.

@aloner: i think s_math has potential, but cases like:
{s_math equation="system('cat /etc/passwd')"}
have to be prevented IMHO
Back to top
View user's profile Send private message Send e-mail Visit poster's website
aloner
Smarty Rookie


Joined: 24 Apr 2003
Posts: 24

PostPosted: Sat May 10, 2003 2:47 am    Post subject: Reply with quote

Well, probably I could hack s_math a bit and make it use 'math' when we have security on. Smile

Will try it some time.
_________________
Your ad here.
Back to top
View user's profile Send private message
aloner
Smarty Rookie


Joined: 24 Apr 2003
Posts: 24

PostPosted: Sat May 10, 2003 2:50 am    Post subject: Reply with quote

Probably we can put out 'equation' check from Math and make it shared.
Then we just have to check it during compile time and alert user.
_________________
Your ad here.
Back to top
View user's profile Send private message
aloner
Smarty Rookie


Joined: 24 Apr 2003
Posts: 24

PostPosted: Sat May 10, 2003 3:33 am    Post subject: Reply with quote

Okay, you asked it - you got it.

There's updated version, which brings compile-time error/security checking (code courtesy of Math plugin) and even runtime variables checking. Smile
Also I revamped some parts of it to make it supposedly faster. Also I think there a couple of bottlenecks which can be bit faster...

Feel free to test and report its 'esoteric features' here. Smile

Maybe it can replace standart Math plugin - it seems to meet all requirements. Smile
_________________
Your ad here.
Back to top
View user's profile Send private message
boots
Administrator


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

PostPosted: Sun May 11, 2003 3:24 pm    Post subject: Reply with quote

aloner, your post got me to thinking that it might be useful if there was a smarty provided function that allowed plugin developers to check if their input parameters are "safe" instead of relying on plugin writers to roll their own checks. It wouldn't remove all the checking that a plugin writer needs to do, but it can at least provide a common place where typical injections get scanned.

Just a thought.
Back to top
View user's profile Send private message
Display posts from previous:   
This forum is locked: you cannot post, reply to, or edit topics.   This topic is locked: you cannot edit posts or make replies.    Smarty Forum Index -> Plugins 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