formula changes

This commit is contained in:
Yuri Kuznetsov
2020-07-15 14:34:05 +03:00
parent f4b2d9b1df
commit bbe96f67f6
21 changed files with 322 additions and 163 deletions
@@ -0,0 +1,34 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2020 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Formula\Exceptions;
class BadArgumentValue extends Error
{
}
@@ -29,23 +29,33 @@
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class AtType extends \Espo\Core\Formula\Functions\Base
class AtType extends BaseFunction
{
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
$args = $this->fetchArguments($item);
if (count($args) < 2) throw new Error("Formula: array\\at: Not enough arguments.");
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$array = $args[0];
$index = $args[1];
if (!is_array($array)) throw new Error("Formula: array\\at: First argument must be array.");
if (!is_int($index)) throw new Error("Formula: array\\at: Second argument must be integer.");
if (!is_array($array)) {
$this->throwBadArgumentType(1, 'array');
}
if (!is_int($index)) {
$this->throwBadArgumentType(2, 'int');
}
if (!array_key_exists($index, $array)) {
$GLOBALS['log']->notice("Formula: array\\at: Index doesn't exist.");
$this->log("index doesn't exist");
return null;
}
@@ -29,17 +29,23 @@
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class IncludesType extends \Espo\Core\Formula\Functions\Base
class IncludesType extends BaseFunction
{
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 2) {
throw new Error('Too few arguments passed to \'array\\includes\'.');
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($item->value[0]);
$needle = $this->evaluate($item->value[1]);
$list = $args[0];
$needle = $args[1];
if (!is_array($list)) {
return false;
@@ -29,17 +29,22 @@
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class LengthType extends \Espo\Core\Formula\Functions\Base
class LengthType extends BaseFunction
{
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error('No argument passed to \'array\\length\'.');
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($item->value[0]);
$list = $args[0];
if (!is_array($list)) {
return 0;
@@ -29,23 +29,28 @@
namespace Espo\Core\Formula\Functions\ArrayGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class PushType extends \Espo\Core\Formula\Functions\Base
class PushType extends BaseFunction
{
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 2) {
throw new Error('Too few arguments passed to \'Array\\push\'.');
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$list = $this->evaluate($item->value[0]);
$list = $this->evaluate($args[0]);
if (!is_array($list)) {
return false;
}
foreach ($item->value as $i => $v) {
foreach ($args as $i => $v) {
if ($i === 0) continue;
$element = $this->evaluate($item->value[$i]);
$element = $this->evaluate($args[$i]);
$list[] = $element;
}
@@ -38,6 +38,7 @@ use Espo\Core\Formula\{
Evaluatable,
Exceptions\TooFewArguments,
Exceptions\BadArgumentType,
Exceptions\BadArgumentValue,
Exceptions\NotPassedEntity,
};
@@ -98,11 +99,17 @@ abstract class BaseFunction
return $this->processor->process($item);
}
/**
* Throw TooFewArguments exception.
*/
protected function throwTooFewArguments()
{
throw new TooFewArguments('function: ' . $this->name);
}
/**
* Throw BadArgumentType exception.
*/
protected function throwBadArgumentType(?int $index = null, ?string $type = null)
{
$msg = 'function: ' . $this->name;
@@ -115,10 +122,38 @@ abstract class BaseFunction
throw new BadArgumentType($msg);
}
/**
* Throw BadArgumentValue exception.
*/
protected function throwBadArgumentValue(?int $index = null, ?string $msg = null)
{
$string = $msg;
$msg = 'function: ' . $this->name;
if ($index !== null) {
$msg .= ', index: ' . $index;
if ($msg) {
$msg .= ', ' . $string;
}
}
throw new BadArgumentValue($msg);
}
/**
* Log a bad argument type.
*/
protected function logBadArgumentType(int $index, string $type)
{
if (!$this->log) return;
$this->log->warning("Formula function: {$this->name}, argument {$index} should be '{$type}'.");
}
/**
* Log a message.
*/
protected function log(string $msg, string $level = 'notice')
{
if (!$this->log) return;
$this->log->log($level, 'function: ' . $this->name . ', ' . $msg);
}
}
@@ -29,26 +29,21 @@
namespace Espo\Core\Formula\Functions\ComparisonGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
abstract class Base extends \Espo\Core\Formula\Functions\Base
abstract class Base extends BaseFunction
{
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 2) {
throw new Error('Formula comparison function: Too few arguments passed.');
if (count($args) < 2) {
$this->throwTooFewArguments();
}
if (is_object($item->value[0])) {
$left = $this->evaluate($item->value[0]);
} else {
$left = $item->value[0];
}
if (is_object($item->value[1])) {
$right = $this->evaluate($item->value[1]);
} else {
$right = $item->value[1];
}
$left = $this->evaluate($args[0]);
$right = $this->evaluate($args[1]);
return $this->compare($left, $right);
}
@@ -29,8 +29,6 @@
namespace Espo\Core\Formula\Functions\ComparisonGroup;
use Espo\Core\Exceptions\Error;
class EqualsType extends Base
{
protected function compare($left, $right)
@@ -36,4 +36,3 @@ class GreaterThanOrEqualsType extends Base
return $left >= $right;
}
}
@@ -29,36 +29,43 @@
namespace Espo\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
abstract class AddIntervalType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
use DateTime;
abstract class AddIntervalType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
protected $timeOnly = false;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 2) {
throw new Error("Add Interval function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$dateTimeString = $this->evaluate($item->value[0]);
$dateTimeString = $args[0];
if (!$dateTimeString) {
return null;
}
if (!is_string($dateTimeString)) {
throw new Error();
$this->throwBadArgumentType(1, 'string');
}
$interval = $this->evaluate($item->value[1]);
$interval = $args[1];
if (!is_numeric($interval)) {
throw new Error();
$this->throwBadArgumentType(2, 'numeric');
}
$isTime = false;
@@ -72,8 +79,9 @@ abstract class AddIntervalType extends \Espo\Core\Formula\Functions\Base impleme
}
try {
$dateTime = new \DateTime($dateTimeString);
$dateTime = new DateTime($dateTimeString);
} catch (\Exception $e) {
$this->log('bad date-time value passed', 'warning');
return null;
}
@@ -29,36 +29,44 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class ClosestType extends \Espo\Core\Formula\Functions\Base implements Di\ConfigAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
use DateTime;
use DateTimeZone;
class ClosestType extends BaseFunction implements Di\ConfigAware
{
use Di\ConfigSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 3) {
throw new Error("Closest function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 3) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$type = $this->evaluate($item->value[1]);
$target = $this->evaluate($item->value[2]);
$value = $args[0];
$type = $args[1];
$target = $args[2];
if (!in_array($type, ['time', 'minute', 'hour', 'date', 'dayOfWeek', 'month'])) {
throw new Error('Bad TYPE passed to datetime\\closest function.');
$this->throwBadArgumentType(1);
}
$inPast = false;
if (count($item->value) > 3) {
$inPast = $this->evaluate($item->value[3]);
if (count($args) > 3) {
$inPast = $args[3];
}
$timezone = null;
if (count($item->value) > 4) {
$timezone = $this->evaluate($item->value[4]);
if (count($args) > 4) {
$timezone = $args[4];
}
if (!$value) {
@@ -66,7 +74,7 @@ class ClosestType extends \Espo\Core\Formula\Functions\Base implements Di\Config
}
if (!is_string($value)) {
throw new Error('Bad VALUE passed to datetime\\closest function.');
$this->throwBadArgumentType(1, 'string');
}
if (!$timezone) {
@@ -85,12 +93,12 @@ class ClosestType extends \Espo\Core\Formula\Functions\Base implements Di\Config
$format = 'Y-m-d H:i:s';
$dt = \DateTime::createFromFormat($format, $value, new \DateTimeZone($timezone));
$dt = DateTime::createFromFormat($format, $value, new DateTimeZone($timezone));
$valueTimestamp = $dt->getTimestamp();
if ($type === 'time') {
if (!is_string($target)) {
throw new Error('Bad TARGET passed to datetime\\closest function.');
$this->throwBadArgumentType(3, 'string');
}
list($hour, $minute) = explode(':', $target);
if (!$hour) {
@@ -209,7 +217,7 @@ class ClosestType extends \Espo\Core\Formula\Functions\Base implements Di\Config
}
if (!$isDate) {
$dt->setTimezone(new \DateTimeZone('UTC'));
$dt->setTimezone(new DateTimeZone('UTC'));
return $dt->format('Y-m-d H:i');
} else {
return $dt->format('Y-m-d');
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class DateType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class DateType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Date function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return 0;
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class DayOfWeekType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class DayOfWeekType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("DayOfWeek function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return -1;
@@ -29,22 +29,34 @@
namespace Espo\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class DiffType extends \Espo\Core\Formula\Functions\Base
use DateTime;
class DiffType extends BaseFunction
{
protected $intevalTypePropertyMap = [
'years' => 'y', 'months' => 'm', 'days' => 'd', 'hours' => 'h', 'minutes' => 'i', 'seconds' => 's'
'years' => 'y',
'months' => 'm',
'days' => 'd',
'hours' => 'h',
'minutes' => 'i',
'seconds' => 's',
];
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 2) {
throw new Error("Diff function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 2) {
$this->throwTooFewArguments();
}
$dateTime1String = $this->evaluate($item->value[0]);
$dateTime2String = $this->evaluate($item->value[1]);
$dateTime1String = $args[0];
$dateTime2String = $args[1];
if (!$dateTime1String) {
return null;
@@ -55,35 +67,34 @@ class DiffType extends \Espo\Core\Formula\Functions\Base
}
if (!is_string($dateTime1String)) {
throw new Error();
$this->throwBadArgumentType(1, 'string');
}
if (!is_string($dateTime2String)) {
throw new Error();
$this->throwBadArgumentType(2, 'string');
}
$intervalType = 'days';
if (count($item->value) > 2) {
$intervalType = $this->evaluate($item->value[2]);
if (count($args) > 2) {
$intervalType = $args[2];
}
if (!is_string($intervalType)) {
throw new Error();
$this->throwBadArgumentType(3, 'string');
}
if (!array_key_exists($intervalType, $this->intevalTypePropertyMap)) {
throw new Error('Not supported interval type' . $intervalType);
$this->throwBadArgumentValue(3, "not supported interval type '{$intervalType}'");
}
$isTime = false;
if (strlen($dateTime1String) > 10) {
$isTime = true;
}
try {
$dateTime1 = new \DateTime($dateTime1String);
$dateTime2 = new \DateTime($dateTime2String);
$dateTime1 = new DateTime($dateTime1String);
$dateTime2 = new DateTime($dateTime2String);
} catch (\Exception $e) {
return null;
}
@@ -29,29 +29,34 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class FormatType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class FormatType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Format function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$format = null;
if (count($item->value) > 2) {
$format = $this->evaluate($item->value[2]);
if (count($args) > 2) {
$format = $args[2];
}
if (strlen($value) > 11) {
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class HourType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class HourType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Hour function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return -1;
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class MinuteType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class MinuteType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Minute function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return -1;
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class MonthType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class MonthType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Month function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return 0;
@@ -31,11 +31,16 @@ namespace Espo\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
class NowType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class NowType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
return $this->dateTime->getInternalNowString();
}
@@ -31,11 +31,16 @@ namespace Espo\Core\Formula\Functions\DatetimeGroup;
use Espo\Core\Di;
class TodayType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class TodayType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
return $this->dateTime->getInternalTodayString();
}
@@ -29,25 +29,30 @@
namespace Espo\Core\Formula\Functions\DateTimeGroup;
use Espo\Core\Exceptions\Error;
use Espo\Core\Di;
class YearType extends \Espo\Core\Formula\Functions\Base implements Di\DateTimeAware
use Espo\Core\Formula\{
Functions\BaseFunction,
ArgumentList,
};
class YearType extends BaseFunction implements Di\DateTimeAware
{
use Di\DateTimeSetter;
public function process(\StdClass $item)
public function process(ArgumentList $args)
{
if (count($item->value) < 1) {
throw new Error("Year function: Too few arguments.");
$args = $this->evaluate($args);
if (count($args) < 1) {
$this->throwTooFewArguments();
}
$value = $this->evaluate($item->value[0]);
$value = $args[0];
$timezone = null;
if (count($item->value) > 1) {
$timezone = $this->evaluate($item->value[1]);
if (count($args) > 1) {
$timezone = $args[1];
}
if (empty($value)) return 0;