From daddee7806045e06d21c4313e7273f40a2f8f6c6 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Wed, 6 Mar 2024 17:50:16 -0500 Subject: [PATCH 01/16] feat: support action expression parsing, validation, and evaluation --- .../Models/Rule/ExpressionController.php | 89 +++++ .../Models/Rule/ValidateExpressionRequest.php | 46 +++ .../Expressions/ActionExpressionEvaluator.php | 95 ++++++ .../ActionExpressionLanguageProvider.php | 37 +++ .../Factory/ActionFactory.php | 8 +- .../Factory/ExpressionLanguageFactory.php | 45 +++ composer.json | 1 + composer.lock | 312 +++++++++++++++++- routes/api.php | 2 + 9 files changed, 633 insertions(+), 2 deletions(-) create mode 100644 app/Api/V1/Controllers/Models/Rule/ExpressionController.php create mode 100644 app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php create mode 100644 app/TransactionRules/Expressions/ActionExpressionEvaluator.php create mode 100644 app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php create mode 100644 app/TransactionRules/Factory/ExpressionLanguageFactory.php diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php new file mode 100644 index 0000000000..dae57c3261 --- /dev/null +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -0,0 +1,89 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Controllers\Models\Rule; + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Models\Rule\ValidateExpressionRequest; +use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; +use FireflyIII\User; +use Illuminate\Http\JsonResponse; +use Symfony\Component\ExpressionLanguage\SyntaxError; + +/** + * Class ExpressionController + */ +class ExpressionController extends Controller +{ + private RuleRepositoryInterface $ruleRepository; + + /** + * RuleController constructor. + * + + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var User $user */ + $user = auth()->user(); + + $this->ruleRepository = app(RuleRepositoryInterface::class); + $this->ruleRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * This endpoint is documented at: + * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/validateExpression + * + * @param ValidateExpressionRequest $request + * + * @return JsonResponse + */ + public function validateExpression(ValidateExpressionRequest $request): JsonResponse + { + $expr = $request->getExpression(); + $expressionLanguage = ExpressionLanguageFactory::get(); + $evaluator = new ActionExpressionEvaluator($expressionLanguage, $expr); + + try { + $evaluator->lint(); + return response()->json([ + "valid" => true, + ]); + } catch (SyntaxError $e) { + return response()->json([ + "valid" => false, + "error" => $e->getMessage() + ]); + } + } +} diff --git a/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php new file mode 100644 index 0000000000..b12c81d2b1 --- /dev/null +++ b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php @@ -0,0 +1,46 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests\Models\Rule; + +use FireflyIII\Support\Request\ChecksLogin; +use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Foundation\Http\FormRequest; + +/** + * Class TestRequest + */ +class ValidateExpressionRequest extends FormRequest +{ + use ConvertsDataTypes; + use ChecksLogin; + + /** + * @return string + */ + public function getExpression(): string + { + return $this->convertString("expression"); + } +} diff --git a/app/TransactionRules/Expressions/ActionExpressionEvaluator.php b/app/TransactionRules/Expressions/ActionExpressionEvaluator.php new file mode 100644 index 0000000000..1f2fe1b6cc --- /dev/null +++ b/app/TransactionRules/Expressions/ActionExpressionEvaluator.php @@ -0,0 +1,95 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\TransactionRules\Expressions; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\SyntaxError; + +class ActionExpressionEvaluator +{ + private static array $NAMES = array("transaction"); + + private string $expr; + private bool $isExpression; + private ExpressionLanguage $expressionLanguage; + + public function __construct(ExpressionLanguage $expressionLanguage, string $expr) + { + $this->expressionLanguage = $expressionLanguage; + $this->expr = $expr; + + $this->isExpression = self::isExpression($expr); + } + + private static function isExpression(string $expr): bool + { + return str_starts_with($expr, "="); + } + + public function isValid(): bool + { + if (!$this->isExpression) { + return true; + } + + try { + $this->lint(array()); + return true; + } catch (SyntaxError $e) { + return false; + } + } + + private function lintExpression(string $expr): void + { + $this->expressionLanguage->lint($expr, self::$NAMES); + } + + public function lint(): void + { + if (!$this->isExpression) { + return; + } + + $this->lintExpression(substr($this->expr, 1)); + } + + private function evaluateExpression(string $expr, array $journal): string + { + $result = $this->expressionLanguage->evaluate($expr, [ + "transaction" => $journal + ]); + return strval($result); + } + + public function evaluate(array $journal): string + { + if (!$this->isExpression) { + return $this->expr; + } + + return $this->evaluateExpression(substr($this->expr, 1), $journal); + } +} diff --git a/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php new file mode 100644 index 0000000000..fbb3df1725 --- /dev/null +++ b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php @@ -0,0 +1,37 @@ +. + */ + +namespace FireflyIII\TransactionRules\Expressions; + +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +class ActionExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + public function getFunctions(): array + { + return [ + ExpressionFunction::fromPhp("substr"), + ExpressionFunction::fromPhp("strlen") + ]; + } +} diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index 0f7275e32e..c53d8c1a67 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Factory; @@ -27,6 +28,8 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleAction; use FireflyIII\Support\Domain; use FireflyIII\TransactionRules\Actions\ActionInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; use Illuminate\Support\Facades\Log; /** @@ -56,7 +59,10 @@ class ActionFactory $class = self::getActionClass($action->action_type); Log::debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); - return new $class($action); + $expressionLanguage = ExpressionLanguageFactory::get(); + $expressionEvaluator = new ActionExpressionEvaluator($expressionLanguage, $action->action_value); + + return new $class($action, $expressionEvaluator); } /** diff --git a/app/TransactionRules/Factory/ExpressionLanguageFactory.php b/app/TransactionRules/Factory/ExpressionLanguageFactory.php new file mode 100644 index 0000000000..3225da66c3 --- /dev/null +++ b/app/TransactionRules/Factory/ExpressionLanguageFactory.php @@ -0,0 +1,45 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\TransactionRules\Factory; + +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use FireflyIII\TransactionRules\Expressions\ActionExpressionLanguageProvider; + +class ExpressionLanguageFactory +{ + protected static ExpressionLanguage $expressionLanguage; + + private static function constructExpressionLanguage(): ExpressionLanguage + { + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider(new ActionExpressionLanguageProvider()); + return $expressionLanguage; + } + + public static function get(): ExpressionLanguage + { + return self::$expressionLanguage ??= self::constructExpressionLanguage(); + } +} diff --git a/composer.json b/composer.json index f0143e13a5..d83366f6dc 100644 --- a/composer.json +++ b/composer.json @@ -105,6 +105,7 @@ "spatie/laravel-html": "^3.2", "spatie/laravel-ignition": "^2", "spatie/period": "^2.4", + "symfony/expression-language": "^6.3", "symfony/http-client": "^6.3", "symfony/mailgun-mailer": "^6.3", "therobfonz/laravel-mandrill-driver": "^5.0" diff --git a/composer.lock b/composer.lock index 214c235c1e..ae7655de64 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "639b971ea13ea3e6ed2f57f862a195b8", + "content-hash": "2dd09680aeb9e09c15bc6f6f19666952", "packages": [ { "name": "bacon/bacon-qr-code", @@ -5946,6 +5946,178 @@ }, "time": "2023-02-20T14:31:09+00:00" }, + { + "name": "symfony/cache", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "84aff8d948d6292d2b5a01ac622760be44dddc72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/84aff8d948d6292d2b5a01ac622760be44dddc72", + "reference": "84aff8d948d6292d2b5a01ac622760be44dddc72", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.3.6" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v6.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-17T14:44:58+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, { "name": "symfony/console", "version": "v6.3.4", @@ -6398,6 +6570,70 @@ ], "time": "2023-05-23T14:45:45+00:00" }, + { + "name": "symfony/expression-language", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "6d560c4c80e7e328708efd923f93ad67e6a0c1c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/6d560c4c80e7e328708efd923f93ad67e6a0c1c0", + "reference": "6d560c4c80e7e328708efd923f93ad67e6a0c1c0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an engine that can compile and evaluate expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/expression-language/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-28T16:05:33+00:00" + }, { "name": "symfony/finder", "version": "v6.3.5", @@ -8525,6 +8761,80 @@ ], "time": "2023-09-12T10:11:35+00:00" }, + { + "name": "symfony/var-exporter", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "374d289c13cb989027274c86206ddc63b16a2441" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/374d289c13cb989027274c86206ddc63b16a2441", + "reference": "374d289c13cb989027274c86206ddc63b16a2441", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-13T09:16:49+00:00" + }, { "name": "therobfonz/laravel-mandrill-driver", "version": "5.0.0", diff --git a/routes/api.php b/routes/api.php index f69558e311..79211ea915 100644 --- a/routes/api.php +++ b/routes/api.php @@ -620,6 +620,8 @@ Route::group( Route::put('{rule}', ['uses' => 'UpdateController@update', 'as' => 'update']); Route::delete('{rule}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']); + Route::post('validateExpression', ['uses' => 'ExpressionController@validateExpression', 'as' => 'validate']); + Route::get('{rule}/test', ['uses' => 'TriggerController@testRule', 'as' => 'test']); // TODO give results back Route::post('{rule}/trigger', ['uses' => 'TriggerController@triggerRule', 'as' => 'trigger']); From 95593f847bac472a62d8c9bd463c82f9a18ce006 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Wed, 6 Mar 2024 20:54:50 -0500 Subject: [PATCH 02/16] feat: update all rules to support action value expressions --- app/TransactionRules/Actions/AddTag.php | 21 +++++--- .../Actions/AppendDescription.php | 11 ++-- app/TransactionRules/Actions/AppendNotes.php | 17 +++--- .../Actions/ConvertToDeposit.php | 48 +++++++++-------- .../Actions/ConvertToTransfer.php | 36 +++++++------ .../Actions/ConvertToWithdrawal.php | 52 +++++++++++-------- app/TransactionRules/Actions/LinkToBill.php | 16 +++--- .../Actions/PrependDescription.php | 12 +++-- app/TransactionRules/Actions/PrependNotes.php | 18 ++++--- app/TransactionRules/Actions/RemoveTag.php | 16 +++--- app/TransactionRules/Actions/SetBudget.php | 10 ++-- app/TransactionRules/Actions/SetCategory.php | 10 ++-- .../Actions/SetDescription.php | 21 +++++--- .../Actions/SetDestinationAccount.php | 31 ++++++----- app/TransactionRules/Actions/SetNotes.php | 17 +++--- .../Actions/SetSourceAccount.php | 31 ++++++----- .../Actions/UpdatePiggybank.php | 20 ++++--- 17 files changed, 236 insertions(+), 151 deletions(-) diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 057473463c..45da90e91e 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\TagFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -37,16 +39,18 @@ use Illuminate\Support\Facades\Log; */ class AddTag implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -58,19 +62,20 @@ class AddTag implements ActionInterface /** @var TagFactory $factory */ $factory = app(TagFactory::class); $factory->setUser(User::find($journal['user_id'])); - $tag = $factory->findOrCreate($this->action->action_value); + $tagName = $this->evaluator->evaluate($journal); + $tag = $factory->findOrCreate($tagName); if (null === $tag) { // could not find, could not create tag. - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.find_or_create_tag_failed', ['tag' => $tagName]))); return false; } $count = DB::table('tag_transaction_journal') - ->where('tag_id', $tag->id) - ->where('transaction_journal_id', $journal['transaction_journal_id']) - ->count(); + ->where('tag_id', $tag->id) + ->where('transaction_journal_id', $journal['transaction_journal_id']) + ->count(); if (0 === $count) { // add to journal: DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); @@ -84,7 +89,7 @@ class AddTag implements ActionInterface Log::debug( sprintf('RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id']) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $tagName]))); return false; } diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index 2a52c2cb79..46eef629af 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,22 +28,25 @@ use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; /** * Class AppendDescription. */ class AppendDescription implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -50,7 +54,8 @@ class AppendDescription implements ActionInterface */ public function actOnArray(array $journal): bool { - $description = sprintf('%s%s', $journal['description'], $this->action->action_value); + $actionValue = $this->evaluator->evaluate($journal); + $description = sprintf('%s%s', $journal['description'], $actionValue); DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); // event for audit log entry diff --git a/app/TransactionRules/Actions/AppendNotes.php b/app/TransactionRules/Actions/AppendNotes.php index 8d130b2521..49d7e6e71f 100644 --- a/app/TransactionRules/Actions/AppendNotes.php +++ b/app/TransactionRules/Actions/AppendNotes.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use Illuminate\Support\Facades\Log; /** @@ -34,16 +36,18 @@ use Illuminate\Support\Facades\Log; */ class AppendNotes implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -51,18 +55,19 @@ class AppendNotes implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->where('noteable_type', TransactionJournal::class) + ->first(['notes.*']); if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; $dbNote->noteable_type = TransactionJournal::class; $dbNote->text = ''; } - Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $this->action->action_value, $dbNote->text)); + Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $actionValue, $dbNote->text)); $before = $dbNote->text; - $text = sprintf('%s%s', $dbNote->text, $this->action->action_value); + $text = sprintf('%s%s', $dbNote->text, $actionValue); $dbNote->text = $text; $dbNote->save(); diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index 36091f7905..bd13cfa406 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -1,4 +1,5 @@ action = $action; + $this->evaluator = $evaluator; } /** @@ -61,6 +65,8 @@ class ConvertToDeposit implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); + // make object from array (so the data is fresh). /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); @@ -88,7 +94,7 @@ class ConvertToDeposit implements ActionInterface Log::debug('Going to transform a withdrawal to a deposit.'); try { - $res = $this->convertWithdrawalArray($object); + $res = $this->convertWithdrawalArray($object, $actionValue); } catch (JsonException | FireflyException $e) { Log::debug('Could not convert withdrawal to deposit.'); Log::error($e->getMessage()); @@ -129,7 +135,7 @@ class ConvertToDeposit implements ActionInterface * @throws FireflyException * @throws JsonException */ - private function convertWithdrawalArray(TransactionJournal $journal): bool + private function convertWithdrawalArray(TransactionJournal $journal, string $actionValue): bool { $user = $journal->user; // find or create revenue account. @@ -145,7 +151,7 @@ class ConvertToDeposit implements ActionInterface // get the action value, or use the original destination name in case the action value is empty: // this becomes a new or existing (revenue) account, which is the source of the new deposit. - $opposingName = '' === $this->action->action_value ? $destAccount->name : $this->action->action_value; + $opposingName = '' === $actionValue ? $destAccount->name : $actionValue; // we check all possible source account types if one exists: $validTypes = config('firefly.expected_source_types.source.Deposit'); $opposingAccount = $repository->findByName($opposingName, $validTypes); @@ -153,26 +159,26 @@ class ConvertToDeposit implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); } - Log::debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $this->action->action_value, $opposingAccount->name)); + Log::debug(sprintf('ConvertToDeposit. Action value is "%s", new opposing name is "%s"', $actionValue, $opposingAccount->name)); // update the source transaction and put in the new revenue ID. DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposingAccount->id]); // update the destination transaction and put in the original source account ID. DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $sourceAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $sourceAccount->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); Log::debug('Converted withdrawal to deposit.'); @@ -237,7 +243,7 @@ class ConvertToDeposit implements ActionInterface // get the action value, or use the original source name in case the action value is empty: // this becomes a new or existing (revenue) account, which is the source of the new deposit. - $opposingName = '' === $this->action->action_value ? $sourceAccount->name : $this->action->action_value; + $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue; // we check all possible source account types if one exists: $validTypes = config('firefly.expected_source_types.source.Deposit'); $opposingAccount = $repository->findByName($opposingName, $validTypes); @@ -245,20 +251,20 @@ class ConvertToDeposit implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); } - Log::debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); + Log::debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $actionValue, $opposingAccount->name)); // update source transaction(s) to be revenue account DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposingAccount->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); Log::debug('Converted transfer to deposit.'); diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index 14fe267fc7..7cd5fd887b 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -1,4 +1,5 @@ action = $action; + $this->evaluator = $evaluator; } /** @@ -59,6 +63,8 @@ class ConvertToTransfer implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); + // make object from array (so the data is fresh). /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); @@ -103,7 +109,7 @@ class ConvertToTransfer implements ActionInterface $expectedType = $this->getDestinationType($journalId); // Deposit? Replace source with account with same type as destination. } - $opposing = $repository->findByName($this->action->action_value, [$expectedType]); + $opposing = $repository->findByName($actionValue, [$expectedType]); if (null === $opposing) { Log::error( @@ -111,11 +117,11 @@ class ConvertToTransfer implements ActionInterface 'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).', $expectedType, $journalId, - $this->action->action_value, + $actionValue, $this->action->rule_id ) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $actionValue]))); return false; } @@ -214,16 +220,16 @@ class ConvertToTransfer implements ActionInterface // update destination transaction: DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposing->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposing->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); Log::debug('Converted withdrawal to transfer.'); @@ -273,16 +279,16 @@ class ConvertToTransfer implements ActionInterface // update source transaction: DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $opposing->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $opposing->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); Log::debug('Converted deposit to transfer.'); diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index f844e213a0..73b04b0ac8 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -1,4 +1,5 @@ action = $action; + $this->evaluator = $evaluator; } /** @@ -61,6 +65,8 @@ class ConvertToWithdrawal implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); + // make object from array (so the data is fresh). /** @var TransactionJournal|null $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); @@ -89,7 +95,7 @@ class ConvertToWithdrawal implements ActionInterface if (TransactionType::DEPOSIT === $type) { Log::debug('Going to transform a deposit to a withdrawal.'); try { - $res = $this->convertDepositArray($object); + $res = $this->convertDepositArray($object, $actionValue); } catch (JsonException | FireflyException $e) { Log::debug('Could not convert transfer to deposit.'); Log::error($e->getMessage()); @@ -104,7 +110,7 @@ class ConvertToWithdrawal implements ActionInterface Log::debug('Going to transform a transfer to a withdrawal.'); try { - $res = $this->convertTransferArray($object); + $res = $this->convertTransferArray($object, $actionValue); } catch (JsonException | FireflyException $e) { Log::debug('Could not convert transfer to deposit.'); Log::error($e->getMessage()); @@ -126,7 +132,7 @@ class ConvertToWithdrawal implements ActionInterface * @throws FireflyException * @throws JsonException */ - private function convertDepositArray(TransactionJournal $journal): bool + private function convertDepositArray(TransactionJournal $journal, string $actionValue): bool { $user = $journal->user; /** @var AccountFactory $factory */ @@ -141,7 +147,7 @@ class ConvertToWithdrawal implements ActionInterface // get the action value, or use the original source name in case the action value is empty: // this becomes a new or existing (expense) account, which is the destination of the new withdrawal. - $opposingName = '' === $this->action->action_value ? $sourceAccount->name : $this->action->action_value; + $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue; // we check all possible source account types if one exists: $validTypes = config('firefly.expected_source_types.destination.Withdrawal'); $opposingAccount = $repository->findByName($opposingName, $validTypes); @@ -149,25 +155,25 @@ class ConvertToWithdrawal implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); } - Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $opposingName)); + Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $actionValue, $opposingName)); // update source transaction(s) to be the original destination account DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '<', 0) - ->update(['account_id' => $destAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '<', 0) + ->update(['account_id' => $destAccount->id]); // update destination transaction(s) to be new expense account. DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposingAccount->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id]); Log::debug('Converted deposit to withdrawal.'); @@ -216,7 +222,7 @@ class ConvertToWithdrawal implements ActionInterface * @throws FireflyException * @throws JsonException */ - private function convertTransferArray(TransactionJournal $journal): bool + private function convertTransferArray(TransactionJournal $journal, string $actionValue): bool { // find or create expense account. $user = $journal->user; @@ -231,7 +237,7 @@ class ConvertToWithdrawal implements ActionInterface // get the action value, or use the original source name in case the action value is empty: // this becomes a new or existing (expense) account, which is the destination of the new withdrawal. - $opposingName = '' === $this->action->action_value ? $destAccount->name : $this->action->action_value; + $opposingName = '' === $actionValue ? $destAccount->name : $actionValue; // we check all possible source account types if one exists: $validTypes = config('firefly.expected_source_types.destination.Withdrawal'); $opposingAccount = $repository->findByName($opposingName, $validTypes); @@ -239,19 +245,19 @@ class ConvertToWithdrawal implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::EXPENSE); } - Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $this->action->action_value, $opposingName)); + Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", destination name is "%s"', $actionValue, $opposingName)); // update destination transaction(s) to be new expense account. DB::table('transactions') - ->where('transaction_journal_id', '=', $journal->id) - ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + ->where('transaction_journal_id', '=', $journal->id) + ->where('amount', '>', 0) + ->update(['account_id' => $opposingAccount->id]); // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); DB::table('transaction_journals') - ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + ->where('id', '=', $journal->id) + ->update(['transaction_type_id' => $newType->id]); Log::debug('Converted transfer to withdrawal.'); diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 74de2eeb41..2b817077c6 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -30,6 +31,7 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -38,7 +40,8 @@ use Illuminate\Support\Facades\Log; */ class LinkToBill implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. @@ -46,9 +49,10 @@ class LinkToBill implements ActionInterface * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -60,12 +64,12 @@ class LinkToBill implements ActionInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($user); - $billName = (string)$this->action->action_value; + $billName = $this->evaluator->evaluate($journal); $bill = $repository->findByName($billName); if (null !== $bill && $journal['transaction_type_type'] === TransactionType::WITHDRAWAL) { $count = DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) - ->where('bill_id', $bill->id)->count(); + ->where('bill_id', $bill->id)->count(); if (0 !== $count) { Log::error( sprintf( @@ -80,8 +84,8 @@ class LinkToBill implements ActionInterface DB::table('transaction_journals') - ->where('id', '=', $journal['transaction_journal_id']) - ->update(['bill_id' => $bill->id]); + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['bill_id' => $bill->id]); Log::debug( sprintf('RuleAction LinkToBill set the bill of journal #%d to bill #%d ("%s").', $journal['transaction_journal_id'], $bill->id, $bill->name) ); diff --git a/app/TransactionRules/Actions/PrependDescription.php b/app/TransactionRules/Actions/PrependDescription.php index c116370d9b..ef8a36931c 100644 --- a/app/TransactionRules/Actions/PrependDescription.php +++ b/app/TransactionRules/Actions/PrependDescription.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,22 +28,25 @@ use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; /** * Class PrependDescription. */ class PrependDescription implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -50,8 +54,10 @@ class PrependDescription implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); + $before = $journal['description']; - $after = sprintf('%s%s', $this->action->action_value, $journal['description']); + $after = sprintf('%s%s', $actionValue, $journal['description']); DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]); // journal diff --git a/app/TransactionRules/Actions/PrependNotes.php b/app/TransactionRules/Actions/PrependNotes.php index 25534fa25d..9f86739067 100644 --- a/app/TransactionRules/Actions/PrependNotes.php +++ b/app/TransactionRules/Actions/PrependNotes.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use Illuminate\Support\Facades\Log; /** @@ -34,16 +36,18 @@ use Illuminate\Support\Facades\Log; */ class PrependNotes implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -51,9 +55,11 @@ class PrependNotes implements ActionInterface */ public function actOnArray(array $journal): bool { + $actionValue = $this->evaluator->evaluate($journal); + $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->where('noteable_type', TransactionJournal::class) + ->first(['notes.*']); if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; @@ -61,8 +67,8 @@ class PrependNotes implements ActionInterface $dbNote->text = ''; } $before = $dbNote->text; - Log::debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $this->action->action_value, $dbNote->text)); - $text = sprintf('%s%s', $this->action->action_value, $dbNote->text); + Log::debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $actionValue, $dbNote->text)); + $text = sprintf('%s%s', $actionValue, $dbNote->text); $dbNote->text = $text; $dbNote->save(); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index f3f23ad9e9..b67a10aaa4 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -28,6 +29,7 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -36,16 +38,18 @@ use Illuminate\Support\Facades\Log; */ class RemoveTag implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -54,7 +58,7 @@ class RemoveTag implements ActionInterface public function actOnArray(array $journal): bool { // if tag does not exist, no need to continue: - $name = $this->action->action_value; + $name = $this->evaluator->evaluate($journal); $user = User::find($journal['user_id']); $tag = $user->tags()->where('tag', $name)->first(); @@ -76,9 +80,9 @@ class RemoveTag implements ActionInterface Log::debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id'])); DB::table('tag_transaction_journal') - ->where('transaction_journal_id', $journal['transaction_journal_id']) - ->where('tag_id', $tag->id) - ->delete(); + ->where('transaction_journal_id', $journal['transaction_journal_id']) + ->where('tag_id', $tag->id) + ->delete(); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 1dbdc54c18..9a88f49257 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -37,16 +39,18 @@ use Illuminate\Support\Facades\Log; */ class SetBudget implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -55,7 +59,7 @@ class SetBudget implements ActionInterface public function actOnArray(array $journal): bool { $user = User::find($journal['user_id']); - $search = $this->action->action_value; + $search = $this->evaluator->evaluate($journal); $budget = $user->budgets()->where('name', $search)->first(); if (null === $budget) { diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index 30608a3f11..a4ed08d7d6 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,6 +30,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -37,16 +39,18 @@ use Illuminate\Support\Facades\Log; */ class SetCategory implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -55,7 +59,7 @@ class SetCategory implements ActionInterface public function actOnArray(array $journal): bool { $user = User::find($journal['user_id']); - $search = $this->action->action_value; + $search = $this->evaluator->evaluate($journal); if (null === $user) { Log::error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); diff --git a/app/TransactionRules/Actions/SetDescription.php b/app/TransactionRules/Actions/SetDescription.php index 26e3a2be9d..799a02ed70 100644 --- a/app/TransactionRules/Actions/SetDescription.php +++ b/app/TransactionRules/Actions/SetDescription.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,6 +28,7 @@ use DB; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use Illuminate\Support\Facades\Log; /** @@ -34,16 +36,18 @@ use Illuminate\Support\Facades\Log; */ class SetDescription implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -53,22 +57,23 @@ class SetDescription implements ActionInterface { /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - $before = $object->description; + $before = $journal['description']; + $after = $this->evaluator->evaluate($journal); DB::table('transaction_journals') - ->where('id', '=', $journal['transaction_journal_id']) - ->update(['description' => $this->action->action_value]); + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['description' => $after]); Log::debug( sprintf( 'RuleAction SetDescription changed the description of journal #%d from "%s" to "%s".', $journal['transaction_journal_id'], - $journal['description'], - $this->action->action_value + $before, + $after ) ); $object->refresh(); - event(new TriggeredAuditLog($this->action->rule, $object, 'update_description', $before, $this->action->action_value)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_description', $before, $after)); return true; } diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 1daa030c64..e4c2688831 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -32,6 +33,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -41,6 +43,7 @@ use Illuminate\Support\Facades\Log; class SetDestinationAccount implements ActionInterface { private RuleAction $action; + private ActionExpressionEvaluator $evaluator; private AccountRepositoryInterface $repository; /** @@ -48,9 +51,10 @@ class SetDestinationAccount implements ActionInterface * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -58,6 +62,7 @@ class SetDestinationAccount implements ActionInterface */ public function actOnArray(array $journal): bool { + $accountName = $this->evaluator->evaluate($journal); $user = User::find($journal['user_id']); $type = $journal['transaction_type_type']; /** @var TransactionJournal|null $object */ @@ -73,16 +78,16 @@ class SetDestinationAccount implements ActionInterface $this->repository->setUser($user); // if this is a transfer or a deposit, the new destination account must be an asset account or a default account, and it MUST exist: - $newAccount = $this->findAssetAccount($type); + $newAccount = $this->findAssetAccount($type, $accountName); if ((TransactionType::DEPOSIT === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { Log::error( sprintf( 'Cant change destination account of journal #%d because no asset account with name "%s" exists.', $object->id, - $this->action->action_value + $accountName ) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $accountName]))); return false; } @@ -116,7 +121,7 @@ class SetDestinationAccount implements ActionInterface // if this is a withdrawal, the new destination account must be a expense account and may be created: // or it is a liability, in which case it must be returned. if (TransactionType::WITHDRAWAL === $type) { - $newAccount = $this->findWithdrawalDestinationAccount(); + $newAccount = $this->findWithdrawalDestinationAccount($accountName); } Log::debug(sprintf('New destination account is #%d ("%s").', $newAccount->id, $newAccount->name)); @@ -125,9 +130,9 @@ class SetDestinationAccount implements ActionInterface // update destination transaction with new destination account: DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '>', 0) - ->update(['account_id' => $newAccount->id]); + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '>', 0) + ->update(['account_id' => $newAccount->id]); Log::debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); @@ -139,26 +144,26 @@ class SetDestinationAccount implements ActionInterface * * @return Account|null */ - private function findAssetAccount(string $type): ?Account + private function findAssetAccount(string $type, string $accountName): ?Account { // switch on type: $allowed = config(sprintf('firefly.expected_source_types.destination.%s', $type)); $allowed = is_array($allowed) ? $allowed : []; Log::debug(sprintf('Check config for expected_source_types.destination.%s, result is', $type), $allowed); - return $this->repository->findByName($this->action->action_value, $allowed); + return $this->repository->findByName($accountName, $allowed); } /** * @return Account */ - private function findWithdrawalDestinationAccount(): Account + private function findWithdrawalDestinationAccount(string $accountName): Account { $allowed = config('firefly.expected_source_types.destination.Withdrawal'); - $account = $this->repository->findByName($this->action->action_value, $allowed); + $account = $this->repository->findByName($accountName, $allowed); if (null === $account) { $data = [ - 'name' => $this->action->action_value, + 'name' => $accountName, 'account_type_name' => 'expense', 'account_type_id' => null, 'virtual_balance' => 0, diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index a1874c241c..28926ba319 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,6 +28,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use Illuminate\Support\Facades\Log; /** @@ -34,16 +36,18 @@ use Illuminate\Support\Facades\Log; */ class SetNotes implements ActionInterface { - private RuleACtion $action; + private RuleACtion $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -52,7 +56,7 @@ class SetNotes implements ActionInterface public function actOnArray(array $journal): bool { $dbNote = Note::where('noteable_id', $journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class)->first(); + ->where('noteable_type', TransactionJournal::class)->first(); if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = $journal['transaction_journal_id']; @@ -60,7 +64,8 @@ class SetNotes implements ActionInterface $dbNote->text = ''; } $oldNotes = $dbNote->text; - $dbNote->text = $this->action->action_value; + $newNotes = $this->evaluator->evaluate($journal); + $dbNote->text = $newNotes; $dbNote->save(); Log::debug( @@ -68,14 +73,14 @@ class SetNotes implements ActionInterface 'RuleAction SetNotes changed the notes of journal #%d from "%s" to "%s".', $journal['transaction_journal_id'], $oldNotes, - $this->action->action_value + $newNotes ) ); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $oldNotes, $this->action->action_value)); + event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $oldNotes, $newNotes)); return true; } diff --git a/app/TransactionRules/Actions/SetSourceAccount.php b/app/TransactionRules/Actions/SetSourceAccount.php index 724b47b986..8a2e1c326b 100644 --- a/app/TransactionRules/Actions/SetSourceAccount.php +++ b/app/TransactionRules/Actions/SetSourceAccount.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -32,6 +33,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -41,6 +43,7 @@ use Illuminate\Support\Facades\Log; class SetSourceAccount implements ActionInterface { private RuleAction $action; + private ActionExpressionEvaluator $evaluator; private AccountRepositoryInterface $repository; /** @@ -48,9 +51,10 @@ class SetSourceAccount implements ActionInterface * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -60,6 +64,7 @@ class SetSourceAccount implements ActionInterface { $user = User::find($journal['user_id']); $type = $journal['transaction_type_type']; + $name = $this->evaluator->evaluate($journal); /** @var TransactionJournal|null $object */ $object = $user->transactionJournals()->find((int)$journal['transaction_journal_id']); $this->repository = app(AccountRepositoryInterface::class); @@ -72,12 +77,12 @@ class SetSourceAccount implements ActionInterface $this->repository->setUser($user); // if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist: - $newAccount = $this->findAssetAccount($type); + $newAccount = $this->findAssetAccount($type, $name); if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && null === $newAccount) { Log::error( - sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $this->action->action_value) + sprintf('Cant change source account of journal #%d because no asset account with name "%s" exists.', $object->id, $name) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_asset', ['name' => $name]))); return false; } @@ -110,16 +115,16 @@ class SetSourceAccount implements ActionInterface // if this is a deposit, the new source account must be a revenue account and may be created: // or it's a liability if (TransactionType::DEPOSIT === $type) { - $newAccount = $this->findDepositSourceAccount(); + $newAccount = $this->findDepositSourceAccount($name); } Log::debug(sprintf('New source account is #%d ("%s").', $newAccount->id, $newAccount->name)); // update source transaction with new source account: DB::table('transactions') - ->where('transaction_journal_id', '=', $object->id) - ->where('amount', '<', 0) - ->update(['account_id' => $newAccount->id]); + ->where('transaction_journal_id', '=', $object->id) + ->where('amount', '<', 0) + ->update(['account_id' => $newAccount->id]); event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $newAccount->name)); @@ -133,27 +138,27 @@ class SetSourceAccount implements ActionInterface * * @return Account|null */ - private function findAssetAccount(string $type): ?Account + private function findAssetAccount(string $type, string $name): ?Account { // switch on type: $allowed = config(sprintf('firefly.expected_source_types.source.%s', $type)); $allowed = is_array($allowed) ? $allowed : []; Log::debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed); - return $this->repository->findByName($this->action->action_value, $allowed); + return $this->repository->findByName($name, $allowed); } /** * @return Account */ - private function findDepositSourceAccount(): Account + private function findDepositSourceAccount(string $name): Account { $allowed = config('firefly.expected_source_types.source.Deposit'); - $account = $this->repository->findByName($this->action->action_value, $allowed); + $account = $this->repository->findByName($name, $allowed); if (null === $account) { // create new revenue account with this name: $data = [ - 'name' => $this->action->action_value, + 'name' => $name, 'account_type_name' => 'revenue', 'account_type_id' => null, 'virtual_balance' => 0, diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index c0699f258d..faab1c7f6c 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -32,6 +32,7 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; use Illuminate\Support\Facades\Log; @@ -40,16 +41,18 @@ use Illuminate\Support\Facades\Log; */ class UpdatePiggybank implements ActionInterface { - private RuleAction $action; + private RuleAction $action; + private ActionExpressionEvaluator $evaluator; /** * TriggerInterface constructor. * * @param RuleAction $action */ - public function __construct(RuleAction $action) + public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) { $this->action = $action; + $this->evaluator = $evaluator; } /** @@ -59,18 +62,19 @@ class UpdatePiggybank implements ActionInterface { Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); + $piggyBankName = $this->evaluator->evaluate($journal); // refresh the transaction type. $user = User::find($journal['user_id']); /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); $type = TransactionType::find((int)$journalObj->transaction_type_id); - $piggyBank = $this->findPiggyBank($user); + $piggyBank = $this->findPiggyBank($user, $piggyBankName); if (null === $piggyBank) { Log::info( - sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $this->action->action_value, $this->action->id, $this->action->rule_id) + sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $piggyBankName, $this->action->id, $this->action->rule_id) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $piggyBankName]))); return false; } @@ -130,7 +134,7 @@ class UpdatePiggybank implements ActionInterface $destination->account_id ) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $this->action->action_value]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $piggyBankName]))); return false; } @@ -139,9 +143,9 @@ class UpdatePiggybank implements ActionInterface * * @return PiggyBank|null */ - private function findPiggyBank(User $user): ?PiggyBank + private function findPiggyBank(User $user, string $name): ?PiggyBank { - return $user->piggyBanks()->where('piggy_banks.name', $this->action->action_value)->first(); + return $user->piggyBanks()->where('piggy_banks.name', $name)->first(); } /** From 438f602961840c7e36af169b47918213d51f9f67 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 12:23:32 -0500 Subject: [PATCH 03/16] feat: surface expression validation errors when creating or updating rules --- .../Models/Rule/ExpressionController.php | 7 ++-- .../V1/Requests/Models/Rule/StoreRequest.php | 2 +- .../V1/Requests/Models/Rule/UpdateRequest.php | 2 +- app/Http/Requests/RuleFormRequest.php | 2 +- .../Expressions/ActionExpressionEvaluator.php | 26 +++++++++++---- app/Validation/FireflyValidator.php | 32 +++++++++++++++++++ resources/lang/en_US/validation.php | 1 + 7 files changed, 58 insertions(+), 14 deletions(-) diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php index dae57c3261..36588ae58f 100644 --- a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -74,15 +74,14 @@ class ExpressionController extends Controller $expressionLanguage = ExpressionLanguageFactory::get(); $evaluator = new ActionExpressionEvaluator($expressionLanguage, $expr); - try { - $evaluator->lint(); + if ($evaluator->isValid()) { return response()->json([ "valid" => true, ]); - } catch (SyntaxError $e) { + } else { return response()->json([ "valid" => false, - "error" => $e->getMessage() + "error" => $evaluator->getValidationError()->getMessage() ]); } } diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index dff5c76d8d..03532a2465 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -123,7 +123,7 @@ class StoreRequest extends FormRequest 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue', + 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue', 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 3e89716d7f..64fc66455b 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -140,7 +140,7 @@ class UpdateRequest extends FormRequest 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionValue', + 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue', 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 6d6f831815..2bc74cc52c 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -147,7 +147,7 @@ class RuleFormRequest extends FormRequest 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), 'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers), 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionValue', $contextActions), + 'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionExpression|ruleActionValue', $contextActions), 'strict' => 'in:0,1', ]; diff --git a/app/TransactionRules/Expressions/ActionExpressionEvaluator.php b/app/TransactionRules/Expressions/ActionExpressionEvaluator.php index 1f2fe1b6cc..7e0b1a3175 100644 --- a/app/TransactionRules/Expressions/ActionExpressionEvaluator.php +++ b/app/TransactionRules/Expressions/ActionExpressionEvaluator.php @@ -31,9 +31,10 @@ class ActionExpressionEvaluator { private static array $NAMES = array("transaction"); + private ExpressionLanguage $expressionLanguage; private string $expr; private bool $isExpression; - private ExpressionLanguage $expressionLanguage; + private ?SyntaxError $validationError; public function __construct(ExpressionLanguage $expressionLanguage, string $expr) { @@ -41,6 +42,7 @@ class ActionExpressionEvaluator $this->expr = $expr; $this->isExpression = self::isExpression($expr); + $this->validationError = $this->validate(); } private static function isExpression(string $expr): bool @@ -48,17 +50,17 @@ class ActionExpressionEvaluator return str_starts_with($expr, "="); } - public function isValid(): bool + private function validate(): ?SyntaxError { if (!$this->isExpression) { - return true; + return null; } try { - $this->lint(array()); - return true; + $this->lint(); + return null; } catch (SyntaxError $e) { - return false; + return $e; } } @@ -67,7 +69,7 @@ class ActionExpressionEvaluator $this->expressionLanguage->lint($expr, self::$NAMES); } - public function lint(): void + private function lint(): void { if (!$this->isExpression) { return; @@ -76,6 +78,16 @@ class ActionExpressionEvaluator $this->lintExpression(substr($this->expr, 1)); } + public function isValid(): bool + { + return $this->validationError === null; + } + + public function getValidationError() + { + return $this->validationError; + } + private function evaluateExpression(string $expr, array $journal): string { $result = $this->expressionLanguage->evaluate($expr, [ diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 891fcc6163..bcfb1c3d46 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -35,6 +35,8 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Services\Password\Verifier; use FireflyIII\Support\ParseDateString; +use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; use FireflyIII\User; use Illuminate\Validation\Validator; use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; @@ -253,6 +255,31 @@ class FireflyValidator extends Validator return 1 === $count; } + public function validateRuleActionExpression(string $attribute, string $value = null): bool + { + $value ??= ''; + + $el = ExpressionLanguageFactory::get(); + $evaluator = new ActionExpressionEvaluator($el, $value); + + return $evaluator->isValid(); + } + + public function replaceRuleActionExpression(string $message, string $attribute): string + { + $value = $this->getValue($attribute); + + $el = ExpressionLanguageFactory::get(); + $evaluator = new ActionExpressionEvaluator($el, $value); + $err = $evaluator->getValidationError(); + + if ($err == null) { + return $message; + } + + return str_replace(":error", $err->getMessage(), $message); + } + public function validateRuleActionValue(string $attribute, string $value = null): bool { // first, get the index from this string: @@ -268,6 +295,11 @@ class FireflyValidator extends Validator return false; } + // if value is an expression, assume valid + if (str_starts_with($value, '=')) { + return true; + } + // if it's set_budget, verify the budget name: if ('set_budget' === $actionType) { /** @var BudgetRepositoryInterface $repository */ diff --git a/resources/lang/en_US/validation.php b/resources/lang/en_US/validation.php index cf6e6fde38..1d01e82c87 100644 --- a/resources/lang/en_US/validation.php +++ b/resources/lang/en_US/validation.php @@ -46,6 +46,7 @@ return [ 'reconciled_forbidden_field' => 'This transaction is already reconciled, you cannot change the ":field"', 'deleted_user' => 'Due to security constraints, you cannot register using this email address.', 'rule_trigger_value' => 'This value is invalid for the selected trigger.', + 'rule_action_expression' => 'Invalid expression. :error', 'rule_action_value' => 'This value is invalid for the selected action.', 'file_already_attached' => 'Uploaded file ":name" is already attached to this object.', 'file_attached' => 'Successfully uploaded file ":name".', From bee219ebf7dcf9fc7d2bf085f7069e7efa299182 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 13:00:57 -0500 Subject: [PATCH 04/16] refactor: inject ExpressionLanguage singleton using DI --- .../Models/Rule/ExpressionController.php | 13 +++--- app/Providers/FireflyServiceProvider.php | 13 ++++++ app/TransactionRules/Actions/AddTag.php | 12 ++--- .../Actions/AppendDescription.php | 12 ++--- app/TransactionRules/Actions/AppendNotes.php | 12 ++--- .../Actions/ConvertToDeposit.php | 12 ++--- .../Actions/ConvertToTransfer.php | 14 +++--- .../Actions/ConvertToWithdrawal.php | 12 ++--- app/TransactionRules/Actions/LinkToBill.php | 12 ++--- .../Actions/PrependDescription.php | 12 ++--- app/TransactionRules/Actions/PrependNotes.php | 14 +++--- app/TransactionRules/Actions/RemoveTag.php | 12 ++--- app/TransactionRules/Actions/SetBudget.php | 12 ++--- app/TransactionRules/Actions/SetCategory.php | 12 ++--- .../Actions/SetDescription.php | 12 ++--- app/TransactionRules/Actions/SetNotes.php | 12 ++--- .../Actions/UpdatePiggybank.php | 12 ++--- ...sionEvaluator.php => ActionExpression.php} | 8 ++-- .../Factory/ActionFactory.php | 8 ++-- .../Factory/ExpressionLanguageFactory.php | 45 ------------------- app/Validation/FireflyValidator.php | 15 +++---- 21 files changed, 122 insertions(+), 164 deletions(-) rename app/TransactionRules/Expressions/{ActionExpressionEvaluator.php => ActionExpression.php} (92%) delete mode 100644 app/TransactionRules/Factory/ExpressionLanguageFactory.php diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php index 36588ae58f..f9737e04df 100644 --- a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -26,11 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Models\Rule; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Rule\ValidateExpressionRequest; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; -use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; use Illuminate\Http\JsonResponse; -use Symfony\Component\ExpressionLanguage\SyntaxError; /** * Class ExpressionController @@ -70,18 +68,17 @@ class ExpressionController extends Controller */ public function validateExpression(ValidateExpressionRequest $request): JsonResponse { - $expr = $request->getExpression(); - $expressionLanguage = ExpressionLanguageFactory::get(); - $evaluator = new ActionExpressionEvaluator($expressionLanguage, $expr); + $value = $request->getExpression(); + $expr = new ActionExpression($value); - if ($evaluator->isValid()) { + if ($expr->isValid()) { return response()->json([ "valid" => true, ]); } else { return response()->json([ "valid" => false, - "error" => $evaluator->getValidationError()->getMessage() + "error" => $expr->getValidationError()->getMessage() ]); } } diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 3f304c33da..200d9c1372 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -19,6 +19,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ + declare(strict_types=1); namespace FireflyIII\Providers; @@ -69,9 +70,11 @@ use FireflyIII\Support\Preferences; use FireflyIII\Support\Steam; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use FireflyIII\TransactionRules\Engine\SearchRuleEngine; +use FireflyIII\TransactionRules\Expressions\ActionExpressionLanguageProvider; use FireflyIII\Validation\FireflyValidator; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; /** * Class FireflyServiceProvider. @@ -200,6 +203,16 @@ class FireflyServiceProvider extends ServiceProvider } ); + // rule expression language + $this->app->singleton( + ExpressionLanguage::class, + static function () { + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->registerProvider(new ActionExpressionLanguageProvider()); + return $expressionLanguage; + } + ); + $this->app->bind( RuleEngineInterface::class, static function (Application $app) { diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index 20d1adfce6..dbe22a8bdb 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -29,7 +29,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\TagFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,16 +37,16 @@ use FireflyIII\User; */ class AddTag implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -58,7 +58,7 @@ class AddTag implements ActionInterface /** @var User $user */ $user = User::find($journal['user_id']); $factory->setUser($user); - $tagName = $this->evaluator->evaluate($journal); + $tagName = $this->expr->evaluate($journal); $tag = $factory->findOrCreate($tagName); if (null === $tag) { diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index 5f8fa29d66..f19927b62f 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -27,28 +27,28 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class AppendDescription. */ class AppendDescription implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); $description = sprintf('%s %s', $journal['description'], $actionValue); \DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); diff --git a/app/TransactionRules/Actions/AppendNotes.php b/app/TransactionRules/Actions/AppendNotes.php index 922be054d2..8b42680dd9 100644 --- a/app/TransactionRules/Actions/AppendNotes.php +++ b/app/TransactionRules/Actions/AppendNotes.php @@ -28,28 +28,28 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class AppendNotes. */ class AppendNotes implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) ->where('noteable_type', TransactionJournal::class) ->first(['notes.*']); diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index fedea72779..dbb8ffe9a8 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -35,28 +35,28 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class ConvertToDeposit */ class ConvertToDeposit implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index 559125eced..bf9ede733f 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -34,23 +34,23 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class ConvertToTransfer */ class ConvertToTransfer implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { - $this->action = $action; - $this->evaluator = $evaluator; + $this->action = $action; + $this->expr = $expr; } /** @@ -59,7 +59,7 @@ class ConvertToTransfer implements ActionInterface */ public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index 14725d1320..8af0a67a35 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -35,28 +35,28 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class ConvertToWithdrawal */ class ConvertToWithdrawal implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 677a3d2f46..6b9a49ed3a 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -30,7 +30,7 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -38,16 +38,16 @@ use FireflyIII\User; */ class LinkToBill implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -58,7 +58,7 @@ class LinkToBill implements ActionInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($user); - $billName = $this->evaluator->evaluate($journal); + $billName = $this->expr->evaluate($journal); $bill = $repository->findByName($billName); if (null !== $bill && TransactionType::WITHDRAWAL === $journal['transaction_type_type']) { diff --git a/app/TransactionRules/Actions/PrependDescription.php b/app/TransactionRules/Actions/PrependDescription.php index ba407f4cdb..2be503f475 100644 --- a/app/TransactionRules/Actions/PrependDescription.php +++ b/app/TransactionRules/Actions/PrependDescription.php @@ -27,28 +27,28 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class PrependDescription. */ class PrependDescription implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); $before = $journal['description']; $after = sprintf('%s%s', $actionValue, $journal['description']); diff --git a/app/TransactionRules/Actions/PrependNotes.php b/app/TransactionRules/Actions/PrependNotes.php index d82cbe43b0..a9e9756755 100644 --- a/app/TransactionRules/Actions/PrependNotes.php +++ b/app/TransactionRules/Actions/PrependNotes.php @@ -28,28 +28,28 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class PrependNotes. */ class PrependNotes implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { - $this->action = $action; - $this->evaluator = $evaluator; + $this->action = $action; + $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->evaluator->evaluate($journal); + $actionValue = $this->expr->evaluate($journal); $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) ->where('noteable_type', TransactionJournal::class) ->first(['notes.*']); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index 20693779bf..846bcbb05a 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -28,7 +28,7 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -36,22 +36,22 @@ use FireflyIII\User; */ class RemoveTag implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { // if tag does not exist, no need to continue: - $name = $this->evaluator->evaluate($journal); + $name = $this->expr->evaluate($journal); /** @var User $user */ $user = User::find($journal['user_id']); diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 5dc595de3d..880d05bed8 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -29,7 +29,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,23 +37,23 @@ use FireflyIII\User; */ class SetBudget implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - $search = $this->evaluator->evaluate($journal); + $search = $this->expr->evaluate($journal); $budget = $user->budgets()->where('name', $search)->first(); if (null === $budget) { diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index fe8309647c..1c2ef454e3 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -29,7 +29,7 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,23 +37,23 @@ use FireflyIII\User; */ class SetCategory implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { /** @var null|User $user */ $user = User::find($journal['user_id']); - $search = $this->evaluator->evaluate($journal); + $search = $this->expr->evaluate($journal); if (null === $user) { app('log')->error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); diff --git a/app/TransactionRules/Actions/SetDescription.php b/app/TransactionRules/Actions/SetDescription.php index 8ce0d001b5..3d55d2f0fe 100644 --- a/app/TransactionRules/Actions/SetDescription.php +++ b/app/TransactionRules/Actions/SetDescription.php @@ -27,23 +27,23 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class SetDescription. */ class SetDescription implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -51,7 +51,7 @@ class SetDescription implements ActionInterface /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); $before = $journal['description']; - $after = $this->evaluator->evaluate($journal); + $after = $this->expr->evaluate($journal); \DB::table('transaction_journals') ->where('id', '=', $journal['transaction_journal_id']) diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index 8abdf02f8c..6d5c86363d 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -28,23 +28,23 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class SetNotes. */ class SetNotes implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -58,7 +58,7 @@ class SetNotes implements ActionInterface $dbNote->text = ''; } $oldNotes = $dbNote->text; - $newNotes = $this->evaluator->evaluate($journal); + $newNotes = $this->expr->evaluate($journal); $dbNote->text = $newNotes; $dbNote->save(); diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index 82061f9b10..4498dd3f7e 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -31,7 +31,7 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -39,23 +39,23 @@ use FireflyIII\User; */ class UpdatePiggybank implements ActionInterface { - private RuleAction $action; - private ActionExpressionEvaluator $evaluator; + private RuleAction $action; + private ActionExpression $expr; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action, ActionExpression $expr) { $this->action = $action; - $this->evaluator = $evaluator; + $this->expr = $expr; } public function actOnArray(array $journal): bool { app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); - $piggyBankName = $this->evaluator->evaluate($journal); + $piggyBankName = $this->expr->evaluate($journal); // refresh the transaction type. /** @var User $user */ $user = User::find($journal['user_id']); diff --git a/app/TransactionRules/Expressions/ActionExpressionEvaluator.php b/app/TransactionRules/Expressions/ActionExpression.php similarity index 92% rename from app/TransactionRules/Expressions/ActionExpressionEvaluator.php rename to app/TransactionRules/Expressions/ActionExpression.php index 7e0b1a3175..1d498ea78e 100644 --- a/app/TransactionRules/Expressions/ActionExpressionEvaluator.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -1,7 +1,7 @@ expressionLanguage = $expressionLanguage; + $this->expressionLanguage = app(ExpressionLanguage::class); $this->expr = $expr; $this->isExpression = self::isExpression($expr); diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index a34bf7615b..c20120d807 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -28,8 +28,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleAction; use FireflyIII\Support\Domain; use FireflyIII\TransactionRules\Actions\ActionInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; -use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; +use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class ActionFactory can create actions. @@ -52,10 +51,9 @@ class ActionFactory $class = self::getActionClass($action->action_type); app('log')->debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); - $expressionLanguage = ExpressionLanguageFactory::get(); - $expressionEvaluator = new ActionExpressionEvaluator($expressionLanguage, $action->action_value); + $expr = new ActionExpression($action->action_value); - return new $class($action, $expressionEvaluator); // @phpstan-ignore-line + return new $class($action, $expr); // @phpstan-ignore-line } /** diff --git a/app/TransactionRules/Factory/ExpressionLanguageFactory.php b/app/TransactionRules/Factory/ExpressionLanguageFactory.php deleted file mode 100644 index 3225da66c3..0000000000 --- a/app/TransactionRules/Factory/ExpressionLanguageFactory.php +++ /dev/null @@ -1,45 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\TransactionRules\Factory; - -use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use FireflyIII\TransactionRules\Expressions\ActionExpressionLanguageProvider; - -class ExpressionLanguageFactory -{ - protected static ExpressionLanguage $expressionLanguage; - - private static function constructExpressionLanguage(): ExpressionLanguage - { - $expressionLanguage = new ExpressionLanguage(); - $expressionLanguage->registerProvider(new ActionExpressionLanguageProvider()); - return $expressionLanguage; - } - - public static function get(): ExpressionLanguage - { - return self::$expressionLanguage ??= self::constructExpressionLanguage(); - } -} diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index bcfb1c3d46..667e3c1411 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -35,8 +35,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Services\Password\Verifier; use FireflyIII\Support\ParseDateString; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; -use FireflyIII\TransactionRules\Factory\ExpressionLanguageFactory; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; use Illuminate\Validation\Validator; use PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException; @@ -258,20 +257,16 @@ class FireflyValidator extends Validator public function validateRuleActionExpression(string $attribute, string $value = null): bool { $value ??= ''; + $expr = new ActionExpression($value); - $el = ExpressionLanguageFactory::get(); - $evaluator = new ActionExpressionEvaluator($el, $value); - - return $evaluator->isValid(); + return $expr->isValid(); } public function replaceRuleActionExpression(string $message, string $attribute): string { $value = $this->getValue($attribute); - - $el = ExpressionLanguageFactory::get(); - $evaluator = new ActionExpressionEvaluator($el, $value); - $err = $evaluator->getValidationError(); + $expr = new ActionExpression($value); + $err = $expr->getValidationError(); if ($err == null) { return $message; From f41397eb4365a2fa320bf5105fcd5f5fb6ce0949 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 17:18:46 -0500 Subject: [PATCH 05/16] refactor: add method on RuleAction to compute action value --- app/Models/RuleAction.php | 7 ++++ app/TransactionRules/Actions/AddTag.php | 13 +++---- .../Actions/AppendDescription.php | 12 ++---- app/TransactionRules/Actions/AppendNotes.php | 17 ++++----- .../Actions/ConvertToDeposit.php | 37 ++++++++++--------- .../Actions/ConvertToTransfer.php | 28 +++++++------- .../Actions/ConvertToWithdrawal.php | 37 ++++++++++--------- app/TransactionRules/Actions/LinkToBill.php | 16 ++++---- .../Actions/PrependDescription.php | 12 ++---- app/TransactionRules/Actions/PrependNotes.php | 17 ++++----- app/TransactionRules/Actions/RemoveTag.php | 15 +++----- app/TransactionRules/Actions/SetBudget.php | 10 ++--- app/TransactionRules/Actions/SetCategory.php | 10 ++--- .../Actions/SetDescription.php | 17 ++++----- .../Actions/SetDestinationAccount.php | 11 ++---- app/TransactionRules/Actions/SetNotes.php | 13 +++---- .../Actions/SetSourceAccount.php | 21 +++++------ .../Actions/UpdatePiggybank.php | 18 ++++----- .../Factory/ActionFactory.php | 4 +- 19 files changed, 138 insertions(+), 177 deletions(-) diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index f8f8e5fe12..cf088a9275 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -26,6 +26,7 @@ namespace FireflyIII\Models; use Carbon\Carbon; use Eloquent; use FireflyIII\Support\Models\ReturnsIntegerIdTrait; +use FireflyIII\TransactionRules\Expressions\ActionExpression; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; @@ -93,4 +94,10 @@ class RuleAction extends Model get: static fn ($value) => (int)$value, ); } + + public function getValue(array $journal) + { + $expr = new ActionExpression($this->action_value); + return $expr->evaluate($journal); + } } diff --git a/app/TransactionRules/Actions/AddTag.php b/app/TransactionRules/Actions/AddTag.php index dbe22a8bdb..3381c86277 100644 --- a/app/TransactionRules/Actions/AddTag.php +++ b/app/TransactionRules/Actions/AddTag.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,7 +28,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\TagFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,16 +35,14 @@ use FireflyIII\User; */ class AddTag implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -58,7 +54,7 @@ class AddTag implements ActionInterface /** @var User $user */ $user = User::find($journal['user_id']); $factory->setUser($user); - $tagName = $this->expr->evaluate($journal); + $tagName = $this->action->getValue($journal); $tag = $factory->findOrCreate($tagName); if (null === $tag) { @@ -71,7 +67,8 @@ class AddTag implements ActionInterface $count = \DB::table('tag_transaction_journal') ->where('tag_id', $tag->id) ->where('transaction_journal_id', $journal['transaction_journal_id']) - ->count(); + ->count() + ; if (0 === $count) { // add to journal: \DB::table('tag_transaction_journal')->insert(['tag_id' => $tag->id, 'transaction_journal_id' => $journal['transaction_journal_id']]); diff --git a/app/TransactionRules/Actions/AppendDescription.php b/app/TransactionRules/Actions/AppendDescription.php index f19927b62f..42d6c58151 100644 --- a/app/TransactionRules/Actions/AppendDescription.php +++ b/app/TransactionRules/Actions/AppendDescription.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,29 +26,26 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class AppendDescription. */ class AppendDescription implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); - $description = sprintf('%s %s', $journal['description'], $actionValue); + $append = $this->action->getValue($journal); + $description = sprintf('%s %s', $journal['description'], $append); \DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $description]); // event for audit log entry diff --git a/app/TransactionRules/Actions/AppendNotes.php b/app/TransactionRules/Actions/AppendNotes.php index 8b42680dd9..e584a48251 100644 --- a/app/TransactionRules/Actions/AppendNotes.php +++ b/app/TransactionRules/Actions/AppendNotes.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -28,46 +27,44 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class AppendNotes. */ class AppendNotes implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->first(['notes.*']) + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; $dbNote->noteable_type = TransactionJournal::class; $dbNote->text = ''; } - app('log')->debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $actionValue, $dbNote->text)); $before = $dbNote->text; - $text = sprintf('%s%s', $dbNote->text, $actionValue); + $append = $this->action->getValue($journal); + $text = sprintf('%s%s', $dbNote->text, $append); $dbNote->text = $text; $dbNote->save(); /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); + app('log')->debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $append, $before)); event(new TriggeredAuditLog($this->action->rule, $object, 'update_notes', $before, $text)); return true; diff --git a/app/TransactionRules/Actions/ConvertToDeposit.php b/app/TransactionRules/Actions/ConvertToDeposit.php index dbb8ffe9a8..942df9f6a6 100644 --- a/app/TransactionRules/Actions/ConvertToDeposit.php +++ b/app/TransactionRules/Actions/ConvertToDeposit.php @@ -1,5 +1,4 @@ action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); + $actionValue = $this->action->getValue($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ - $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); + $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); return false; } - $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); + $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); @@ -76,7 +72,7 @@ class ConvertToDeposit implements ActionInterface } app('log')->debug(sprintf('Convert journal #%d to deposit.', $journal['transaction_journal_id'])); - $type = $object->transactionType->type; + $type = $object->transactionType->type; if (TransactionType::DEPOSIT === $type) { app('log')->error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit'))); @@ -128,7 +124,7 @@ class ConvertToDeposit implements ActionInterface * * @throws FireflyException */ - private function convertWithdrawalArray(TransactionJournal $journal, string $actionValue): bool + private function convertWithdrawalArray(TransactionJournal $journal, string $actionValue = ''): bool { $user = $journal->user; @@ -159,20 +155,23 @@ class ConvertToDeposit implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + ->update(['account_id' => $opposingAccount->id]) + ; // update the destination transaction and put in the original source account ID. \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '>', 0) - ->update(['account_id' => $sourceAccount->id]); + ->update(['account_id' => $sourceAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted withdrawal to deposit.'); @@ -214,7 +213,7 @@ class ConvertToDeposit implements ActionInterface * * @throws FireflyException */ - private function convertTransferArray(TransactionJournal $journal, string $actionValue): bool + private function convertTransferArray(TransactionJournal $journal, string $actionValue = ''): bool { $user = $journal->user; @@ -238,20 +237,22 @@ class ConvertToDeposit implements ActionInterface $opposingAccount = $factory->findOrCreate($opposingName, AccountType::REVENUE); } - app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $this->action->action_value, $opposingAccount->name)); + app('log')->debug(sprintf('ConvertToDeposit. Action value is "%s", revenue name is "%s"', $actionValue, $opposingAccount->name)); // update source transaction(s) to be revenue account \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '<', 0) - ->update(['account_id' => $opposingAccount->id]); + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted transfer to deposit.'); diff --git a/app/TransactionRules/Actions/ConvertToTransfer.php b/app/TransactionRules/Actions/ConvertToTransfer.php index bf9ede733f..f2a0438f74 100644 --- a/app/TransactionRules/Actions/ConvertToTransfer.php +++ b/app/TransactionRules/Actions/ConvertToTransfer.php @@ -1,5 +1,4 @@ action = $action; - $this->expr = $expr; } /** @@ -59,7 +55,7 @@ class ConvertToTransfer implements ActionInterface */ public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); + $accountName = $this->action->getValue($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ @@ -108,7 +104,7 @@ class ConvertToTransfer implements ActionInterface $expectedType = $this->getDestinationType($journalId); // Deposit? Replace source with account with same type as destination. } - $opposing = $repository->findByName($actionValue, [$expectedType]); + $opposing = $repository->findByName($accountName, [$expectedType]); if (null === $opposing) { app('log')->error( @@ -116,11 +112,11 @@ class ConvertToTransfer implements ActionInterface 'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).', $expectedType, $journalId, - $actionValue, + $accountName, $this->action->rule_id ) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $actionValue]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $accountName]))); return false; } @@ -214,14 +210,16 @@ class ConvertToTransfer implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '>', 0) - ->update(['account_id' => $opposing->id]); + ->update(['account_id' => $opposing->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted withdrawal to transfer.'); @@ -267,14 +265,16 @@ class ConvertToTransfer implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '<', 0) - ->update(['account_id' => $opposing->id]); + ->update(['account_id' => $opposing->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::TRANSFER)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]); + ->update(['transaction_type_id' => $newType->id, 'bill_id' => null]) + ; app('log')->debug('Converted deposit to transfer.'); diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index 8af0a67a35..79c18a0333 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -1,5 +1,4 @@ action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); + $actionValue = $this->action->getValue($journal); // make object from array (so the data is fresh). /** @var null|TransactionJournal $object */ - $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); + $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); if (null === $object) { app('log')->error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found'))); return false; } - $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); + $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); if ($groupCount > 1) { app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id'])); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); @@ -75,7 +71,7 @@ class ConvertToWithdrawal implements ActionInterface return false; } - $type = $object->transactionType->type; + $type = $object->transactionType->type; if (TransactionType::WITHDRAWAL === $type) { app('log')->error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal'))); @@ -123,7 +119,7 @@ class ConvertToWithdrawal implements ActionInterface /** * @throws FireflyException */ - private function convertDepositArray(TransactionJournal $journal, string $actionValue): bool + private function convertDepositArray(TransactionJournal $journal, string $actionValue = ''): bool { $user = $journal->user; @@ -139,7 +135,7 @@ class ConvertToWithdrawal implements ActionInterface // get the action value, or use the original source name in case the action value is empty: // this becomes a new or existing (expense) account, which is the destination of the new withdrawal. - $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue; + $opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue; // we check all possible source account types if one exists: $validTypes = config('firefly.expected_source_types.destination.Withdrawal'); $opposingAccount = $repository->findByName($opposingName, $validTypes); @@ -153,19 +149,22 @@ class ConvertToWithdrawal implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '<', 0) - ->update(['account_id' => $destAccount->id]); + ->update(['account_id' => $destAccount->id]) + ; // update destination transaction(s) to be new expense account. \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + ->update(['transaction_type_id' => $newType->id]) + ; app('log')->debug('Converted deposit to withdrawal.'); @@ -206,7 +205,7 @@ class ConvertToWithdrawal implements ActionInterface * * @throws FireflyException */ - private function convertTransferArray(TransactionJournal $journal, string $actionValue): bool + private function convertTransferArray(TransactionJournal $journal, string $actionValue = ''): bool { // find or create expense account. $user = $journal->user; @@ -236,13 +235,15 @@ class ConvertToWithdrawal implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $journal->id) ->where('amount', '>', 0) - ->update(['account_id' => $opposingAccount->id]); + ->update(['account_id' => $opposingAccount->id]) + ; // change transaction type of journal: $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); \DB::table('transaction_journals') ->where('id', '=', $journal->id) - ->update(['transaction_type_id' => $newType->id]); + ->update(['transaction_type_id' => $newType->id]) + ; app('log')->debug('Converted transfer to withdrawal.'); diff --git a/app/TransactionRules/Actions/LinkToBill.php b/app/TransactionRules/Actions/LinkToBill.php index 6b9a49ed3a..a073c657e1 100644 --- a/app/TransactionRules/Actions/LinkToBill.php +++ b/app/TransactionRules/Actions/LinkToBill.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -30,7 +29,6 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Bill\BillRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -38,16 +36,14 @@ use FireflyIII\User; */ class LinkToBill implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool @@ -58,12 +54,13 @@ class LinkToBill implements ActionInterface /** @var BillRepositoryInterface $repository */ $repository = app(BillRepositoryInterface::class); $repository->setUser($user); - $billName = $this->expr->evaluate($journal); + $billName = $this->action->getValue($journal); $bill = $repository->findByName($billName); if (null !== $bill && TransactionType::WITHDRAWAL === $journal['transaction_type_type']) { $count = \DB::table('transaction_journals')->where('id', '=', $journal['transaction_journal_id']) - ->where('bill_id', $bill->id)->count(); + ->where('bill_id', $bill->id)->count() + ; if (0 !== $count) { app('log')->error( sprintf( @@ -79,7 +76,8 @@ class LinkToBill implements ActionInterface \DB::table('transaction_journals') ->where('id', '=', $journal['transaction_journal_id']) - ->update(['bill_id' => $bill->id]); + ->update(['bill_id' => $bill->id]) + ; app('log')->debug( sprintf('RuleAction LinkToBill set the bill of journal #%d to bill #%d ("%s").', $journal['transaction_journal_id'], $bill->id, $bill->name) ); diff --git a/app/TransactionRules/Actions/PrependDescription.php b/app/TransactionRules/Actions/PrependDescription.php index 2be503f475..47fda2aab5 100644 --- a/app/TransactionRules/Actions/PrependDescription.php +++ b/app/TransactionRules/Actions/PrependDescription.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,31 +26,26 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class PrependDescription. */ class PrependDescription implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); - $before = $journal['description']; - $after = sprintf('%s%s', $actionValue, $journal['description']); + $after = sprintf('%s%s', $this->action->getValue($journal), $journal['description']); \DB::table('transaction_journals')->where('id', $journal['transaction_journal_id'])->limit(1)->update(['description' => $after]); // journal diff --git a/app/TransactionRules/Actions/PrependNotes.php b/app/TransactionRules/Actions/PrependNotes.php index a9e9756755..31e455a38b 100644 --- a/app/TransactionRules/Actions/PrependNotes.php +++ b/app/TransactionRules/Actions/PrependNotes.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -28,31 +27,28 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class PrependNotes. */ class PrependNotes implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - $actionValue = $this->expr->evaluate($journal); $dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id']) ->where('noteable_type', TransactionJournal::class) - ->first(['notes.*']); + ->first(['notes.*']) + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = (int)$journal['transaction_journal_id']; @@ -60,8 +56,9 @@ class PrependNotes implements ActionInterface $dbNote->text = ''; } $before = $dbNote->text; - app('log')->debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $actionValue, $dbNote->text)); - $text = sprintf('%s%s', $actionValue, $dbNote->text); + $after = $this->action->getValue($journal); + app('log')->debug(sprintf('RuleAction PrependNotes prepended "%s" to "%s".', $after, $dbNote->text)); + $text = sprintf('%s%s', $after, $dbNote->text); $dbNote->text = $text; $dbNote->save(); diff --git a/app/TransactionRules/Actions/RemoveTag.php b/app/TransactionRules/Actions/RemoveTag.php index 846bcbb05a..a717043ce2 100644 --- a/app/TransactionRules/Actions/RemoveTag.php +++ b/app/TransactionRules/Actions/RemoveTag.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -28,7 +27,6 @@ use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -36,27 +34,25 @@ use FireflyIII\User; */ class RemoveTag implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { - // if tag does not exist, no need to continue: - $name = $this->expr->evaluate($journal); + $name = $this->action->getValue($journal); /** @var User $user */ $user = User::find($journal['user_id']); $tag = $user->tags()->where('tag', $name)->first(); + // if tag does not exist, no need to continue: if (null === $tag) { app('log')->debug( sprintf('RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.', $name, $journal['transaction_journal_id']) @@ -79,7 +75,8 @@ class RemoveTag implements ActionInterface \DB::table('tag_transaction_journal') ->where('transaction_journal_id', $journal['transaction_journal_id']) ->where('tag_id', $tag->id) - ->delete(); + ->delete() + ; /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); diff --git a/app/TransactionRules/Actions/SetBudget.php b/app/TransactionRules/Actions/SetBudget.php index 880d05bed8..067dc93a49 100644 --- a/app/TransactionRules/Actions/SetBudget.php +++ b/app/TransactionRules/Actions/SetBudget.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,7 +28,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,23 +35,21 @@ use FireflyIII\User; */ class SetBudget implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { /** @var User $user */ $user = User::find($journal['user_id']); - $search = $this->expr->evaluate($journal); + $search = $this->action->getValue($journal); $budget = $user->budgets()->where('name', $search)->first(); if (null === $budget) { diff --git a/app/TransactionRules/Actions/SetCategory.php b/app/TransactionRules/Actions/SetCategory.php index 1c2ef454e3..760f0e01d5 100644 --- a/app/TransactionRules/Actions/SetCategory.php +++ b/app/TransactionRules/Actions/SetCategory.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -29,7 +28,6 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Factory\CategoryFactory; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -37,23 +35,21 @@ use FireflyIII\User; */ class SetCategory implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { /** @var null|User $user */ $user = User::find($journal['user_id']); - $search = $this->expr->evaluate($journal); + $search = $this->action->getValue($journal); if (null === $user) { app('log')->error(sprintf('Journal has no valid user ID so action SetCategory("%s") cannot be applied', $search), $journal); event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_such_journal'))); diff --git a/app/TransactionRules/Actions/SetDescription.php b/app/TransactionRules/Actions/SetDescription.php index 3d55d2f0fe..f423806e5e 100644 --- a/app/TransactionRules/Actions/SetDescription.php +++ b/app/TransactionRules/Actions/SetDescription.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -27,41 +26,39 @@ namespace FireflyIII\TransactionRules\Actions; use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class SetDescription. */ class SetDescription implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { /** @var TransactionJournal $object */ $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); - $before = $journal['description']; - $after = $this->expr->evaluate($journal); + $before = $object->description; + $after = $this->action->getValue($journal); \DB::table('transaction_journals') ->where('id', '=', $journal['transaction_journal_id']) - ->update(['description' => $this->action->action_value]); + ->update(['description' => $after]) + ; app('log')->debug( sprintf( 'RuleAction SetDescription changed the description of journal #%d from "%s" to "%s".', $journal['transaction_journal_id'], - $before, + $journal['description'], $after ) ); diff --git a/app/TransactionRules/Actions/SetDestinationAccount.php b/app/TransactionRules/Actions/SetDestinationAccount.php index 6521e02e94..4b8498de8d 100644 --- a/app/TransactionRules/Actions/SetDestinationAccount.php +++ b/app/TransactionRules/Actions/SetDestinationAccount.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -32,7 +31,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; /** @@ -41,21 +39,19 @@ use FireflyIII\User; class SetDestinationAccount implements ActionInterface { private RuleAction $action; - private ActionExpressionEvaluator $evaluator; private AccountRepositoryInterface $repository; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action) { $this->action = $action; - $this->evaluator = $evaluator; } public function actOnArray(array $journal): bool { - $accountName = $this->evaluator->evaluate($journal); + $accountName = $this->action->getValue($journal); /** @var User $user */ $user = User::find($journal['user_id']); @@ -132,7 +128,8 @@ class SetDestinationAccount implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $object->id) ->where('amount', '>', 0) - ->update(['account_id' => $newAccount->id]); + ->update(['account_id' => $newAccount->id]) + ; app('log')->debug(sprintf('Updated journal #%d (group #%d) and gave it new destination account ID.', $object->id, $object->transaction_group_id)); diff --git a/app/TransactionRules/Actions/SetNotes.php b/app/TransactionRules/Actions/SetNotes.php index 6d5c86363d..e8e3e92f2c 100644 --- a/app/TransactionRules/Actions/SetNotes.php +++ b/app/TransactionRules/Actions/SetNotes.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -28,29 +27,27 @@ use FireflyIII\Events\TriggeredAuditLog; use FireflyIII\Models\Note; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class SetNotes. */ class SetNotes implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { $dbNote = Note::where('noteable_id', $journal['transaction_journal_id']) - ->where('noteable_type', TransactionJournal::class)->first(); + ->where('noteable_type', TransactionJournal::class)->first() + ; if (null === $dbNote) { $dbNote = new Note(); $dbNote->noteable_id = $journal['transaction_journal_id']; @@ -58,7 +55,7 @@ class SetNotes implements ActionInterface $dbNote->text = ''; } $oldNotes = $dbNote->text; - $newNotes = $this->expr->evaluate($journal); + $newNotes = $this->action->getValue($journal); $dbNote->text = $newNotes; $dbNote->save(); diff --git a/app/TransactionRules/Actions/SetSourceAccount.php b/app/TransactionRules/Actions/SetSourceAccount.php index dc195b9d61..9da778d2a6 100644 --- a/app/TransactionRules/Actions/SetSourceAccount.php +++ b/app/TransactionRules/Actions/SetSourceAccount.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Actions; @@ -32,7 +31,6 @@ use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpressionEvaluator; use FireflyIII\User; /** @@ -41,21 +39,19 @@ use FireflyIII\User; class SetSourceAccount implements ActionInterface { private RuleAction $action; - private ActionExpressionEvaluator $evaluator; private AccountRepositoryInterface $repository; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpressionEvaluator $evaluator) + public function __construct(RuleAction $action) { $this->action = $action; - $this->evaluator = $evaluator; } public function actOnArray(array $journal): bool { - $accountName = $this->evaluator->evaluate($journal); + $accountName = $this->action->getValue($journal); /** @var User $user */ $user = User::find($journal['user_id']); @@ -124,7 +120,8 @@ class SetSourceAccount implements ActionInterface \DB::table('transactions') ->where('transaction_journal_id', '=', $object->id) ->where('amount', '<', 0) - ->update(['account_id' => $newAccount->id]); + ->update(['account_id' => $newAccount->id]) + ; event(new TriggeredAuditLog($this->action->rule, $object, 'set_source', null, $newAccount->name)); @@ -133,24 +130,24 @@ class SetSourceAccount implements ActionInterface return true; } - private function findAssetAccount(string $type, string $name): ?Account + private function findAssetAccount(string $type, string $accountName): ?Account { // switch on type: $allowed = config(sprintf('firefly.expected_source_types.source.%s', $type)); $allowed = is_array($allowed) ? $allowed : []; app('log')->debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed); - return $this->repository->findByName($name, $allowed); + return $this->repository->findByName($accountName, $allowed); } - private function findDepositSourceAccount(string $name): Account + private function findDepositSourceAccount(string $accountName): Account { $allowed = config('firefly.expected_source_types.source.Deposit'); - $account = $this->repository->findByName($name, $allowed); + $account = $this->repository->findByName($accountName, $allowed); if (null === $account) { // create new revenue account with this name: $data = [ - 'name' => $name, + 'name' => $accountName, 'account_type_name' => 'revenue', 'account_type_id' => null, 'virtual_balance' => 0, diff --git a/app/TransactionRules/Actions/UpdatePiggybank.php b/app/TransactionRules/Actions/UpdatePiggybank.php index 4498dd3f7e..a978ed4946 100644 --- a/app/TransactionRules/Actions/UpdatePiggybank.php +++ b/app/TransactionRules/Actions/UpdatePiggybank.php @@ -31,7 +31,6 @@ use FireflyIII\Models\RuleAction; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use FireflyIII\User; /** @@ -39,23 +38,22 @@ use FireflyIII\User; */ class UpdatePiggybank implements ActionInterface { - private RuleAction $action; - private ActionExpression $expr; + private RuleAction $action; /** * TriggerInterface constructor. */ - public function __construct(RuleAction $action, ActionExpression $expr) + public function __construct(RuleAction $action) { $this->action = $action; - $this->expr = $expr; } public function actOnArray(array $journal): bool { + $actionValue = $this->action->getValue($journal); + app('log')->debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal['transaction_journal_id'])); - $piggyBankName = $this->expr->evaluate($journal); // refresh the transaction type. /** @var User $user */ $user = User::find($journal['user_id']); @@ -63,12 +61,12 @@ class UpdatePiggybank implements ActionInterface /** @var TransactionJournal $journalObj */ $journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']); - $piggyBank = $this->findPiggyBank($user, $piggyBankName); + $piggyBank = $this->findPiggyBank($user, $actionValue); if (null === $piggyBank) { app('log')->info( - sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $piggyBankName, $this->action->id, $this->action->rule_id) + sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $actionValue, $this->action->id, $this->action->rule_id) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $piggyBankName]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue]))); return false; } @@ -130,7 +128,7 @@ class UpdatePiggybank implements ActionInterface $destination->account_id ) ); - event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $piggyBankName]))); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $actionValue]))); return false; } diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index c20120d807..83209926c2 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -51,9 +51,7 @@ class ActionFactory $class = self::getActionClass($action->action_type); app('log')->debug(sprintf('self::getActionClass("%s") = "%s"', $action->action_type, $class)); - $expr = new ActionExpression($action->action_value); - - return new $class($action, $expr); // @phpstan-ignore-line + return new $class($action); // @phpstan-ignore-line } /** From 55a2b4e789b2375126c9aae7d0f02ea42a643729 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 20:58:43 -0500 Subject: [PATCH 06/16] feat: make all transaction journal variables globals removes redundant reference to the `transaction` object by making all its properties global --- .../Expressions/ActionExpression.php | 51 +++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/app/TransactionRules/Expressions/ActionExpression.php b/app/TransactionRules/Expressions/ActionExpression.php index 1d498ea78e..37d506f6e8 100644 --- a/app/TransactionRules/Expressions/ActionExpression.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -29,7 +29,52 @@ use Symfony\Component\ExpressionLanguage\SyntaxError; class ActionExpression { - private static array $NAMES = array("transaction"); + private static array $NAMES = array( + "id", + "created_at", + "updated_at", + "deleted_at", + "user_id", + "transaction_type_id", + "transaction_group_id", + "bill_id", + "transaction_currency_id", + "description", + "date", + "interest_date", + "book_date", + "process_date", + "order", + "tag_count", + "transaction_type_type", + "encrypted", + "completed", + "attachments", + "attachments_count", + "bill", + "budgets", + "budgets_count", + "categories", + "categories_count", + "destJournalLinks", + "dest_journal_links_count", + "notes", + "notes_count", + "piggyBankEvents", + "piggy_bank_events_count", + "sourceJournalLinks", + "source_journal_links_count", + "tags", + "tags_count", + "transactionCurrency", + "transactionGroup", + "transactionJournalMeta", + "transaction_journal_meta_count", + "transactionType", + "transactions", + "transactions_count", + "user", + ); private ExpressionLanguage $expressionLanguage; private string $expr; @@ -90,9 +135,7 @@ class ActionExpression private function evaluateExpression(string $expr, array $journal): string { - $result = $this->expressionLanguage->evaluate($expr, [ - "transaction" => $journal - ]); + $result = $this->expressionLanguage->evaluate($expr, $journal); return strval($result); } From 6acd5be5dc63f48fd0e9eacd0bf489c4d280dd56 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 21:10:11 -0500 Subject: [PATCH 07/16] chore: remove accidental changes --- app/Providers/FireflyServiceProvider.php | 1 - app/TransactionRules/Factory/ActionFactory.php | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php index 200d9c1372..ff7a4f6d30 100644 --- a/app/Providers/FireflyServiceProvider.php +++ b/app/Providers/FireflyServiceProvider.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\Providers; diff --git a/app/TransactionRules/Factory/ActionFactory.php b/app/TransactionRules/Factory/ActionFactory.php index 83209926c2..23780bce6d 100644 --- a/app/TransactionRules/Factory/ActionFactory.php +++ b/app/TransactionRules/Factory/ActionFactory.php @@ -19,7 +19,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ - declare(strict_types=1); namespace FireflyIII\TransactionRules\Factory; @@ -28,7 +27,6 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleAction; use FireflyIII\Support\Domain; use FireflyIII\TransactionRules\Actions\ActionInterface; -use FireflyIII\TransactionRules\Expressions\ActionExpression; /** * Class ActionFactory can create actions. @@ -66,12 +64,12 @@ class ActionFactory $actionTypes = self::getActionTypes(); if (!array_key_exists($actionType, $actionTypes)) { - throw new FireflyException('No such action exists ("' . e($actionType) . '").'); + throw new FireflyException('No such action exists ("'.e($actionType).'").'); } $class = $actionTypes[$actionType]; if (!class_exists($class)) { - throw new FireflyException('Could not instantiate class for rule action type "' . e($actionType) . '" (' . e($class) . ').'); + throw new FireflyException('Could not instantiate class for rule action type "'.e($actionType).'" ('.e($class).').'); } return $class; From 34349e4475717aa586966dfbaa404ac117f73cee Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 7 Mar 2024 21:37:24 -0500 Subject: [PATCH 08/16] chore: fix typo --- app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php index b12c81d2b1..dbd9faa1d7 100644 --- a/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php +++ b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php @@ -29,7 +29,7 @@ use FireflyIII\Support\Request\ConvertsDataTypes; use Illuminate\Foundation\Http\FormRequest; /** - * Class TestRequest + * Class ValidateExpressionRequest */ class ValidateExpressionRequest extends FormRequest { From fc2da9eb42991b52993a875eace3284043caf4c3 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 11:27:21 -0500 Subject: [PATCH 09/16] fix(ExpressionController): remove unnecessary rule repository --- .../Models/Rule/ExpressionController.php | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php index f9737e04df..fd1d51ab2a 100644 --- a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -25,9 +25,7 @@ namespace FireflyIII\Api\V1\Controllers\Models\Rule; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Rule\ValidateExpressionRequest; -use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\TransactionRules\Expressions\ActionExpression; -use FireflyIII\User; use Illuminate\Http\JsonResponse; /** @@ -35,29 +33,6 @@ use Illuminate\Http\JsonResponse; */ class ExpressionController extends Controller { - private RuleRepositoryInterface $ruleRepository; - - /** - * RuleController constructor. - * - - */ - public function __construct() - { - parent::__construct(); - $this->middleware( - function ($request, $next) { - /** @var User $user */ - $user = auth()->user(); - - $this->ruleRepository = app(RuleRepositoryInterface::class); - $this->ruleRepository->setUser($user); - - return $next($request); - } - ); - } - /** * This endpoint is documented at: * https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/rules/validateExpression From d22f9c09d7b57df5304bb60f60caf739e7b45bb1 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 12:02:47 -0500 Subject: [PATCH 10/16] fix(RuleAction): add return type to `getValue` --- app/Models/RuleAction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index cf088a9275..a6f8d44c71 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -95,7 +95,7 @@ class RuleAction extends Model ); } - public function getValue(array $journal) + public function getValue(array $journal): string { $expr = new ActionExpression($this->action_value); return $expr->evaluate($journal); From f19bfc3b4b2f1c60e1e124ea2d77c5af9e7b36ad Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 12:03:46 -0500 Subject: [PATCH 11/16] fix(ActionExpression): update list of valid variable names to reflect actual values --- .../Expressions/ActionExpression.php | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/app/TransactionRules/Expressions/ActionExpression.php b/app/TransactionRules/Expressions/ActionExpression.php index 37d506f6e8..c4069dee92 100644 --- a/app/TransactionRules/Expressions/ActionExpression.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -30,50 +30,55 @@ use Symfony\Component\ExpressionLanguage\SyntaxError; class ActionExpression { private static array $NAMES = array( - "id", + "transaction_group_id", + "user_id", + "user_group_id", "created_at", "updated_at", - "deleted_at", - "user_id", + "transaction_group_title", + "group_created_at", + "group_updated_at", + "transaction_journal_id", "transaction_type_id", - "transaction_group_id", - "bill_id", - "transaction_currency_id", "description", "date", - "interest_date", - "book_date", - "process_date", "order", - "tag_count", "transaction_type_type", - "encrypted", - "completed", - "attachments", - "attachments_count", - "bill", - "budgets", - "budgets_count", - "categories", - "categories_count", - "destJournalLinks", - "dest_journal_links_count", - "notes", - "notes_count", - "piggyBankEvents", - "piggy_bank_events_count", - "sourceJournalLinks", - "source_journal_links_count", + "source_transaction_id", + "source_account_id", + "reconciled", + "amount", + "currency_id", + "currency_code", + "currency_name", + "currency_symbol", + "currency_decimal_places", + "foreign_amount", + "foreign_currency_id", + "foreign_currency_code", + "foreign_currency_name", + "foreign_currency_symbol", + "foreign_currency_decimal_places", + "destination_account_id", + "source_account_name", + "source_account_iban", + "source_account_type", + "destination_account_name", + "destination_account_iban", + "destination_account_type", + "category_id", + "category_name", + "budget_id", + "budget_name", "tags", - "tags_count", - "transactionCurrency", - "transactionGroup", - "transactionJournalMeta", - "transaction_journal_meta_count", - "transactionType", - "transactions", - "transactions_count", - "user", + "attachments", + "interest_date", + "payment_date", + "invoice_date", + "book_date", + "due_date", + "process_date", + "destination_transaction_id" ); private ExpressionLanguage $expressionLanguage; From b38b7b2534c6336e4d0d0782e2afe84183aa53fc Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 12:05:56 -0500 Subject: [PATCH 12/16] fix: drop unnecessary changes to composer.lock --- composer.lock | 1682 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 1285 insertions(+), 397 deletions(-) diff --git a/composer.lock b/composer.lock index 52b57a37e7..0c43ded69a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "58ae8806859163b7e368713d917a12e0", + "content-hash": "9cfa71fcc341ecf39399d0a0ff39dd69", "packages": [ { "name": "bacon/bacon-qr-code", @@ -41,7 +41,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-2-Clause"], + "license": [ + "BSD-2-Clause" + ], "authors": [ { "name": "Ben Scholzen 'DASPRiD'", @@ -87,7 +89,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Arbitrary-precision arithmetic library", "keywords": [ "Arbitrary-precision", @@ -143,7 +147,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "KyleKatarn", @@ -151,7 +157,13 @@ } ], "description": "Types to use Carbon in Doctrine", - "keywords": ["carbon", "date", "datetime", "doctrine", "time"], + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], "support": { "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" @@ -200,7 +212,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-2-Clause"], + "license": [ + "BSD-2-Clause" + ], "authors": [ { "name": "Ben Scholzen 'DASPRiD'", @@ -210,7 +224,10 @@ } ], "description": "PHP 7.1 enum implementation", - "keywords": ["enum", "map"], + "keywords": [ + "enum", + "map" + ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" @@ -240,7 +257,9 @@ "phpunit/phpunit": "^5|^6|^7|^8|^9|^10", "yoast/phpunit-polyfills": "^2.0.0" }, - "bin": ["bin/generate-defuse-key"], + "bin": [ + "bin/generate-defuse-key" + ], "type": "library", "autoload": { "psr-4": { @@ -248,7 +267,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Hornby", @@ -316,7 +337,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Dragonfly Development Inc.", @@ -341,7 +364,12 @@ ], "description": "Given a deep data structure, access data by dot notation.", "homepage": "https://github.com/dflydev/dflydev-dot-access-data", - "keywords": ["access", "data", "dot", "notation"], + "keywords": [ + "access", + "data", + "dot", + "notation" + ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" @@ -379,7 +407,9 @@ "type": "library", "extra": { "laravel": { - "providers": ["Diglactic\\Breadcrumbs\\ServiceProvider"], + "providers": [ + "Diglactic\\Breadcrumbs\\ServiceProvider" + ], "aliases": { "Breadcrumbs": "Diglactic\\Breadcrumbs\\Breadcrumbs" } @@ -391,7 +421,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Sheng Slogar", @@ -406,7 +438,9 @@ ], "description": "A simple Laravel-style way to create breadcrumbs.", "homepage": "https://github.com/diglactic/laravel-breadcrumbs", - "keywords": ["laravel"], + "keywords": [ + "laravel" + ], "support": { "issues": "https://github.com/diglactic/laravel-breadcrumbs/issues", "source": "https://github.com/diglactic/laravel-breadcrumbs/tree/v8.1.1" @@ -448,7 +482,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Guilherme Blanco", @@ -544,7 +580,9 @@ "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, - "bin": ["bin/doctrine-dbal"], + "bin": [ + "bin/doctrine-dbal" + ], "type": "library", "autoload": { "psr-4": { @@ -552,7 +590,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Guilherme Blanco", @@ -649,7 +689,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", "homepage": "https://www.doctrine-project.org/", "support": { @@ -691,7 +733,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Guilherme Blanco", @@ -779,7 +823,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Guilherme Blanco", @@ -867,7 +913,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Guilherme Blanco", @@ -884,7 +932,13 @@ ], "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": ["annotations", "docblock", "lexer", "parser", "php"], + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], "support": { "issues": "https://github.com/doctrine/lexer/issues", "source": "https://github.com/doctrine/lexer/tree/3.0.1" @@ -939,7 +993,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Chris Tankersley", @@ -948,7 +1004,10 @@ } ], "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", - "keywords": ["cron", "schedule"], + "keywords": [ + "cron", + "schedule" + ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" @@ -999,7 +1058,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Eduardo Gulias Davis" @@ -1055,7 +1116,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Freek Van der Herten", @@ -1066,7 +1129,11 @@ ], "description": "Solution contracts for Ignition", "homepage": "https://github.com/facade/ignition-contracts", - "keywords": ["contracts", "flare", "ignition"], + "keywords": [ + "contracts", + "flare", + "ignition" + ], "support": { "issues": "https://github.com/facade/ignition-contracts/issues", "source": "https://github.com/facade/ignition-contracts/tree/1.0.2" @@ -1112,7 +1179,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Filipe Dobreira", @@ -1178,7 +1247,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Neuman Vong", @@ -1193,7 +1264,10 @@ ], "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", "homepage": "https://github.com/firebase/php-jwt", - "keywords": ["jwt", "php"], + "keywords": [ + "jwt", + "php" + ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", "source": "https://github.com/firebase/php-jwt/tree/v6.10.0" @@ -1235,7 +1309,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fruitcake", @@ -1248,7 +1324,11 @@ ], "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", "homepage": "https://github.com/fruitcake/php-cors", - "keywords": ["cors", "laravel", "symfony"], + "keywords": [ + "cors", + "laravel", + "symfony" + ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" @@ -1293,7 +1373,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["Apache-2.0"], + "license": [ + "Apache-2.0" + ], "description": "Php library that converts search queries into terms, phrases, hashtags, mentions, etc.", "homepage": "https://github.com/gdbots/query-parser-php", "support": { @@ -1330,7 +1412,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -1408,13 +1492,17 @@ } }, "autoload": { - "files": ["src/functions_include.php"], + "files": [ + "src/functions_include.php" + ], "psr-4": { "GuzzleHttp\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -1518,7 +1606,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -1542,7 +1632,9 @@ } ], "description": "Guzzle promises library", - "keywords": ["promise"], + "keywords": [ + "promise" + ], "support": { "issues": "https://github.com/guzzle/promises/issues", "source": "https://github.com/guzzle/promises/tree/2.0.2" @@ -1608,7 +1700,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -1713,7 +1807,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Graham Campbell", @@ -1737,7 +1833,10 @@ } ], "description": "A polyfill class for uri_template of PHP", - "keywords": ["guzzlehttp", "uri-template"], + "keywords": [ + "guzzlehttp", + "uri-template" + ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" @@ -1789,7 +1888,9 @@ "type": "library", "extra": { "component": "package", - "frameworks": ["Laravel"], + "frameworks": [ + "Laravel" + ], "branch-alias": { "dev-master": "0.2-dev" }, @@ -1809,7 +1910,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Antonio Carlos Ribeiro", @@ -1874,7 +1977,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Antonio Carlos Ribeiro", @@ -2092,7 +2197,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2101,7 +2208,10 @@ ], "description": "The Laravel Framework.", "homepage": "https://laravel.com", - "keywords": ["framework", "laravel"], + "keywords": [ + "framework", + "laravel" + ], "support": { "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" @@ -2153,7 +2263,9 @@ "dev-master": "11.x-dev" }, "laravel": { - "providers": ["Laravel\\Passport\\PassportServiceProvider"] + "providers": [ + "Laravel\\Passport\\PassportServiceProvider" + ] } }, "autoload": { @@ -2163,7 +2275,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2171,7 +2285,11 @@ } ], "description": "Laravel Passport provides OAuth2 server support to Laravel.", - "keywords": ["laravel", "oauth", "passport"], + "keywords": [ + "laravel", + "oauth", + "passport" + ], "support": { "issues": "https://github.com/laravel/passport/issues", "source": "https://github.com/laravel/passport" @@ -2218,13 +2336,17 @@ } }, "autoload": { - "files": ["src/helpers.php"], + "files": [ + "src/helpers.php" + ], "psr-4": { "Laravel\\Prompts\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "support": { "issues": "https://github.com/laravel/prompts/issues", "source": "https://github.com/laravel/prompts/tree/v0.1.16" @@ -2265,7 +2387,9 @@ "dev-master": "3.x-dev" }, "laravel": { - "providers": ["Laravel\\Sanctum\\SanctumServiceProvider"] + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] } }, "autoload": { @@ -2274,7 +2398,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2282,7 +2408,11 @@ } ], "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", - "keywords": ["auth", "laravel", "sanctum"], + "keywords": [ + "auth", + "laravel", + "sanctum" + ], "support": { "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" @@ -2324,7 +2454,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2336,7 +2468,11 @@ } ], "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", - "keywords": ["closure", "laravel", "serializable"], + "keywords": [ + "closure", + "laravel", + "serializable" + ], "support": { "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" @@ -2387,7 +2523,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2395,7 +2533,11 @@ } ], "description": "Slack Notification Channel for laravel.", - "keywords": ["laravel", "notifications", "slack"], + "keywords": [ + "laravel", + "notifications", + "slack" + ], "support": { "issues": "https://github.com/laravel/slack-notification-channel/issues", "source": "https://github.com/laravel/slack-notification-channel/tree/v3.2.0" @@ -2433,7 +2575,9 @@ "dev-master": "4.x-dev" }, "laravel": { - "providers": ["Laravel\\Ui\\UiServiceProvider"] + "providers": [ + "Laravel\\Ui\\UiServiceProvider" + ] } }, "autoload": { @@ -2443,7 +2587,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Taylor Otwell", @@ -2451,7 +2597,10 @@ } ], "description": "Laravel UI utilities and presets.", - "keywords": ["laravel", "ui"], + "keywords": [ + "laravel", + "ui" + ], "support": { "source": "https://github.com/laravel/ui/tree/v4.5.0" }, @@ -2495,7 +2644,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Luís Cobucci", @@ -2561,7 +2712,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Luís Cobucci", @@ -2570,7 +2723,10 @@ } ], "description": "A simple library to work with JSON Web Token and JSON Web Signature", - "keywords": ["JWS", "jwt"], + "keywords": [ + "JWS", + "jwt" + ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", "source": "https://github.com/lcobucci/jwt/tree/5.2.0" @@ -2643,7 +2799,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Colin O'Dell", @@ -2729,7 +2887,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Colin O'Dell", @@ -2815,13 +2975,17 @@ } }, "autoload": { - "files": ["src/functions_include.php"], + "files": [ + "src/functions_include.php" + ], "psr-4": { "League\\Csv\\": "src" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ignace Nyamagana Butera", @@ -2889,7 +3053,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Frank de Jonge", @@ -2897,7 +3063,11 @@ } ], "description": "Event package", - "keywords": ["emitter", "event", "listener"], + "keywords": [ + "emitter", + "event", + "listener" + ], "support": { "issues": "https://github.com/thephpleague/event/issues", "source": "https://github.com/thephpleague/event/tree/master" @@ -2955,7 +3125,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Frank de Jonge", @@ -3019,7 +3191,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Frank de Jonge", @@ -3027,7 +3201,13 @@ } ], "description": "Local filesystem adapter for Flysystem.", - "keywords": ["Flysystem", "file", "files", "filesystem", "local"], + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], "support": { "issues": "https://github.com/thephpleague/flysystem-local/issues", "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.1" @@ -3089,7 +3269,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Phil Sturgeon", @@ -3100,7 +3282,12 @@ ], "description": "Handle the output of complex data structures ready for API output.", "homepage": "http://fractal.thephpleague.com/", - "keywords": ["api", "json", "league", "rest"], + "keywords": [ + "api", + "json", + "league", + "rest" + ], "support": { "issues": "https://github.com/thephpleague/fractal/issues", "source": "https://github.com/thephpleague/fractal/tree/0.20.1" @@ -3137,7 +3324,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Frank de Jonge", @@ -3203,7 +3392,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Alex Bilbie", @@ -3290,7 +3481,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ignace Nyamagana Butera", @@ -3374,7 +3567,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ignace Nyamagana Butera", @@ -3450,7 +3645,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["proprietary"], + "license": [ + "proprietary" + ], "authors": [ { "name": "Mailchimp", @@ -3458,7 +3655,12 @@ } ], "homepage": "http://swagger.io", - "keywords": ["api", "php", "sdk", "swagger"], + "keywords": [ + "api", + "php", + "sdk", + "swagger" + ], "support": { "source": "https://github.com/mailchimp/mailchimp-transactional-php/tree/v1.0.59" }, @@ -3532,7 +3734,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Jordi Boggiano", @@ -3542,7 +3746,11 @@ ], "description": "Sends your logs to files, sockets, inboxes, databases and various web services", "homepage": "https://github.com/Seldaek/monolog", - "keywords": ["log", "logging", "psr-3"], + "keywords": [ + "log", + "logging", + "psr-3" + ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", "source": "https://github.com/Seldaek/monolog/tree/3.5.0" @@ -3598,7 +3806,9 @@ "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", "squizlabs/php_codesniffer": "^3.4" }, - "bin": ["bin/carbon"], + "bin": [ + "bin/carbon" + ], "type": "library", "extra": { "branch-alias": { @@ -3606,10 +3816,14 @@ "dev-master": "2.x-dev" }, "laravel": { - "providers": ["Carbon\\Laravel\\ServiceProvider"] + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] }, "phpstan": { - "includes": ["extension.neon"] + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -3618,7 +3832,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Brian Nesbitt", @@ -3632,7 +3848,11 @@ ], "description": "An API extension for DateTime that supports 281 different languages.", "homepage": "https://carbon.nesbot.com", - "keywords": ["date", "datetime", "time"], + "keywords": [ + "date", + "datetime", + "time" + ], "support": { "docs": "https://carbon.nesbot.com/docs", "issues": "https://github.com/briannesbitt/Carbon/issues", @@ -3684,10 +3904,16 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], "authors": [ { "name": "David Grudl", @@ -3700,7 +3926,10 @@ ], "description": "📐 Nette Schema: validating data structures against a given Schema.", "homepage": "https://nette.org", - "keywords": ["config", "nette"], + "keywords": [ + "config", + "nette" + ], "support": { "issues": "https://github.com/nette/schema/issues", "source": "https://github.com/nette/schema/tree/v1.3.0" @@ -3749,10 +3978,16 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause", "GPL-2.0-only", "GPL-3.0-only"], + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], "authors": [ { "name": "David Grudl", @@ -3833,13 +4068,17 @@ } }, "autoload": { - "files": ["./src/Adapters/Phpunit/Autoload.php"], + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], "psr-4": { "NunoMaduro\\Collision\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nuno Maduro", @@ -3913,17 +4152,23 @@ "type": "library", "extra": { "laravel": { - "providers": ["Termwind\\Laravel\\TermwindServiceProvider"] + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] } }, "autoload": { - "files": ["src/Functions.php"], + "files": [ + "src/Functions.php" + ], "psr-4": { "Termwind\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nuno Maduro", @@ -3931,7 +4176,14 @@ } ], "description": "Its like Tailwind CSS, but for the console.", - "keywords": ["cli", "console", "css", "package", "php", "style"], + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" @@ -3995,7 +4247,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Tobias Nyholm", @@ -4008,7 +4262,10 @@ ], "description": "A fast PHP7 implementation of PSR-7", "homepage": "https://tnyholm.se", - "keywords": ["psr-17", "psr-7"], + "keywords": [ + "psr-17", + "psr-7" + ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", "source": "https://github.com/Nyholm/psr7/tree/1.8.1" @@ -4053,7 +4310,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Paragon Initiative Enterprises", @@ -4116,7 +4375,9 @@ }, "type": "library", "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Paragon Initiative Enterprises", @@ -4125,7 +4386,12 @@ } ], "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", - "keywords": ["csprng", "polyfill", "pseudorandom", "random"], + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], "support": { "email": "info@paragonie.com", "issues": "https://github.com/paragonie/random_compat/issues", @@ -4170,7 +4436,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["Apache-2.0"], + "license": [ + "Apache-2.0" + ], "authors": [ { "name": "Johannes M. Schmitt", @@ -4184,7 +4452,12 @@ } ], "description": "Option Type for PHP", - "keywords": ["language", "option", "php", "type"], + "keywords": [ + "language", + "option", + "php", + "type" + ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" @@ -4232,13 +4505,17 @@ }, "type": "library", "autoload": { - "files": ["phpseclib/bootstrap.php"], + "files": [ + "phpseclib/bootstrap.php" + ], "psr-4": { "phpseclib3\\": "phpseclib/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Jim Wigginton", @@ -4336,7 +4613,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Antonio Carlos Ribeiro", @@ -4394,7 +4673,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Antonio Carlos Ribeiro", @@ -4456,7 +4737,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Antonio Carlos Ribeiro", @@ -4514,7 +4797,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Till Krüss", @@ -4524,7 +4809,11 @@ ], "description": "A flexible and feature-complete Redis client for PHP.", "homepage": "http://github.com/predis/predis", - "keywords": ["nosql", "predis", "redis"], + "keywords": [ + "nosql", + "predis", + "redis" + ], "support": { "issues": "https://github.com/predis/predis/issues", "source": "https://github.com/predis/predis/tree/v2.2.2" @@ -4566,7 +4855,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4574,7 +4865,11 @@ } ], "description": "Common interface for caching libraries", - "keywords": ["cache", "psr", "psr-6"], + "keywords": [ + "cache", + "psr", + "psr-6" + ], "support": { "source": "https://github.com/php-fig/cache/tree/3.0.0" }, @@ -4604,7 +4899,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4613,7 +4910,13 @@ ], "description": "Common interface for reading the clock.", "homepage": "https://github.com/php-fig/clock", - "keywords": ["clock", "now", "psr", "psr-20", "time"], + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], "support": { "issues": "https://github.com/php-fig/clock/issues", "source": "https://github.com/php-fig/clock/tree/1.0.0" @@ -4649,7 +4952,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4700,7 +5005,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4708,7 +5015,11 @@ } ], "description": "Standard interfaces for event handling.", - "keywords": ["events", "psr", "psr-14"], + "keywords": [ + "events", + "psr", + "psr-14" + ], "support": { "issues": "https://github.com/php-fig/event-dispatcher/issues", "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" @@ -4745,7 +5056,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4754,7 +5067,12 @@ ], "description": "Common interface for HTTP clients", "homepage": "https://github.com/php-fig/http-client", - "keywords": ["http", "http-client", "psr", "psr-18"], + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], "support": { "source": "https://github.com/php-fig/http-client" }, @@ -4790,7 +5108,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4842,7 +5162,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4893,7 +5215,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4902,7 +5226,11 @@ ], "description": "Common interface for logging libraries", "homepage": "https://github.com/php-fig/log", - "keywords": ["log", "psr", "psr-3"], + "keywords": [ + "log", + "psr", + "psr-3" + ], "support": { "source": "https://github.com/php-fig/log/tree/3.0.0" }, @@ -4937,7 +5265,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "PHP-FIG", @@ -4945,7 +5275,13 @@ } ], "description": "Common interfaces for simple caching", - "keywords": ["cache", "caching", "psr", "psr-16", "simple-cache"], + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], "support": { "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, @@ -4974,10 +5310,14 @@ }, "type": "library", "autoload": { - "files": ["src/getallheaders.php"] + "files": [ + "src/getallheaders.php" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ralph Khattar", @@ -5045,7 +5385,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ben Ramsey", @@ -5054,7 +5396,14 @@ } ], "description": "A PHP library for representing and manipulating collections.", - "keywords": ["array", "collection", "hash", "map", "queue", "set"], + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], "support": { "issues": "https://github.com/ramsey/collection/issues", "source": "https://github.com/ramsey/collection/tree/2.0.0" @@ -5130,15 +5479,23 @@ } }, "autoload": { - "files": ["src/functions.php"], + "files": [ + "src/functions.php" + ], "psr-4": { "Ramsey\\Uuid\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", - "keywords": ["guid", "identifier", "uuid"], + "keywords": [ + "guid", + "identifier", + "uuid" + ], "support": { "issues": "https://github.com/ramsey/uuid/issues", "source": "https://github.com/ramsey/uuid/tree/4.7.5" @@ -5192,7 +5549,9 @@ "dev-master": "0.14-dev" }, "laravel": { - "providers": ["TwigBridge\\ServiceProvider"], + "providers": [ + "TwigBridge\\ServiceProvider" + ], "aliases": { "Twig": "TwigBridge\\Facade\\Twig" } @@ -5205,7 +5564,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Rob Crowe", @@ -5217,7 +5578,10 @@ } ], "description": "Adds the power of Twig to Laravel", - "keywords": ["laravel", "twig"], + "keywords": [ + "laravel", + "twig" + ], "support": { "issues": "https://github.com/rcrowe/TwigBridge/issues", "source": "https://github.com/rcrowe/TwigBridge/tree/v0.14.1" @@ -5254,7 +5618,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Freek Van de Herten", @@ -5265,7 +5631,10 @@ ], "description": "A better backtrace", "homepage": "https://github.com/spatie/backtrace", - "keywords": ["Backtrace", "spatie"], + "keywords": [ + "Backtrace", + "spatie" + ], "support": { "source": "https://github.com/spatie/backtrace/tree/1.5.3" }, @@ -5319,16 +5688,25 @@ } }, "autoload": { - "files": ["src/helpers.php"], + "files": [ + "src/helpers.php" + ], "psr-4": { "Spatie\\FlareClient\\": "src" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Send PHP errors to Flare", "homepage": "https://github.com/spatie/flare-client-php", - "keywords": ["exception", "flare", "reporting", "spatie"], + "keywords": [ + "exception", + "flare", + "reporting", + "spatie" + ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", "source": "https://github.com/spatie/flare-client-php/tree/1.4.4" @@ -5392,7 +5770,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Spatie", @@ -5402,7 +5782,12 @@ ], "description": "A beautiful error page for PHP applications.", "homepage": "https://flareapp.io/ignition", - "keywords": ["error", "flare", "laravel", "page"], + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], "support": { "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", "forum": "https://twitter.com/flareappio", @@ -5444,20 +5829,26 @@ "type": "library", "extra": { "laravel": { - "providers": ["Spatie\\Html\\HtmlServiceProvider"], + "providers": [ + "Spatie\\Html\\HtmlServiceProvider" + ], "aliases": { "Html": "Spatie\\Html\\Facades\\Html" } } }, "autoload": { - "files": ["src/helpers.php"], + "files": [ + "src/helpers.php" + ], "psr-4": { "Spatie\\Html\\": "src" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Sebastian De Deyne", @@ -5474,7 +5865,10 @@ ], "description": "A fluent html builder", "homepage": "https://github.com/spatie/laravel-html", - "keywords": ["html", "spatie"], + "keywords": [ + "html", + "spatie" + ], "support": { "source": "https://github.com/spatie/laravel-html/tree/3.5.0" }, @@ -5538,13 +5932,17 @@ } }, "autoload": { - "files": ["src/helpers.php"], + "files": [ + "src/helpers.php" + ], "psr-4": { "Spatie\\LaravelIgnition\\": "src" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Spatie", @@ -5554,7 +5952,12 @@ ], "description": "A beautiful error page for Laravel applications.", "homepage": "https://flareapp.io/ignition", - "keywords": ["error", "flare", "laravel", "page"], + "keywords": [ + "error", + "flare", + "laravel", + "page" + ], "support": { "docs": "https://flareapp.io/docs/ignition-for-laravel/introduction", "forum": "https://twitter.com/flareappio", @@ -5600,7 +6003,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Brent Roose", @@ -5611,7 +6016,10 @@ ], "description": "Complex period comparisons", "homepage": "https://github.com/spatie/period", - "keywords": ["period", "spatie"], + "keywords": [ + "period", + "spatie" + ], "support": { "issues": "https://github.com/spatie/period/issues", "source": "https://github.com/spatie/period/tree/2.4.0" @@ -5620,31 +6028,31 @@ }, { "name": "symfony/cache", - "version": "v6.3.6", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "84aff8d948d6292d2b5a01ac622760be44dddc72" + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/84aff8d948d6292d2b5a01ac622760be44dddc72", - "reference": "84aff8d948d6292d2b5a01ac622760be44dddc72", + "url": "https://api.github.com/repos/symfony/cache/zipball/fc822951dd360a593224bb2cef90a087d0dff60f", + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/cache": "^2.0|^3.0", "psr/log": "^1.1|^2|^3", "symfony/cache-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3", - "symfony/var-exporter": "^6.3.6" + "symfony/var-exporter": "^6.4|^7.0" }, "conflict": { - "doctrine/dbal": "<2.13.1", - "symfony/dependency-injection": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/var-dumper": "<5.4" + "doctrine/dbal": "<3.6", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" }, "provide": { "psr/cache-implementation": "2.0|3.0", @@ -5653,26 +6061,32 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/dbal": "^2.13.1|^3|^4", + "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", "psr/simple-cache": "^1.0|^2.0|^3.0", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/messenger": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/filesystem": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\Cache\\": "" }, - "classmap": ["Traits/ValueWrapper.php"], - "exclude-from-classmap": ["/Tests/"] + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -5685,9 +6099,12 @@ ], "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", "homepage": "https://symfony.com", - "keywords": ["caching", "psr6"], + "keywords": [ + "caching", + "psr6" + ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.3.6" + "source": "https://github.com/symfony/cache/tree/v7.0.4" }, "funding": [ { @@ -5703,20 +6120,20 @@ "type": "tidelift" } ], - "time": "2023-10-17T14:44:58+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/cache-contracts", - "version": "v3.3.0", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/cache-contracts.git", - "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b" + "reference": "1d74b127da04ffa87aa940abe15446fa89653778" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/ad945640ccc0ae6e208bcea7d7de4b39b569896b", - "reference": "ad945640ccc0ae6e208bcea7d7de4b39b569896b", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1d74b127da04ffa87aa940abe15446fa89653778", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778", "shasum": "" }, "require": { @@ -5739,7 +6156,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -5761,7 +6180,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/cache-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/cache-contracts/tree/v3.4.0" }, "funding": [ { @@ -5777,7 +6196,7 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2023-09-25T12:52:38+00:00" }, { "name": "symfony/console", @@ -5828,10 +6247,14 @@ "psr-4": { "Symfony\\Component\\Console\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -5844,7 +6267,12 @@ ], "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", - "keywords": ["cli", "command-line", "console", "terminal"], + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], "support": { "source": "https://github.com/symfony/console/tree/v6.4.4" }, @@ -5886,10 +6314,14 @@ "psr-4": { "Symfony\\Component\\CssSelector\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -5953,10 +6385,14 @@ } }, "autoload": { - "files": ["function.php"] + "files": [ + "function.php" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -6016,16 +6452,22 @@ "symfony/http-kernel": "^6.4|^7.0", "symfony/serializer": "^5.4|^6.0|^7.0" }, - "bin": ["Resources/bin/patch-type-declarations"], + "bin": [ + "Resources/bin/patch-type-declarations" + ], "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\ErrorHandler\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6098,10 +6540,14 @@ "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6167,7 +6613,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -6232,10 +6680,14 @@ "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6292,10 +6744,14 @@ "psr-4": { "Symfony\\Component\\Finder\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6377,10 +6833,14 @@ "psr-4": { "Symfony\\Component\\HttpClient\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -6393,7 +6853,9 @@ ], "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", - "keywords": ["http"], + "keywords": [ + "http" + ], "support": { "source": "https://github.com/symfony/http-client/tree/v7.0.5" }, @@ -6444,10 +6906,14 @@ "psr-4": { "Symfony\\Contracts\\HttpClient\\": "" }, - "exclude-from-classmap": ["/Test/"] + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -6525,10 +6991,14 @@ "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6634,10 +7104,14 @@ "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6710,10 +7184,14 @@ "psr-4": { "Symfony\\Component\\Mailer\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6775,10 +7253,14 @@ "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6851,10 +7333,14 @@ "psr-4": { "Symfony\\Component\\Mime\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -6867,7 +7353,10 @@ ], "description": "Allows manipulating MIME messages", "homepage": "https://symfony.com", - "keywords": ["mime", "mime-type"], + "keywords": [ + "mime", + "mime-type" + ], "support": { "source": "https://github.com/symfony/mime/tree/v6.4.3" }, @@ -6918,13 +7407,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Gert de Pagter", @@ -6937,7 +7430,12 @@ ], "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", - "keywords": ["compatibility", "ctype", "polyfill", "portable"], + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], "support": { "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, @@ -6985,13 +7483,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7061,13 +7563,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Intl\\Idn\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Laurent Bassin", @@ -7139,14 +7645,20 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "classmap": ["Resources/stubs"] + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7217,13 +7729,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7287,13 +7803,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Php72\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7306,7 +7826,12 @@ ], "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", - "keywords": ["compatibility", "polyfill", "portable", "shim"], + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { "source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0" }, @@ -7351,14 +7876,20 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Php80\\": "" }, - "classmap": ["Resources/stubs"] + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Ion Bazan", @@ -7375,7 +7906,12 @@ ], "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", - "keywords": ["compatibility", "polyfill", "portable", "shim"], + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" }, @@ -7421,14 +7957,20 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Php83\\": "" }, - "classmap": ["Resources/stubs"] + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7441,7 +7983,12 @@ ], "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", "homepage": "https://symfony.com", - "keywords": ["compatibility", "polyfill", "portable", "shim"], + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "support": { "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" }, @@ -7492,13 +8039,17 @@ } }, "autoload": { - "files": ["bootstrap.php"], + "files": [ + "bootstrap.php" + ], "psr-4": { "Symfony\\Polyfill\\Uuid\\": "" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Grégoire Pineau", @@ -7511,7 +8062,12 @@ ], "description": "Symfony polyfill for uuid functions", "homepage": "https://symfony.com", - "keywords": ["compatibility", "polyfill", "portable", "uuid"], + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], "support": { "source": "https://github.com/symfony/polyfill-uuid/tree/v1.29.0" }, @@ -7553,10 +8109,14 @@ "psr-4": { "Symfony\\Component\\Process\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -7631,10 +8191,14 @@ "psr-4": { "Symfony\\Bridge\\PsrHttpMessage\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -7647,7 +8211,12 @@ ], "description": "PSR HTTP message bridge", "homepage": "http://symfony.com", - "keywords": ["http", "http-message", "psr-17", "psr-7"], + "keywords": [ + "http", + "http-message", + "psr-17", + "psr-7" + ], "support": { "issues": "https://github.com/symfony/psr-http-message-bridge/issues", "source": "https://github.com/symfony/psr-http-message-bridge/tree/v2.3.1" @@ -7706,10 +8275,14 @@ "psr-4": { "Symfony\\Component\\Routing\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -7722,7 +8295,12 @@ ], "description": "Maps an HTTP request to a set of configuration variables", "homepage": "https://symfony.com", - "keywords": ["router", "routing", "uri", "url"], + "keywords": [ + "router", + "routing", + "uri", + "url" + ], "support": { "source": "https://github.com/symfony/routing/tree/v6.4.5" }, @@ -7777,10 +8355,14 @@ "psr-4": { "Symfony\\Contracts\\Service\\": "" }, - "exclude-from-classmap": ["/Test/"] + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7853,14 +8435,20 @@ }, "type": "library", "autoload": { - "files": ["Resources/functions.php"], + "files": [ + "Resources/functions.php" + ], "psr-4": { "Symfony\\Component\\String\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -7950,14 +8538,20 @@ }, "type": "library", "autoload": { - "files": ["Resources/functions.php"], + "files": [ + "Resources/functions.php" + ], "psr-4": { "Symfony\\Component\\Translation\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Fabien Potencier", @@ -8020,10 +8614,14 @@ "psr-4": { "Symfony\\Contracts\\Translation\\": "" }, - "exclude-from-classmap": ["/Test/"] + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -8089,10 +8687,14 @@ "psr-4": { "Symfony\\Component\\Uid\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Grégoire Pineau", @@ -8109,7 +8711,11 @@ ], "description": "Provides an object-oriented API to generate and represent UIDs", "homepage": "https://symfony.com", - "keywords": ["UID", "ulid", "uuid"], + "keywords": [ + "UID", + "ulid", + "uuid" + ], "support": { "source": "https://github.com/symfony/uid/tree/v6.4.3" }, @@ -8160,17 +8766,25 @@ "symfony/uid": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" }, - "bin": ["Resources/bin/var-dump-server"], + "bin": [ + "Resources/bin/var-dump-server" + ], "type": "library", "autoload": { - "files": ["Resources/functions/dump.php"], + "files": [ + "Resources/functions/dump.php" + ], "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -8183,7 +8797,10 @@ ], "description": "Provides mechanisms for walking through any arbitrary PHP variable", "homepage": "https://symfony.com", - "keywords": ["debug", "dump"], + "keywords": [ + "debug", + "dump" + ], "support": { "source": "https://github.com/symfony/var-dumper/tree/v6.4.4" }, @@ -8205,33 +8822,37 @@ }, { "name": "symfony/var-exporter", - "version": "v6.3.6", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "374d289c13cb989027274c86206ddc63b16a2441" + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/374d289c13cb989027274c86206ddc63b16a2441", - "reference": "374d289c13cb989027274c86206ddc63b16a2441", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { "psr-4": { "Symfony\\Component\\VarExporter\\": "" }, - "exclude-from-classmap": ["/Tests/"] + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Nicolas Grekas", @@ -8255,7 +8876,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.3.6" + "source": "https://github.com/symfony/var-exporter/tree/v7.0.4" }, "funding": [ { @@ -8271,7 +8892,7 @@ "type": "tidelift" } ], - "time": "2023-10-13T09:16:49+00:00" + "time": "2024-02-26T10:35:24+00:00" }, { "name": "therobfonz/laravel-mandrill-driver", @@ -8300,7 +8921,9 @@ "type": "library", "extra": { "laravel": { - "providers": ["LaravelMandrill\\MandrillServiceProvider"] + "providers": [ + "LaravelMandrill\\MandrillServiceProvider" + ] } }, "autoload": { @@ -8309,7 +8932,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Rob Fonseca", @@ -8321,7 +8946,10 @@ } ], "description": "Mandrill Driver for Laravel 9+", - "keywords": ["laravel", "mandrill"], + "keywords": [ + "laravel", + "mandrill" + ], "support": { "issues": "https://github.com/luisdalmolin/laravel-mandrill-driver/issues", "source": "https://github.com/luisdalmolin/laravel-mandrill-driver/tree/5.0.0" @@ -8363,7 +8991,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Tijs Verkoyen", @@ -8410,7 +9040,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Fabien Potencier", @@ -8430,7 +9062,9 @@ ], "description": "Twig, the flexible, fast, and secure template language for PHP", "homepage": "https://twig.symfony.com", - "keywords": ["templating"], + "keywords": [ + "templating" + ], "support": { "issues": "https://github.com/twigphp/Twig/issues", "source": "https://github.com/twigphp/Twig/tree/v3.8.0" @@ -8494,7 +9128,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Graham Campbell", @@ -8508,7 +9144,11 @@ } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", - "keywords": ["dotenv", "env", "environment"], + "keywords": [ + "dotenv", + "env", + "environment" + ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" @@ -8555,7 +9195,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Lars Moelleken", @@ -8564,7 +9206,11 @@ ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", "homepage": "https://github.com/voku/portable-ascii", - "keywords": ["ascii", "clean", "php"], + "keywords": [ + "ascii", + "clean", + "php" + ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", "source": "https://github.com/voku/portable-ascii/tree/2.0.1" @@ -8630,7 +9276,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Bernhard Schussek", @@ -8638,7 +9286,11 @@ } ], "description": "Assertions to validate method input/output with nice error messages.", - "keywords": ["assert", "check", "validate"], + "keywords": [ + "assert", + "check", + "validate" + ], "support": { "issues": "https://github.com/webmozarts/assert/issues", "source": "https://github.com/webmozarts/assert/tree/1.11.0" @@ -8681,20 +9333,26 @@ "dev-master": "3.10-dev" }, "laravel": { - "providers": ["Barryvdh\\Debugbar\\ServiceProvider"], + "providers": [ + "Barryvdh\\Debugbar\\ServiceProvider" + ], "aliases": { "Debugbar": "Barryvdh\\Debugbar\\Facades\\Debugbar" } } }, "autoload": { - "files": ["src/helpers.php"], + "files": [ + "src/helpers.php" + ], "psr-4": { "Barryvdh\\Debugbar\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Barry vd. Heuvel", @@ -8782,7 +9440,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Barry vd. Heuvel", @@ -8849,11 +9509,15 @@ }, "autoload": { "psr-0": { - "Barryvdh": ["src/"] + "Barryvdh": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Mike van Riel", @@ -8904,7 +9568,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Jordi Boggiano", @@ -8913,7 +9579,9 @@ } ], "description": "Utilities to scan PHP code and generate class maps.", - "keywords": ["classmap"], + "keywords": [ + "classmap" + ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", "source": "https://github.com/composer/class-map-generator/tree/1.1.0" @@ -8968,7 +9636,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Jordi Boggiano", @@ -8977,7 +9647,12 @@ } ], "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": ["PCRE", "preg", "regex", "regular expression"], + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], "support": { "issues": "https://github.com/composer/pcre/issues", "source": "https://github.com/composer/pcre/tree/3.1.1" @@ -9036,7 +9711,9 @@ "type": "phpstan-extension", "extra": { "phpstan": { - "includes": ["rules.neon"] + "includes": [ + "rules.neon" + ] } }, "autoload": { @@ -9045,7 +9722,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Andreas Möller", @@ -9055,7 +9734,10 @@ ], "description": "Provides rules for phpstan/phpstan.", "homepage": "https://github.com/ergebnis/phpstan-rules", - "keywords": ["PHPStan", "phpstan-rules"], + "keywords": [ + "PHPStan", + "phpstan-rules" + ], "support": { "issues": "https://github.com/ergebnis/phpstan-rules/issues", "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md", @@ -9106,14 +9788,20 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "François Zaninotto" } ], "description": "Faker is a PHP library that generates fake data for you.", - "keywords": ["data", "faker", "fixtures"], + "keywords": [ + "data", + "faker", + "fixtures" + ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" @@ -9153,12 +9841,18 @@ } }, "autoload": { - "classmap": ["hamcrest"] + "classmap": [ + "hamcrest" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "description": "This is the PHP port of Hamcrest Matchers", - "keywords": ["test"], + "keywords": [ + "test" + ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" @@ -9208,7 +9902,9 @@ "dev-master": "2.0-dev" }, "phpstan": { - "includes": ["extension.neon"] + "includes": [ + "extension.neon" + ] } }, "autoload": { @@ -9217,7 +9913,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Can Vural", @@ -9303,7 +10001,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Maxime Bouroumeau-Fuseau", @@ -9317,7 +10017,10 @@ ], "description": "Debug bar in the browser for php application", "homepage": "https://github.com/maximebf/php-debugbar", - "keywords": ["debug", "debugbar"], + "keywords": [ + "debug", + "debugbar" + ], "support": { "issues": "https://github.com/maximebf/php-debugbar/issues", "source": "https://github.com/maximebf/php-debugbar/tree/v1.20.2" @@ -9352,13 +10055,18 @@ }, "type": "library", "autoload": { - "files": ["library/helpers.php", "library/Mockery.php"], + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], "psr-4": { "Mockery\\": "library/Mockery" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Pádraic Brady", @@ -9430,13 +10138,17 @@ }, "type": "library", "autoload": { - "files": ["src/DeepCopy/deep_copy.php"], + "files": [ + "src/DeepCopy/deep_copy.php" + ], "psr-4": { "DeepCopy\\": "src/DeepCopy/" } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Create deep copies (clones) of your objects", "keywords": [ "clone", @@ -9481,7 +10193,9 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, - "bin": ["bin/php-parse"], + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { @@ -9494,14 +10208,19 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Nikita Popov" } ], "description": "A PHP parser written in PHP", - "keywords": ["parser", "php"], + "keywords": [ + "parser", + "php" + ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" @@ -9537,10 +10256,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", @@ -9590,10 +10313,14 @@ }, "type": "library", "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", @@ -9647,7 +10374,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Jaap van Otterdijk", @@ -9711,7 +10440,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "Mike van Riel", @@ -9777,7 +10508,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["GPL-2.0-or-later"], + "license": [ + "GPL-2.0-or-later" + ], "authors": [ { "name": "The phpMyAdmin Team", @@ -9845,7 +10578,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Composer plugin for automatic installation of PHPStan extensions", "support": { "issues": "https://github.com/phpstan/extension-installer/issues", @@ -9884,11 +10619,15 @@ "type": "library", "autoload": { "psr-4": { - "PHPStan\\PhpDocParser\\": ["src/"] + "PHPStan\\PhpDocParser\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", @@ -9916,15 +10655,25 @@ "conflict": { "phpstan/phpstan-shim": "*" }, - "bin": ["phpstan", "phpstan.phar"], + "bin": [ + "phpstan", + "phpstan.phar" + ], "type": "library", "autoload": { - "files": ["bootstrap.php"] + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "PHPStan - PHP Static Analysis Tool", - "keywords": ["dev", "static analysis"], + "keywords": [ + "dev", + "static analysis" + ], "support": { "docs": "https://phpstan.org/user-guide/getting-started", "forum": "https://github.com/phpstan/phpstan/discussions", @@ -9975,7 +10724,9 @@ "type": "phpstan-extension", "extra": { "phpstan": { - "includes": ["rules.neon"] + "includes": [ + "rules.neon" + ] } }, "autoload": { @@ -9984,7 +10735,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", "support": { "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", @@ -10020,7 +10773,9 @@ "type": "phpstan-extension", "extra": { "phpstan": { - "includes": ["rules.neon"] + "includes": [ + "rules.neon" + ] } }, "autoload": { @@ -10029,7 +10784,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", @@ -10080,10 +10837,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10093,7 +10854,11 @@ ], "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": ["coverage", "testing", "xunit"], + "keywords": [ + "coverage", + "testing", + "xunit" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", @@ -10134,10 +10899,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10147,7 +10916,10 @@ ], "description": "FilterIterator implementation that filters files based on a list of suffixes.", "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": ["filesystem", "iterator"], + "keywords": [ + "filesystem", + "iterator" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", @@ -10192,10 +10964,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10205,7 +10981,9 @@ ], "description": "Invoke callables with a timeout", "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": ["process"], + "keywords": [ + "process" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" @@ -10245,10 +11023,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10258,7 +11040,9 @@ ], "description": "Simple template engine.", "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": ["template"], + "keywords": [ + "template" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", @@ -10299,10 +11083,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10312,7 +11100,9 @@ ], "description": "Utility class for timing", "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": ["timer"], + "keywords": [ + "timer" + ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" @@ -10370,7 +11160,9 @@ "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" }, - "bin": ["phpunit"], + "bin": [ + "phpunit" + ], "type": "library", "extra": { "branch-alias": { @@ -10378,11 +11170,17 @@ } }, "autoload": { - "files": ["src/Framework/Assert/Functions.php"], - "classmap": ["src/"] + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10392,7 +11190,11 @@ ], "description": "The PHP Unit Testing framework.", "homepage": "https://phpunit.de/", - "keywords": ["phpunit", "testing", "xunit"], + "keywords": [ + "phpunit", + "testing", + "xunit" + ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", @@ -10441,10 +11243,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10494,10 +11300,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10546,10 +11356,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10601,10 +11415,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10625,7 +11443,11 @@ ], "description": "Provides the functionality to compare PHP values for equality", "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": ["comparator", "compare", "equality"], + "keywords": [ + "comparator", + "compare", + "equality" + ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", @@ -10667,10 +11489,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10721,10 +11547,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10737,7 +11567,12 @@ ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": ["diff", "udiff", "unidiff", "unified diff"], + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", @@ -10781,10 +11616,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10793,7 +11632,11 @@ ], "description": "Provides functionality to handle HHVM/PHP environments", "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": ["Xdebug", "environment", "hhvm"], + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", @@ -10836,10 +11679,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10864,7 +11711,10 @@ ], "description": "Provides the functionality to export PHP variables for visualization", "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": ["export", "exporter"], + "keywords": [ + "export", + "exporter" + ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", @@ -10908,10 +11758,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -10920,7 +11774,9 @@ ], "description": "Snapshotting of global state", "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": ["global state"], + "keywords": [ + "global state" + ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", @@ -10962,10 +11818,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11017,10 +11877,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11068,10 +11932,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11119,10 +11987,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11178,10 +12050,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11227,10 +12103,14 @@ } }, "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Sebastian Bergmann", @@ -11280,7 +12160,9 @@ "dev-master": "1.0-dev" }, "phpstan": { - "includes": ["phpstan-strict-rules.neon"] + "includes": [ + "phpstan-strict-rules.neon" + ] } }, "autoload": { @@ -11289,7 +12171,9 @@ } }, "notification-url": "https://packagist.org/downloads/", - "license": ["MIT"], + "license": [ + "MIT" + ], "authors": [ { "name": "David Négrier", @@ -11325,10 +12209,14 @@ }, "type": "library", "autoload": { - "classmap": ["src/"] + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", - "license": ["BSD-3-Clause"], + "license": [ + "BSD-3-Clause" + ], "authors": [ { "name": "Arne Blankerts", From 69ca88d9f8051964a2b5b35dcd51a702323e2345 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 12:07:20 -0500 Subject: [PATCH 13/16] fix(api): use kebab case route for validate-expression endpoint --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index 33e3e5c35c..dfb7da5268 100644 --- a/routes/api.php +++ b/routes/api.php @@ -606,7 +606,7 @@ Route::group( Route::put('{rule}', ['uses' => 'UpdateController@update', 'as' => 'update']); Route::delete('{rule}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']); - Route::post('validateExpression', ['uses' => 'ExpressionController@validateExpression', 'as' => 'validate']); + Route::post('validate-expression', ['uses' => 'ExpressionController@validateExpression', 'as' => 'validate']); Route::get('{rule}/test', ['uses' => 'TriggerController@testRule', 'as' => 'test']); // TODO give results back From c4bf2aae7d0d3418780c017f18d9228d0561840b Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 12:57:34 -0500 Subject: [PATCH 14/16] fix: migrate action expression validation to separate rule class --- .../Models/Rule/ExpressionController.php | 17 ++----- .../V1/Requests/Models/Rule/StoreRequest.php | 3 +- .../V1/Requests/Models/Rule/UpdateRequest.php | 3 +- .../Models/Rule/ValidateExpressionRequest.php | 33 ++++++++++--- app/Http/Requests/RuleFormRequest.php | 3 +- app/Rules/IsValidActionExpression.php | 48 +++++++++++++++++++ app/Validation/FireflyValidator.php | 21 -------- 7 files changed, 83 insertions(+), 45 deletions(-) create mode 100644 app/Rules/IsValidActionExpression.php diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php index fd1d51ab2a..1912c9f09f 100644 --- a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -25,7 +25,6 @@ namespace FireflyIII\Api\V1\Controllers\Models\Rule; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Rule\ValidateExpressionRequest; -use FireflyIII\TransactionRules\Expressions\ActionExpression; use Illuminate\Http\JsonResponse; /** @@ -43,18 +42,8 @@ class ExpressionController extends Controller */ public function validateExpression(ValidateExpressionRequest $request): JsonResponse { - $value = $request->getExpression(); - $expr = new ActionExpression($value); - - if ($expr->isValid()) { - return response()->json([ - "valid" => true, - ]); - } else { - return response()->json([ - "valid" => false, - "error" => $expr->getValidationError()->getMessage() - ]); - } + return response()->json([ + "valid" => true, + ]); } } diff --git a/app/Api/V1/Requests/Models/Rule/StoreRequest.php b/app/Api/V1/Requests/Models/Rule/StoreRequest.php index 03532a2465..669604a99a 100644 --- a/app/Api/V1/Requests/Models/Rule/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Rule/StoreRequest.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; use FireflyIII\Rules\IsBoolean; +use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; @@ -123,7 +124,7 @@ class StoreRequest extends FormRequest 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue', + 'actions.*.value' => ['required_if:actions.*.type,'.$contextActions, new IsValidActionExpression(), 'ruleActionValue'], 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php index 64fc66455b..2d7488b505 100644 --- a/app/Api/V1/Requests/Models/Rule/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Rule/UpdateRequest.php @@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests\Models\Rule; use FireflyIII\Models\Rule; use FireflyIII\Rules\IsBoolean; +use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; @@ -140,7 +141,7 @@ class UpdateRequest extends FormRequest 'triggers.*.stop_processing' => [new IsBoolean()], 'triggers.*.active' => [new IsBoolean()], 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => 'required_if:actions.*.type,'.$contextActions.'|ruleActionExpression|ruleActionValue', + 'actions.*.value' => ['required_if:actions.*.type,'.$contextActions, new IsValidActionExpression(), 'ruleActionValue'], 'actions.*.stop_processing' => [new IsBoolean()], 'actions.*.active' => [new IsBoolean()], 'strict' => [new IsBoolean()], diff --git a/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php index dbd9faa1d7..b0ef5a3998 100644 --- a/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php +++ b/app/Api/V1/Requests/Models/Rule/ValidateExpressionRequest.php @@ -24,23 +24,42 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Requests\Models\Rule; +use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; -use FireflyIII\Support\Request\ConvertsDataTypes; +use Illuminate\Contracts\Validation\Validator; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\ValidationException; /** * Class ValidateExpressionRequest */ class ValidateExpressionRequest extends FormRequest { - use ConvertsDataTypes; use ChecksLogin; - /** - * @return string - */ - public function getExpression(): string + public function rules(): array { - return $this->convertString("expression"); + return ['expression' => ['required', new IsValidActionExpression()]]; + } + + /** + * Handle a failed validation attempt. + * + * @param \Illuminate\Contracts\Validation\Validator $validator + * @return void + * + * @throws \Illuminate\Validation\ValidationException + */ + protected function failedValidation(Validator $validator): void + { + $error = $validator->errors()->first('expression'); + + throw new ValidationException( + $validator, + response()->json([ + 'valid' => false, + 'error' => $error + ], 200) + ); } } diff --git a/app/Http/Requests/RuleFormRequest.php b/app/Http/Requests/RuleFormRequest.php index 2bc74cc52c..29c2352a56 100644 --- a/app/Http/Requests/RuleFormRequest.php +++ b/app/Http/Requests/RuleFormRequest.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Requests; use FireflyIII\Models\Rule; +use FireflyIII\Rules\IsValidActionExpression; use FireflyIII\Support\Request\ChecksLogin; use FireflyIII\Support\Request\ConvertsDataTypes; use FireflyIII\Support\Request\GetRuleConfiguration; @@ -147,7 +148,7 @@ class RuleFormRequest extends FormRequest 'triggers.*.type' => 'required|in:'.implode(',', $validTriggers), 'triggers.*.value' => sprintf('required_if:triggers.*.type,%s|max:1024|min:1|ruleTriggerValue', $contextTriggers), 'actions.*.type' => 'required|in:'.implode(',', $validActions), - 'actions.*.value' => sprintf('required_if:actions.*.type,%s|min:0|max:1024|ruleActionExpression|ruleActionValue', $contextActions), + 'actions.*.value' => [sprintf('required_if:actions.*.type,%s|min:0|max:1024', $contextActions), new IsValidActionExpression(), 'ruleActionValue'], 'strict' => 'in:0,1', ]; diff --git a/app/Rules/IsValidActionExpression.php b/app/Rules/IsValidActionExpression.php new file mode 100644 index 0000000000..f1c488331d --- /dev/null +++ b/app/Rules/IsValidActionExpression.php @@ -0,0 +1,48 @@ +. + */ + +namespace FireflyIII\Rules; + +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; +use FireflyIII\TransactionRules\Expressions\ActionExpression; + +class IsValidActionExpression implements ValidationRule +{ + /** + * Check that the given action expression is syntactically valid. + * + * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $value ??= ''; + $expr = new ActionExpression($value); + + if (!$expr->isValid()) { + $fail('validation.rule_action_expression')->translate([ + 'error' => $expr->getValidationError()->getMessage() + ]); + } + } +} diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 667e3c1411..9a554d79b4 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -254,27 +254,6 @@ class FireflyValidator extends Validator return 1 === $count; } - public function validateRuleActionExpression(string $attribute, string $value = null): bool - { - $value ??= ''; - $expr = new ActionExpression($value); - - return $expr->isValid(); - } - - public function replaceRuleActionExpression(string $message, string $attribute): string - { - $value = $this->getValue($attribute); - $expr = new ActionExpression($value); - $err = $expr->getValidationError(); - - if ($err == null) { - return $message; - } - - return str_replace(":error", $err->getMessage(), $message); - } - public function validateRuleActionValue(string $attribute, string $value = null): bool { // first, get the index from this string: From 1ec830521a41249155a0a54b5bbd87491fbed43a Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 13:02:04 -0500 Subject: [PATCH 15/16] fix: resolve PHPstan errors --- app/Rules/IsValidActionExpression.php | 2 ++ app/TransactionRules/Expressions/ActionExpression.php | 2 +- .../Expressions/ActionExpressionLanguageProvider.php | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Rules/IsValidActionExpression.php b/app/Rules/IsValidActionExpression.php index f1c488331d..ae22e48708 100644 --- a/app/Rules/IsValidActionExpression.php +++ b/app/Rules/IsValidActionExpression.php @@ -21,6 +21,8 @@ * along with this program. If not, see . */ +declare(strict_types=1); + namespace FireflyIII\Rules; use Closure; diff --git a/app/TransactionRules/Expressions/ActionExpression.php b/app/TransactionRules/Expressions/ActionExpression.php index c4069dee92..59b9770e14 100644 --- a/app/TransactionRules/Expressions/ActionExpression.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -133,7 +133,7 @@ class ActionExpression return $this->validationError === null; } - public function getValidationError() + public function getValidationError(): ?SyntaxError { return $this->validationError; } diff --git a/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php index fbb3df1725..2c54752b00 100644 --- a/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php +++ b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php @@ -20,6 +20,8 @@ * along with this program. If not, see . */ +declare(strict_types=1); + namespace FireflyIII\TransactionRules\Expressions; use Symfony\Component\ExpressionLanguage\ExpressionFunction; From 1371b6773ebfd7e99264bd027a8da79aa488e6ee Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sat, 9 Mar 2024 14:09:36 -0500 Subject: [PATCH 16/16] chore: ignore PHPMD unused parameter errors --- app/Api/V1/Controllers/Models/Rule/ExpressionController.php | 2 ++ app/Rules/IsValidActionExpression.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php index 1912c9f09f..8019919161 100644 --- a/app/Api/V1/Controllers/Models/Rule/ExpressionController.php +++ b/app/Api/V1/Controllers/Models/Rule/ExpressionController.php @@ -39,6 +39,8 @@ class ExpressionController extends Controller * @param ValidateExpressionRequest $request * * @return JsonResponse + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validateExpression(ValidateExpressionRequest $request): JsonResponse { diff --git a/app/Rules/IsValidActionExpression.php b/app/Rules/IsValidActionExpression.php index ae22e48708..3022043fa7 100644 --- a/app/Rules/IsValidActionExpression.php +++ b/app/Rules/IsValidActionExpression.php @@ -35,6 +35,8 @@ class IsValidActionExpression implements ValidationRule * Check that the given action expression is syntactically valid. * * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validate(string $attribute, mixed $value, Closure $fail): void {