<?php
 namespace App\Reports\Sources; class CustomColumnFormulaResult { private array $formula; private array $reportRow; public function __construct (array $formula, array $reportRow) { $this->formula = $formula; $this->reportRow = $reportRow; } public function getResult() :?string { switch ($this->formula['type']) { case 'math': { $calculation = $this->formula['calculation']; foreach($this->reportRow as $col=>$val) { $calculation = str_replace('%'.$col.'%', '('.(Float)$val.')', $calculation); } if (strpos($calculation, '%')!==false) { throw new \App\Reports\Sources\CustomColumnFormulaException('Invalid math formula - there should be no `%` char in math calculation after replacing values. Formula: '.$this->formula['calculation']); } $mathResult = null; try { if (eval("\$mathResult = $calculation;")!==null) { throw new \App\Reports\Sources\CustomColumnFormulaException('Invalid math formula during eval(). Formula: '.$this->formula['calculation']); } } catch(\DivisionByZeroError $e) { return null; } catch (\ParseError $e) { throw new \App\Reports\Sources\CustomColumnFormulaException('Invalid math formula during eval() - parse error: '.$e->getMessage().'. Formula: '.$this->formula['calculation']); } return $mathResult; } case 'entity': { $class = "\\App\\ReportsData\\EntityFields\\".$this->formula['entity']; $idEntity = $this->reportRow[$this->formula['id']]; if (!$idEntity) { return null; } $entObj = new $class($idEntity); $attributes = explode('.', $this->formula['attribute']); $result = $entObj; foreach($attributes as $attr) { if ( !($result instanceof \App\ReportsData\EntityFields\GeneralAbstract) ) { throw new \App\Reports\Sources\CustomColumnFormulaException( sprintf('Invalid formula for entity custom column on class type; entity=`%s`, id=`%s`, attr=`%s`.', $this->formula['entity'], $this->formula['id'], $this->formula['attribute']) ); } try { $result = $result->getValue($attr); } catch (\Exception $e) { throw new \App\Reports\Sources\CustomColumnFormulaException( sprintf('Invalid formula for entity custom column on attribute access; entity=`%s`, id=`%s`, attr=`%s`. Error: %s', $this->formula['entity'], $this->formula['id'], $this->formula['attribute'], $e->getMessage()) ); } } if (is_object($result)) { throw new \App\Reports\Sources\CustomColumnFormulaException( sprintf('Invalid formula for entity custom column on last attribute (must be scalar value); entity=`%s`, id=`%s`, attr=`%s`.', $this->formula['entity'], $this->formula['id'], $this->formula['attribute']) ); } return $result; } case 'arrayValue': { $sourceColumnCode = $this->formula['source']; $sourceColumnValueSer = $this->reportRow[$sourceColumnCode]; if (!$sourceColumnValueSer) { return null; } $sourceColumnValue = json_decode($sourceColumnValueSer, true); if (!is_array($sourceColumnValue)) { throw new \App\Reports\Sources\CustomColumnFormulaException( sprintf('Invalid formula for arrayValue custom column on source field; source=`%s`,  key=`%s`.', $this->formula['source'], $this->formula['key']) ); } $keys = explode('.', $this->formula['key']); $ok = true; $traversedArray = $sourceColumnValue; foreach ($keys as $key) { if (!is_array($traversedArray) or !key_exists($key, $traversedArray)) { $ok = false; break; } $traversedArray = $traversedArray[$key]; } if (!$ok) { return null; } if (!is_scalar($traversedArray)) { return null; } return $traversedArray; } default: { throw new \App\Reports\Sources\CustomColumnFormulaException("Invalid formula type `{$this->formula['type']}`. Formula details: " . var_export($this->formula, true)); } } } }