------------------------------------------------------------ revno: 1148 committer: Roger Martin <rg1...@gmail.com> branch nick: aikiframework timestamp: Tue 2012-03-20 23:49:57 +0100 message: eval_expression added added: tests/libs/markup.php modified: libs/Controls.php libs/Engine_v2.php libs/markup.php
-- lp:aikiframework https://code.launchpad.net/~aikiframework-devel/aikiframework/trunk Your team Aiki Framework Developers is subscribed to branch lp:aikiframework. To unsubscribe from this branch go to https://code.launchpad.net/~aikiframework-devel/aikiframework/trunk/+edit-subscription
=== modified file 'libs/Controls.php' --- libs/Controls.php 2012-03-17 07:56:19 +0000 +++ libs/Controls.php 2012-03-20 22:49:57 +0000 @@ -27,29 +27,6 @@ return is_array($array) && isset($array[$key]) ? $array[$key]: $ifnotfound; } -function htmlAttributes( &$field, $moreAttributes ){ - - $allAttributes= array( - "class","onchange","onfocus","onblur" ); - - if ( !is_null($moreAttributes ) ){ - $allAttributes = array_merge($allAttributes, explode(" ",$moreAttributes)); - } - - $ret=""; - foreach ($allAttributes as $attribute ){ - if ( isset($field[$attribute]) ){ - if ( $attribute=="checked" || $attribute=="disabled" ){ - $ret .= " $attribute"; - } else { - $ret .= " $attribute='". addslashes($field[$attribute]). "'"; - } - } - } - return $ret; -} - - /* @@ -75,11 +52,11 @@ ); $this->last_error =""; // read new parameters. - if ( is_array($parameters) ){ - $this->defaults = $parameters + $defaults; + if ( is_array($parameters) && count($parameters)>0 && is_array($parameters[0]) ){ + $this->defaults = array_shift($parameters) + $defaults; + $this->defaults["fields"]= $parameters ; } - } @@ -109,13 +86,13 @@ if ( $this->is_set("save") ){ if ( isset($_POST[$this->defaults["save"]]) ){ if ( !$this->save_data() ) { - $message = $this->message) + $message = $this->message( "Error saving/adding record " . ( is_debug_on() ? $this->last_error : "" ), "error"); } elseif ( $this->is_set("pkey") ){ - $message = $this->message( "Record saved", "ok form-record-saved"), + $message = $this->message( "Record saved", "ok form-record-saved"); } else { - $message = $this->message( "Record added", "ok form-record-add"), + $message = $this->message( "Record added", "ok form-record-add"); } } // to avoid re-write the form @@ -177,6 +154,13 @@ return isset($this->defaults[$key]) && $this->defaults[$key]; } + + /** + * + * Load data in a form + * + */ + function load_data(){ global $db; @@ -203,12 +187,11 @@ } elseif ( $this->is_set("fields") && is_array($this->defaults["fields"]) ){ return $values; } - - // @TODO WHAT DO WITH error's + $db->last_error = ""; $news = $db->get_row($SQL,ARRAY_A); if ( $db->last_error ){ - $this->last_error = $db->last_error; + $this->last_error = t("Error reading data") . (is_debug_on()? ": ". $db->last_error : "") ; return $values; } @@ -223,7 +206,7 @@ return false; } - // check if there is a name + // check if there type adequate switch ($field['type']){ case 'submit': case 'reset' : @@ -248,6 +231,7 @@ return ( is_null($ret) ? NULL: "'". Controls::unescape_textarea($ret). "'"); } + function save_data(){ global $db; @@ -329,26 +313,62 @@ class Controls { + /** + * return the htm attributes for a input control + * + * @param array Fields defintion + * @param array moreAttributes. + * + * @return string attributes list ( foo='bar' bar='foo' + * + */ + + function htmlAttributes( &$field, $moreAttributes ){ + + + // this is the common attribute list supported by all inputs. + $allAttributes= array("class","onchange","onfocus","onblur" ); + + if ( !is_null($moreAttributes ) ){ + if ( is_array($moreAttributes) ){ + $allAttributes = array_merge($allAttributes, $moreAttributes); + } else { + $allAttributes = array_merge($allAttributes, explode(" ",$moreAttributes)); + } + } + + $ret=""; + foreach ($allAttributes as $attribute ){ + if ( isset($field[$attribute]) ){ + if ( $attribute=="checked" || $attribute=="disabled" ){ + $ret .= " $attribute"; + } else { + $ret .= " $attribute='". addslashes($field[$attribute]). "'"; + } + } + } + return $ret; + } + + function make_list(&$list){ global $db; if ( is_array($list) ) { return $list; } - if ( substr($list,0,4)=="SQL:" ){ - $ret= array(); - if ($results= $db->query( substr($list,5), N_ARRAY )){ - foreach ($results as $result){ - $ret[$resul[0]]= $resul[1]; - } - } - return $ret; - } elseif ( $list=="gender") { + if ( $list=="gender") { return array("Male", "Female"); } elseif ( $list=="yn"){ return array("Yes", "No"); + } + $ret= array(); + if ($results= $db->query( $list, ARRAY_A )){ + foreach ($results as $result){ + $ret[$resul[0]]= $resul[1]; + } } - return explode("|",$list); + return $ret; } @@ -358,22 +378,55 @@ * */ - function input_hidden($field,$value){ + function input_hidden($field){ $name = $field['name']; - $value= addslashes($value); - return "<input type='hidden' name='{$field['name']}' value='$value'>"; + if (!$name) { + return ""; + } + + $value = byDefault($field,"default" ,NULL); + if ( is_null($value)){ + $value= byDefault($values,$name,NULL); + } + + $value = is_null($value) ? "": addslashes($value); + return "<input type='hidden' name='{$name}' value='{$value}'>"; } + /** + * + * Escape a string to insert it in html value attribute + * + */ + function escape_value($value){ return htmlentities($value, ENT_QUOTES); } + /** + * + * Escape a string to insert it a html textarea controls + * + * Textarea allow html tag,quotes but not another textarea, so this + * tag is transformed in <text-area>. + * + */ + function escape_textarea($value){ return strtr( $value, array("<textarea"=>"<text-area", "</textarea>"=>"</text-area>")); } + /** + * + * Unscape a string to insert it a html textarea controls + * + * As textarea doesn't allow <textarea>, this function reverts all previous converted <text-area> + * in <textarea> + * + */ + function unescape_textarea($value){ return strtr( $value, @@ -412,7 +465,7 @@ "image" =>"value src", "month" =>"value", "number" =>"value min max step", - "password" =>"value", + "password" =>"value size maxlength", "radio" =>"checked disabled value", "range" =>"", "reset" =>"value", @@ -425,7 +478,6 @@ "week" =>"value" ); - if ( is_null($form) ){ $layout = bydefault($field,'layout','table'); $formID = bydefault($field,'form',"aiki_form"); @@ -444,7 +496,7 @@ // first, discard hidden, and find field label switch ( $type ) { case "hidden": - return Controls::input_hidden($field,$value); + return Controls::input_hidden($field); case "radios": $label = "<span class='label'>$label</span>"; break; @@ -454,8 +506,7 @@ // now the hard work, input; $input=""; - - $attributes = htmlAttributes( $field, isset($defined[$type]) ? $defined[$type] : NULL ); + $attributes = Controls::htmlAttributes( $field, isset($defined[$type]) ? $defined[$type] : NULL ); switch ( $type ){ case "radios": @@ -502,7 +553,7 @@ } // no break default: - $attributes = htmlAttributes( $field, isset($defined[$type]) ? $defined[$type] : NULL ); + $temp = Controls::escape_value($value); $input = "<input type='$type' value='$temp' name='$name' id='{$formID}_$name'$attributes>"; break; @@ -531,15 +582,34 @@ * */ function inputs( &$inputs, $form, &$values){ - $hidden = ""; - $ret = "" ; + $hidden = ""; + $ret = ""; + $fieldset = ""; + $layout = is_null($form->layout) ? 'table' : $form->layout; + + foreach($inputs as $input) { if ( !is_array($input) ){ continue; } - - //hidden values - switch ($input["type"]){ + + switch ($input["type"]){ + case "fieldset": + if ($fieldset!="") { + $ret .= "\n</fieldset>"; + } + $fieldset= ( isset($input["name"])? $input["name"] : uniqid("fieldset")); + if ($fieldset !="" ){ + $ret .= "\n<fieldset ". Controls::htmlAttributes($input,array("id")). ">"; + if ( isset($input["legend"]) && $input["legend"] ){ + $ret .= "\<legend>{$input['legend']}</legend>"; + } + } + break; + case "hidden": + $hidden = Controls::input_hidden($input); + break; + case "html": $ret .= $input["html"]; break; @@ -551,6 +621,12 @@ } } + // close all open fieldset + if ($fieldset!="") { + $ret .= "</fieldset>"; + } + + // layout for all fieldset $layouts= array( "table" => "\n<table>\$inputs</table>", "none" => "\$inputs", @@ -561,7 +637,7 @@ $layout = is_null($form->layout) ? 'table' : $form->layout ; $useLayout = isset($layouts[$layout]) ? $layouts[$layout] : $layouts['table']; - return $hidden.str_replace ( '$inputs', $ret, $useLayout); + return str_replace ( '$inputs', $ret, $useLayout); } } === modified file 'libs/Engine_v2.php' --- libs/Engine_v2.php 2012-03-17 07:56:19 +0000 +++ libs/Engine_v2.php 2012-03-20 22:49:57 +0000 @@ -261,9 +261,7 @@ 'site_name' => $aiki->site->site_name(), 'site' => $aiki->site->get_site(), 'view' => $aiki->site->view(), - 'direction' => $aiki->languages->dir, - 'insertedby_username' => $aiki->membership->username, - 'insertedby_userid' => $aiki->membership->userid, + 'direction' => $aiki->languages->dir, 'current_month' => $current_month, 'current_year' => $current_year, 'current_day' => $current_day, @@ -643,10 +641,8 @@ * forms */ - function parse_forms( $para, $true, $false){ - $formParameters = array_shift($para) ; - $formParameters["fields"]= $para; - $form= new Forms2($formParameters); + function parse_forms( $para, $true, $false){ + $form= new Forms2($para); return $form->render(); } @@ -662,17 +658,15 @@ * if */ function parse_if($condition,$trueblock,$elseblock){ - if (isset($condtion[0]) && $condition[0]) { - return $trueblock; + + if (isset($condition[0]) && $condition[0]) { + return $trueblock ; } else { return $elseblock ; } } - - - function convert_widget($widget){ // no_loop === modified file 'libs/markup.php' --- libs/markup.php 2012-03-17 07:56:19 +0000 +++ libs/markup.php 2012-03-20 22:49:57 +0000 @@ -355,3 +355,141 @@ return array ( $ret, substr( $string,$i)); } + + + +function eval_expression($expr) { + global $aiki_space_vars; + $matches = 0; + $operators= "\.\+\-\\/%\*=<>!&\|"; //quoted operators + + if ($expr == "") { + return 0; + } else { + // note: its very important to trim all trailing space. + $expr= trim($expr); + } + + // check if it is a integer + if (preg_match('/^[+-]?[0-9]*$/', $expr, $matches)) { + return (integer) $expr; + + // check if it is a number + } elseif (preg_match('/^[+-]?[0-9]*(\.[0-9]*)?$/', $expr, $matches)) { + return (float) $expr; + + // check if it is false + } elseif ( $expr===false || $expr == "false" ) { + return false; + + // check if it is true + } elseif ( $expr===true || $expr == "true" ) { + return true; + + /* check if it is a string . Thanks tohttp://blog.stevenlevithan.com/archives/match-quoted-string + } elseif (preg_match( "#^([\"'])(?:\\\1|.)*?\1\$#", $expr ) ){ + return $expr; + */ + // check if it is a string delimited by simple quotes. + } elseif (preg_match('/^\'([^\']*)\'$/', $expr, $matches)) { + return $matches[1]; + + // check if it is a string delimited by double quotes. + } elseif (preg_match('/^"([^"]*)"$/', $expr, $matches)) { + return $matches[1]; + + // check if it is a var + } elseif (preg_match('/^([a-z_]+[a-z_0-9]*)$/i', $expr, $matches)) { + return ( isset($aiki_space_vars[$matches[1]]) ? $aiki_space_vars[$matches[1]] : 0); + + // check if it is a parenthesis + } elseif (preg_match('~^\((.*)\)(.*)$~', $expr, $matches)){ + $op1=eval_expression( $matches[1]); + return eval_expression ( "$op1{$matches[2]}"); + + // check if it call function + } elseif (preg_match('~^(trim|substr|str_replace)\s*\((.*)$~', $expr, $matches)){ + $para= extract_parameters($matches[2]); + foreach ($para[0] as $toEval ){ + $real[] = eval_expression($toEval); + } + $temp=call_user_func_array($matches[1],$real); + return eval_expression ( (is_string($temp)? "'$temp'": $temp) .$para[1]); + + // boolean operators. (\|\||OR|AND|&&) + } elseif (preg_match("~^(.*)\s*(\|\||OR|AND|&&)\s*(.*)\$~u", $expr, $matches)) { + + $op1= eval_expression(trim($matches[1])); + $operator = $matches[2]; + + // Lazy evaluation of AND + if ( ($operator=="AND" || $operator=="&&") && !$op1) { + return false; + } + + // Lazy evaluation of OR + if ( ($operator=="OR" || $operator=="||") && $op1) { + return true; + } + + $op2= eval_expression(trim($matches[3])); + if( is_null($op1) || is_null($op2) ) { + return NULL; + } + + switch ($operator){ + case "AND": return (bool) $op2; //lazy evaluation: op1 is true + case "&&" : return (bool) $op2; + case "OR" : return (bool) $op2; //lazy evaluation: op1 is false + case "||" : return (bool) $op2; + + } + + // check a *, \ division, \ entire division, % rest of division + } elseif (preg_match("~^([^$operators]*)\s*([\*\/\\%])\s*([^$operators].*)\$~", $expr, $matches)) { + $op1= eval_expression($matches[1]); + $op2= eval_expression($matches[3]); + switch ($matches[2] ){ + case "*" : return $op1*$op2; + case "/" : return (int)($op1/$op2); + case "\\": return $op1/$op2; + case "%" : return $op1%$op2; + } + + // check a sum,rest + } elseif (preg_match("~^([^$operators]*)\s*([+-])\s*([^$operators].*)\$~", $expr, $matches)) { + $op1= eval_expression(trim($matches[1])); + $op2= eval_expression(trim($matches[3])); + return $matches[2]=="+" ? $op1+$op2 : $op1-$op2; + + + // check comparison operators + } elseif (preg_match("~^([^$operators]*)\s*([\<\>\!\=\.]{1,2})\s*([+-]?[^$operators].*)\$~", $expr, $matches)) { + $op1= eval_expression($matches[1]); + $op2= eval_expression($matches[3]); + + if( is_null($op1) || is_null($op2) ) { + return NULL; + } + $operator = trim($matches[2]); + + switch ($operator){ + case "." : return $op1.$op2; + case ">" : return $op1 > $op2; + case ">=": return $op1 >= $op2; + case "<" : return $op1 < $op2; + case "<=": return $op1 <= $op2; + case "==": return $op1 == $op2; + case "<>": return $op1 <> $op2; + case "!=": return $op1 <> $op2; + } + return ""; + + // check if it a function with one parameter + } elseif (preg_match('/^(trim)\((.*)\)$/i', $expr, $matches)) { + return meval($matches[1] . meval($matches[2],$aiki_space_vars) . $matches[3], $aiki_space_vars); + + + } + return NULL; + } === added file 'tests/libs/markup.php' --- tests/libs/markup.php 1970-01-01 00:00:00 +0000 +++ tests/libs/markup.php 2012-03-20 22:49:57 +0000 @@ -0,0 +1,169 @@ +<?php + +/** Aiki Framework Tests (PHP) + * + * Tests the markup library + * + * LICENSE + * + * This source file is subject to the AGPL-3.0 license that is bundled + * with this package in the file LICENSE. + * + * @author Roger Martin + * @copyright (c) 2008-2011 Aiki Lab Pte Ltd + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html + * @link http://www.aikiframework.org + * @category Aiki + * @package Tests + * @filesource */ + + +define("IN_AIKI","testing"); + +error_reporting(E_ALL); + +include_once("../../libs/markup.php"); + +// require_once 'PHPUnit/Autoload.php'; + +class MarkupTest { // extends PHPUnit_Framework_TestCase { + + public function testEval_expression() { + global $complete; + + // tests + $tests= array ( + + // primitive values + "'hello'" => "hello", + '"hello"' => "hello", + "Joes" => "Joes", + "2"=>2, + "false"=>false, + "true" =>true, + "''" =>"", + "-3" => -3, + "+5" => 5, + "1.5" => 1.5, + + // same as above with space + //" 'hello' " => "hello", + " 2 "=>2, + " false " =>false, + " true " =>true, + "'' " =>"", + " -3 " => -3, + " 1.5 " => 1.5, + + // aritmetric operation + "2 + 1"=>3, + "2+1"=>3, + " 2 + 1 "=>3, + + // string operations + "'hello '.'world'"=> "hello world", + + // vars + "x"=> 5, + // non existants variable. + "z"=> 0, + "x + 1"=> 6, + "9 - y" => 2, + + // precendence and () + "(2)" =>2, + "( 2 + 1 ) * 5 "=>15, + "x+2*y" => 19, + "x+(2*y)" => 19, + "(x+2)*y" => 49, + + // comparison with sorroundig space + "5 > 11" => false, + "5>1" => true, + "5 < 11" => true, + "5<1" => false, + "5<=11" => true, + "5<=1" => false, + "5>=11" => false, + "5>=1" => true, + + // not equals + "5 <> 11" => true, + "5<>11" => true, + "5!=1" => true, + " 5 <> 5 " =>false, + "5<>5" =>false, + "5<>+5" =>false, + "5 != 5" =>false, + + "5==5" =>true, + "5==+5" =>true, + "+5==5" =>true, + + // operations with var + "y>11" =>false, + "y>=11" =>false, + "x>4" => true, + "x>=4" => true, + "y==x" => false, + "(y+1)>x" => true, + "(y+1)<x" => false, + + // boolean operators + "y>2 AND x>3" => true, + "y>20 AND x>3" => false, + "y > 20 AND x > 3" => false, + "y>20 OR x>3" => true, + "y>20 OR x>30" => false, + + "y>2 && x>3" => true, + "y>20 && x>3" => false, + "y > 20 && x > 3" => false, + "y>20 || x>3" => true, + "y>20 || x>30" => false, + + // function call + "substr('hello',2)" =>'llo', + "trim(' hello ')"=>'hello', + "trim(' hello ').' world'" =>'hello world', + "substr('foo.png',-3)=='png'" =>true, + ); + + + + $error=0; + + foreach ( $tests as $test=>$result){ + if ( eval_expression($test)!= $result ){ + echo "<br><strong>fails</strong> |$test|"," expected ", var_dump($result), " given ", var_dump(eval_expression($test)); + $error++; + } elseif ( $complete ){ + echo "<br>|$test| ok ($result)"; + } + //$this->assertEquals( eval_expression($test), $result); + } + if ( !$error ){ + echo "<p>eval_expression passed all test (", count($tests) ,")"; + } else { + echo "<p>eval_expression fails in $error of ", count($tests), " tests" ; + } + + } + +} // end class + + +// initialize some data. +global $aiki_space_vars, $complete; +$aiki_space_vars= array("x"=>5, "y" =>7 ); +$complete = ( isset($_GET["complete"]) && $_GET["complete"]=='1' ? true: false); + +// making the test +$suite = new MarkupTest(); +$suite->testEval_expression(); + +if ( $complete){ + echo "<p><a href='markup.php?'>see only errors</a>"; +} else { + echo "<p><a href='markup.php?complete=1'>see complete test </a>"; +}
_______________________________________________ Mailing list: https://launchpad.net/~aikiframework-devel Post to : aikiframework-devel@lists.launchpad.net Unsubscribe : https://launchpad.net/~aikiframework-devel More help : https://help.launchpad.net/ListHelp