174 lines
4.4 KiB
PHP
174 lines
4.4 KiB
PHP
<?php
|
|
/*+*******************************************************************************
|
|
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
|
|
* ("License"); You may not use this file except in compliance with the License
|
|
* The Original Code is: vtiger CRM Open Source
|
|
* The Initial Developer of the Original Code is vtiger.
|
|
* Portions created by vtiger are Copyright (C) vtiger.
|
|
* All Rights Reserved.
|
|
******************************************************************************/
|
|
|
|
class VTExpressionException extends Exception{}
|
|
|
|
class VTThunk{
|
|
function __construct($env, $expr){
|
|
$this->expr = new VTExpressionEvaluater($expr);
|
|
$this->env = $env;
|
|
}
|
|
|
|
function get(){
|
|
if(!isset($this->val)){
|
|
$this->val = $this->expr->evaluate($this->env);
|
|
}
|
|
return $this->val;
|
|
}
|
|
}
|
|
|
|
class VTExpressionEngineEnv implements VTEnv{
|
|
function __construct($data){
|
|
$this->data = $data;
|
|
$this->vars = array();
|
|
}
|
|
|
|
function bind($name, $expr){
|
|
$this->vars[$name] = new VTThunk($this, $expr);
|
|
}
|
|
|
|
function get($var){
|
|
if(array_key_exists($var, $this->vars)){
|
|
return $this->vars[$var]->get();
|
|
}else{
|
|
return $this->data[$var];
|
|
}
|
|
}
|
|
}
|
|
|
|
class VTExpressionEngine{
|
|
function __construct(){
|
|
$this->data = array();
|
|
$this->expressions = array();
|
|
$this->unparsedExpressions = array();
|
|
$this->vars = array();
|
|
}
|
|
|
|
|
|
/**
|
|
* Load dynamic fields and their expressions
|
|
*
|
|
*/
|
|
function loadExpressions($arr){
|
|
$this->unparsedExpressions = array_merge($this->unparsedExpressions, $arr);
|
|
$this->expressions = array_merge($this->expressions, array_map(array($this, 'expression'), $arr));
|
|
$this->regenerate();
|
|
}
|
|
|
|
function removeExpression($fieldName){
|
|
unset($this->unparsedExpressions[$fieldName]);
|
|
unset($this->expressions[$fieldName]);
|
|
$this->regenerate();
|
|
}
|
|
|
|
private function regenerate(){
|
|
$this->vars = array_keys($this->expressions);
|
|
|
|
$dyn_vars = array();
|
|
foreach($this->expressions as $var => $expr){
|
|
$syms = array_keys($this->getSyms($expr));
|
|
$dyn_vars[$var] = $syms;
|
|
}
|
|
|
|
$independents = array_keys(array_filter($dyn_vars, array($this, 'isEmpty')));
|
|
$arr = array();
|
|
foreach($dyn_vars as $var => $parents){
|
|
foreach($parents as $parent){
|
|
$arr[$parent][$var] = $var;
|
|
}
|
|
}
|
|
$dependents = array_map('array_keys', $arr);
|
|
$this->independents = $independents;
|
|
$this->dependents = $dependents;
|
|
$this->checkForCycles();
|
|
}
|
|
|
|
|
|
/**
|
|
* Evaluate the expressions using the data in the array
|
|
* provided.
|
|
*
|
|
* @param $data An array of bound variables containing the
|
|
* the variable name as the key and the value as data.
|
|
* @return An array containing the variables bound to the expressions
|
|
* and their values.
|
|
*/
|
|
function evaluate($data){
|
|
$env = new VTExpressionEngineEnv($data);
|
|
foreach($this->expressions as $var=>$expr){
|
|
$env->bind($var, $expr);
|
|
}
|
|
|
|
$out = array();
|
|
foreach($this->vars as $var){
|
|
$out[$var] = $env->get($var);
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
private function checkForCycles(){
|
|
foreach($this->independents as $independent){
|
|
$this->testCall($independent, array(), sizeof($this->dependents));
|
|
}
|
|
}
|
|
|
|
private function testCall($cur, $stack, $n){
|
|
if($n<0){
|
|
throw new VTExpressionException('There appears to be a loop in ('.implode(", ", $stack).')');
|
|
}
|
|
$dependents = $this->dependents;
|
|
if(array_key_exists($cur, $dependents)){
|
|
foreach($dependents[$cur] as $var){
|
|
$this->testCall($var, array_merge($stack, array($cur)), $n-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function getSyms($expr){
|
|
if($expr instanceof VTTreeNode){
|
|
$params = $expr->getParams();
|
|
if(sizeof($params)!=0){
|
|
$sym_arr = array_map(array($this, "getSyms"), $params);
|
|
if(sizeof($sym_arr)>1){
|
|
$syms = call_user_func_array('array_merge', $sym_arr);
|
|
|
|
}else{
|
|
$syms = $sym_arr[0];
|
|
}
|
|
}else{
|
|
$syms = array();
|
|
}
|
|
return $syms;
|
|
}else if($expr instanceof Symbol && in_array($expr->value, $this->vars)){
|
|
return array($expr->value=>$expr);
|
|
}else{
|
|
return array();
|
|
}
|
|
}
|
|
|
|
private function loadExpression($var, $expr){
|
|
$this->expressions[$var] = $value;
|
|
}
|
|
|
|
private function parse($str){
|
|
$parser = new VTParser(new SpaceFilter(new VTTokenizer($str)));
|
|
return $parser->expression();
|
|
}
|
|
|
|
private function isEmpty($arr){
|
|
return sizeof($arr)!=0;
|
|
}
|
|
|
|
private function expression($str){
|
|
$parser = new VTParser(new SpaceFilter(new VTTokenizer($str)));
|
|
return $parser->expression();
|
|
}
|
|
}
|
|
?>
|