From 50db978fa2cb72d49a7717c0c50944271bf5282a Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 21 Apr 2021 13:57:18 +0300 Subject: [PATCH] cs fix --- .../FieldValidationManager.php | 29 ++-- .../Espo/Core/FieldValidation/Params.php | 10 +- .../Core/FieldValidation/ValidatorFactory.php | 8 +- .../Espo/Core/FileStorage/Attachment.php | 2 +- .../FileStorage/AttachmentEntityWrapper.php | 2 +- application/Espo/Core/FileStorage/Factory.php | 2 +- application/Espo/Core/FileStorage/Local.php | 2 +- application/Espo/Core/FileStorage/Manager.php | 22 +-- application/Espo/Core/FileStorage/Storage.php | 10 +- .../Espo/Core/FileStorage/Storages/AwsS3.php | 10 +- .../FileStorage/Storages/EspoUploadDir.php | 12 +- application/Espo/Core/Formula/Argument.php | 16 +- .../Espo/Core/Formula/ArgumentList.php | 23 ++- .../Espo/Core/Formula/AttributeFetcher.php | 17 +- application/Espo/Core/Formula/Evaluator.php | 6 +- .../Espo/Core/Formula/FunctionFactory.php | 13 +- .../Core/Formula/Functions/BaseFunction.php | 10 +- application/Espo/Core/Formula/Manager.php | 2 + application/Espo/Core/Formula/Parser.php | 164 +++++++++++++----- application/Espo/Core/Formula/Processor.php | 11 +- 20 files changed, 257 insertions(+), 114 deletions(-) diff --git a/application/Espo/Core/FieldValidation/FieldValidationManager.php b/application/Espo/Core/FieldValidation/FieldValidationManager.php index 483e334e0d..ae09be51d9 100644 --- a/application/Espo/Core/FieldValidation/FieldValidationManager.php +++ b/application/Espo/Core/FieldValidation/FieldValidationManager.php @@ -65,11 +65,10 @@ class FieldValidationManager * @param Entity $entity An entity. * @param ?StdClass $data Raw request payload data. * @param ?Params $params Validation additional parameters. - * * @throws BadRequest If data is not valid. */ - public function process(Entity $entity, ?StdClass $data = null, ?Params $params = null) : void + public function process(Entity $entity, ?StdClass $data = null, ?Params $params = null): void { $dataIsSet = $data !== null; @@ -105,7 +104,7 @@ class FieldValidationManager /** * Check a specific field for a specific validation type. */ - public function check(Entity $entity, string $field, string $type, ?StdClass $data = null) : bool + public function check(Entity $entity, string $field, string $type, ?StdClass $data = null): bool { if (!$data) { $data = $data ?? (object) []; @@ -139,7 +138,7 @@ class FieldValidationManager return true; } - private function processField(Entity $entity, string $field, Params $params, StdClass $data) : void + private function processField(Entity $entity, string $field, Params $params, StdClass $data): void { $entityType = $entity->getEntityType(); @@ -168,8 +167,12 @@ class FieldValidationManager } private function processFieldCheck( - string $entityType, string $type, Entity $entity, string $field, $validationValue - ) : bool { + string $entityType, + string $type, + Entity $entity, + string $field, + $validationValue + ): bool { $checker = $this->getFieldTypeChecker($entityType, $field); @@ -187,8 +190,12 @@ class FieldValidationManager } private function processFieldRawCheck( - string $entityType, string $type, StdClass $data, string $field, $validationValue - ) : bool { + string $entityType, + string $type, + StdClass $data, + string $field, + $validationValue + ): bool { $checker = $this->getFieldTypeChecker($entityType, $field); @@ -205,7 +212,7 @@ class FieldValidationManager return $checker->$methodName($data, $field, $validationValue); } - private function getFieldTypeChecker(string $entityType, string $field) : ?object + private function getFieldTypeChecker(string $entityType, string $field): ?object { $key = $entityType . '_' . $field; @@ -216,7 +223,7 @@ class FieldValidationManager return $this->checkerCache[$key]; } - private function loadFieldTypeChecker(string $entityType, string $field) : void + private function loadFieldTypeChecker(string $entityType, string $field): void { $key = $entityType . '_' . $field; @@ -229,7 +236,7 @@ class FieldValidationManager $this->checkerCache[$key] = $this->factory->create($entityType, $field); } - private function isFieldSetInData(string $entityType, string $field, StdClass $data) : bool + private function isFieldSetInData(string $entityType, string $field, StdClass $data): bool { $attributeList = $this->fieldUtil->getActualAttributeList($entityType, $field); diff --git a/application/Espo/Core/FieldValidation/Params.php b/application/Espo/Core/FieldValidation/Params.php index 9385ec674e..103398e3ab 100644 --- a/application/Espo/Core/FieldValidation/Params.php +++ b/application/Espo/Core/FieldValidation/Params.php @@ -44,7 +44,7 @@ class Params * * @return array */ - public function getSkipFieldList() : array + public function getSkipFieldList(): array { return $this->skipFieldList; } @@ -54,7 +54,7 @@ class Params * * @return array */ - public function getTypeSkipFieldList(string $type) : array + public function getTypeSkipFieldList(string $type): array { return $this->typeSkipFieldListData[$type] ?? []; } @@ -64,7 +64,7 @@ class Params * * @param array $list */ - public function withSkipFieldList(array $list) : self + public function withSkipFieldList(array $list): self { $obj = clone $this; @@ -78,7 +78,7 @@ class Params * * @param array $list */ - public function withTypeSkipFieldList(string $type, array $list) : self + public function withTypeSkipFieldList(string $type, array $list): self { $obj = clone $this; @@ -90,7 +90,7 @@ class Params /** * Create an empty instance. */ - public static function fromNothing() : self + public static function fromNothing(): self { return new self(); } diff --git a/application/Espo/Core/FieldValidation/ValidatorFactory.php b/application/Espo/Core/FieldValidation/ValidatorFactory.php index 88bebdfe3d..03b85df05e 100644 --- a/application/Espo/Core/FieldValidation/ValidatorFactory.php +++ b/application/Espo/Core/FieldValidation/ValidatorFactory.php @@ -53,7 +53,7 @@ class ValidatorFactory /** * Whether can be created. */ - public function isCreatable(string $entityType, string $field) : bool + public function isCreatable(string $entityType, string $field): bool { return (bool) $this->getClassName($entityType, $field); } @@ -63,7 +63,7 @@ class ValidatorFactory * * @throws RuntimeException */ - public function create(string $entityType, string $field) : object + public function create(string $entityType, string $field): object { $className = $this->getClassName($entityType, $field); @@ -74,7 +74,7 @@ class ValidatorFactory return $this->injectableFactory->create($className); } - private function getClassName(string $entityType, string $field) : ?string + private function getClassName(string $entityType, string $field): ?string { $key = $entityType . '_' . $field; @@ -85,7 +85,7 @@ class ValidatorFactory return $this->classNameCache[$key]; } - private function getClassNameNoCache(string $entityType, string $field) : ?string + private function getClassNameNoCache(string $entityType, string $field): ?string { $className1 = $this->metadata ->get(['entityDefs', $entityType, 'fields', $field, 'validatorClassName']); diff --git a/application/Espo/Core/FileStorage/Attachment.php b/application/Espo/Core/FileStorage/Attachment.php index 724dcae761..c7039a5f58 100644 --- a/application/Espo/Core/FileStorage/Attachment.php +++ b/application/Espo/Core/FileStorage/Attachment.php @@ -34,5 +34,5 @@ interface Attachment /** * Get a source ID. */ - public function getSourceId() : string; + public function getSourceId(): string; } diff --git a/application/Espo/Core/FileStorage/AttachmentEntityWrapper.php b/application/Espo/Core/FileStorage/AttachmentEntityWrapper.php index 2e38f34fdf..4570cce701 100644 --- a/application/Espo/Core/FileStorage/AttachmentEntityWrapper.php +++ b/application/Espo/Core/FileStorage/AttachmentEntityWrapper.php @@ -46,7 +46,7 @@ class AttachmentEntityWrapper implements Attachment $this->attachment = $attachment; } - public function getSourceId() : string + public function getSourceId(): string { return $this->attachment->getSourceId(); } diff --git a/application/Espo/Core/FileStorage/Factory.php b/application/Espo/Core/FileStorage/Factory.php index df82361aea..134893f7e4 100644 --- a/application/Espo/Core/FileStorage/Factory.php +++ b/application/Espo/Core/FileStorage/Factory.php @@ -48,7 +48,7 @@ class Factory $this->injectableFactory = $injectableFactory; } - public function create(string $name) : Storage + public function create(string $name): Storage { $className = $this->metadata->get(['app', 'fileStorage', 'implementationClassNameMap', $name]); diff --git a/application/Espo/Core/FileStorage/Local.php b/application/Espo/Core/FileStorage/Local.php index 83c276c035..385d765503 100644 --- a/application/Espo/Core/FileStorage/Local.php +++ b/application/Espo/Core/FileStorage/Local.php @@ -37,5 +37,5 @@ interface Local /** * Get a local file path. */ - public function getLocalFilePath(Attachment $attachment) : string; + public function getLocalFilePath(Attachment $attachment): string; } diff --git a/application/Espo/Core/FileStorage/Manager.php b/application/Espo/Core/FileStorage/Manager.php index 92ff154f0a..c48138a777 100644 --- a/application/Espo/Core/FileStorage/Manager.php +++ b/application/Espo/Core/FileStorage/Manager.php @@ -56,7 +56,7 @@ class Manager /** * Whether a file exists in a storage. */ - public function exists(AttachmentEntity $attachment) : bool + public function exists(AttachmentEntity $attachment): bool { $implementation = $this->getImplementation($attachment); @@ -66,7 +66,7 @@ class Manager /** * Get a file size. */ - public function getSize(AttachmentEntity $attachment) : int + public function getSize(AttachmentEntity $attachment): int { $implementation = $this->getImplementation($attachment); @@ -76,7 +76,7 @@ class Manager /** * Get file contents. */ - public function getContents(AttachmentEntity $attachment) : string + public function getContents(AttachmentEntity $attachment): string { $implementation = $this->getImplementation($attachment); @@ -86,7 +86,7 @@ class Manager /** * Get a file contents stream. */ - public function getStream(AttachmentEntity $attachment) : StreamInterface + public function getStream(AttachmentEntity $attachment): StreamInterface { $implementation = $this->getImplementation($attachment); @@ -96,7 +96,7 @@ class Manager /** * Store file contents represented as a stream. */ - public function putStream(AttachmentEntity $attachment, StreamInterface $stream) : void + public function putStream(AttachmentEntity $attachment, StreamInterface $stream): void { $implementation = $this->getImplementation($attachment); @@ -106,7 +106,7 @@ class Manager /** * Store file contents. */ - public function putContents(AttachmentEntity $attachment, string $contents) : void + public function putContents(AttachmentEntity $attachment, string $contents): void { $implementation = $this->getImplementation($attachment); @@ -118,7 +118,7 @@ class Manager /** * Remove a file. */ - public function unlink(AttachmentEntity $attachment) : void + public function unlink(AttachmentEntity $attachment): void { $implementation = $this->getImplementation($attachment); @@ -128,7 +128,7 @@ class Manager /** * Whether an attachment storage is local. */ - public function isLocal(AttachmentEntity $attachment) : bool + public function isLocal(AttachmentEntity $attachment): bool { $implementation = $this->getImplementation($attachment); @@ -138,7 +138,7 @@ class Manager /** * Get a local file path. If a file is not stored locally, a temporary file will be created. */ - public function getLocalFilePath(AttachmentEntity $attachment) : string + public function getLocalFilePath(AttachmentEntity $attachment): string { $implementation = $this->getImplementation($attachment); @@ -160,12 +160,12 @@ class Manager return $path; } - private static function wrapAttachmentEntity(AttachmentEntity $attachment) : AttachmentEntityWrapper + private static function wrapAttachmentEntity(AttachmentEntity $attachment): AttachmentEntityWrapper { return new AttachmentEntityWrapper($attachment); } - private function getImplementation(AttachmentEntity $attachment) : Storage + private function getImplementation(AttachmentEntity $attachment): Storage { $storage = $attachment->getStorage(); diff --git a/application/Espo/Core/FileStorage/Storage.php b/application/Espo/Core/FileStorage/Storage.php index a1b54f9c09..6fa985d8bf 100644 --- a/application/Espo/Core/FileStorage/Storage.php +++ b/application/Espo/Core/FileStorage/Storage.php @@ -39,25 +39,25 @@ interface Storage /** * Get file contents as a stream. */ - public function getStream(Attachment $attachment) : StreamInterface; + public function getStream(Attachment $attachment): StreamInterface; /** * Store file contents. */ - public function putStream(Attachment $attachment, StreamInterface $stream) : void; + public function putStream(Attachment $attachment, StreamInterface $stream): void; /** * Whether a file exists. */ - public function exists(Attachment $attachment) : bool; + public function exists(Attachment $attachment): bool; /** * Delete a file. */ - public function unlink(Attachment $attachment) : void; + public function unlink(Attachment $attachment): void; /** * Get a file size. */ - public function getSize(Attachment $attachment) : int; + public function getSize(Attachment $attachment): int; } diff --git a/application/Espo/Core/FileStorage/Storages/AwsS3.php b/application/Espo/Core/FileStorage/Storages/AwsS3.php index 71116193be..d99f7d08cd 100644 --- a/application/Espo/Core/FileStorage/Storages/AwsS3.php +++ b/application/Espo/Core/FileStorage/Storages/AwsS3.php @@ -80,29 +80,29 @@ class AwsS3 implements Storage $this->filesystem = new Filesystem($adapter); } - public function unlink(Attachment $attachment) : void + public function unlink(Attachment $attachment): void { $this->filesystem->delete($attachment->getSourceId()); } - public function exists(Attachment $attachment) : bool + public function exists(Attachment $attachment): bool { return $this->filesystem->fileExists($attachment->getSourceId()); } - public function getSize(Attachment $attachment) : int + public function getSize(Attachment $attachment): int { return $this->filesystem->fileSize($attachment->getSourceId()); } - public function getStream(Attachment $attachment) : StreamInterface + public function getStream(Attachment $attachment): StreamInterface { $resource = $this->filesystem->readStream($attachment->getSourceId()); return new Stream($resource); } - public function putStream(Attachment $attachment, StreamInterface $stream) : void + public function putStream(Attachment $attachment, StreamInterface $stream): void { // League\Flysystem does not support StreamInterface. // Need to pass a resource. diff --git a/application/Espo/Core/FileStorage/Storages/EspoUploadDir.php b/application/Espo/Core/FileStorage/Storages/EspoUploadDir.php index b4e2aa48e6..8e4d2707b9 100644 --- a/application/Espo/Core/FileStorage/Storages/EspoUploadDir.php +++ b/application/Espo/Core/FileStorage/Storages/EspoUploadDir.php @@ -50,21 +50,21 @@ class EspoUploadDir implements Storage, Local $this->fileManager = $fileManager; } - public function unlink(Attachment $attachment) : void + public function unlink(Attachment $attachment): void { $this->fileManager->unlink( $this->getFilePath($attachment) ); } - public function exists(Attachment $attachment) : bool + public function exists(Attachment $attachment): bool { $filePath = $this->getFilePath($attachment); return $this->fileManager->isFile($filePath); } - public function getSize(Attachment $attachment) : int + public function getSize(Attachment $attachment): int { $filePath = $this->getFilePath($attachment); @@ -75,7 +75,7 @@ class EspoUploadDir implements Storage, Local return filesize($filePath); } - public function getStream(Attachment $attachment) : StreamInterface + public function getStream(Attachment $attachment): StreamInterface { $filePath = $this->getFilePath($attachment); @@ -88,7 +88,7 @@ class EspoUploadDir implements Storage, Local return new Stream($resouce); } - public function putStream(Attachment $attachment, StreamInterface $stream) : void + public function putStream(Attachment $attachment, StreamInterface $stream): void { $filePath = $this->getFilePath($attachment); @@ -101,7 +101,7 @@ class EspoUploadDir implements Storage, Local } } - public function getLocalFilePath(Attachment $attachment) : string + public function getLocalFilePath(Attachment $attachment): string { return $this->getFilePath($attachment); } diff --git a/application/Espo/Core/Formula/Argument.php b/application/Espo/Core/Formula/Argument.php index 4ec77bfce9..b7c5d861f3 100644 --- a/application/Espo/Core/Formula/Argument.php +++ b/application/Espo/Core/Formula/Argument.php @@ -46,9 +46,11 @@ class Argument implements Evaluatable /** * Get an argument type (function name). */ - public function getType() : ?string + public function getType(): ?string { - if (!is_object($this->data)) return null; + if (!is_object($this->data)) { + return null; + } return $this->data->type ?? null; } @@ -56,7 +58,7 @@ class Argument implements Evaluatable /** * Get a nested argument list. */ - public function getArgumentList() : ArgumentList + public function getArgumentList(): ArgumentList { if (!is_object($this->data)) { throw new Error("Can't get an argument list from a scalar value item."); @@ -64,9 +66,11 @@ class Argument implements Evaluatable if (!property_exists($this->data, 'value')) { $argumentList = new ArgumentList([]); - } else if (is_array($this->data->value)) { + } + else if (is_array($this->data->value)) { $argumentList = new ArgumentList($this->data->value); - } else { + } + else { $argumentList = new ArgumentList([$this->data->value]); } @@ -75,6 +79,8 @@ class Argument implements Evaluatable /** * Get RAW data. + * + * @return mixed */ public function getData() { diff --git a/application/Espo/Core/Formula/ArgumentList.php b/application/Espo/Core/Formula/ArgumentList.php index 9bc1233ee2..96677ecf35 100644 --- a/application/Espo/Core/Formula/ArgumentList.php +++ b/application/Espo/Core/Formula/ArgumentList.php @@ -29,10 +29,17 @@ namespace Espo\Core\Formula; +use BadMethodCallException; +use OutOfBoundsException; +use Iterator; +use Countable; +use ArrayAccess; +use SeekableIterator; + /** * A list of function arguments. */ -class ArgumentList implements Evaluatable, \Iterator, \Countable, \ArrayAccess, \SeekableIterator +class ArgumentList implements Evaluatable, Iterator, Countable, ArrayAccess, SeekableIterator { protected $dataList; @@ -46,25 +53,30 @@ class ArgumentList implements Evaluatable, \Iterator, \Countable, \ArrayAccess, private function getLastValidKey() { $keys = array_keys($this->dataList); + $i = end($keys); + while ($i > 0) { if (array_key_exists($i, $this->dataList)) { break; } + $i--; } + return $i; } public function rewind() { $this->position = 0; + while (!$this->valid() && $this->position <= $this->getLastValidKey()) { $this->position ++; } } - protected function getArgumentByIndex(int $index) : Argument + private function getArgumentByIndex(int $index): Argument { return new Argument($this->dataList[$index]); } @@ -110,12 +122,12 @@ class ArgumentList implements Evaluatable, \Iterator, \Countable, \ArrayAccess, public function offsetSet($offset, $value) { - throw new \BadMethodCallException('Setting is not allowed.'); + throw new BadMethodCallException('Setting is not allowed.'); } public function offsetUnset($offset) { - throw new \BadMethodCallException('Unsetting is not allowed.'); + throw new BadMethodCallException('Unsetting is not allowed.'); } public function count() : int @@ -126,8 +138,9 @@ class ArgumentList implements Evaluatable, \Iterator, \Countable, \ArrayAccess, public function seek($offset) { $this->position = $offset; + if (!$this->valid()) { - throw new \OutOfBoundsException("Invalid seek offset ($offset)."); + throw new OutOfBoundsException("Invalid seek offset ($offset)."); } } } diff --git a/application/Espo/Core/Formula/AttributeFetcher.php b/application/Espo/Core/Formula/AttributeFetcher.php index 8447d91d88..9e7e31d0a5 100644 --- a/application/Espo/Core/Formula/AttributeFetcher.php +++ b/application/Espo/Core/Formula/AttributeFetcher.php @@ -38,34 +38,45 @@ class AttributeFetcher { private $relatedEntitiesCacheMap = []; + /** + * @return mixed + */ public function fetch(Entity $entity, string $attribute, bool $getFetchedAttribute = false) { if (strpos($attribute, '.') !== false) { $arr = explode('.', $attribute); $key = $this->buildKey($entity, $arr[0]); + if (!array_key_exists($key, $this->relatedEntitiesCacheMap)) { $this->relatedEntitiesCacheMap[$key] = $entity->get($arr[0]); } + $relatedEntity = $this->relatedEntitiesCacheMap[$key]; + if ($relatedEntity && ($relatedEntity instanceof Entity) && count($arr) > 0) { return $this->fetch($relatedEntity, $arr[1]); } + return null; } $methodName = 'get'; + if ($getFetchedAttribute) { $methodName = 'getFetched'; } if ($entity->getAttributeParam($attribute, 'isParentName') && $methodName == 'get') { $relationName = $entity->getAttributeParam($attribute, 'relation'); + if ($parent = $entity->get($relationName)) { return $parent->get('name'); } - } else if ($entity->getAttributeParam($attribute, 'isLinkMultipleIdList') && $methodName == 'get') { + } + else if ($entity->getAttributeParam($attribute, 'isLinkMultipleIdList') && $methodName == 'get') { $relationName = $entity->getAttributeParam($attribute, 'relation'); + if (!$entity->has($attribute)) { $entity->loadLinkMultipleField($relationName); } @@ -74,12 +85,12 @@ class AttributeFetcher return $entity->$methodName($attribute); } - public function resetRuntimeCache() + public function resetRuntimeCache(): void { $this->relatedEntitiesCacheMap = []; } - protected function buildKey(Entity $entity, string $link) : string + private function buildKey(Entity $entity, string $link): string { return spl_object_hash($entity) . '-' . $link; } diff --git a/application/Espo/Core/Formula/Evaluator.php b/application/Espo/Core/Formula/Evaluator.php index f019a3ca9c..8da9e74997 100644 --- a/application/Espo/Core/Formula/Evaluator.php +++ b/application/Espo/Core/Formula/Evaluator.php @@ -61,9 +61,13 @@ class Evaluator $this->functionClassNameMap = $functionClassNameMap; $this->parser = new Parser(); + $this->parsedHash = []; } + /** + * @return mixed + */ public function process(string $expression, ?Entity $entity = null, ?StdClass $variables = null) { $this->processor = new Processor( @@ -79,7 +83,7 @@ class Evaluator return $result; } - private function getParsedExpression(string $expression) : Argument + private function getParsedExpression(string $expression): Argument { if (!array_key_exists($expression, $this->parsedHash)) { $this->parsedHash[$expression] = $this->parser->parse($expression); diff --git a/application/Espo/Core/Formula/FunctionFactory.php b/application/Espo/Core/Formula/FunctionFactory.php index 57b580db2e..2323d7c0e9 100644 --- a/application/Espo/Core/Formula/FunctionFactory.php +++ b/application/Espo/Core/Formula/FunctionFactory.php @@ -59,19 +59,23 @@ class FunctionFactory $this->classNameMap = $classNameMap; } - public function create(string $name, ?Entity $entity = null, ?StdClass $variables = null) + public function create(string $name, ?Entity $entity = null, ?StdClass $variables = null): object { if ($this->classNameMap && array_key_exists($name, $this->classNameMap)) { $className = $this->classNameMap[$name]; - } else { + } + else { $arr = explode('\\', $name); + foreach ($arr as $i => $part) { if ($i < count($arr) - 1) { $part = $part . 'Group'; } $arr[$i] = ucfirst($part); } + $typeName = implode('\\', $arr); + $className = 'Espo\\Core\\Formula\\Functions\\' . $typeName . 'Type'; } @@ -87,7 +91,10 @@ class FunctionFactory 'attributeFetcher' => $this->attributeFetcher, ]); - if (property_exists($className, 'hasAttributeFetcher') || method_exists($className, 'setAttributeFetcher')) { + if ( + property_exists($className, 'hasAttributeFetcher') || + method_exists($className, 'setAttributeFetcher') + ) { $object->setAttributeFetcher($this->attributeFetcher); } diff --git a/application/Espo/Core/Formula/Functions/BaseFunction.php b/application/Espo/Core/Formula/Functions/BaseFunction.php index fbe983b389..434bd23455 100644 --- a/application/Espo/Core/Formula/Functions/BaseFunction.php +++ b/application/Espo/Core/Formula/Functions/BaseFunction.php @@ -59,12 +59,12 @@ abstract class BaseFunction protected $log; - protected function getVariables() : StdClass + protected function getVariables(): StdClass { return $this->variables; } - protected function getEntity() : Entity + protected function getEntity(): Entity { if (!$this->entity) { throw new NotPassedEntity('function: ' . $this->name); @@ -74,7 +74,11 @@ abstract class BaseFunction } public function __construct( - string $name, Processor $processor, ?Entity $entity = null, ?StdClass $variables = null, ?Log $log = null + string $name, + Processor $processor, + ?Entity $entity = null, + ?StdClass $variables = null, + ?Log $log = null ) { $this->name = $name; $this->processor = $processor; diff --git a/application/Espo/Core/Formula/Manager.php b/application/Espo/Core/Formula/Manager.php index 01c2ade91a..94470f7010 100644 --- a/application/Espo/Core/Formula/Manager.php +++ b/application/Espo/Core/Formula/Manager.php @@ -52,6 +52,8 @@ class Manager /** * Executes a script and returns its result. + * + * @return mixed */ public function run(string $script, ?Entity $entity = null, ?StdClass $variables = null) { diff --git a/application/Espo/Core/Formula/Parser.php b/application/Espo/Core/Formula/Parser.php index c75cf85994..8b219a7bb8 100644 --- a/application/Espo/Core/Formula/Parser.php +++ b/application/Espo/Core/Formula/Parser.php @@ -38,7 +38,7 @@ use StdClass; */ class Parser { - protected $priorityList = [ + private $priorityList = [ ['='], ['||'], ['&&'], @@ -47,7 +47,7 @@ class Parser ['*', '/', '%'], ]; - protected $operatorMap = [ + private $operatorMap = [ '=' => 'assign', '||' => 'logical\\or', '&&' => 'logical\\and', @@ -64,12 +64,12 @@ class Parser '<=' => 'comparison\\lessThanOrEquals', ]; - public function parse(string $expression) : StdClass + public function parse(string $expression): StdClass { return $this->split($expression); } - protected function applyOperator(string $operator, string $firstPart, string $secondPart) : StdClass + private function applyOperator(string $operator, string $firstPart, string $secondPart): StdClass { if ($operator === '=') { if (strlen($firstPart)) { @@ -97,6 +97,7 @@ class Parser ] ]; } + throw new SyntaxError("Bad operator usage."); } @@ -111,9 +112,13 @@ class Parser ]; } - protected function processStrings( - string &$string, string &$modifiedString, ?array &$splitterIndexList = null, bool $intoOneLine = false - ) { + private function processStrings( + string &$string, + string &$modifiedString, + ?array &$splitterIndexList = null, + bool $intoOneLine = false + ): bool { + $isString = false; $isSingleQuote = false; $isComment = false; @@ -153,12 +158,13 @@ class Parser if ($isString) { if ($string[$i] === '(' || $string[$i] === ')') { $modifiedString[$i] = '_'; - } else if (!$isStringStart) { + } + else if (!$isStringStart) { $modifiedString[$i] = ' '; } - } else { + } + else { if (!$isLineComment && !$isComment) { - if (!$isComment) { if ($i && $string[$i] === '/' && $string[$i - 1] === '/') { $isLineComment = true; @@ -174,6 +180,7 @@ class Parser if ($string[$i] === '(') { $braceCounter++; } + if ($string[$i] === ')') { $braceCounter--; } @@ -184,6 +191,7 @@ class Parser $splitterIndexList[] = $i; } } + if ($intoOneLine) { if ($string[$i] === "\r" || $string[$i] === "\n" || $string[$i] === "\t") { $string[$i] = ' '; @@ -209,7 +217,7 @@ class Parser return $isString; } - protected function split(string $expression) : StdClass + private function split(string $expression): StdClass { $expression = trim($expression); @@ -237,24 +245,34 @@ class Parser if ($modifiedExpression[$i] === '(') { $braceCounter++; } + if ($modifiedExpression[$i] === ')') { $braceCounter--; } + if ($braceCounter === 0 && $i < strlen($modifiedExpression) - 1) { $hasExcessBraces = false; } + if ($braceCounter === 0) { $expressionOutOfBraceList[] = true; } else { $expressionOutOfBraceList[] = false; } } + if ($braceCounter !== 0) { throw new SyntaxError('Incorrect round brackets in expression ' . $expression . '.'); } - if (strlen($expression) > 1 && $expression[0] === '(' && $expression[strlen($expression) - 1] === ')' && $hasExcessBraces) { + if ( + strlen($expression) > 1 && + $expression[0] === '(' && + $expression[strlen($expression) - 1] === ')' && + $hasExcessBraces + ) { $expression = substr($expression, 1, strlen($expression) - 2); + return $this->split($expression); } @@ -262,14 +280,25 @@ class Parser if ($expression[strlen($expression) - 1] !== ';') { $splitterIndexList[] = strlen($expression); } + $parsedPartList = []; + for ($i = 0; $i < count($splitterIndexList); $i++) { if ($i > 0) { $previousSplitterIndex = $splitterIndexList[$i - 1] + 1; - } else { + } + else { $previousSplitterIndex = 0; } - $part = trim(substr($expression, $previousSplitterIndex, $splitterIndexList[$i] - $previousSplitterIndex)); + + $part = trim( + substr( + $expression, + $previousSplitterIndex, + $splitterIndexList[$i] - $previousSplitterIndex + ) + ); + $parsedPartList[] = $this->parse($part); } return (object) [ @@ -281,44 +310,62 @@ class Parser $firstOperator = null; $minIndex = null; - if ($expression === '') return (object) [ - 'type' => 'value', - 'value' => null, - ]; + if ($expression === '') { + return (object) [ + 'type' => 'value', + 'value' => null, + ]; + } foreach ($this->priorityList as $operationList) { foreach ($operationList as $operator) { $startFrom = 1; + while (true) { $index = strpos($expression, $operator, $startFrom); - if ($index === false) break; - if ($expressionOutOfBraceList[$index]) break; + + if ($index === false) { + break; + } + + if ($expressionOutOfBraceList[$index]) { + break; + } + $startFrom = $index + 1; } if ($index !== false) { $possibleRightOperator = null; + if (strlen($operator) === 1) { if ($index < strlen($expression) - 1) { $possibleRightOperator = trim($operator . $expression[$index + 1]); } } + if ( $possibleRightOperator && $possibleRightOperator != $operator && !empty($this->operatorMap[$possibleRightOperator]) - ) continue; + ) { + continue; + } $possibleLeftOperator = null; + if (strlen($operator) === 1) { if ($index > 0) { $possibleLeftOperator = trim($expression[$index - 1] . $operator); } } + if ( $possibleLeftOperator && $possibleLeftOperator != $operator && !empty($this->operatorMap[$possibleLeftOperator]) - ) continue; + ) { + continue; + } $firstPart = substr($expression, 0, $index); $secondPart = substr($expression, $index + strlen($operator)); @@ -326,6 +373,7 @@ class Parser $modifiedFirstPart = $modifiedSecondPart = ''; $isString = $this->processStrings($firstPart, $modifiedFirstPart); + $this->processStrings($secondPart, $modifiedSecondPart); if ( @@ -337,14 +385,18 @@ class Parser ) { if ($minIndex === null) { $minIndex = $index; + $firstOperator = $operator; - } else if ($index < $minIndex) { + } + else if ($index < $minIndex) { $minIndex = $index; + $firstOperator = $operator; } } } } + if ($firstOperator) { break; } @@ -358,7 +410,8 @@ class Parser $secondPart = trim($secondPart); return $this->applyOperator($firstOperator, $firstPart, $secondPart); - } else { + } + else { $expression = trim($expression); if ($expression[0] === '!') { @@ -420,22 +473,25 @@ class Parser if ($expression === 'true') { return (object) [ 'type' => 'value', - 'value' => true + 'value' => true, ]; - } else if ($expression === 'false') { + } + else if ($expression === 'false') { return (object) [ 'type' => 'value', - 'value' => false + 'value' => false, ]; - } else if ($expression === 'null') { + } + else if ($expression === 'null') { return (object) [ 'type' => 'value', - 'value' => null + 'value' => null, ]; } if ($expression[strlen($expression) - 1] === ')') { $firstOpeningBraceIndex = strpos($expression, '('); + if ($firstOpeningBraceIndex > 0) { $functionName = trim(substr($expression, 0, $firstOpeningBraceIndex)); $functionContent = substr($expression, $firstOpeningBraceIndex + 1, -1); @@ -443,13 +499,14 @@ class Parser $argumentList = $this->parseArgumentListFromFunctionContent($functionContent); $argumentSplitedList = []; + foreach ($argumentList as $argument) { $argumentSplitedList[] = $this->split($argument); } return (object) [ 'type' => $functionName, - 'value' => $argumentSplitedList + 'value' => $argumentSplitedList, ]; } } @@ -461,21 +518,28 @@ class Parser } } - protected function stripComments(string &$expression, string &$modifiedExpression) + private function stripComments(string &$expression, string &$modifiedExpression): void { $commentIndexStart = null; for ($i = 0; $i < strlen($modifiedExpression); $i++) { if (is_null($commentIndexStart)) { - if ($modifiedExpression[$i] === '/' && $i < strlen($modifiedExpression) - 1 && $modifiedExpression[$i + 1] === '/') { + if ( + $modifiedExpression[$i] === '/' && + $i < strlen($modifiedExpression) - 1 && + $modifiedExpression[$i + 1] === '/' + ) { $commentIndexStart = $i; } - } else { + } + else { if ($modifiedExpression[$i] === "\n" || $i === strlen($modifiedExpression) - 1) { for ($j = $commentIndexStart; $j <= $i; $j++) { $modifiedExpression[$j] = ' '; + $expression[$j] = ' '; } + $commentIndexStart = null; } } @@ -486,19 +550,22 @@ class Parser if ($modifiedExpression[$i] === '/' && $modifiedExpression[$i + 1] === '*') { $commentIndexStart = $i; } - } else { + } + else { if ($modifiedExpression[$i] === '*' && $modifiedExpression[$i + 1] === '/') { for ($j = $commentIndexStart; $j <= $i + 1; $j++) { $modifiedExpression[$j] = ' '; + $expression[$j] = ' '; } + $commentIndexStart = null; } } } } - protected function parseArgumentListFromFunctionContent(string $functionContent) : array + private function parseArgumentListFromFunctionContent(string $functionContent): array { $functionContent = trim($functionContent); @@ -511,21 +578,25 @@ class Parser $commaIndexList = []; $braceCounter = 0; + for ($i = 0; $i < strlen($functionContent); $i++) { if ($functionContent[$i] === "'" && ($i === 0 || $functionContent[$i - 1] !== "\\")) { if (!$isString) { $isString = true; $isSingleQuote = true; - } else { + } + else { if ($isSingleQuote) { $isString = false; } } - } else if ($functionContent[$i] === "\"" && ($i === 0 || $functionContent[$i - 1] !== "\\")) { + } + else if ($functionContent[$i] === "\"" && ($i === 0 || $functionContent[$i - 1] !== "\\")) { if (!$isString) { $isString = true; $isSingleQuote = false; - } else { + } + else { if (!$isSingleQuote) { $isString = false; } @@ -535,7 +606,8 @@ class Parser if (!$isString) { if ($functionContent[$i] === '(') { $braceCounter++; - } else if ($functionContent[$i] === ')') { + } + else if ($functionContent[$i] === ')') { $braceCounter--; } } @@ -548,13 +620,23 @@ class Parser $commaIndexList[] = strlen($functionContent); $argumentList = []; + for ($i = 0; $i < count($commaIndexList); $i++) { if ($i > 0) { $previousCommaIndex = $commaIndexList[$i - 1] + 1; - } else { + } + else { $previousCommaIndex = 0; } - $argument = trim(substr($functionContent, $previousCommaIndex, $commaIndexList[$i] - $previousCommaIndex)); + + $argument = trim( + substr( + $functionContent, + $previousCommaIndex, + $commaIndexList[$i] - $previousCommaIndex + ) + ); + $argumentList[] = $argument; } diff --git a/application/Espo/Core/Formula/Processor.php b/application/Espo/Core/Formula/Processor.php index 5da727a2c0..f629c3e25b 100644 --- a/application/Espo/Core/Formula/Processor.php +++ b/application/Espo/Core/Formula/Processor.php @@ -57,7 +57,12 @@ class Processor ?Entity $entity = null, ?StdClass $variables = null ) { - $this->functionFactory = new FunctionFactory($this, $injectableFactory, $attributeFetcher, $functionClassNameMap); + $this->functionFactory = new FunctionFactory( + $this, + $injectableFactory, + $attributeFetcher, + $functionClassNameMap + ); $this->entity = $entity; $this->variables = $variables ?? (object) []; @@ -88,12 +93,14 @@ class Processor return $function->process($item->getArgumentList()); } - private function processList(ArgumentList $args) : array + private function processList(ArgumentList $args): array { $list = []; + foreach ($args as $item) { $list[] = $this->process($item); } + return $list; } }