From dbcd400fe27584abe5e7ae2e25bea5bb4a249d25 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 9 Sep 2022 15:56:45 -0400 Subject: [PATCH 01/92] feat(app): add event dispatcher provided by `league/event` --- composer.json | 3 ++- packages/app/composer.json | 3 ++- .../ServiceProvider/AppServiceProvider.php | 21 +++++++++++++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index c4dc37696..f688ab8a9 100644 --- a/composer.json +++ b/composer.json @@ -59,7 +59,8 @@ "studio-42/elfinder": "2.1.61", "symfony/translation": "^3.4", "tedivm/stash": "~0.16", - "vlucas/phpdotenv": "^5.4" + "vlucas/phpdotenv": "^5.4", + "league/event": "^3.0" }, "require-dev": { "cache/void-adapter": "^1.0", diff --git a/packages/app/composer.json b/packages/app/composer.json index 691aa2a5f..62227b271 100644 --- a/packages/app/composer.json +++ b/packages/app/composer.json @@ -37,7 +37,8 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "slim/slim": "^3.7", - "vlucas/phpdotenv": "^5.4" + "vlucas/phpdotenv": "^5.4", + "league/event": "^3.0" }, "require-dev": { "league/flysystem-aws-s3-v3": "^1.0", diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index c40b7df69..c0b7cc8de 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -4,14 +4,17 @@ // From PSR-7 use Charcoal\Factory\GenericResolver; +use phpDocumentor\Reflection\Types\Void_; +use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\UriInterface; // From Pimple use Pimple\ServiceProviderInterface; use Pimple\Container; // From Slim use Slim\Http\Uri; -// From 'league/climate' +// From 'league' use League\CLImate\CLImate; +use League\Event\EventDispatcher; // From Mustache use Mustache_LambdaHelper as LambdaHelper; use Charcoal\Factory\GenericFactory as Factory; @@ -35,7 +38,6 @@ use Charcoal\App\ServiceProvider\ScriptServiceProvider; use Charcoal\App\ServiceProvider\LoggerServiceProvider; use Charcoal\App\Template\TemplateInterface; -use Charcoal\App\Template\TemplateBuilder; use Charcoal\App\Template\WidgetInterface; use Charcoal\App\Template\WidgetBuilder; use Charcoal\View\Twig\DebugHelpers as TwigDebugHelpers; @@ -85,6 +87,7 @@ public function register(Container $container) $this->registerRequestControllerServices($container); $this->registerModuleServices($container); $this->registerViewServices($container); + $this->registerEventServices($container); } /** @@ -587,4 +590,18 @@ protected function registerTwigHelpersServices(Container $container): void ); }); } + + /** + * @param Container $container The DI container. + * @return void + */ + protected function registerEventServices(Container $container): void + { + /** + * @return EventDispatcherInterface + */ + $container['event/dispatcher'] = function (): EventDispatcherInterface { + return new EventDispatcher(); + }; + } } From af9de975d08543a93be8a365f5203f2f755968c0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 9 Sep 2022 17:23:37 -0400 Subject: [PATCH 02/92] feat(app): add PropertyEvent for Property related events --- .../Charcoal/Property/Event/PropertyEvent.php | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 packages/property/src/Charcoal/Property/Event/PropertyEvent.php diff --git a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php new file mode 100644 index 000000000..81d1bdc5b --- /dev/null +++ b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php @@ -0,0 +1,67 @@ +event = $event; + $this->property = $property; + } + + /** + * @return string + */ + public function eventName(): string + { + return $this->generateEventName($this->getEvent(), $this->getProperty()->type()); + } + + /** + * @param string $event The event name. + * @param string $propertyType The property type. + * @return string + */ + public static function generateEventName(string $event, string $propertyType): string + { + return implode('.', [self::EVENT_PREFIX, $propertyType, $event]); + } + + /** + * @return string + */ + public function getEvent(): string + { + return $this->event; + } + + /** + * The property triggering the event. + * + * @return PropertyInterface + */ + public function getProperty(): PropertyInterface + { + return $this->property; + } +} From 35590ee6344f83faa1ed3f5a5dec48b665a547ef Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 9 Sep 2022 17:24:14 -0400 Subject: [PATCH 03/92] feat(app): add EventDispatcherTrait --- .../src/Charcoal/App/EventDispatcherTrait.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/app/src/Charcoal/App/EventDispatcherTrait.php diff --git a/packages/app/src/Charcoal/App/EventDispatcherTrait.php b/packages/app/src/Charcoal/App/EventDispatcherTrait.php new file mode 100644 index 000000000..33abd33a0 --- /dev/null +++ b/packages/app/src/Charcoal/App/EventDispatcherTrait.php @@ -0,0 +1,33 @@ +eventDispatcher; + } + + /** + * @param EventDispatcherInterface $eventDispatcher EventDispatcher for EventDispatcherTrait. + * @return self + */ + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): self + { + $this->eventDispatcher = $eventDispatcher; + + return $this; + } +} From 0ba9b24c3f99a04e94f08a9b50fff2036b7e61db Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 9 Sep 2022 17:25:40 -0400 Subject: [PATCH 04/92] feat(property): (wip) add EventDispatcherTrait to AbstractProperty --- packages/property/src/Charcoal/Property/AbstractProperty.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/property/src/Charcoal/Property/AbstractProperty.php b/packages/property/src/Charcoal/Property/AbstractProperty.php index 4b5b97ae9..1cf96c4c9 100644 --- a/packages/property/src/Charcoal/Property/AbstractProperty.php +++ b/packages/property/src/Charcoal/Property/AbstractProperty.php @@ -31,6 +31,8 @@ use Charcoal\Property\PropertyValidator; use Charcoal\Property\StorablePropertyInterface; use Charcoal\Property\StorablePropertyTrait; +// From 'charcoal-app' +use Charcoal\App\EventDispatcherTrait; /** * An abstract class that implements the full `PropertyInterface`. @@ -49,6 +51,7 @@ abstract class AbstractProperty extends AbstractEntity implements use StorablePropertyTrait; use TranslatorAwareTrait; use ValidatableTrait; + use EventDispatcherTrait; public const DEFAULT_L10N = false; public const DEFAULT_MULTIPLE = false; @@ -1009,6 +1012,7 @@ protected function setDependencies(Container $container) { $this->setPropertyFactory($container['property/factory']); $this->setMetadataLoader($container['metadata/loader']); + $this->setEventDispatcher($container['event/dispatcher']); } /** From 243ae4d308bbd277cdf3513e5d1d6b9b0d06d04f Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 13:05:15 -0400 Subject: [PATCH 05/92] docs(event): add event dispatcher documentation --- packages/app/README.md | 4 +++- packages/app/docs/providers.md | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/app/README.md b/packages/app/README.md index 6e2786cd7..4dc2053a4 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -90,6 +90,7 @@ For a complete, ready-to-use project, start from the [official boilerplate][gh-c - [**locomotivemtl/charcoal-translator**][charcoal-translator]: Service provider for tools to internationalize your application, using [Symfony Translation][symfony/translation]. - [**locomotivemtl/charcoal-view**][charcoal-view]: Service provider for a view renderer and templating engine adapters for [Mustache][mustache] and [Twig][twig]. - [**league/climate**][climate]: Command-line abstraction for designing console commands for your application. +- [**league/event**][event]: Event dispatcher. - [**league/flysystem**][flysystem]: File system abstraction for working with local and remote storage spaces. - [**monolog/monolog**][monolog]: PSR-3 compliant client for logging your application's requests, errors, and information. - [**pimple/pimple**][pimple]: PSR-11 compliant service container and provider library. @@ -302,7 +303,8 @@ The sources of this package are contained in the Charcoal monorepo. We welcome c [climate]: https://packagist.org/packages/league/climate [fastroute]: https://packagist.org/packages/nikic/fast-route -[flysystem]: https://packagist.org/packages/league/flysystem +[event]: https://packagist.org/packages/league/event +[flysystem]: https://packagist.org/packages/league/flysystem [monolog]: https://packagist.org/packages/monolog/monolog [mustache]: https://packagist.org/packages/mustache/mustache [phpmailer]: https://packagist.org/packages/phpmailer/phpmailer diff --git a/packages/app/docs/providers.md b/packages/app/docs/providers.md index 98aacb8c2..f343a3dd9 100644 --- a/packages/app/docs/providers.md +++ b/packages/app/docs/providers.md @@ -8,6 +8,7 @@ The Charcoal App comes with several providers out of the box. All of these are w - [`AppServiceProvider`](#app-service-provider) - [`DatabaseServicePovider`](#database-service-provider) +- [`EventServiceProvider`](#event-service-provider) - [`FilesystemServiceProvider`](#filesystem-service-provider) - [`LoggerServiceProvider`](#logger-service-provider) @@ -29,31 +30,34 @@ Dependencies are handled with a `Pimple` dependency Container. Basic "App" services are: -- `cache` +- `cache` - A cache storage service for the [Stash Cache Library][stash]. - Configured by `config['cache']` - Provided by [`charcoal-cache`][charcoal-cache] -- `config` +- `config` - A `\Charcoal\App\AppConfig` instance. -- `database` +- `database` - The default _PDO_ database. - From a pool of database, available through `databases`. - Configured by `config['databases']` and `config['default_database']`. -- `filesystems` +- `event` + - A pimple container of `\League\Event\EventDispatcher` + - Psr 14 compatible EventDispatcherInterface +- `filesystems` - A (pimple) container of `\League\Flysystem\Filesystem` - Configured by `config['filesystem]` - Also provide a `\League\Flysystem\MountManager` as `filesystem/manager`. -- `logger` +- `logger` - A `\Psr\Log\Logger` instance. - Provided by _Monolog_. - Configured by `config['logger']` -- `translator` +- `translator` - A `Charcoal\Translator\Translation` object for multilingual strings. - A `Charcoal\Translator\Translator` service based on [Symfony's Translator][symfony/translation]. - A `Charcoal\Translator\LocalesManager` for managing available languages. - Configured by `config['translator']` and `config['locales']` - Provided by [`charcoal-translator`][charcoal-translator] -- `view` +- `view` - A `Charcoal\View\ViewInterface` instance - Typically a `\Charcoal\View\GenericView` object. - Configured by `config['view']` @@ -189,6 +193,14 @@ Or, in JSON format: ``` +## Event Service Provider + +The `EventServiceProvider` provides the following services: + +| Service | Type | Description | +|----------------------|---------------------------------| ----------- | +| **event/dispatcher** | `\League\Event\EventDispatcher` | Event manager, allows subscribing to and dispatch events. + ## Filesystem Service Provider From 2a9a40101120e15d20ff97de246611c13bf2c0f3 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 13:14:26 -0400 Subject: [PATCH 06/92] feat(property-event): add data and fix event const for PropertyEvent --- .../Charcoal/Property/Event/PropertyEvent.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php index 81d1bdc5b..0b1b44bc0 100644 --- a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php +++ b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php @@ -12,21 +12,24 @@ */ class PropertyEvent implements HasEventName { - private const EVENT_PREFIX = 'property'; - public const EVENT_SAVE = 'save'; - public const EVENT_UPDATE = 'update'; + private const EVENT_PREFIX = 'property'; + public const EVENT_SAVE = 'save'; + public const EVENT_PRE_SAVE = 'pre-save'; private string $event; private PropertyInterface $property; + private array $data; /** * @param string $event The event name. * @param PropertyInterface $property The property triggering the event. + * @param array $data Data to send with the event. */ - public function __construct(string $event, PropertyInterface $property) + public function __construct(string $event, PropertyInterface $property, array $data = []) { $this->event = $event; $this->property = $property; + $this->data = $data; } /** @@ -64,4 +67,12 @@ public function getProperty(): PropertyInterface { return $this->property; } + + /** + * @return array + */ + public function getData(): array + { + return $this->data; + } } From 3ba892085a25eac453d01f701370fed1e04f9f55 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 14:13:45 -0400 Subject: [PATCH 07/92] feat(property): dispatch events for `pre-save` and `save` event for ImageProperty --- packages/property/src/Charcoal/Property/ImageProperty.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/property/src/Charcoal/Property/ImageProperty.php b/packages/property/src/Charcoal/Property/ImageProperty.php index 970d30e0c..d6d9df188 100644 --- a/packages/property/src/Charcoal/Property/ImageProperty.php +++ b/packages/property/src/Charcoal/Property/ImageProperty.php @@ -10,6 +10,7 @@ // From 'charccoal-translator' use Charcoal\Translator\Translation; // From 'charcoal-property' +use Charcoal\Property\Event\PropertyEvent; use Charcoal\Property\FileProperty; /** @@ -314,12 +315,16 @@ protected function resolveExtensionFromMimeType($type) */ public function save($val) { + $this->getEventDispatcher()->dispatch(new PropertyEvent(PropertyEvent::EVENT_PRE_SAVE, $this, ['val' => $val])); + $val = parent::save($val); if ($this->canApplyEffects('save')) { $val = $this->processEffects($val); } + $this->getEventDispatcher()->dispatch(new PropertyEvent(PropertyEvent::EVENT_SAVE, $this, ['val' => $val])); + return $val; } From 85ccf13a97c7153bfd1027affaac7f5d681d743f Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 15:36:44 -0400 Subject: [PATCH 08/92] test(user): fix phpunit error due to missing composer['event/dispatcher'] --- packages/user/composer.json | 3 ++- .../user/tests/Charcoal/User/ContainerProvider.php | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/user/composer.json b/packages/user/composer.json index f3358bbd2..e0c7ad36e 100644 --- a/packages/user/composer.json +++ b/packages/user/composer.json @@ -30,7 +30,8 @@ "php-coveralls/php-coveralls": "^2.2", "cache/void-adapter": "^1.0", "tedivm/stash": "~0.16", - "seld/jsonlint": "^1.9" + "seld/jsonlint": "^1.9", + "charcoal/app": "^3.2" }, "autoload": { "psr-4": { diff --git a/packages/user/tests/Charcoal/User/ContainerProvider.php b/packages/user/tests/Charcoal/User/ContainerProvider.php index e2bf4f9c5..c66edd065 100644 --- a/packages/user/tests/Charcoal/User/ContainerProvider.php +++ b/packages/user/tests/Charcoal/User/ContainerProvider.php @@ -2,6 +2,7 @@ namespace Charcoal\Tests\User; +use League\Event\EventDispatcher; use PDO; // From PSR-3 @@ -50,6 +51,7 @@ public function registerBaseServices(Container $container) $this->registerLogger($container); $this->registerCache($container); $this->registerTranslator($container); + $this->registerEvent($container); } /** @@ -236,4 +238,15 @@ public function registerTranslator(Container $container) ]); }; } + + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } } From 9bf2ac3ff86d05481b4812211562147a73119419 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 15:47:54 -0400 Subject: [PATCH 09/92] test(charcoal): fix phpunit error due to missing composer['event/dispatcher'] --- .../tests/Charcoal/Admin/ContainerProvider.php | 14 ++++++++++++++ .../core/tests/Charcoal/CoreContainerProvider.php | 14 ++++++++++++++ packages/object/composer.json | 1 + .../tests/Charcoal/Object/ContainerProvider.php | 14 ++++++++++++++ .../tests/Charcoal/Property/ContainerProvider.php | 14 ++++++++++++++ 5 files changed, 57 insertions(+) diff --git a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php index fea6cebe0..dd0f8b434 100644 --- a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php +++ b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php @@ -28,6 +28,7 @@ use League\CLImate\Util\Output; use League\CLImate\Util\Reader\Stdin; use League\CLImate\Util\UtilFactory; +use League\Event\EventDispatcher; // From 'charcoal-factory' use Charcoal\Factory\GenericFactory as Factory; @@ -96,6 +97,7 @@ public function registerBaseServices(Container $container) $this->registerDatabase($container); $this->registerLogger($container); $this->registerCache($container); + $this->registerEvent($container); } /** @@ -379,6 +381,17 @@ public function registerCache(Container $container) }; } + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } + /** * @param Container $container A DI container. * @return void @@ -545,6 +558,7 @@ public function registerActionDependencies(Container $container) $this->registerLogger($container); $this->registerDatabase($container); $this->registerCache($container); + $this->registerEvent($container); $this->registerAdminConfig($container); $this->registerBaseUrl($container); diff --git a/packages/core/tests/Charcoal/CoreContainerProvider.php b/packages/core/tests/Charcoal/CoreContainerProvider.php index 6de374c33..f2354c147 100644 --- a/packages/core/tests/Charcoal/CoreContainerProvider.php +++ b/packages/core/tests/Charcoal/CoreContainerProvider.php @@ -35,6 +35,8 @@ // From 'charcoal-app' use Charcoal\App\AppConfig; +use League\Event\EventDispatcher; + /** * Service Container for Unit Tests */ @@ -52,6 +54,7 @@ public function registerBaseServices(Container $container) $this->registerSource($container); $this->registerLogger($container); $this->registerCache($container); + $this->registerEvent($container); } /** @@ -112,6 +115,17 @@ public function registerCache(Container $container) }; } + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } + /** * Setup the application's translator service. * diff --git a/packages/object/composer.json b/packages/object/composer.json index 97e4163e3..9d36e3eeb 100644 --- a/packages/object/composer.json +++ b/packages/object/composer.json @@ -29,6 +29,7 @@ "seld/jsonlint": "^1.9", "squizlabs/php_codesniffer": "^3.5", "tedivm/stash": "~0.16", + "charcoal/app": "^3.2", "ext-json": "*" }, "autoload": { diff --git a/packages/object/tests/Charcoal/Object/ContainerProvider.php b/packages/object/tests/Charcoal/Object/ContainerProvider.php index b930fa1a8..1ede70d6e 100644 --- a/packages/object/tests/Charcoal/Object/ContainerProvider.php +++ b/packages/object/tests/Charcoal/Object/ContainerProvider.php @@ -25,6 +25,8 @@ use Charcoal\Translator\LocalesManager; use Charcoal\Translator\Translator; +use League\Event\EventDispatcher; + /** * Service Container for Unit Tests */ @@ -42,6 +44,7 @@ public function registerBaseServices(Container $container) $this->registerLogger($container); $this->registerCache($container); $this->registerTranslator($container); + $this->registerEvent($container); } /** @@ -228,4 +231,15 @@ public function registerTranslator(Container $container) ]); }; } + + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } } diff --git a/packages/property/tests/Charcoal/Property/ContainerProvider.php b/packages/property/tests/Charcoal/Property/ContainerProvider.php index 59512fc74..9039ccab5 100644 --- a/packages/property/tests/Charcoal/Property/ContainerProvider.php +++ b/packages/property/tests/Charcoal/Property/ContainerProvider.php @@ -37,6 +37,8 @@ use Charcoal\Translator\LocalesManager; use Charcoal\Translator\Translator; +use League\Event\EventDispatcher; + /** * Service Container for Unit Tests */ @@ -54,6 +56,7 @@ public function registerBaseServices(Container $container) $this->registerSource($container); $this->registerLogger($container); $this->registerCache($container); + $this->registerEvent($container); } /** @@ -336,4 +339,15 @@ public function registerModelCollectionLoader(Container $container) ]); }; } + + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } } From 001bb226b7c812f7e6bc8dd8577c23eb5fd7ecd0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 15:57:39 -0400 Subject: [PATCH 10/92] style(readme): prefix league packages links --- packages/app/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/app/README.md b/packages/app/README.md index 4dc2053a4..754350d33 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -89,9 +89,9 @@ For a complete, ready-to-use project, start from the [official boilerplate][gh-c - [**locomotivemtl/charcoal-factory**][charcoal-factory]: Factory interface for creating providers, processors, and controllers. - [**locomotivemtl/charcoal-translator**][charcoal-translator]: Service provider for tools to internationalize your application, using [Symfony Translation][symfony/translation]. - [**locomotivemtl/charcoal-view**][charcoal-view]: Service provider for a view renderer and templating engine adapters for [Mustache][mustache] and [Twig][twig]. -- [**league/climate**][climate]: Command-line abstraction for designing console commands for your application. -- [**league/event**][event]: Event dispatcher. -- [**league/flysystem**][flysystem]: File system abstraction for working with local and remote storage spaces. +- [**league/climate**][league/climate]: Command-line abstraction for designing console commands for your application. +- [**league/event**][league/event]: Event dispatcher. +- [**league/flysystem**][league/flysystem]: File system abstraction for working with local and remote storage spaces. - [**monolog/monolog**][monolog]: PSR-3 compliant client for logging your application's requests, errors, and information. - [**pimple/pimple**][pimple]: PSR-11 compliant service container and provider library. - [**slim/slim**][slim]: PSR-7 compliant HTTP client and router. @@ -301,10 +301,9 @@ The sources of this package are contained in the Charcoal monorepo. We welcome c [badge-sensiolabs]: https://img.shields.io/sensiolabs/i/533b5796-7e69-42a7-a046-71342146308a.svg?style=flat-square [badge-travis]: https://img.shields.io/travis/locomotivemtl/charcoal-app.svg?style=flat-square -[climate]: https://packagist.org/packages/league/climate -[fastroute]: https://packagist.org/packages/nikic/fast-route -[event]: https://packagist.org/packages/league/event -[flysystem]: https://packagist.org/packages/league/flysystem +[league/climate]: https://packagist.org/packages/league/climate +[league/event]: https://packagist.org/packages/league/event +[league/flysystem]: https://packagist.org/packages/league/flysystem [monolog]: https://packagist.org/packages/monolog/monolog [mustache]: https://packagist.org/packages/mustache/mustache [phpmailer]: https://packagist.org/packages/phpmailer/phpmailer From c5c98e3f99dd7fb5a4f661755f59dbb36de23096 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 15:58:11 -0400 Subject: [PATCH 11/92] style(composer): order packages alphabetically --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f688ab8a9..01688f994 100644 --- a/composer.json +++ b/composer.json @@ -45,6 +45,7 @@ "kriswallsmith/assetic": "^1.4", "laminas/laminas-permissions-acl": "^2.8", "league/climate": "^3.2", + "league/event": "^3.0", "league/flysystem": "^1.0", "mcaskill/php-html-build-attributes": "^1.0", "monolog/monolog": "^1.17", @@ -59,8 +60,7 @@ "studio-42/elfinder": "2.1.61", "symfony/translation": "^3.4", "tedivm/stash": "~0.16", - "vlucas/phpdotenv": "^5.4", - "league/event": "^3.0" + "vlucas/phpdotenv": "^5.4" }, "require-dev": { "cache/void-adapter": "^1.0", From ec0a6b62fd5caea80198fd9f88da82c99de374cd Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 16:04:32 -0400 Subject: [PATCH 12/92] chore(app): remove unneeded import --- .../app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index c0b7cc8de..82d0d680e 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -4,7 +4,6 @@ // From PSR-7 use Charcoal\Factory\GenericResolver; -use phpDocumentor\Reflection\Types\Void_; use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\UriInterface; // From Pimple From 53a8475cf5f77f24c786b8c1edb7fdab745dbfc8 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 16:13:53 -0400 Subject: [PATCH 13/92] refactor(property): change `event` property to `type` property --- .../src/Charcoal/Property/Event/PropertyEvent.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php index 0b1b44bc0..78db5bacc 100644 --- a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php +++ b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php @@ -16,18 +16,18 @@ class PropertyEvent implements HasEventName public const EVENT_SAVE = 'save'; public const EVENT_PRE_SAVE = 'pre-save'; - private string $event; + private string $type; private PropertyInterface $property; private array $data; /** - * @param string $event The event name. + * @param string $type The event type. * @param PropertyInterface $property The property triggering the event. * @param array $data Data to send with the event. */ - public function __construct(string $event, PropertyInterface $property, array $data = []) + public function __construct(string $type, PropertyInterface $property, array $data = []) { - $this->event = $event; + $this->type = $type; $this->property = $property; $this->data = $data; } @@ -37,7 +37,7 @@ public function __construct(string $event, PropertyInterface $property, array $d */ public function eventName(): string { - return $this->generateEventName($this->getEvent(), $this->getProperty()->type()); + return $this->generateEventName($this->getType(), $this->getProperty()->type()); } /** @@ -53,9 +53,9 @@ public static function generateEventName(string $event, string $propertyType): s /** * @return string */ - public function getEvent(): string + public function getType(): string { - return $this->event; + return $this->type; } /** From a0918a8db21fb4f3ce8b981b9c466c497d80f4bd Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 12 Sep 2022 17:29:40 -0400 Subject: [PATCH 14/92] fix(psr4): fix some namespacing issues --- .../admin/tests/Charcoal/Admin/Service/ExporterTest.php | 2 +- packages/cms/tests/Charcoal/Cms/TemplateableTraitTest.php | 2 +- .../tests/Charcoal/Model/Service/MetadataLoaderTest.php | 2 +- .../Charcoal/Model/Service/ModelLoaderBuilderTest.php | 2 +- packages/email/tests/Charcoal/Email/EmailTest.php | 2 +- .../Translator/Middleware/LanguageMiddlewareTest.php | 2 +- .../Translator/Script/TranslationParserScriptTest.php | 2 +- .../ServiceProvider/TranslatorServiceProviderTest.php | 2 +- .../tests/Charcoal/Translator/TranslationTest.php | 2 +- .../tests/Charcoal/Ui/Dashboard/GenericDashboardTest.php | 2 +- packages/ui/tests/Charcoal/Ui/Form/GenericFormTest.php | 5 +++-- .../tests/Charcoal/Ui/FormGroup/AbstractFormGroupTest.php | 7 ++++--- .../tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php | 5 +++-- .../tests/Charcoal/Ui/FormInput/GenericFormInputTest.php | 5 +++-- packages/ui/tests/Charcoal/Ui/Layout/GenericLayoutTest.php | 2 +- packages/ui/tests/Charcoal/Ui/Menu/AbstractMenuTest.php | 2 +- packages/ui/tests/Charcoal/Ui/Menu/GenericMenuTest.php | 2 +- .../ui/tests/Charcoal/Ui/MenuItem/GenericMenuItemTest.php | 2 +- packages/view/tests/Charcoal/View/ViewConfigTest.php | 3 ++- 19 files changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php b/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php index 49551abe9..818a77803 100644 --- a/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php +++ b/packages/admin/tests/Charcoal/Admin/Service/ExporterTest.php @@ -1,6 +1,6 @@ obj; - $cb = function($o) { + $cb = function() { return 'foo'; }; $ret = $obj->setInputCallback($cb); diff --git a/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php b/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php index 9d8791a0b..550a659d3 100644 --- a/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php +++ b/packages/ui/tests/Charcoal/Ui/FormGroup/GenericFormGroupTest.php @@ -1,6 +1,6 @@ Date: Mon, 12 Sep 2022 17:43:05 -0400 Subject: [PATCH 15/92] tests(property): run phpcbf on `property` package --- .../Charcoal/Property/Event/PropertyEvent.php | 4 +- .../Property/AbstractFilePropertyTestCase.php | 9 ++-- .../Property/AbstractPropertyTest.php | 3 +- .../Charcoal/Property/AudioPropertyTest.php | 4 +- .../Charcoal/Property/BooleanPropertyTest.php | 9 ++-- .../Charcoal/Property/ColorPropertyTest.php | 11 +++-- .../Property/ContainerIntegrationTrait.php | 1 - .../Charcoal/Property/ContainerProvider.php | 14 +------ .../Property/DateTimePropertyTest.php | 3 +- .../Charcoal/Property/FilePropertyTest.php | 42 +++++++++---------- .../tests/Charcoal/Property/FixturesTrait.php | 2 +- .../Charcoal/Property/IdPropertyTest.php | 3 +- .../Charcoal/Property/ImagePropertyTest.php | 15 ++++--- .../Charcoal/Property/IpPropertyTest.php | 1 - .../Charcoal/Property/LangPropertyTest.php | 1 - .../Charcoal/Property/Mocks/GenericModel.php | 2 - .../Charcoal/Property/ObjectPropertyTest.php | 6 +-- .../Charcoal/Property/PropertyFieldTest.php | 1 - .../Property/SelectablePropertyTraitTest.php | 2 - .../Charcoal/Property/SpritePropertyTest.php | 1 - .../Property/StorablePropertyTraitTest.php | 1 - .../Charcoal/Property/StringPropertyTest.php | 2 - .../Property/StructurePropertyTest.php | 3 +- 23 files changed, 52 insertions(+), 88 deletions(-) diff --git a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php index 78db5bacc..89370c3d1 100644 --- a/packages/property/src/Charcoal/Property/Event/PropertyEvent.php +++ b/packages/property/src/Charcoal/Property/Event/PropertyEvent.php @@ -16,9 +16,9 @@ class PropertyEvent implements HasEventName public const EVENT_SAVE = 'save'; public const EVENT_PRE_SAVE = 'pre-save'; - private string $type; + private string $type; private PropertyInterface $property; - private array $data; + private array $data; /** * @param string $type The event type. diff --git a/packages/property/tests/Charcoal/Property/AbstractFilePropertyTestCase.php b/packages/property/tests/Charcoal/Property/AbstractFilePropertyTestCase.php index 1fe7f3a25..bf677425a 100644 --- a/packages/property/tests/Charcoal/Property/AbstractFilePropertyTestCase.php +++ b/packages/property/tests/Charcoal/Property/AbstractFilePropertyTestCase.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\FileProperty; use Charcoal\Tests\AbstractTestCase; @@ -59,7 +58,7 @@ public function getFileMapOfFixtures() if ($this->fileMapOfFixtures === null) { $this->fileMapOfFixtures = []; foreach (self::FIXTURES as $filename) { - $this->fileMapOfFixtures[$filename] = $this->getPathToFixture('files/'.$filename); + $this->fileMapOfFixtures[$filename] = $this->getPathToFixture('files/' . $filename); } } @@ -222,7 +221,7 @@ public function testFilesizeFromBadVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/blank.txt'); $this->assertEquals(0, $obj['filesize']); @@ -261,7 +260,7 @@ public function testMimetypeFromBadVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/bad.txt'); $this->assertNull($obj['mimetype']); @@ -277,7 +276,7 @@ public function testMimetypeFromEmptyFile() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/blank.txt'); $this->assertEquals('application/x-empty', $obj['mimetype']); diff --git a/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php b/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php index 8a840cd0d..02b8c2b99 100644 --- a/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/AbstractPropertyTest.php @@ -6,7 +6,6 @@ use LogicException; use RuntimeException; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\AbstractProperty; use Charcoal\Tests\AbstractTestCase; @@ -254,7 +253,7 @@ public function testMultipleSeparator() $this->assertEquals(',', $this->obj->multipleSeparator()); $this->obj->setMultipleOptions([ - 'separator'=>'/' + 'separator' => '/' ]); $this->assertEquals('/', $this->obj->multipleSeparator()); } diff --git a/packages/property/tests/Charcoal/Property/AudioPropertyTest.php b/packages/property/tests/Charcoal/Property/AudioPropertyTest.php index 9516c518f..f3b3c730f 100644 --- a/packages/property/tests/Charcoal/Property/AudioPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/AudioPropertyTest.php @@ -87,7 +87,7 @@ public function testFilesizeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/buzzer.mp3'); $this->assertEquals(16512, $obj['filesize']); @@ -104,7 +104,7 @@ public function testMimetypeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/buzzer.mp3'); $mime = $obj['mimetype']; diff --git a/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php b/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php index bbe46f710..ae5b430e9 100644 --- a/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/BooleanPropertyTest.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use PDO; - // From 'charcoal-property' use Charcoal\Property\BooleanProperty; use Charcoal\Tests\AbstractTestCase; @@ -63,8 +62,8 @@ public function testDisplayVal() $this->assertEquals('Yes', $this->obj->displayVal(true)); $this->assertEquals('No', $this->obj->displayVal(false)); - $this->assertEquals('V', $this->obj->displayVal(true, ['true_label'=>'V'])); - $this->assertEquals('F', $this->obj->displayVal(false, ['false_label'=>'F'])); + $this->assertEquals('V', $this->obj->displayVal(true, ['true_label' => 'V'])); + $this->assertEquals('F', $this->obj->displayVal(false, ['false_label' => 'F'])); } /** @@ -104,8 +103,8 @@ public function testSetData() { $obj = $this->obj; $data = [ - 'true_label'=>'foo', - 'false_label'=>'bar' + 'true_label' => 'foo', + 'false_label' => 'bar' ]; $ret = $obj->setData($data); diff --git a/packages/property/tests/Charcoal/Property/ColorPropertyTest.php b/packages/property/tests/Charcoal/Property/ColorPropertyTest.php index 1a11f7daf..a739baf2a 100644 --- a/packages/property/tests/Charcoal/Property/ColorPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ColorPropertyTest.php @@ -6,7 +6,6 @@ use InvalidArgumentException; use PDO; use ReflectionClass; - // From 'charcoal-property' use Charcoal\Property\ColorProperty; use Charcoal\Tests\AbstractTestCase; @@ -73,7 +72,7 @@ public function parseOneFalse() public function parseOneArray() { - $this->assertEquals(['r'=>255, 'g'=>255, 'b'=>255], $this->obj->parseOne([255,255,255])); + $this->assertEquals(['r' => 255, 'g' => 255, 'b' => 255], $this->obj->parseOne([255,255,255])); $this->expectException(InvalidArgumentException::class); $this->obj->parseOne([255]); } @@ -162,8 +161,8 @@ public function colorProviderNoAlpha() ['Red', '#FF0000'], ['RED', '#FF0000'], [[255,0,255], '#FF00FF'], - [['r'=>255, 'g'=>0, 'b'=>255], '#FF00FF'], - [['r'=>255, 'g'=>0, 'b'=>255, 'a'=>0], '#FF00FF'], + [['r' => 255, 'g' => 0, 'b' => 255], '#FF00FF'], + [['r' => 255, 'g' => 0, 'b' => 255, 'a' => 0], '#FF00FF'], ['ABC', '#AABBCC'] ]; } @@ -188,8 +187,8 @@ public function colorProviderAlpha() ['Red', 'rgba(255,0,0,0)'], ['RED', 'rgba(255,0,0,0)'], [[255,0,255], 'rgba(255,0,255,0)'], - [['r'=>255, 'g'=>0, 'b'=>255], 'rgba(255,0,255,0)'], - [['r'=>255, 'g'=>0, 'b'=>255, 'a'=>0], 'rgba(255,0,255,0)'] + [['r' => 255, 'g' => 0, 'b' => 255], 'rgba(255,0,255,0)'], + [['r' => 255, 'g' => 0, 'b' => 255, 'a' => 0], 'rgba(255,0,255,0)'] ]; } diff --git a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php index 5af9bbfba..980467f17 100644 --- a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php +++ b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php @@ -4,7 +4,6 @@ // From Pimple use Pimple\Container; - // From 'charcoal-property/tests' use Charcoal\Tests\Property\ContainerProvider; diff --git a/packages/property/tests/Charcoal/Property/ContainerProvider.php b/packages/property/tests/Charcoal/Property/ContainerProvider.php index 9039ccab5..fae49cb67 100644 --- a/packages/property/tests/Charcoal/Property/ContainerProvider.php +++ b/packages/property/tests/Charcoal/Property/ContainerProvider.php @@ -3,40 +3,30 @@ namespace Charcoal\Tests\Property; use PDO; - // From PSR-3 use Psr\Log\NullLogger; - // From 'cache/void-adapter' (PSR-6) use Cache\Adapter\Void\VoidCachePool; - // From 'tedivm/stash' (PSR-6) use Stash\Pool; use Stash\Driver\Ephemeral; - // From Pimple use Pimple\Container; - // From 'symfony/translator' use Symfony\Component\Translation\Loader\ArrayLoader; - // From 'charcoal-factory' use Charcoal\Factory\GenericFactory as Factory; - // From 'charcoal-core' use Charcoal\Model\Service\MetadataLoader; use Charcoal\Loader\CollectionLoader; use Charcoal\Source\DatabaseSource; - // From 'charcoal-view' use Charcoal\View\GenericView; use Charcoal\View\Mustache\MustacheEngine; use Charcoal\View\Mustache\MustacheLoader; - // From 'charcoal-translator' use Charcoal\Translator\LocalesManager; use Charcoal\Translator\Translator; - use League\Event\EventDispatcher; /** @@ -68,8 +58,8 @@ public function registerBaseServices(Container $container) public function registerConfig(Container $container) { $container['config'] = [ - 'base_path' => realpath(__DIR__.'/../../..'), - 'public_path' => realpath(__DIR__.'/../../..'), + 'base_path' => realpath(__DIR__ . '/../../..'), + 'public_path' => realpath(__DIR__ . '/../../..'), ]; } diff --git a/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php b/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php index eb0b1c001..100da858e 100644 --- a/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/DateTimePropertyTest.php @@ -6,7 +6,6 @@ use DateTime; use Exception; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\DateTimeProperty; use Charcoal\Tests\AbstractTestCase; @@ -144,7 +143,7 @@ public function testDisplayVal() $this->assertEquals('2015/09/01', $this->obj->displayVal(new DateTime('September 1st, 2015'))); // Test with custom format passed as parameter - $this->assertEquals('2017/12/12', $this->obj->displayVal('December 12, 2017', ['format'=>'Y/m/d'])); + $this->assertEquals('2017/12/12', $this->obj->displayVal('December 12, 2017', ['format' => 'Y/m/d'])); // Test with null value $this->assertEquals('', $this->obj->displayVal(null)); diff --git a/packages/property/tests/Charcoal/Property/FilePropertyTest.php b/packages/property/tests/Charcoal/Property/FilePropertyTest.php index 2978d68ca..f79594d3d 100644 --- a/packages/property/tests/Charcoal/Property/FilePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/FilePropertyTest.php @@ -5,10 +5,8 @@ use PDO; use InvalidArgumentException; use ReflectionClass; - // From 'charcoal-core' use Charcoal\Validator\ValidatorInterface as Validator; - // From 'charcoal-property' use Charcoal\Property\FileProperty; @@ -91,7 +89,7 @@ public function testFilesizeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/document.txt'); $this->assertEquals(743, $obj['filesize']); @@ -106,7 +104,7 @@ public function testMimetypeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/document.txt'); $this->assertEquals('text/plain', $obj['mimetype']); @@ -183,7 +181,7 @@ public function testValidateMimetypes( ) { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['acceptedMimetypes'] = $acceptedMimetypes; $obj['l10n'] = $l10n; $obj['multiple'] = $multiple; @@ -220,7 +218,7 @@ public function testValidateFilesizes( ) { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['maxFilesize'] = $maxFilesize; $obj['l10n'] = $l10n; $obj['multiple'] = $multiple; @@ -423,7 +421,7 @@ public function provideDataForValidateMimetypes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', + 'File [' . $paths['panda.png'] . '] has unacceptable MIME type [image/png]', ], ], ], @@ -435,7 +433,7 @@ public function provideDataForValidateMimetypes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['nonexistent.txt'].'] not found or MIME type unrecognizable', + 'File [' . $paths['nonexistent.txt'] . '] not found or MIME type unrecognizable', ], ], ], @@ -458,7 +456,7 @@ public function provideDataForValidateMimetypes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', + 'File [' . $paths['panda.png'] . '] has unacceptable MIME type [image/png]', ], ], ], @@ -484,13 +482,13 @@ public function provideDataForValidateMimetypes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] has unacceptable MIME type [image/png]', + 'File [' . $paths['panda.png'] . '] has unacceptable MIME type [image/png]', ], ], ], 'text/plain, l10n + multiple #1' => [ 'propertyValues' => [ - 'en' => $paths['document.txt'].','.$paths['todo.txt'], + 'en' => $paths['document.txt'] . ',' . $paths['todo.txt'], 'fr' => [ $paths['stuff.txt'], $paths['draft.txt'] ], ], 'propertyL10n' => false, @@ -501,7 +499,7 @@ public function provideDataForValidateMimetypes() ], 'text/plain, l10n + multiple #2' => [ 'propertyValues' => [ - 'en' => $paths['document.txt'].','.$paths['scream.wav'], + 'en' => $paths['document.txt'] . ',' . $paths['scream.wav'], 'fr' => [ $paths['stuff.txt'], $paths['cat.jpg'] ], ], 'propertyL10n' => false, @@ -510,8 +508,8 @@ public function provideDataForValidateMimetypes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['scream.wav'].'] has unacceptable MIME type [audio/%s]', - 'File ['.$paths['cat.jpg'].'] has unacceptable MIME type [image/%s]', + 'File [' . $paths['scream.wav'] . '] has unacceptable MIME type [audio/%s]', + 'File [' . $paths['cat.jpg'] . '] has unacceptable MIME type [image/%s]', ], ], ], @@ -569,7 +567,7 @@ public function provideDataForValidateFilesizes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', + 'File [' . $paths['panda.png'] . '] exceeds maximum file size [%s]', ], ], ], @@ -581,7 +579,7 @@ public function provideDataForValidateFilesizes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['nonexistent.txt'].'] not found or size unknown', + 'File [' . $paths['nonexistent.txt'] . '] not found or size unknown', ], ], ], @@ -604,7 +602,7 @@ public function provideDataForValidateFilesizes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', + 'File [' . $paths['panda.png'] . '] exceeds maximum file size [%s]', ], ], ], @@ -630,13 +628,13 @@ public function provideDataForValidateFilesizes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', + 'File [' . $paths['panda.png'] . '] exceeds maximum file size [%s]', ], ], ], 'max 10kB, l10n + multiple #1' => [ 'propertyValues' => [ - 'en' => $paths['document.txt'].','.$paths['todo.txt'], + 'en' => $paths['document.txt'] . ',' . $paths['todo.txt'], 'fr' => [ $paths['stuff.txt'], $paths['draft.txt'] ], ], 'propertyL10n' => false, @@ -647,7 +645,7 @@ public function provideDataForValidateFilesizes() ], 'max 10kB, l10n + multiple #2' => [ 'propertyValues' => [ - 'en' => $paths['document.txt'].','.$paths['scream.wav'], + 'en' => $paths['document.txt'] . ',' . $paths['scream.wav'], 'fr' => [ $paths['stuff.txt'], $paths['panda.png'] ], ], 'propertyL10n' => false, @@ -656,8 +654,8 @@ public function provideDataForValidateFilesizes() 'assertValidationReturn' => false, 'assertValidationResults' => [ Validator::ERROR => [ - 'File ['.$paths['scream.wav'].'] exceeds maximum file size [%s]', - 'File ['.$paths['panda.png'].'] exceeds maximum file size [%s]', + 'File [' . $paths['scream.wav'] . '] exceeds maximum file size [%s]', + 'File [' . $paths['panda.png'] . '] exceeds maximum file size [%s]', ], ], ], diff --git a/packages/property/tests/Charcoal/Property/FixturesTrait.php b/packages/property/tests/Charcoal/Property/FixturesTrait.php index e04cdbb25..a457e4aa5 100644 --- a/packages/property/tests/Charcoal/Property/FixturesTrait.php +++ b/packages/property/tests/Charcoal/Property/FixturesTrait.php @@ -15,7 +15,7 @@ trait FixturesTrait */ public function getPathToFixture($file) { - return $this->getPathToFixtures().'/'.ltrim($file, '/'); + return $this->getPathToFixtures() . '/' . ltrim($file, '/'); } /** diff --git a/packages/property/tests/Charcoal/Property/IdPropertyTest.php b/packages/property/tests/Charcoal/Property/IdPropertyTest.php index ff05b7748..bd66cc4af 100644 --- a/packages/property/tests/Charcoal/Property/IdPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/IdPropertyTest.php @@ -5,7 +5,6 @@ use PDO; use DomainException; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\IdProperty; use Charcoal\Tests\AbstractTestCase; @@ -52,7 +51,7 @@ public function testSetData() { $ret = $this->obj->setData( [ - 'mode'=>'uniqid' + 'mode' => 'uniqid' ] ); $this->assertSame($ret, $this->obj); diff --git a/packages/property/tests/Charcoal/Property/ImagePropertyTest.php b/packages/property/tests/Charcoal/Property/ImagePropertyTest.php index 845c5208d..be4c0e5b5 100644 --- a/packages/property/tests/Charcoal/Property/ImagePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ImagePropertyTest.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\ImageProperty; @@ -92,7 +91,7 @@ public function testFilesizeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/panda.png'); $this->assertEquals(170276, $obj['filesize']); @@ -107,7 +106,7 @@ public function testMimetypeFromVal() { $obj = $this->obj; - $obj['uploadPath'] = $this->getPathToFixtures().'/files'; + $obj['uploadPath'] = $this->getPathToFixtures() . '/files'; $obj['val'] = $this->getPathToFixture('files/panda.png'); $this->assertEquals('image/png', $obj['mimetype']); @@ -119,13 +118,13 @@ public function testMimetypeFromVal() public function testSetEffects() { $this->assertEquals([], $this->obj['effects']); - $ret = $this->obj->setEffects([['type'=>'blur', 'sigma'=>'1']]); + $ret = $this->obj->setEffects([['type' => 'blur', 'sigma' => '1']]); $this->assertSame($ret, $this->obj); - $this->obj['effects'] = [['type'=>'blur', 'sigma'=>'1'], ['type'=>'revert']]; + $this->obj['effects'] = [['type' => 'blur', 'sigma' => '1'], ['type' => 'revert']]; $this->assertEquals(2, count($this->obj['effects'])); - $this->obj->set('effects', [['type'=>'blur', 'sigma'=>'1']]); + $this->obj->set('effects', [['type' => 'blur', 'sigma' => '1']]); $this->assertEquals(1, count($this->obj['effects'])); $this->assertEquals(1, count($this->obj['effects'])); @@ -138,11 +137,11 @@ public function testAddEffect() { $this->assertEquals(0, count($this->obj['effects'])); - $ret = $this->obj->addEffect(['type'=>'grayscale']); + $ret = $this->obj->addEffect(['type' => 'grayscale']); $this->assertSame($ret, $this->obj); $this->assertEquals(1, count($this->obj['effects'])); - $this->obj->addEffect(['type'=>'blur', 'sigma'=>1]); + $this->obj->addEffect(['type' => 'blur', 'sigma' => 1]); $this->assertEquals(2, count($this->obj['effects'])); } diff --git a/packages/property/tests/Charcoal/Property/IpPropertyTest.php b/packages/property/tests/Charcoal/Property/IpPropertyTest.php index 63056fe5f..2692c9300 100644 --- a/packages/property/tests/Charcoal/Property/IpPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/IpPropertyTest.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use PDO; - // From 'charcoal-property' use Charcoal\Property\IpProperty; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/LangPropertyTest.php b/packages/property/tests/Charcoal/Property/LangPropertyTest.php index bdf9ac286..b0186261b 100644 --- a/packages/property/tests/Charcoal/Property/LangPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/LangPropertyTest.php @@ -4,7 +4,6 @@ use PDO; use ReflectionClass; - // From 'charcoal-property' use Charcoal\Property\LangProperty; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php b/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php index ae900ac45..3b471734f 100644 --- a/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php +++ b/packages/property/tests/Charcoal/Property/Mocks/GenericModel.php @@ -11,10 +11,8 @@ // From Pimple use Pimple\Container; - // From 'charcoal-core' use Charcoal\Model\AbstractModel; - // From 'charcoal-translator' use Charcoal\Translator\Translation; use Charcoal\Translator\TranslatorAwareTrait; diff --git a/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php b/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php index 43e67c399..e7d759c8f 100644 --- a/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/ObjectPropertyTest.php @@ -6,18 +6,14 @@ use ReflectionClass; use RuntimeException; use InvalidArgumentException; - // From PSR-6 use Psr\Cache\CacheItemPoolInterface; - // From 'charcoal-core' use Charcoal\Loader\CollectionLoader; use Charcoal\Model\Service\ModelLoader; use Charcoal\Source\StorableInterface; - // From 'charcoal-factory' use Charcoal\Factory\FactoryInterface; - // From 'charcoal-property' use Charcoal\Property\ObjectProperty; use Charcoal\Tests\AbstractTestCase; @@ -314,7 +310,7 @@ public function testDisplayVal() $this->obj->setL10n(false); $this->obj->setMultiple(true); - $expected = 'Foo, '.self::OBJ_2.', Baz, Qux, Xyz'; + $expected = 'Foo, ' . self::OBJ_2 . ', Baz, Qux, Xyz'; $actual = $this->obj->displayVal(implode(',', array_keys($objs))); $this->assertEquals($expected, $actual); diff --git a/packages/property/tests/Charcoal/Property/PropertyFieldTest.php b/packages/property/tests/Charcoal/Property/PropertyFieldTest.php index c97f77728..2ca01f612 100644 --- a/packages/property/tests/Charcoal/Property/PropertyFieldTest.php +++ b/packages/property/tests/Charcoal/Property/PropertyFieldTest.php @@ -4,7 +4,6 @@ use PDO; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\PropertyField; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php b/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php index 162435816..ff0b9cff3 100644 --- a/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php +++ b/packages/property/tests/Charcoal/Property/SelectablePropertyTraitTest.php @@ -3,10 +3,8 @@ namespace Charcoal\Tests\Property; use ReflectionClass; - // From 'charcoal-translator' use Charcoal\Translator\Translation; - // From 'charcoal-property' use Charcoal\Property\SelectablePropertyTrait; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/SpritePropertyTest.php b/packages/property/tests/Charcoal/Property/SpritePropertyTest.php index 4de3258f2..e406112ce 100644 --- a/packages/property/tests/Charcoal/Property/SpritePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/SpritePropertyTest.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\SpriteProperty; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php b/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php index 4dcf5a96f..94acfde44 100644 --- a/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php +++ b/packages/property/tests/Charcoal/Property/StorablePropertyTraitTest.php @@ -3,7 +3,6 @@ namespace Charcoal\Tests\Property; use ReflectionMethod; - // From 'charcoal-property' use Charcoal\Property\GenericProperty; use Charcoal\Property\PropertyField; diff --git a/packages/property/tests/Charcoal/Property/StringPropertyTest.php b/packages/property/tests/Charcoal/Property/StringPropertyTest.php index 18bcc58ce..c0053477d 100644 --- a/packages/property/tests/Charcoal/Property/StringPropertyTest.php +++ b/packages/property/tests/Charcoal/Property/StringPropertyTest.php @@ -3,10 +3,8 @@ namespace Charcoal\Tests\Property; use PDO; - // From 'charcoal-translator' use Charcoal\Translator\Translation; - // From 'charcoal-property' use Charcoal\Property\StringProperty; use Charcoal\Tests\AbstractTestCase; diff --git a/packages/property/tests/Charcoal/Property/StructurePropertyTest.php b/packages/property/tests/Charcoal/Property/StructurePropertyTest.php index 0ca1ae2f8..5e31f3536 100644 --- a/packages/property/tests/Charcoal/Property/StructurePropertyTest.php +++ b/packages/property/tests/Charcoal/Property/StructurePropertyTest.php @@ -4,7 +4,6 @@ use Exception; use InvalidArgumentException; - // From 'charcoal-property' use Charcoal\Property\StructureProperty; use Charcoal\Tests\AbstractTestCase; @@ -69,7 +68,7 @@ public function testParseOneString() $this->assertEquals('', $this->obj->parseOne('')); // $this->assertEquals('foo', $this->obj->parseOne('foo')); $this->assertEquals(['foo'], $this->obj->parseOne('["foo"]')); - $this->assertEquals(['foo'=>'bar'], $this->obj->parseOne('{"foo":"bar"}')); + $this->assertEquals(['foo' => 'bar'], $this->obj->parseOne('{"foo":"bar"}')); } public function testSqlType() From 52f0dc25104858345d6e421cd83990d75921c3da Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 13 Sep 2022 13:28:22 -0400 Subject: [PATCH 16/92] style(test): fix code style --- .../Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php b/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php index d2e5704a2..cc6fb06f1 100644 --- a/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php +++ b/packages/core/tests/Charcoal/Model/ServiceProvider/ModelServiceProviderTest.php @@ -179,7 +179,7 @@ public function testExtraMetadataPaths() { $container = new Container([ 'config' => [ - 'base_path' => dirname(dirname(dirname(dirname(__DIR__)))), + 'base_path' => dirname(__DIR__, 4), ], 'module/classes' => [ 'Charcoal\\Tests\\Mock\\MockModule', From 1c31bbc53323adddfc845b32aefdf949c4e686d1 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 14 Sep 2022 10:46:29 -0400 Subject: [PATCH 17/92] refactor(php): improve type hinting and argument typing of AppConfig and it's descendants --- packages/admin/src/Charcoal/Admin/Config.php | 2 +- .../Charcoal/Admin/User/AuthTokenMetadata.php | 2 +- packages/app/src/Charcoal/App/AppConfig.php | 206 ++++++------------ .../Charcoal/App/Config/DatabaseConfig.php | 2 +- .../src/Charcoal/App/Config/LoggerConfig.php | 2 +- .../cache/src/Charcoal/Cache/CacheConfig.php | 2 +- .../Cache/Middleware/CacheMiddleware.php | 2 +- .../src/Charcoal/Config/AbstractConfig.php | 6 +- .../src/Charcoal/Config/ConfigInterface.php | 6 +- .../Charcoal/Config/Mock/MacroConfig.php | 2 +- .../Charcoal/Model/Service/MetadataConfig.php | 4 +- .../Charcoal/Source/DatabaseSourceConfig.php | 2 +- .../core/src/Charcoal/Source/SourceConfig.php | 2 +- .../src/Charcoal/Translator/LocalesConfig.php | 2 +- .../Middleware/LanguageMiddleware.php | 2 +- .../Charcoal/Translator/TranslatorConfig.php | 2 +- .../src/Charcoal/User/AuthTokenMetadata.php | 2 +- 17 files changed, 92 insertions(+), 156 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Config.php b/packages/admin/src/Charcoal/Admin/Config.php index 0f750216a..5fd4c9949 100644 --- a/packages/admin/src/Charcoal/Admin/Config.php +++ b/packages/admin/src/Charcoal/Admin/Config.php @@ -50,7 +50,7 @@ class Config extends AbstractConfig * * @return array */ - public function defaults() + public function defaults(): array { $baseDir = rtrim(realpath(__DIR__ . '/../../../'), '/'); $confDir = $baseDir . '/config'; diff --git a/packages/admin/src/Charcoal/Admin/User/AuthTokenMetadata.php b/packages/admin/src/Charcoal/Admin/User/AuthTokenMetadata.php index 9874dfae5..36161df0c 100644 --- a/packages/admin/src/Charcoal/Admin/User/AuthTokenMetadata.php +++ b/packages/admin/src/Charcoal/Admin/User/AuthTokenMetadata.php @@ -13,7 +13,7 @@ class AuthTokenMetadata extends BaseAuthTokenMetadata /** * @return array */ - public function defaults() + public function defaults(): array { $parentDefaults = parent::defaults(); diff --git a/packages/app/src/Charcoal/App/AppConfig.php b/packages/app/src/Charcoal/App/AppConfig.php index 8c7b8a809..0853462af 100644 --- a/packages/app/src/Charcoal/App/AppConfig.php +++ b/packages/app/src/Charcoal/App/AppConfig.php @@ -19,68 +19,51 @@ class AppConfig extends AbstractConfig { /** * The application's timezone. - * - * @var string|null */ - private $timezone; + private ?string $timezone; /** * The application's name. * * For internal usage. - * - * @var string|null */ - private $projectName; + private ?string $projectName; /** * The base URL (public) for the Charcoal installation. - * - * @var UriInterface|null */ - private $baseUrl; + private ?UriInterface $baseUrl = null; /** * The base path for the Charcoal installation. - * - * @var string|null */ - private $basePath; + private ?string $basePath = null; /** * The path to the public / web directory. * - * @var string|null */ - private $publicPath; + private ?string $publicPath = null; /** * The path to the cache directory. - * - * @var string|null */ - private $cachePath; + private ?string $cachePath = null; /** * The path to the logs directory. - * - * @var string|null */ - private $logsPath; + private ?string $logsPath = null; /** * Whether the debug mode is enabled (TRUE) or not (FALSE). - * - * @var boolean */ - private $devMode = false; + private bool $devMode = false; /** * The application's routes. - * - * @var array */ - private $routes = []; + private array $routes = []; /** * The application's dynamic routes. @@ -91,84 +74,63 @@ class AppConfig extends AbstractConfig /** * The application's HTTP middleware. - * - * @var array */ - private $middlewares = []; + private array $middlewares = []; /** * The application's handlers. - * - * @var array */ - private $handlers = []; + private array $handlers = []; /** * The application's modules. - * - * @var array */ - private $modules = []; + private array $modules = []; /** * The application's API credentials and service configsets. - * - * @var array */ - private $apis = []; + private array $apis = []; /** * The application's caching configset. - * - * @var array */ - private $cache; + private array $cache = []; /** * The application's logging configset. * * @var array */ - private $logger; + private array $logger = []; /** * The application's view/rendering configset. - * - * @var array */ - protected $view; + protected array $view = []; /** * The application's database configsets. - * - * @var array */ - private $databases = []; + private array $databases = []; /** * The application's default database configset. - * - * @var string */ - private $defaultDatabase; + private ?string $defaultDatabase; /** * The application's filesystem configset. - * - * @var array */ - private $filesystem; + private array $filesystem = []; /** * Default app-config values. * * @return array */ - public function defaults() + public function defaults(): array { - /** @var string $baseDir Presume that Charcoal App _is_ the application */ - $baseDir = rtrim(realpath(__DIR__ . '/../../../'), '/') . '/'; - return [ 'project_name' => '', 'timezone' => 'UTC', @@ -182,7 +144,7 @@ public function defaults() 'view' => [], 'databases' => [], 'default_database' => 'default', - 'dev_mode' => false + 'dev_mode' => false, ]; } @@ -209,7 +171,7 @@ public function resolveValue($value) 'app.public_path' => $this->publicPath(), 'app.cache_path' => $this->cachePath(), 'app.logs_path' => $this->logsPath(), - 'packages.path' => ($_ENV['PACKAGES_PATH'] ?? 'vendor/charcoal') + 'packages.path' => ($_ENV['PACKAGES_PATH'] ?? 'vendor/charcoal'), ]; if (is_string($value)) { @@ -251,7 +213,7 @@ public function resolveValue($value) * @param string $path The file to load and add. * @return self */ - public function addFile($path) + public function addFile(string $path): self { $path = $this->resolveValue($path); @@ -263,11 +225,11 @@ public function addFile($path) * * Resolves symlinks with realpath() and ensure trailing slash. * - * @param string $path The absolute path to the application's root directory. + * @param string|null $path The absolute path to the application's root directory. * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setBasePath($path) + public function setBasePath(?string $path): self { if ($path === null) { throw new InvalidArgumentException( @@ -290,7 +252,7 @@ public function setBasePath($path) * * @return string|null The absolute path to the application's root directory. */ - public function basePath() + public function basePath(): ?string { return $this->basePath; } @@ -298,11 +260,11 @@ public function basePath() /** * Set the application's absolute path to the public web directory. * - * @param string $path The path to the application's public directory. + * @param string|null $path The path to the application's public directory. * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setPublicPath($path) + public function setPublicPath(?string $path): self { if ($path === null) { $this->publicPath = null; @@ -324,7 +286,7 @@ public function setPublicPath($path) * * @return string The absolute path to the application's public directory. */ - public function publicPath() + public function publicPath(): string { if ($this->publicPath === null) { $this->publicPath = $this->basePath() . DIRECTORY_SEPARATOR . 'www'; @@ -336,11 +298,11 @@ public function publicPath() /** * Set the application's absolute path to the cache directory. * - * @param string $path The path to the application's cache directory. + * @param string|null $path The path to the application's cache directory. * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setCachePath($path) + public function setCachePath(?string $path): self { if ($path === null) { $this->cachePath = null; @@ -362,7 +324,7 @@ public function setCachePath($path) * * @return string The absolute path to the application's cache directory. */ - public function cachePath() + public function cachePath(): string { if ($this->cachePath === null) { $this->cachePath = $this->basePath() . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'cache'; @@ -374,11 +336,11 @@ public function cachePath() /** * Set the application's absolute path to the logs directory. * - * @param string $path The path to the application's logs directory. + * @param string|null $path The path to the application's logs directory. * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setLogsPath($path) + public function setLogsPath(?string $path): self { if ($path === null) { $this->logsPath = null; @@ -400,7 +362,7 @@ public function setLogsPath($path) * * @return string The absolute path to the application's logs directory. */ - public function logsPath() + public function logsPath(): string { if ($this->logsPath === null) { $this->logsPath = $this->basePath() . DIRECTORY_SEPARATOR . 'var' . DIRECTORY_SEPARATOR . 'logs'; @@ -415,7 +377,7 @@ public function logsPath() * @param UriInterface|string $uri The base URI to the application's web directory. * @return self */ - public function setBaseUrl($uri) + public function setBaseUrl($uri): self { if (is_string($uri)) { $this->baseUrl = Uri::createFromString($uri); @@ -430,7 +392,7 @@ public function setBaseUrl($uri) * * @return UriInterface|null The base URI to the application's web directory. */ - public function baseUrl() + public function baseUrl(): ?UriInterface { return $this->baseUrl; } @@ -442,14 +404,8 @@ public function baseUrl() * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setTimezone($timezone) + public function setTimezone(string $timezone): self { - if (!is_string($timezone)) { - throw new InvalidArgumentException( - 'Timezone must be a string.' - ); - } - $this->timezone = $timezone; return $this; } @@ -461,13 +417,9 @@ public function setTimezone($timezone) * * @return string */ - public function timezone() + public function timezone(): string { - if (isset($this->timezone)) { - return $this->timezone; - } else { - return 'UTC'; - } + return ($this->timezone ?? 'UTC'); } /** @@ -477,7 +429,7 @@ public function timezone() * @throws InvalidArgumentException If the project argument is not a string (or null). * @return self */ - public function setProjectName($projectName) + public function setProjectName(?string $projectName): self { if ($projectName === null) { $this->projectName = null; @@ -496,7 +448,7 @@ public function setProjectName($projectName) /** * @return string|null */ - public function projectName() + public function projectName(): ?string { if ($this->projectName === null) { $baseUrl = $this->baseUrl(); @@ -512,7 +464,7 @@ public function projectName() * @param boolean $devMode The "dev mode" flag. * @return self */ - public function setDevMode($devMode) + public function setDevMode(bool $devMode): self { $this->devMode = !!$devMode; return $this; @@ -521,7 +473,7 @@ public function setDevMode($devMode) /** * @return boolean */ - public function devMode() + public function devMode(): bool { return !!$this->devMode; } @@ -533,7 +485,7 @@ public function devMode() * @throws InvalidArgumentException If the argument is not a configset. * @return self */ - public function setView(array $view) + public function setView(array $view): self { $this->view = $view; return $this; @@ -544,7 +496,7 @@ public function setView(array $view) * * @return array */ - public function view() + public function view(): array { return $this->view; } @@ -555,7 +507,7 @@ public function view() * @param array $apis The API configuration structure to set. * @return self */ - public function setApis(array $apis) + public function setApis(array $apis): self { $this->apis = $apis; return $this; @@ -564,7 +516,7 @@ public function setApis(array $apis) /** * @return array */ - public function apis() + public function apis(): array { return $this->apis; } @@ -576,7 +528,7 @@ public function apis() * @param array $routes The route configuration structure to set. * @return self */ - public function setRoutes(array $routes) + public function setRoutes(array $routes): self { $this->routes = $routes; return $this; @@ -585,7 +537,7 @@ public function setRoutes(array $routes) /** * @return array */ - public function routes() + public function routes(): array { return $this->routes; } @@ -594,7 +546,7 @@ public function routes() * @param array|boolean $routables The routable configuration structure to set or FALSE to disable dynamic routing. * @return self */ - public function setRoutables($routables) + public function setRoutables($routables): self { if ($routables !== false) { if (!is_array($routables) || empty($routables)) { @@ -620,7 +572,7 @@ public function routables() * @param array $middlewares The middleware configuration structure to set. * @return self */ - public function setMiddlewares(array $middlewares) + public function setMiddlewares(array $middlewares): self { $this->middlewares = $middlewares; return $this; @@ -629,7 +581,7 @@ public function setMiddlewares(array $middlewares) /** * @return array */ - public function middlewares() + public function middlewares(): array { return $this->middlewares; } @@ -647,7 +599,7 @@ public function middlewares() * @param array $handlers The handlers configuration structure to set. * @return self */ - public function setHandlers(array $handlers) + public function setHandlers(array $handlers): self { $this->handlers = $handlers; return $this; @@ -656,7 +608,7 @@ public function setHandlers(array $handlers) /** * @return array */ - public function handlers() + public function handlers(): array { return $this->handlers; } @@ -667,7 +619,7 @@ public function handlers() * @param array $modules The module configuration structure to set. * @return self */ - public function setModules(array $modules) + public function setModules(array $modules): self { $this->modules = $modules; return $this; @@ -676,7 +628,7 @@ public function setModules(array $modules) /** * @return array */ - public function modules() + public function modules(): array { return $this->modules; } @@ -688,7 +640,7 @@ public function modules() * @throws InvalidArgumentException If the argument is not a configset. * @return self */ - public function setCache(array $cache) + public function setCache(array $cache): self { $this->cache = $cache; return $this; @@ -699,7 +651,7 @@ public function setCache(array $cache) * * @return array */ - public function cache() + public function cache(): array { return $this->cache; } @@ -711,7 +663,7 @@ public function cache() * @throws InvalidArgumentException If the argument is not a configset. * @return self */ - public function setLogger(array $logger) + public function setLogger(array $logger): self { $this->logger = $logger; return $this; @@ -722,16 +674,16 @@ public function setLogger(array $logger) * * @return array */ - public function logger() + public function logger(): array { return $this->logger; } /** - * @param array $databases The avaiable databases config. + * @param array $databases The available databases config. * @return self */ - public function setDatabases(array $databases) + public function setDatabases(array $databases): self { $this->databases = $databases; return $this; @@ -741,7 +693,7 @@ public function setDatabases(array $databases) * @throws Exception If trying to access this method and no databases were set. * @return array */ - public function databases() + public function databases(): array { if ($this->databases === null) { throw new Exception( @@ -757,13 +709,8 @@ public function databases() * @throws Exception If trying to access an invalid database. * @return array */ - public function databaseConfig($ident) + public function databaseConfig(string $ident): array { - if (!is_string($ident)) { - throw new InvalidArgumentException( - 'Invalid app config: default database must be a string.' - ); - } $databases = $this->databases(); if (!isset($databases[$ident])) { throw new Exception( @@ -778,13 +725,8 @@ public function databaseConfig($ident) * @throws InvalidArgumentException If the argument is not a string. * @return self */ - public function setDefaultDatabase($defaultDatabase) + public function setDefaultDatabase(string $defaultDatabase): self { - if (!is_string($defaultDatabase)) { - throw new InvalidArgumentException( - 'Invalid app config: Default database must be a string.' - ); - } $this->defaultDatabase = $defaultDatabase; return $this; } @@ -795,14 +737,8 @@ public function setDefaultDatabase($defaultDatabase) * @throws InvalidArgumentException If the arguments are invalid. * @return self */ - public function addDatabase($ident, array $config) + public function addDatabase(string $ident, array $config): self { - if (!is_string($ident)) { - throw new InvalidArgumentException( - 'Invalid app config: database ident must be a string.' - ); - } - if ($this->databases === null) { $this->databases = []; } @@ -812,9 +748,9 @@ public function addDatabase($ident, array $config) /** * @throws Exception If trying to access this method before a setter. - * @return mixed + * @return string */ - public function defaultDatabase() + public function defaultDatabase(): string { if ($this->defaultDatabase === null) { throw new Exception( @@ -831,7 +767,7 @@ public function defaultDatabase() * @throws InvalidArgumentException If the argument is not a configset. * @return self */ - public function setFilesystem(array $filesystem) + public function setFilesystem(array $filesystem): self { $this->filesystem = $filesystem; return $this; @@ -842,7 +778,7 @@ public function setFilesystem(array $filesystem) * * @return array */ - public function filesystem() + public function filesystem(): array { return $this->filesystem; } diff --git a/packages/app/src/Charcoal/App/Config/DatabaseConfig.php b/packages/app/src/Charcoal/App/Config/DatabaseConfig.php index c5533aa49..a76296eb3 100644 --- a/packages/app/src/Charcoal/App/Config/DatabaseConfig.php +++ b/packages/app/src/Charcoal/App/Config/DatabaseConfig.php @@ -44,7 +44,7 @@ class DatabaseConfig extends AbstractConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'type' => 'mysql', diff --git a/packages/app/src/Charcoal/App/Config/LoggerConfig.php b/packages/app/src/Charcoal/App/Config/LoggerConfig.php index 800314189..214056ff1 100644 --- a/packages/app/src/Charcoal/App/Config/LoggerConfig.php +++ b/packages/app/src/Charcoal/App/Config/LoggerConfig.php @@ -50,7 +50,7 @@ class LoggerConfig extends AbstractConfig * * @return array */ - public function defaults() + public function defaults(): array { return [ 'active' => true, diff --git a/packages/cache/src/Charcoal/Cache/CacheConfig.php b/packages/cache/src/Charcoal/Cache/CacheConfig.php index 041df2bc9..ad8724fb6 100644 --- a/packages/cache/src/Charcoal/Cache/CacheConfig.php +++ b/packages/cache/src/Charcoal/Cache/CacheConfig.php @@ -66,7 +66,7 @@ class CacheConfig extends AbstractConfig * * @return array */ - public function defaults() + public function defaults(): array { return [ 'active' => true, diff --git a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php index 1c94a670c..f71037999 100644 --- a/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php +++ b/packages/cache/src/Charcoal/Cache/Middleware/CacheMiddleware.php @@ -145,7 +145,7 @@ public function __construct(array $data) * * @return array */ - public function defaults() + public function defaults(): array { return [ 'ttl' => CacheConfig::DAY_IN_SECONDS, diff --git a/packages/config/src/Charcoal/Config/AbstractConfig.php b/packages/config/src/Charcoal/Config/AbstractConfig.php index d9a33d9e8..5a06b0f8b 100644 --- a/packages/config/src/Charcoal/Config/AbstractConfig.php +++ b/packages/config/src/Charcoal/Config/AbstractConfig.php @@ -82,7 +82,7 @@ final public function __construct($data = null, array $delegates = null) * * @return array Key-value array of data */ - public function defaults() + public function defaults(): array { return []; } @@ -96,7 +96,7 @@ public function defaults() * (such as {@see ConfigInterface}). * @return self */ - public function merge($data) + public function merge($data): self { foreach ($data as $key => $value) { $this->offsetReplace($key, $value); @@ -334,7 +334,7 @@ public function offsetReplace($key, $value) * @param string $path The file to load and add. * @return self */ - public function addFile($path) + public function addFile(string $path): self { $config = $this->loadFile($path); if (is_array($config)) { diff --git a/packages/config/src/Charcoal/Config/ConfigInterface.php b/packages/config/src/Charcoal/Config/ConfigInterface.php index 780dcd384..83ed59e94 100644 --- a/packages/config/src/Charcoal/Config/ConfigInterface.php +++ b/packages/config/src/Charcoal/Config/ConfigInterface.php @@ -18,7 +18,7 @@ interface ConfigInterface extends * * @return array Key-value array of data */ - public function defaults(); + public function defaults(): array; /** * Adds new data, replacing / merging existing data with the same key. @@ -26,7 +26,7 @@ public function defaults(); * @param array|\Traversable $data Key-value array of data to merge. * @return ConfigInterface Chainable */ - public function merge($data); + public function merge(array $data): ConfigInterface; /** * Add a configuration file to the configset. @@ -34,5 +34,5 @@ public function merge($data); * @param string $path The file to load and add. * @return ConfigInterface Chainable */ - public function addFile($path); + public function addFile(string $path): ConfigInterface; } diff --git a/packages/config/tests/Charcoal/Config/Mock/MacroConfig.php b/packages/config/tests/Charcoal/Config/Mock/MacroConfig.php index 9a4dd4933..128abc9a3 100644 --- a/packages/config/tests/Charcoal/Config/Mock/MacroConfig.php +++ b/packages/config/tests/Charcoal/Config/Mock/MacroConfig.php @@ -16,7 +16,7 @@ class MacroConfig extends AbstractConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'foo' => -3, diff --git a/packages/core/src/Charcoal/Model/Service/MetadataConfig.php b/packages/core/src/Charcoal/Model/Service/MetadataConfig.php index 446dc61bf..442f59c20 100644 --- a/packages/core/src/Charcoal/Model/Service/MetadataConfig.php +++ b/packages/core/src/Charcoal/Model/Service/MetadataConfig.php @@ -34,7 +34,7 @@ class MetadataConfig extends AbstractConfig * @return mixed An associative array if $key is NULL. * If $key is specified, the value of that data key if it exists, NULL on failure. */ - public function defaults($key = null) + public function defaults($key = null): array { $data = [ 'paths' => [], @@ -55,7 +55,7 @@ public function defaults($key = null) * @param array|Traversable $data The data to merge. * @return self */ - public function merge($data) + public function merge($data): self { foreach ($data as $key => $val) { if ($key === 'paths') { diff --git a/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php b/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php index 6cfe1042c..1cfbfee80 100644 --- a/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php +++ b/packages/core/src/Charcoal/Source/DatabaseSourceConfig.php @@ -44,7 +44,7 @@ class DatabaseSourceConfig extends SourceConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'type' => 'mysql', diff --git a/packages/core/src/Charcoal/Source/SourceConfig.php b/packages/core/src/Charcoal/Source/SourceConfig.php index 5f4081606..09b9b756a 100644 --- a/packages/core/src/Charcoal/Source/SourceConfig.php +++ b/packages/core/src/Charcoal/Source/SourceConfig.php @@ -19,7 +19,7 @@ class SourceConfig extends AbstractConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'type' => '' diff --git a/packages/translator/src/Charcoal/Translator/LocalesConfig.php b/packages/translator/src/Charcoal/Translator/LocalesConfig.php index 7640c6111..f042e59d4 100644 --- a/packages/translator/src/Charcoal/Translator/LocalesConfig.php +++ b/packages/translator/src/Charcoal/Translator/LocalesConfig.php @@ -37,7 +37,7 @@ class LocalesConfig extends AbstractConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'languages' => [ diff --git a/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php b/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php index 388157429..4ebaec46b 100644 --- a/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php +++ b/packages/translator/src/Charcoal/Translator/Middleware/LanguageMiddleware.php @@ -119,7 +119,7 @@ public function __construct(array $data) * * @return array */ - public function defaults() + public function defaults(): array { return [ 'default_language' => null, diff --git a/packages/translator/src/Charcoal/Translator/TranslatorConfig.php b/packages/translator/src/Charcoal/Translator/TranslatorConfig.php index 9063debdb..29a2d1e77 100644 --- a/packages/translator/src/Charcoal/Translator/TranslatorConfig.php +++ b/packages/translator/src/Charcoal/Translator/TranslatorConfig.php @@ -52,7 +52,7 @@ class TranslatorConfig extends AbstractConfig /** * @return array */ - public function defaults() + public function defaults(): array { return [ 'loaders' => [ diff --git a/packages/user/src/Charcoal/User/AuthTokenMetadata.php b/packages/user/src/Charcoal/User/AuthTokenMetadata.php index 43c445d54..de39268a0 100644 --- a/packages/user/src/Charcoal/User/AuthTokenMetadata.php +++ b/packages/user/src/Charcoal/User/AuthTokenMetadata.php @@ -36,7 +36,7 @@ class AuthTokenMetadata extends ModelMetadata * * @return array */ - public function defaults() + public function defaults(): array { $parentDefaults = parent::defaults(); From 59f3deb0e68588ace84a967ca232ca09b2f953ce Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 16 Sep 2022 17:48:40 -0400 Subject: [PATCH 18/92] feat(event): add an AbstractEventListener and EventListenerInterface --- .../App/Event/AbstractEventListener.php | 23 +++++++++++++++++++ .../App/Event/EventListenerInterface.php | 17 ++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Event/AbstractEventListener.php create mode 100644 packages/app/src/Charcoal/App/Event/EventListenerInterface.php diff --git a/packages/app/src/Charcoal/App/Event/AbstractEventListener.php b/packages/app/src/Charcoal/App/Event/AbstractEventListener.php new file mode 100644 index 000000000..08c281b50 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/AbstractEventListener.php @@ -0,0 +1,23 @@ +setLogger($container['logger']); + } +} diff --git a/packages/app/src/Charcoal/App/Event/EventListenerInterface.php b/packages/app/src/Charcoal/App/Event/EventListenerInterface.php new file mode 100644 index 000000000..81cd67021 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/EventListenerInterface.php @@ -0,0 +1,17 @@ + Date: Fri, 16 Sep 2022 17:49:35 -0400 Subject: [PATCH 19/92] feat(event): add a base event and a (wip) generic event --- packages/app/src/Charcoal/App/Event/Event.php | 32 +++++++ .../src/Charcoal/App/Event/GenericEvent.php | 95 +++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Event/Event.php create mode 100644 packages/app/src/Charcoal/App/Event/GenericEvent.php diff --git a/packages/app/src/Charcoal/App/Event/Event.php b/packages/app/src/Charcoal/App/Event/Event.php new file mode 100644 index 000000000..d9cb3a3a3 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/Event.php @@ -0,0 +1,32 @@ +stopped; + } + + /** + * Stop the propagation of the event to further listeners. + * The remainder of the subscribed listeners won't be dispatched + * + * @return void + */ + protected function stopPropagation() + { + $this->stopped = true; + } +} diff --git a/packages/app/src/Charcoal/App/Event/GenericEvent.php b/packages/app/src/Charcoal/App/Event/GenericEvent.php new file mode 100644 index 000000000..d3702d6b6 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/GenericEvent.php @@ -0,0 +1,95 @@ +eventName = $eventName; + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * @return string + */ + public function eventName(): string + { + return $this->getEventName(); + } + + /** + * @return mixed + */ + public function getSubject() + { + return $this->subject; + } + + /** + * @param mixed $subject Subject for GenericEvent. + * @return self + */ + public function setSubject($subject): self + { + $this->subject = $subject; + + return $this; + } + + /** + * @return array + */ + public function getArguments(): array + { + return $this->arguments; + } + + /** + * @param array $arguments Arguments for GenericEvent. + * @return self + */ + public function setArguments(array $arguments): self + { + $this->arguments = $arguments; + + return $this; + } + + /** + * @return string + */ + public function getEventName(): string + { + return $this->eventName; + } + + /** + * @param string $eventName EventName for GenericEvent. + * @return self + */ + public function setEventName(string $eventName): self + { + $this->eventName = $eventName; + + return $this; + } +} From 67cc23cedf90f028de8853fe9dd364665794a11e Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 16 Sep 2022 17:50:38 -0400 Subject: [PATCH 20/92] feat(event): add a charcoal EventDispatcher which extends League\Event\EventDispatcher to add logging --- .../Charcoal/App/Event/EventDispatcher.php | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Event/EventDispatcher.php diff --git a/packages/app/src/Charcoal/App/Event/EventDispatcher.php b/packages/app/src/Charcoal/App/Event/EventDispatcher.php new file mode 100644 index 000000000..5a75bc422 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/EventDispatcher.php @@ -0,0 +1,25 @@ +logger) { + $this->logger->notice('Event dispatched : [' . get_class($event) . ']'); + } + + return parent::dispatch($event); + } +} From c130dfafd3a6b7c0278cb90a74745e9b1380f2d0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 16 Sep 2022 17:52:45 -0400 Subject: [PATCH 21/92] refactor(event): move EventDispatcherTrait.php to the `event` namespace --- .../app/src/Charcoal/App/{ => Event}/EventDispatcherTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename packages/app/src/Charcoal/App/{ => Event}/EventDispatcherTrait.php (91%) diff --git a/packages/app/src/Charcoal/App/EventDispatcherTrait.php b/packages/app/src/Charcoal/App/Event/EventDispatcherTrait.php similarity index 91% rename from packages/app/src/Charcoal/App/EventDispatcherTrait.php rename to packages/app/src/Charcoal/App/Event/EventDispatcherTrait.php index 33abd33a0..43cecfc84 100644 --- a/packages/app/src/Charcoal/App/EventDispatcherTrait.php +++ b/packages/app/src/Charcoal/App/Event/EventDispatcherTrait.php @@ -1,12 +1,12 @@ Date: Fri, 16 Sep 2022 18:00:36 -0400 Subject: [PATCH 22/92] feat(event): Add an event service provider and improve listeners registration --- .../ServiceProvider/AppServiceProvider.php | 18 +---- .../ServiceProvider/EventServiceProvider.php | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index 82d0d680e..0a9d7b660 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -4,7 +4,6 @@ // From PSR-7 use Charcoal\Factory\GenericResolver; -use Psr\EventDispatcher\EventDispatcherInterface; use Psr\Http\Message\UriInterface; // From Pimple use Pimple\ServiceProviderInterface; @@ -13,7 +12,6 @@ use Slim\Http\Uri; // From 'league' use League\CLImate\CLImate; -use League\Event\EventDispatcher; // From Mustache use Mustache_LambdaHelper as LambdaHelper; use Charcoal\Factory\GenericFactory as Factory; @@ -73,6 +71,7 @@ public function register(Container $container) { $container->register(new CacheServiceProvider()); $container->register(new DatabaseServiceProvider()); + $container->register(new EventServiceProvider()); $container->register(new FilesystemServiceProvider()); $container->register(new LoggerServiceProvider()); $container->register(new ScriptServiceProvider()); @@ -86,7 +85,6 @@ public function register(Container $container) $this->registerRequestControllerServices($container); $this->registerModuleServices($container); $this->registerViewServices($container); - $this->registerEventServices($container); } /** @@ -589,18 +587,4 @@ protected function registerTwigHelpersServices(Container $container): void ); }); } - - /** - * @param Container $container The DI container. - * @return void - */ - protected function registerEventServices(Container $container): void - { - /** - * @return EventDispatcherInterface - */ - $container['event/dispatcher'] = function (): EventDispatcherInterface { - return new EventDispatcher(); - }; - } } diff --git a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php new file mode 100644 index 000000000..7cdaf826a --- /dev/null +++ b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php @@ -0,0 +1,79 @@ +setLogger($container['logger']); + + foreach ($container['event/listeners'] as $event => $listeners) { + if (is_string($listeners)) { + $listeners = [$listeners]; + } + + foreach ($listeners as $listener => $options) { + $listener = $container['event/listener/factory']->create($listener); + + $priority = ($options['priority'] ?? 0); + $once = ($options['once'] ?? false); + + if ($once) { + $dispatcher->subscribeOnceTo($event, $listener, $priority); + continue; + } + + $dispatcher->subscribeTo($event, $listener, $priority); + } + } + + return $dispatcher; + }; + + /** + * @param Container $container + * @return array + */ + $container['event/listeners'] = function (Container $container): array { + return ($container['config']->get('events.listeners') ?? []); + }; + + /** + * @param Container $container The Pimple DI container. + * @return FactoryInterface + */ + $container['event/listener/factory'] = function (Container $container) { + return new GenericFactory([ + 'base_class' => EventListenerInterface::class, + 'resolver_options' => [ + 'suffix' => 'Listener' + ], + 'callback' => function ($listener) use ($container) { + $listener->setDependencies($container); + } + ]); + }; + } +} From e49ff797d20d13c979d16d9a77c47adcaac8f286 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 16 Sep 2022 18:01:01 -0400 Subject: [PATCH 23/92] feat(event): add a FileWasUploaded event --- .../Charcoal/App/Event/FileWasUploaded.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Event/FileWasUploaded.php diff --git a/packages/app/src/Charcoal/App/Event/FileWasUploaded.php b/packages/app/src/Charcoal/App/Event/FileWasUploaded.php new file mode 100644 index 000000000..554daf78b --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/FileWasUploaded.php @@ -0,0 +1,27 @@ +file = $file; + } + + /** + * @return string + */ + public function getFile(): string + { + return $this->file; + } +} From ecc7180316be4d4f8b1b0652d87d4611f6f4d642 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 19 Sep 2022 10:57:22 -0400 Subject: [PATCH 24/92] feat(elfinder): dispatch FileWasUploaded on elfinder upload.presave --- .../Admin/Action/ElfinderConnectorAction.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index a1b37ca7d..ca77970cd 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -2,6 +2,9 @@ namespace Charcoal\Admin\Action; +use Charcoal\App\Event\EventDispatcherTrait; +use Charcoal\App\Event\FileWasUploaded; +use finfo; use InvalidArgumentException; use RuntimeException; use UnexpectedValueException; @@ -33,6 +36,7 @@ class ElfinderConnectorAction extends AdminAction { use CallableResolverAwareTrait; + use EventDispatcherTrait; /** * The default relative path (from filesystem's root) to the storage directory. @@ -186,6 +190,18 @@ public function setupElfinder(array $extraOptions = []) define('ELFINDER_IMG_PARENT_URL', (string)$this->baseUrl(ElfinderTemplate::ELFINDER_ASSETS_REL_PATH)); } + $extraOptions = array_merge($extraOptions, ['bind' => [ + 'upload.presave' => [function (&$thash, &$name, $src) { + if (!$src || !file_exists($src)) { + return false; + } + + $this->getEventDispatcher()->dispatch(new FileWasUploaded($src)); + + return true; + }] + ]]); + $options = $this->buildConnectorOptions($extraOptions); // Run elFinder @@ -897,6 +913,8 @@ public function setDependencies(Container $container) /** @see \Charcoal\App\ServiceProvide\FilesystemServiceProvider */ $this->filesystemConfig = $container['filesystem/config']; $this->filesystems = $container['filesystems']; + + $this->setEventDispatcher($container['event/dispatcher']); } /** From 9dbe114cfdd6c12e3ef5bb0ea4238d8216244f33 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 19 Sep 2022 13:05:00 -0400 Subject: [PATCH 25/92] build(composer): update composer lock file --- composer.lock | 123 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index f098188f5..727234a2e 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": "604ff6da0b39eb152bc0a35f54757e8c", + "content-hash": "afd41cc24ded974941402c1cc5cd8a2b", "packages": [ { "name": "barryvdh/elfinder-flysystem-driver", @@ -802,6 +802,65 @@ }, "time": "2022-06-18T14:42:08+00:00" }, + { + "name": "league/event", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/event.git", + "reference": "6d6d88d3c398f4e32995fccd4ec50a5bdaef131b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/event/zipball/6d6d88d3c398f4e32995fccd4ec50a5bdaef131b", + "reference": "6d6d88d3c398f4e32995fccd4ec50a5bdaef131b", + "shasum": "" + }, + "require": { + "php": ">=7.2.0", + "psr/event-dispatcher": "^1.0" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.45", + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "support": { + "issues": "https://github.com/thephpleague/event/issues", + "source": "https://github.com/thephpleague/event/tree/3.0.0" + }, + "time": "2020-09-29T17:42:28+00:00" + }, { "name": "league/flysystem", "version": "1.1.9", @@ -1494,6 +1553,56 @@ }, "time": "2021-11-05T16:50:12+00:00" }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "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" + }, + "time": "2019-01-08T18:20:26+00:00" + }, { "name": "psr/http-client", "version": "1.0.1", @@ -2690,16 +2799,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.235.8", + "version": "3.235.10", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "cc33d53d735a3835adff212598f2a20ee9ac9531" + "reference": "943f96f50d19244584675c34fb3e4c8aa3eaddce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/cc33d53d735a3835adff212598f2a20ee9ac9531", - "reference": "cc33d53d735a3835adff212598f2a20ee9ac9531", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/943f96f50d19244584675c34fb3e4c8aa3eaddce", + "reference": "943f96f50d19244584675c34fb3e4c8aa3eaddce", "shasum": "" }, "require": { @@ -2778,9 +2887,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.235.8" + "source": "https://github.com/aws/aws-sdk-php/tree/3.235.10" }, - "time": "2022-09-14T18:18:31+00:00" + "time": "2022-09-16T18:18:42+00:00" }, { "name": "cache/adapter-common", From 3d0ca304074f939b8393e2ee11c8cc20efa54683 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 19 Sep 2022 13:22:56 -0400 Subject: [PATCH 26/92] tests(property): fix phpunit test --- packages/property/src/Charcoal/Property/AbstractProperty.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/property/src/Charcoal/Property/AbstractProperty.php b/packages/property/src/Charcoal/Property/AbstractProperty.php index 1cf96c4c9..44e624624 100644 --- a/packages/property/src/Charcoal/Property/AbstractProperty.php +++ b/packages/property/src/Charcoal/Property/AbstractProperty.php @@ -32,7 +32,7 @@ use Charcoal\Property\StorablePropertyInterface; use Charcoal\Property\StorablePropertyTrait; // From 'charcoal-app' -use Charcoal\App\EventDispatcherTrait; +use Charcoal\App\Event\EventDispatcherTrait; /** * An abstract class that implements the full `PropertyInterface`. From 9ab5164fad951877742913eb87a36049aa8f2a4e Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 19 Sep 2022 17:24:15 -0400 Subject: [PATCH 27/92] fix(translation-parser): fix paths related problems with translation-parser script --- .../Charcoal/Translator/Script/TranslationParserScript.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php b/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php index 094e4237f..a6de9e8f0 100644 --- a/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php +++ b/packages/translator/src/Charcoal/Translator/Script/TranslationParserScript.php @@ -266,7 +266,7 @@ public function output() return $this->output; } $output = $this->argOrInput('output'); - $this->output = (string)$output; + $this->output = rtrim((string)$output, '/') . DIRECTORY_SEPARATOR; return $this->output; } @@ -357,7 +357,6 @@ public function getTranslations() */ public function getTranslationsFromPath($path, $fileType) { - // remove vendor/locomotivemtl/charcoal-app $base = $this->appConfig->get('base_path'); $glob = $this->globRecursive($base . DIRECTORY_SEPARATOR . $path . '*.' . $fileType); $regex = $this->regEx($fileType); @@ -445,7 +444,7 @@ public function paths() { if (!$this->paths) { $this->paths = $this->appConfig->get('translator.parser.view.paths') ?: - $this->appConfig->get('view.paths'); + $this->appConfig->resolveValues($this->appConfig->get('view.paths')); /** @todo Hardcoded; Change this! */ $this->paths[] = 'src/'; @@ -495,7 +494,7 @@ public function toCSV(array $translations) if (!file_exists($filePath)) { mkdir($filePath, 0755, true); } - $file = fopen($base . $output . $domain . '.' . $lang . '.csv', 'w'); + $file = fopen($base . DIRECTORY_SEPARATOR . $output . $domain . '.' . $lang . '.csv', 'w'); if (!$file) { continue; } From 79bde11d0e88ab1a4ac91601b62748ec7c08c055 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 19 Sep 2022 17:25:22 -0400 Subject: [PATCH 28/92] feat(translation-parser): add translation-parser script tag to default.admin.config --- packages/admin/config/admin.config.default.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/admin/config/admin.config.default.json b/packages/admin/config/admin.config.default.json index 1cb5d3dfa..adb8c02fa 100644 --- a/packages/admin/config/admin.config.default.json +++ b/packages/admin/config/admin.config.default.json @@ -239,6 +239,9 @@ }, "tools/resize-images": { "ident": "charcoal/admin/script/tools/resize-images" + }, + "translation/parse": { + "ident": "charcoal/translator/script/translation-parser" } } }, From 0f741a169db8a5a76567ed182348e7d0ef6697aa Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:41:57 -0400 Subject: [PATCH 29/92] refactor(event): rename `stopped` property --- packages/app/src/Charcoal/App/Event/Event.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/app/src/Charcoal/App/Event/Event.php b/packages/app/src/Charcoal/App/Event/Event.php index d9cb3a3a3..b0d07ae5f 100644 --- a/packages/app/src/Charcoal/App/Event/Event.php +++ b/packages/app/src/Charcoal/App/Event/Event.php @@ -9,14 +9,14 @@ */ class Event implements StoppableEventInterface { - private bool $stopped = false; + private bool $propagationStopped = false; /** * @inheritDoc */ public function isPropagationStopped(): bool { - return !!$this->stopped; + return !!$this->propagationStopped; } /** @@ -27,6 +27,6 @@ public function isPropagationStopped(): bool */ protected function stopPropagation() { - $this->stopped = true; + $this->propagationStopped = true; } } From ce6d9662d8cb40afe1ce2de8e58bdee73627c9ea Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:45:42 -0400 Subject: [PATCH 30/92] docs(event): improve class comment --- packages/app/src/Charcoal/App/Event/AbstractEventListener.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/src/Charcoal/App/Event/AbstractEventListener.php b/packages/app/src/Charcoal/App/Event/AbstractEventListener.php index 08c281b50..ed9b88ec6 100644 --- a/packages/app/src/Charcoal/App/Event/AbstractEventListener.php +++ b/packages/app/src/Charcoal/App/Event/AbstractEventListener.php @@ -7,6 +7,8 @@ /** * Abstract Event Listener + * + * Starting point to create an eventListener. */ abstract class AbstractEventListener implements EventListenerInterface { From 610d5898bf5695dcecf121364520c05c345b1985 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:50:41 -0400 Subject: [PATCH 31/92] refactor(app): improve readability in FilesystemServiceProviderTest.php --- .../App/ServiceProvider/FilesystemServiceProviderTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php b/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php index a9a6e9cc4..b745de795 100644 --- a/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php +++ b/packages/app/tests/Charcoal/App/ServiceProvider/FilesystemServiceProviderTest.php @@ -207,7 +207,9 @@ public function testConfigWithoutTypeThrowsException() private function createAppConfig($defaults = null) { - return new AppConfig(array_replace(['base_path' => sys_get_temp_dir()], $defaults)); + return new AppConfig(array_replace([ + 'base_path' => sys_get_temp_dir() + ], $defaults)); } private function getContainer($defaults = null) From c9057bb56d03d9b8ccf52b185a58844df2444b85 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:59:26 -0400 Subject: [PATCH 32/92] refactor(event): add a method to register event listeners in EventServiceProvider.php --- .../ServiceProvider/EventServiceProvider.php | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php index 7cdaf826a..98f85abb3 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php @@ -29,25 +29,7 @@ public function register(Container $container) $dispatcher = new EventDispatcher(); $dispatcher->setLogger($container['logger']); - foreach ($container['event/listeners'] as $event => $listeners) { - if (is_string($listeners)) { - $listeners = [$listeners]; - } - - foreach ($listeners as $listener => $options) { - $listener = $container['event/listener/factory']->create($listener); - - $priority = ($options['priority'] ?? 0); - $once = ($options['once'] ?? false); - - if ($once) { - $dispatcher->subscribeOnceTo($event, $listener, $priority); - continue; - } - - $dispatcher->subscribeTo($event, $listener, $priority); - } - } + $this->registerEventListeners($dispatcher, $container['event/listeners']); return $dispatcher; }; @@ -76,4 +58,32 @@ public function register(Container $container) ]); }; } + + /** + * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface + * @param Container $container Pimple DI container + * @return void + */ + private function registerEventListeners(EventDispatcherInterface $dispatcher, Container $container) + { + foreach ($container['event/listeners'] as $event => $listeners) { + if (is_string($listeners)) { + $listeners = [$listeners]; + } + + foreach ($listeners as $listener => $options) { + $listener = $container['event/listener/factory']->create($listener); + + $priority = ($options['priority'] ?? 0); + $once = ($options['once'] ?? false); + + if ($once) { + $dispatcher->subscribeOnceTo($event, $listener, $priority); + continue; + } + + $dispatcher->subscribeTo($event, $listener, $priority); + } + } + } } From 86184429e1c51e2b311e6f17b23da9cafb7aacb6 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 10:07:21 -0400 Subject: [PATCH 33/92] docs(event): add container comment in foreach loop --- .../src/Charcoal/App/ServiceProvider/EventServiceProvider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php index 98f85abb3..cb1c2df01 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php @@ -66,6 +66,10 @@ public function register(Container $container) */ private function registerEventListeners(EventDispatcherInterface $dispatcher, Container $container) { + /** + * @var array|array, + * array>)> $container['event/listeners'] + */ foreach ($container['event/listeners'] as $event => $listeners) { if (is_string($listeners)) { $listeners = [$listeners]; From 3c3a85e4ca466deb68521abbd640472621647e62 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 11:02:17 -0400 Subject: [PATCH 34/92] refactor(event): force config defined listeners to be registered as array --- .../App/ServiceProvider/EventServiceProvider.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php index cb1c2df01..615340b53 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php @@ -29,7 +29,7 @@ public function register(Container $container) $dispatcher = new EventDispatcher(); $dispatcher->setLogger($container['logger']); - $this->registerEventListeners($dispatcher, $container['event/listeners']); + $this->registerEventListeners($dispatcher, $container); return $dispatcher; }; @@ -67,15 +67,18 @@ public function register(Container $container) private function registerEventListeners(EventDispatcherInterface $dispatcher, Container $container) { /** - * @var array|array, - * array>)> $container['event/listeners'] + * @var array> $container['event/listeners'] */ foreach ($container['event/listeners'] as $event => $listeners) { - if (is_string($listeners)) { - $listeners = [$listeners]; + if (!is_iterable($listeners)) { + throw new \InvalidArgumentException(); } foreach ($listeners as $listener => $options) { + if (!is_string($listener)) { + throw new \InvalidArgumentException(); + } + $listener = $container['event/listener/factory']->create($listener); $priority = ($options['priority'] ?? 0); From e1dd427f0434b5c8b555fa0b43112f1f53d52534 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:48:30 -0400 Subject: [PATCH 35/92] refactor(event-dispatcher): improve logging context Co-authored-by: Chauncey McAskill --- .../app/src/Charcoal/App/Event/EventDispatcher.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/app/src/Charcoal/App/Event/EventDispatcher.php b/packages/app/src/Charcoal/App/Event/EventDispatcher.php index 5a75bc422..2824b6775 100644 --- a/packages/app/src/Charcoal/App/Event/EventDispatcher.php +++ b/packages/app/src/Charcoal/App/Event/EventDispatcher.php @@ -17,7 +17,15 @@ class EventDispatcher extends LeagueEventDispatcher public function dispatch(object $event): object { if ($this->logger) { - $this->logger->notice('Event dispatched : [' . get_class($event) . ']'); + if ($event instanceof HasEventName) { + $this->logger->notice('Event [' . $event->eventName() . '] dispatched', [ + 'event' => get_class($event), + ]); + } else { + $this->logger->notice('Event dispatched', [ + 'event' => get_class($event), + ]); + } } return parent::dispatch($event); From 33c1943c83a0191978aa8c4bb3b041d6a80a7a9c Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 09:51:23 -0400 Subject: [PATCH 36/92] style(event): fix typo Co-authored-by: Chauncey McAskill --- packages/app/src/Charcoal/App/Event/FileWasUploaded.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/Charcoal/App/Event/FileWasUploaded.php b/packages/app/src/Charcoal/App/Event/FileWasUploaded.php index 554daf78b..8963a4c33 100644 --- a/packages/app/src/Charcoal/App/Event/FileWasUploaded.php +++ b/packages/app/src/Charcoal/App/Event/FileWasUploaded.php @@ -10,7 +10,7 @@ class FileWasUploaded extends Event private string $file; /** - * @param string $file THe file path. + * @param string $file The file path. */ public function __construct(string $file) { From 3b1f4babb8d3aa8ff791301b40275d8e4c09b14b Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 11:06:01 -0400 Subject: [PATCH 37/92] refactor(config): improve path readability Co-authored-by: Chauncey McAskill --- packages/admin/src/Charcoal/Admin/Config.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Config.php b/packages/admin/src/Charcoal/Admin/Config.php index 5fd4c9949..2c25ee540 100644 --- a/packages/admin/src/Charcoal/Admin/Config.php +++ b/packages/admin/src/Charcoal/Admin/Config.php @@ -52,8 +52,7 @@ class Config extends AbstractConfig */ public function defaults(): array { - $baseDir = rtrim(realpath(__DIR__ . '/../../../'), '/'); - $confDir = $baseDir . '/config'; + $confDir = dirname(__DIR__, 3) . DIRECTORY_SEPARATOR . 'config'; return $this->loadFile($confDir . '/admin.config.default.json'); } From 8784c148fc12a5e80f05f49bc138c438ecf428f4 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 14:20:41 -0400 Subject: [PATCH 38/92] feat(event): add support for ListenerSubscriber through config files and add AbstractListenerSubscriber.php --- .../App/Event/AbstractListenerSubscriber.php | 38 ++++++++++++++ .../ServiceProvider/EventServiceProvider.php | 49 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Event/AbstractListenerSubscriber.php diff --git a/packages/app/src/Charcoal/App/Event/AbstractListenerSubscriber.php b/packages/app/src/Charcoal/App/Event/AbstractListenerSubscriber.php new file mode 100644 index 000000000..7d347cc74 --- /dev/null +++ b/packages/app/src/Charcoal/App/Event/AbstractListenerSubscriber.php @@ -0,0 +1,38 @@ +listenerFactory = $container['event/listener/factory']; + } + + /** + * @param $listener + * @return EventListenerInterface + */ + protected function createListener($listener): EventListenerInterface + { + return $this->listenerFactory->create($listener); + } + + abstract public function subscribeListeners(ListenerRegistry $acceptor): void; +} diff --git a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php index 615340b53..e5e179feb 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/EventServiceProvider.php @@ -6,6 +6,7 @@ use Charcoal\App\Event\EventListenerInterface; use Charcoal\Factory\FactoryInterface; use Charcoal\Factory\GenericFactory; +use League\Event\ListenerSubscriber; use Pimple\Container; use Pimple\ServiceProviderInterface; use Psr\EventDispatcher\EventDispatcherInterface; @@ -30,6 +31,7 @@ public function register(Container $container) $dispatcher->setLogger($container['logger']); $this->registerEventListeners($dispatcher, $container); + $this->registerListenerSubscribers($dispatcher, $container); return $dispatcher; }; @@ -42,6 +44,17 @@ public function register(Container $container) return ($container['config']->get('events.listeners') ?? []); }; + /** + * Subscribers are classes that implements `\League\Event\ListenerSubscriber` + * It allows to subscribe many grouped listeners at once. + * + * @param Container $container + * @return array + */ + $container['event/subscribers'] = function (Container $container): array { + return ($container['config']->get('events.subscribers') ?? []); + }; + /** * @param Container $container The Pimple DI container. * @return FactoryInterface @@ -57,6 +70,24 @@ public function register(Container $container) } ]); }; + + /** + * @param Container $container The Pimple DI container. + * @return FactoryInterface + */ + $container['event/listener-subscriber/factory'] = function (Container $container) { + return new GenericFactory([ + 'base_class' => ListenerSubscriber::class, + 'resolver_options' => [ + 'suffix' => 'Subscriber' + ], + 'callback' => function ($subscriber) use ($container) { + if (is_callable([$subscriber, 'setDependencies'])) { + $subscriber->setDependencies($container); + } + } + ]); + }; } /** @@ -93,4 +124,22 @@ private function registerEventListeners(EventDispatcherInterface $dispatcher, Co } } } + + /** + * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface + * @param Container $container Pimple DI container + * @return void + */ + private function registerListenerSubscribers(EventDispatcherInterface $dispatcher, Container $container) + { + foreach ($container['event/subscribers'] as $subscriber) { + if (!is_string($subscriber) || !class_exists($subscriber)) { + throw new \InvalidArgumentException(); + } + + $subscriber = $container['event/listener-subscriber/factory']->create($subscriber); + + $dispatcher->subscribeListenersFrom($subscriber); + } + } } From 4a010cfb983d3d16b4e431261f7904074542a976 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 17:17:08 -0400 Subject: [PATCH 39/92] refactor(event): move the event system to it's own package --- composer.json | 3 + packages/app/composer.json | 4 +- .../ServiceProvider/AppServiceProvider.php | 3 +- packages/event/.editorconfig | 17 ++++ packages/event/.gitattributes | 9 ++ packages/event/.gitignore | 28 ++++++ packages/event/LICENSE | 21 +++++ packages/event/README.md | 86 +++++++++++++++++++ packages/event/composer.json | 66 ++++++++++++++ packages/event/phpcs.xml.dist | 4 + packages/event/phpunit.xml.dist | 32 +++++++ .../Charcoal}/Event/AbstractEventListener.php | 2 +- .../Event/AbstractListenerSubscriber.php | 2 +- .../src/Charcoal}/Event/Event.php | 2 +- .../src/Charcoal}/Event/EventDispatcher.php | 3 +- .../Charcoal}/Event/EventDispatcherTrait.php | 2 +- .../Event/EventListenerInterface.php | 2 +- .../Event/Events}/FileWasUploaded.php | 4 +- .../src/Charcoal}/Event/GenericEvent.php | 2 +- .../ServiceProvider/EventServiceProvider.php | 6 +- 20 files changed, 284 insertions(+), 14 deletions(-) create mode 100644 packages/event/.editorconfig create mode 100644 packages/event/.gitattributes create mode 100644 packages/event/.gitignore create mode 100644 packages/event/LICENSE create mode 100644 packages/event/README.md create mode 100644 packages/event/composer.json create mode 100644 packages/event/phpcs.xml.dist create mode 100644 packages/event/phpunit.xml.dist rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/AbstractEventListener.php (93%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/AbstractListenerSubscriber.php (96%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/Event.php (95%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/EventDispatcher.php (93%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/EventDispatcherTrait.php (95%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/EventListenerInterface.php (89%) rename packages/{app/src/Charcoal/App/Event => event/src/Charcoal/Event/Events}/FileWasUploaded.php (86%) rename packages/{app/src/Charcoal/App => event/src/Charcoal}/Event/GenericEvent.php (98%) rename packages/{app/src/Charcoal/App => event/src/Charcoal/Event}/ServiceProvider/EventServiceProvider.php (97%) diff --git a/composer.json b/composer.json index 01688f994..89aeba973 100644 --- a/composer.json +++ b/composer.json @@ -96,6 +96,7 @@ ], "Charcoal\\Admin\\": "packages/admin/src/Charcoal/Admin/", "Charcoal\\Email\\": "packages/email/src/Charcoal/Email", + "Charcoal\\Event\\": "packages/event/src/Charcoal/Event/", "Charcoal\\Object\\": "packages/object/src/Charcoal/Object", "Charcoal\\User\\": "packages/user/src/Charcoal/User", "Charcoal\\View\\": "packages/view/src/Charcoal/View" @@ -115,6 +116,7 @@ "packages/config/tests/Charcoal/", "packages/core/tests/Charcoal", "packages/email/tests/Charcoal", + "packages/event/tests/Charcoal", "packages/factory/tests/Charcoal/", "packages/image/tests/Charcoal", "packages/object/tests/Charcoal/", @@ -136,6 +138,7 @@ "charcoal/config": "self.version", "charcoal/core": "self.version", "charcoal/email": "self.version", + "charcoal/event": "3.1.8", "charcoal/factory": "self.version", "charcoal/image": "self.version", "charcoal/object": "self.version", diff --git a/packages/app/composer.json b/packages/app/composer.json index 62227b271..9eb42a435 100644 --- a/packages/app/composer.json +++ b/packages/app/composer.json @@ -30,6 +30,7 @@ "league/flysystem": "^1.0", "charcoal/cache": "^4.0.1", "charcoal/config": "^4.0.1", + "charcoal/event": "^3.2", "charcoal/factory": "^4.0.1", "charcoal/translator": "^4.0.1", "charcoal/view": "^4.0.1", @@ -37,8 +38,7 @@ "psr/http-message": "^1.0", "psr/log": "^1.0", "slim/slim": "^3.7", - "vlucas/phpdotenv": "^5.4", - "league/event": "^3.0" + "vlucas/phpdotenv": "^5.4" }, "require-dev": { "league/flysystem-aws-s3-v3": "^1.0", diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index 0a9d7b660..3859433ac 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -3,7 +3,6 @@ namespace Charcoal\App\ServiceProvider; // From PSR-7 -use Charcoal\Factory\GenericResolver; use Psr\Http\Message\UriInterface; // From Pimple use Pimple\ServiceProviderInterface; @@ -14,7 +13,9 @@ use League\CLImate\CLImate; // From Mustache use Mustache_LambdaHelper as LambdaHelper; +use Charcoal\Event\ServiceProvider\EventServiceProvider; use Charcoal\Factory\GenericFactory as Factory; +use Charcoal\Factory\GenericResolver; use Charcoal\Cache\ServiceProvider\CacheServiceProvider; use Charcoal\Translator\ServiceProvider\TranslatorServiceProvider; use Charcoal\App\AppConfig; diff --git a/packages/event/.editorconfig b/packages/event/.editorconfig new file mode 100644 index 000000000..1a2c8bde6 --- /dev/null +++ b/packages/event/.editorconfig @@ -0,0 +1,17 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{md,markdown}] +trim_trailing_whitespace = false + +[*.{ms,mustache}] +insert_final_newline = false diff --git a/packages/event/.gitattributes b/packages/event/.gitattributes new file mode 100644 index 000000000..b8c994f2a --- /dev/null +++ b/packages/event/.gitattributes @@ -0,0 +1,9 @@ +# Ignore for "dist". +/build/travis export-ignore +/tests export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/phpcs.xml.dist export-ignore +/phpunit.xml.dist export-ignore diff --git a/packages/event/.gitignore b/packages/event/.gitignore new file mode 100644 index 000000000..4a3b017d9 --- /dev/null +++ b/packages/event/.gitignore @@ -0,0 +1,28 @@ +# Package Managers + +composer.phar +composer.lock +/vendor/ +/node_modules/ + +# Logging + +*.log +/logs/ + +# Caching + +/cache/ +/.phplint-cache/ +/.phpunit.result.cache + +# Testing + +phpcs.xml +phpunit.xml + +# Codebase + +/build/docs/ +/build/logs/ +/build/report/ diff --git a/packages/event/LICENSE b/packages/event/LICENSE new file mode 100644 index 000000000..5cd151496 --- /dev/null +++ b/packages/event/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Locomotive Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/event/README.md b/packages/event/README.md new file mode 100644 index 000000000..8d0f951b0 --- /dev/null +++ b/packages/event/README.md @@ -0,0 +1,86 @@ +Charcoal Event +============== + +The `Charcoal\Event` module (`charcoalphp/event`) provides a [`Psr-14`](https://www.php-fig.org/psr/psr-14/) compliant event system using [`League\Event`](https://event.thephpleague.com/3.0/). + + +# How to install + +The preferred (and only supported) way of installing charcoal-event is with **composer**: + +```shell +$ composer require charcoal/event +``` +To install a full Charcoal project, which includes `charcoal-event`: + +```shell +$ composer create-project charcoalphp/boilerplate:@dev --prefer-source +``` + +> Note that charcoal-event is intended to be run along a `charcoal-app` based project. To start from a boilerplate: +> +> ```shell +> $ composer create-project locomotivemtl/charcoal-project-boilerplate + +## Dependencies + +- `PHP 7.3+` + + +# Development + +To install the development environment: + +```shell +$ composer install --prefer-source +``` + +To run the tests: + +```shell +$ composer test +``` + +## Coding style + +The Charcoal-Admin module follows the Charcoal coding-style: + +- [_PSR-1_](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md), except for + - Method names MUST be declared in `snake_case`. +- [_PSR-2_](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md), except for the PSR-1 requirement.q +- [_PSR-4_](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md), autoloading is therefore provided by _Composer_ +- [_phpDocumentor_](http://phpdoc.org/) + - Add DocBlocks for all classes, methods, and functions; + - For type-hinting, use `boolean` (instead of `bool`), `integer` (instead of `int`), `float` (instead of `double` or `real`); + - Omit the `@return` tag if the method does not return anything. +- Naming conventions + - Read the [phpcs.xml.dist](phpcs.xml.dist) file for all the details. + +> Coding style validation / enforcement can be performed with `composer phpcs`. An auto-fixer is also available with `composer phpcbf`. + +For Javascript, the following coding style is enforced: + +- **todo** + +Every classes, methods and functions should be covered by unit tests. PHP code can be tested with _PHPUnit_ and Javascript code with _QUnit_. + +# Authors + +- Joel Alphonso + +# License + +Charcoal is licensed under the MIT license. See [LICENSE](LICENSE) for details. + + + +## Report Issues + +In case you are experiencing a bug or want to request a new feature head over to the [Charcoal monorepo issue tracker](https://github.com/charcoalphp/charcoal/issues) + + + +## Contribute + +The sources of this package are contained in the Charcoal monorepo. We welcome contributions for this package on [charcoalphp/charcoal](https://github.com/charcoalphp/charcoal). + diff --git a/packages/event/composer.json b/packages/event/composer.json new file mode 100644 index 000000000..7a177f9b8 --- /dev/null +++ b/packages/event/composer.json @@ -0,0 +1,66 @@ +{ + "type": "library", + "name": "charcoal/event", + "description": "Charcoal service provider for Event system", + "keywords": ["module", "charcoal", "event"], + "homepage": "https://charcoal.locomotive.ca", + "license": "MIT", + "authors": [ + { + "name": "Locomotive", + "homepage": "https://locomotive.ca" + }, + { + "name": "Joel Alphonso", + "email": "joel@locomotive.ca" + } + ], + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "require": { + "charoal/factory": "^3.2", + "league/event": "^3.0", + "php": "^7.4 || ^8.0", + "pimple/pimple": "^3.0", + "psr/event-dispatcher": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.6", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "autoload": { + "psr-4": { + "Charcoal\\Event\\": "src/Charcoal/Event/" + } + }, + "autoload-dev": { + "psr-4": { + "Charcoal\\Tests\\": "tests/Charcoal/" + } + }, + "scripts": { + "test": [ + "@tests" + ], + "tests": [ + "@phplint", + "@phpcs", + "@phpstan", + "@phpunit" + ], + "phplint": "find src tests -type f -name '*.php' -print0 | xargs -0 -n1 -P8 php -l | grep -v '^No syntax errors detected'; test $? -eq 1", + "phpcs": "php vendor/bin/phpcs -ps --colors src/", + "phpcbf": "php vendor/bin/phpcbf -ps --colors src/", + "phpstan": "php vendor/bin/phpstan analyze -l1 src/", + "phpunit": "php vendor/bin/phpunit --coverage-text" + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/packages/event/phpcs.xml.dist b/packages/event/phpcs.xml.dist new file mode 100644 index 000000000..01c267ac3 --- /dev/null +++ b/packages/event/phpcs.xml.dist @@ -0,0 +1,4 @@ + + + + diff --git a/packages/event/phpunit.xml.dist b/packages/event/phpunit.xml.dist new file mode 100644 index 000000000..5c94aaf08 --- /dev/null +++ b/packages/event/phpunit.xml.dist @@ -0,0 +1,32 @@ + + + + + ./tests/Charcoal + + + + + + ./src/Charcoal + + + + + + + + + + diff --git a/packages/app/src/Charcoal/App/Event/AbstractEventListener.php b/packages/event/src/Charcoal/Event/AbstractEventListener.php similarity index 93% rename from packages/app/src/Charcoal/App/Event/AbstractEventListener.php rename to packages/event/src/Charcoal/Event/AbstractEventListener.php index ed9b88ec6..c17dc3f2d 100644 --- a/packages/app/src/Charcoal/App/Event/AbstractEventListener.php +++ b/packages/event/src/Charcoal/Event/AbstractEventListener.php @@ -1,6 +1,6 @@ Date: Tue, 20 Sep 2022 17:50:53 -0400 Subject: [PATCH 40/92] fix(image-property): remove event dispatch for property for now --- packages/property/src/Charcoal/Property/ImageProperty.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/property/src/Charcoal/Property/ImageProperty.php b/packages/property/src/Charcoal/Property/ImageProperty.php index d6d9df188..a588dc91e 100644 --- a/packages/property/src/Charcoal/Property/ImageProperty.php +++ b/packages/property/src/Charcoal/Property/ImageProperty.php @@ -315,16 +315,12 @@ protected function resolveExtensionFromMimeType($type) */ public function save($val) { - $this->getEventDispatcher()->dispatch(new PropertyEvent(PropertyEvent::EVENT_PRE_SAVE, $this, ['val' => $val])); - $val = parent::save($val); if ($this->canApplyEffects('save')) { $val = $this->processEffects($val); } - $this->getEventDispatcher()->dispatch(new PropertyEvent(PropertyEvent::EVENT_SAVE, $this, ['val' => $val])); - return $val; } From 7e4b1c1e5cb14ec7bcec4e336bb97e7cb5d5c608 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 17:51:35 -0400 Subject: [PATCH 41/92] refactor(property): remove EventDispatcherTrait from AbstractProperty for now --- packages/property/src/Charcoal/Property/AbstractProperty.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/property/src/Charcoal/Property/AbstractProperty.php b/packages/property/src/Charcoal/Property/AbstractProperty.php index 44e624624..4b5b97ae9 100644 --- a/packages/property/src/Charcoal/Property/AbstractProperty.php +++ b/packages/property/src/Charcoal/Property/AbstractProperty.php @@ -31,8 +31,6 @@ use Charcoal\Property\PropertyValidator; use Charcoal\Property\StorablePropertyInterface; use Charcoal\Property\StorablePropertyTrait; -// From 'charcoal-app' -use Charcoal\App\Event\EventDispatcherTrait; /** * An abstract class that implements the full `PropertyInterface`. @@ -51,7 +49,6 @@ abstract class AbstractProperty extends AbstractEntity implements use StorablePropertyTrait; use TranslatorAwareTrait; use ValidatableTrait; - use EventDispatcherTrait; public const DEFAULT_L10N = false; public const DEFAULT_MULTIPLE = false; @@ -1012,7 +1009,6 @@ protected function setDependencies(Container $container) { $this->setPropertyFactory($container['property/factory']); $this->setMetadataLoader($container['metadata/loader']); - $this->setEventDispatcher($container['event/dispatcher']); } /** From 5d22f7c4267866a68d1bd64591d76381847d9ec9 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 18:07:57 -0400 Subject: [PATCH 42/92] fix(event): fix bad references to `event` package --- packages/admin/composer.json | 1 + .../src/Charcoal/Admin/Action/ElfinderConnectorAction.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/admin/composer.json b/packages/admin/composer.json index 32055adec..873c473d9 100644 --- a/packages/admin/composer.json +++ b/packages/admin/composer.json @@ -34,6 +34,7 @@ "charcoal/cms": "^4.0.1", "charcoal/core": "^4.0.1", "charcoal/email": "^4.0.1", + "charcoal/event": "^3.2", "charcoal/object": "^4.0.1", "charcoal/translator": "^4.0.1", "charcoal/ui": "^4.0.1", diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index ca77970cd..61ea974ea 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -2,8 +2,8 @@ namespace Charcoal\Admin\Action; -use Charcoal\App\Event\EventDispatcherTrait; -use Charcoal\App\Event\FileWasUploaded; +use Charcoal\Event\EventDispatcherTrait; +use Charcoal\Event\Events\FileWasUploaded; use finfo; use InvalidArgumentException; use RuntimeException; From b198240209250c8dac2fa2438f14c3e911ffee6f Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 18:09:41 -0400 Subject: [PATCH 43/92] tests(event): remove event related container dependencies for now --- .../tests/Charcoal/Admin/ContainerProvider.php | 14 -------------- .../core/tests/Charcoal/CoreContainerProvider.php | 14 -------------- packages/object/composer.json | 1 - .../tests/Charcoal/Object/ContainerProvider.php | 14 -------------- .../tests/Charcoal/Property/ContainerProvider.php | 13 ------------- packages/user/composer.json | 3 +-- .../user/tests/Charcoal/User/ContainerProvider.php | 13 ------------- 7 files changed, 1 insertion(+), 71 deletions(-) diff --git a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php index dd0f8b434..fea6cebe0 100644 --- a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php +++ b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php @@ -28,7 +28,6 @@ use League\CLImate\Util\Output; use League\CLImate\Util\Reader\Stdin; use League\CLImate\Util\UtilFactory; -use League\Event\EventDispatcher; // From 'charcoal-factory' use Charcoal\Factory\GenericFactory as Factory; @@ -97,7 +96,6 @@ public function registerBaseServices(Container $container) $this->registerDatabase($container); $this->registerLogger($container); $this->registerCache($container); - $this->registerEvent($container); } /** @@ -381,17 +379,6 @@ public function registerCache(Container $container) }; } - /** - * Setup event dispatcher. - * - * @param Container $container A DI container. - * @return void - */ - public function registerEvent(Container $container) - { - $container['event/dispatcher'] = new EventDispatcher(); - } - /** * @param Container $container A DI container. * @return void @@ -558,7 +545,6 @@ public function registerActionDependencies(Container $container) $this->registerLogger($container); $this->registerDatabase($container); $this->registerCache($container); - $this->registerEvent($container); $this->registerAdminConfig($container); $this->registerBaseUrl($container); diff --git a/packages/core/tests/Charcoal/CoreContainerProvider.php b/packages/core/tests/Charcoal/CoreContainerProvider.php index f2354c147..6de374c33 100644 --- a/packages/core/tests/Charcoal/CoreContainerProvider.php +++ b/packages/core/tests/Charcoal/CoreContainerProvider.php @@ -35,8 +35,6 @@ // From 'charcoal-app' use Charcoal\App\AppConfig; -use League\Event\EventDispatcher; - /** * Service Container for Unit Tests */ @@ -54,7 +52,6 @@ public function registerBaseServices(Container $container) $this->registerSource($container); $this->registerLogger($container); $this->registerCache($container); - $this->registerEvent($container); } /** @@ -115,17 +112,6 @@ public function registerCache(Container $container) }; } - /** - * Setup event dispatcher. - * - * @param Container $container A DI container. - * @return void - */ - public function registerEvent(Container $container) - { - $container['event/dispatcher'] = new EventDispatcher(); - } - /** * Setup the application's translator service. * diff --git a/packages/object/composer.json b/packages/object/composer.json index 9d36e3eeb..97e4163e3 100644 --- a/packages/object/composer.json +++ b/packages/object/composer.json @@ -29,7 +29,6 @@ "seld/jsonlint": "^1.9", "squizlabs/php_codesniffer": "^3.5", "tedivm/stash": "~0.16", - "charcoal/app": "^3.2", "ext-json": "*" }, "autoload": { diff --git a/packages/object/tests/Charcoal/Object/ContainerProvider.php b/packages/object/tests/Charcoal/Object/ContainerProvider.php index 1ede70d6e..b930fa1a8 100644 --- a/packages/object/tests/Charcoal/Object/ContainerProvider.php +++ b/packages/object/tests/Charcoal/Object/ContainerProvider.php @@ -25,8 +25,6 @@ use Charcoal\Translator\LocalesManager; use Charcoal\Translator\Translator; -use League\Event\EventDispatcher; - /** * Service Container for Unit Tests */ @@ -44,7 +42,6 @@ public function registerBaseServices(Container $container) $this->registerLogger($container); $this->registerCache($container); $this->registerTranslator($container); - $this->registerEvent($container); } /** @@ -231,15 +228,4 @@ public function registerTranslator(Container $container) ]); }; } - - /** - * Setup event dispatcher. - * - * @param Container $container A DI container. - * @return void - */ - public function registerEvent(Container $container) - { - $container['event/dispatcher'] = new EventDispatcher(); - } } diff --git a/packages/property/tests/Charcoal/Property/ContainerProvider.php b/packages/property/tests/Charcoal/Property/ContainerProvider.php index fae49cb67..295f18d70 100644 --- a/packages/property/tests/Charcoal/Property/ContainerProvider.php +++ b/packages/property/tests/Charcoal/Property/ContainerProvider.php @@ -27,7 +27,6 @@ // From 'charcoal-translator' use Charcoal\Translator\LocalesManager; use Charcoal\Translator\Translator; -use League\Event\EventDispatcher; /** * Service Container for Unit Tests @@ -46,7 +45,6 @@ public function registerBaseServices(Container $container) $this->registerSource($container); $this->registerLogger($container); $this->registerCache($container); - $this->registerEvent($container); } /** @@ -329,15 +327,4 @@ public function registerModelCollectionLoader(Container $container) ]); }; } - - /** - * Setup event dispatcher. - * - * @param Container $container A DI container. - * @return void - */ - public function registerEvent(Container $container) - { - $container['event/dispatcher'] = new EventDispatcher(); - } } diff --git a/packages/user/composer.json b/packages/user/composer.json index e0c7ad36e..f3358bbd2 100644 --- a/packages/user/composer.json +++ b/packages/user/composer.json @@ -30,8 +30,7 @@ "php-coveralls/php-coveralls": "^2.2", "cache/void-adapter": "^1.0", "tedivm/stash": "~0.16", - "seld/jsonlint": "^1.9", - "charcoal/app": "^3.2" + "seld/jsonlint": "^1.9" }, "autoload": { "psr-4": { diff --git a/packages/user/tests/Charcoal/User/ContainerProvider.php b/packages/user/tests/Charcoal/User/ContainerProvider.php index c66edd065..e2bf4f9c5 100644 --- a/packages/user/tests/Charcoal/User/ContainerProvider.php +++ b/packages/user/tests/Charcoal/User/ContainerProvider.php @@ -2,7 +2,6 @@ namespace Charcoal\Tests\User; -use League\Event\EventDispatcher; use PDO; // From PSR-3 @@ -51,7 +50,6 @@ public function registerBaseServices(Container $container) $this->registerLogger($container); $this->registerCache($container); $this->registerTranslator($container); - $this->registerEvent($container); } /** @@ -238,15 +236,4 @@ public function registerTranslator(Container $container) ]); }; } - - /** - * Setup event dispatcher. - * - * @param Container $container A DI container. - * @return void - */ - public function registerEvent(Container $container) - { - $container['event/dispatcher'] = new EventDispatcher(); - } } From c8f98f7d595f2071fdcec08a811308eed3cfc821 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 20 Sep 2022 18:14:44 -0400 Subject: [PATCH 44/92] build(composer): update composer lock file --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 727234a2e..635598c7c 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": "afd41cc24ded974941402c1cc5cd8a2b", + "content-hash": "3bc44ca00298e3f4add3926db355a4a3", "packages": [ { "name": "barryvdh/elfinder-flysystem-driver", @@ -2799,16 +2799,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.235.10", + "version": "3.235.12", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "943f96f50d19244584675c34fb3e4c8aa3eaddce" + "reference": "e0ebecfb0284dc44dc99a172cd9c68c40739904c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/943f96f50d19244584675c34fb3e4c8aa3eaddce", - "reference": "943f96f50d19244584675c34fb3e4c8aa3eaddce", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e0ebecfb0284dc44dc99a172cd9c68c40739904c", + "reference": "e0ebecfb0284dc44dc99a172cd9c68c40739904c", "shasum": "" }, "require": { @@ -2887,9 +2887,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.235.10" + "source": "https://github.com/aws/aws-sdk-php/tree/3.235.12" }, - "time": "2022-09-16T18:18:42+00:00" + "time": "2022-09-20T18:18:07+00:00" }, { "name": "cache/adapter-common", From bb2331c43e6f73acf77ca4ca326d354962d6d1c2 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 21 Sep 2022 10:04:58 -0400 Subject: [PATCH 45/92] tests(event): add placeholder test class to prevent test failure --- .../ServiceProvider/EventServiceProviderTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php diff --git a/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php b/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php new file mode 100644 index 000000000..3207eb676 --- /dev/null +++ b/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php @@ -0,0 +1,12 @@ + Date: Wed, 21 Sep 2022 10:18:11 -0400 Subject: [PATCH 46/92] style: apply requested style changes by @mcaskill Co-authored-by: Chauncey McAskill --- packages/event/README.md | 4 ++-- packages/event/composer.json | 2 +- .../src/Charcoal/Event/EventDispatcher.php | 2 +- .../event/src/Charcoal/Event/GenericEvent.php | 2 +- .../ServiceProvider/EventServiceProvider.php | 20 +++++++++++++------ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/event/README.md b/packages/event/README.md index 8d0f951b0..7f9efc1c7 100644 --- a/packages/event/README.md +++ b/packages/event/README.md @@ -1,7 +1,7 @@ Charcoal Event ============== -The `Charcoal\Event` module (`charcoalphp/event`) provides a [`Psr-14`](https://www.php-fig.org/psr/psr-14/) compliant event system using [`League\Event`](https://event.thephpleague.com/3.0/). +The `Charcoal\Event` module (`charcoal/event`) provides a [`Psr-14`](https://www.php-fig.org/psr/psr-14/) compliant event system using [`League\Event`](https://event.thephpleague.com/3.0/). # How to install @@ -24,7 +24,7 @@ $ composer create-project charcoalphp/boilerplate:@dev --prefer-source ## Dependencies -- `PHP 7.3+` +- `PHP 7.4+` # Development diff --git a/packages/event/composer.json b/packages/event/composer.json index 7a177f9b8..73d294200 100644 --- a/packages/event/composer.json +++ b/packages/event/composer.json @@ -1,7 +1,7 @@ { "type": "library", "name": "charcoal/event", - "description": "Charcoal service provider for Event system", + "description": "Charcoal service provider for events system", "keywords": ["module", "charcoal", "event"], "homepage": "https://charcoal.locomotive.ca", "license": "MIT", diff --git a/packages/event/src/Charcoal/Event/EventDispatcher.php b/packages/event/src/Charcoal/Event/EventDispatcher.php index 9c30d3427..36b15c702 100644 --- a/packages/event/src/Charcoal/Event/EventDispatcher.php +++ b/packages/event/src/Charcoal/Event/EventDispatcher.php @@ -19,7 +19,7 @@ public function dispatch(object $event): object { if ($this->logger) { if ($event instanceof HasEventName) { - $this->logger->notice('Event [' . $event->eventName() . '] dispatched', [ + $this->logger->notice('Event dispatched [' . $event->eventName() . ']', [ 'event' => get_class($event), ]); } else { diff --git a/packages/event/src/Charcoal/Event/GenericEvent.php b/packages/event/src/Charcoal/Event/GenericEvent.php index eaed40cf2..ad2864ab7 100644 --- a/packages/event/src/Charcoal/Event/GenericEvent.php +++ b/packages/event/src/Charcoal/Event/GenericEvent.php @@ -13,7 +13,7 @@ final class GenericEvent extends Event implements HasEventName * @var mixed */ private $subject; - private array $arguments = []; + private array $arguments; private string $eventName; /** diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index df25b4a65..c4fc3d767 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -6,6 +6,7 @@ use Charcoal\Event\EventListenerInterface; use Charcoal\Factory\FactoryInterface; use Charcoal\Factory\GenericFactory; +use InvalidArgumentException; use League\Event\ListenerSubscriber; use Pimple\Container; use Pimple\ServiceProviderInterface; @@ -102,12 +103,17 @@ private function registerEventListeners(EventDispatcherInterface $dispatcher, Co */ foreach ($container['event/listeners'] as $event => $listeners) { if (!is_iterable($listeners)) { - throw new \InvalidArgumentException(); + throw new InvalidArgumentException(sprintf( + 'Expected iterable map of event listeners for [%s]', + $event + )); } foreach ($listeners as $listener => $options) { if (!is_string($listener)) { - throw new \InvalidArgumentException(); + throw new InvalidArgumentException(sprintf( + 'Expected event listener class string as map key for [%s]' + )); } $listener = $container['event/listener/factory']->create($listener); @@ -117,10 +123,9 @@ private function registerEventListeners(EventDispatcherInterface $dispatcher, Co if ($once) { $dispatcher->subscribeOnceTo($event, $listener, $priority); - continue; + } else { + $dispatcher->subscribeTo($event, $listener, $priority); } - - $dispatcher->subscribeTo($event, $listener, $priority); } } } @@ -134,7 +139,10 @@ private function registerListenerSubscribers(EventDispatcherInterface $dispatche { foreach ($container['event/subscribers'] as $subscriber) { if (!is_string($subscriber) || !class_exists($subscriber)) { - throw new \InvalidArgumentException(); + throw new InvalidArgumentException(sprintf( + 'Expected event subscriber as class string, received %s', + (is_string($subscriber) ? $subscriber : gettype($subscriber)) + )); } $subscriber = $container['event/listener-subscriber/factory']->create($subscriber); From 09fe34a804f9ebf352daaa43e473a85baf87b8db Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 21 Sep 2022 10:20:38 -0400 Subject: [PATCH 47/92] fix(exception): fix missing sprintf context --- .../Charcoal/Event/ServiceProvider/EventServiceProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index c4fc3d767..3582bc67d 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -112,7 +112,8 @@ private function registerEventListeners(EventDispatcherInterface $dispatcher, Co foreach ($listeners as $listener => $options) { if (!is_string($listener)) { throw new InvalidArgumentException(sprintf( - 'Expected event listener class string as map key for [%s]' + 'Expected event listener class string as map key for [%s]', + $event )); } From 4a9f26c84f157a6279374fdc89d9170c2c4f2e95 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 21 Sep 2022 14:06:36 -0400 Subject: [PATCH 48/92] refactor(event): ensure setDependencies is callable --- .../Charcoal/Event/ServiceProvider/EventServiceProvider.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index 3582bc67d..c53d2934a 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -67,7 +67,9 @@ public function register(Container $container) 'suffix' => 'Listener' ], 'callback' => function ($listener) use ($container) { - $listener->setDependencies($container); + if (is_callable([$listener, 'setDependencies'])) { + $listener->setDependencies($container); + } } ]); }; From a7f5f88c6d16b7634f37016342bcc029dd2b00c0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 21 Sep 2022 14:07:13 -0400 Subject: [PATCH 49/92] docs(event): update `README.md` for the `Event` package --- packages/event/README.md | 121 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 3 deletions(-) diff --git a/packages/event/README.md b/packages/event/README.md index 7f9efc1c7..e6bec3eef 100644 --- a/packages/event/README.md +++ b/packages/event/README.md @@ -27,6 +27,124 @@ $ composer create-project charcoalphp/boilerplate:@dev --prefer-source - `PHP 7.4+` +## Service Provider + +The following services are provided with the use of [_event_](https://github.com/charcoalphp/event) + +### Services + +* [$container['event/dispatcher']](src/Charcoal/Event/ServiceProvider/EventServiceProvider.php) instance of `\League\Event\Dispatcher` + + +## Configuration + +The configuration of the event module is done via the `event` key of the project configuration. + + +There is two ways to bind listeners to events : + +- The first one is a direct mapping between `Event` classes and listeners : + + +```json +{ + "events": { + "listeners": { + "Namespace\\For\\My\\EventClass": { + "Namespace\\For\\My\\ListenerClass": {...} + } + } + } +} +``` + +- The second one is through a `ListenerSubscriber` which is a class that registers listeners internally. +See the [Subscribers](#subscribers) section for more details. + +```json +{ + "events": { + "subscribers": [ + "Namespace\\For\\My\\ListenerSubscriber" + ] + } +} +``` + +# Usage + +## Events + +An event is a class that can be anything you want. Although, for consistency purposes, certain guidelines can be +applied to ensure ease of use: + +- The [`Charcoal/Event/Event`](src/Charcoal/Event/Event.php) class can be used as a base for a new Event. This base event ensures the event is [Stoppable](https://www.php-fig.org/psr/psr-14/#stoppable-events). +- The class name should be composed of a context and an action applied to it. (e.g) `FileWasUploaded`, `ModelWasUpdated`. +- Since the class implies a context, an event should be able to set said context in its constructor : +```php +class FileWasUploaded extends Event +{ + public function __construct(string $file) + { + $this->file = $file; + + } +} +``` + +## Listeners + +A Listener may be any PHP callable. A Listener MUST have one and only one parameter, which is the Event to which it responds. +See [Psr-14](https://www.php-fig.org/psr/psr-14/#listeners) documentation for more info about listeners. + +In charcoal's context, listeners that are destined to be loaded through json config files should : +- extend [`AbstractEventListener`](src/Charcoal/Event/AbstractEventListener.php) or implement [`EventListenerInterface`](src/Charcoal/Event/EventListenerInterface.php) +- have the `Listener` suffix in its class name + +Config injected listeners are instantiated through a factory and are provided with a `setDependencies()` method for dependency injection. +The `__invoke` method receives the `$event` object as sole parameter. + +If a listener is to be subscribed outside the config, manually, it can be a mere callable function that receives the `event` object. + +To bind a listener to an event, one can manually subscribe the listener using the `event/dispatcher` container key, or use the app config system to attach listeners to events. +By doing so, options can be passed to the listener to dictate its behaviour : + +```json +{ + "events": { + "Namespace\\For\\Some\\Event": { + "Namespace\\For\\Some\\Listener": { + "priority": -1000, + "once": true + } + } + } +} +``` + +### options + +- `priority` : Define the listener priority. Higher priority means the listener will be triggered before lower priority listeners. +[Default: 0] +- `once` : Only trigger the listener once [Default: false] + +## Subscribers + +Listener subscribers are a convenient way to subscribe multiple listeners at once. They allow grouping listener +registrations by concern. Usually, a package will provide a `ListenerSubscriber` to group event listeners and streamline +the registration process. See [League\Event\ListenerSubscriber](https://event.thephpleague.com/3.0/extra-utilities/listener-subscriber/) +for more details about subscribers. + +The [`AbstractListenerSubscriber`](src/Charcoal/Event/AbstractListenerSubscriber.php) class can be extended to create +a listener subscriber. Implement the method `subscribeListeners` and subscribe the listeners on `$acceptor` + +```php +public function subscribeListeners(ListenerRegistry $acceptor): void +{ + $acceptor->subscribeTo(MyEvent::class, $this->createListener(MyListener::class)); +} +``` + # Development To install the development environment: @@ -58,9 +176,6 @@ The Charcoal-Admin module follows the Charcoal coding-style: > Coding style validation / enforcement can be performed with `composer phpcs`. An auto-fixer is also available with `composer phpcbf`. -For Javascript, the following coding style is enforced: - -- **todo** Every classes, methods and functions should be covered by unit tests. PHP code can be tested with _PHPUnit_ and Javascript code with _QUnit_. From 19fd32888fc8bc149f34e0e6115165bb960171fd Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 21 Sep 2022 16:32:31 -0400 Subject: [PATCH 50/92] feat(event): add events related to object --- .../Admin/Action/Object/SaveAction.php | 20 +++++++++++++ .../Admin/Action/Object/UpdateAction.php | 20 +++++++++++++ .../Charcoal/Event/EventDispatcherTrait.php | 23 ++++++++++++++ .../Events/Object/AbstractObjectEvent.php | 30 +++++++++++++++++++ .../Charcoal/Event/Events/Object/WasSaved.php | 10 +++++++ .../Event/Events/Object/WasSavedOrUpdated.php | 10 +++++++ .../Event/Events/Object/WasUpdated.php | 10 +++++++ .../Charcoal/Event/Events/Object/WillSave.php | 10 +++++++ .../Event/Events/Object/WillSaveOrUpdate.php | 10 +++++++ .../Event/Events/Object/WillUpdate.php | 10 +++++++ 10 files changed, 153 insertions(+) create mode 100644 packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasSaved.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasSavedOrUpdated.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasUpdated.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillSave.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillSaveOrUpdate.php create mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillUpdate.php diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php index 6c189bace..d7049a6fb 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php @@ -4,6 +4,7 @@ use Exception; use PDOException; +use Pimple\Container; // From PSR-7 use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -15,6 +16,12 @@ use Charcoal\Property\DescribablePropertyInterface; // From 'charcoal-object' use Charcoal\Object\AuthorableInterface; +// From 'charcoal-event' +use Charcoal\Event\EventDispatcherTrait; +use Charcoal\Event\Events\Object\WasSaved; +use Charcoal\Event\Events\Object\WasSavedOrUpdated; +use Charcoal\Event\Events\Object\WillSave; +use Charcoal\Event\Events\Object\WillSaveOrUpdate; /** * Action: Create an object and insert into storage. @@ -37,6 +44,8 @@ */ class SaveAction extends AbstractSaveAction { + use EventDispatcherTrait; + /** * Data for the target model. * @@ -208,8 +217,12 @@ public function run(RequestInterface $request, ResponseInterface $response) } } + $this->dispatchEvents([new WillSave($obj), new WillSaveOrUpdate($obj)]); + $result = $obj->save(); + $this->dispatchEvents([new WasSaved($obj), new WasSavedOrUpdated($obj)]); + if ($result) { $this->setObj($obj); @@ -256,4 +269,11 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(500); } } + + protected function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->setEventDispatcher($container['event/dispatcher']); + } } diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php index 10b9a87ba..41db5f4d8 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php @@ -3,6 +3,7 @@ namespace Charcoal\Admin\Action\Object; use Exception; +use Pimple\Container; // From PSR-7 use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -10,6 +11,12 @@ use Charcoal\Model\ModelValidator; // From 'charcoal-object' use Charcoal\Object\AuthorableInterface; +// From 'charcoal-event' +use Charcoal\Event\EventDispatcherTrait; +use Charcoal\Event\Events\Object\WasSavedOrUpdated; +use Charcoal\Event\Events\Object\WasUpdated; +use Charcoal\Event\Events\Object\WillSaveOrUpdate; +use Charcoal\Event\Events\Object\WillUpdate; /** * Action: Save an object and update copy in storage. @@ -31,6 +38,8 @@ */ class UpdateAction extends AbstractSaveAction { + use EventDispatcherTrait; + /** * Data for the target model. * @@ -183,8 +192,12 @@ public function run(RequestInterface $request, ResponseInterface $response) $obj->setLastModifiedBy($this->getAuthorIdent()); } + $this->dispatchEvents([new WillUpdate($obj), new WillSaveOrUpdate($obj)]); + $result = $obj->update(); + $this->dispatchEvents([new WasUpdated($obj), new WasSavedOrUpdated($obj)]); + if ($result) { $this->addFeedback('success', $this->translator()->translate('Object has been successfully updated.')); $this->addFeedback('success', strtr($this->translator()->translate('Updated Object: {{ objId }}'), [ @@ -210,4 +223,11 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(500); } } + + protected function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->setEventDispatcher($container['event/dispatcher']); + } } diff --git a/packages/event/src/Charcoal/Event/EventDispatcherTrait.php b/packages/event/src/Charcoal/Event/EventDispatcherTrait.php index 0932989e2..b2777a759 100644 --- a/packages/event/src/Charcoal/Event/EventDispatcherTrait.php +++ b/packages/event/src/Charcoal/Event/EventDispatcherTrait.php @@ -30,4 +30,27 @@ public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): s return $this; } + + /** + * Provide all relevant listeners with an event to process. + * + * @param object $event + * The object to process. + * + * @return object + * The Event that was passed, now modified by listeners. + */ + protected function dispatchEvent(object $event): object + { + return $this->getEventDispatcher()->dispatch($event); + } + + /** + * @param array $events + * @return void + */ + protected function dispatchEvents(array $events) + { + array_map([$this, 'dispatchEvent'], $events); + } } diff --git a/packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php b/packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php new file mode 100644 index 000000000..17812261e --- /dev/null +++ b/packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php @@ -0,0 +1,30 @@ +object = $object; + } + + /** + * @return ModelInterface + */ + public function getObject(): ModelInterface + { + return $this->object; + } +} diff --git a/packages/event/src/Charcoal/Event/Events/Object/WasSaved.php b/packages/event/src/Charcoal/Event/Events/Object/WasSaved.php new file mode 100644 index 000000000..0a6e3777c --- /dev/null +++ b/packages/event/src/Charcoal/Event/Events/Object/WasSaved.php @@ -0,0 +1,10 @@ + Date: Wed, 21 Sep 2022 16:38:48 -0400 Subject: [PATCH 51/92] tests(phpunit): fix failed tests due to `event\dispatcher` container --- .../tests/Charcoal/Admin/ContainerProvider.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php index fea6cebe0..dd0f8b434 100644 --- a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php +++ b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php @@ -28,6 +28,7 @@ use League\CLImate\Util\Output; use League\CLImate\Util\Reader\Stdin; use League\CLImate\Util\UtilFactory; +use League\Event\EventDispatcher; // From 'charcoal-factory' use Charcoal\Factory\GenericFactory as Factory; @@ -96,6 +97,7 @@ public function registerBaseServices(Container $container) $this->registerDatabase($container); $this->registerLogger($container); $this->registerCache($container); + $this->registerEvent($container); } /** @@ -379,6 +381,17 @@ public function registerCache(Container $container) }; } + /** + * Setup event dispatcher. + * + * @param Container $container A DI container. + * @return void + */ + public function registerEvent(Container $container) + { + $container['event/dispatcher'] = new EventDispatcher(); + } + /** * @param Container $container A DI container. * @return void @@ -545,6 +558,7 @@ public function registerActionDependencies(Container $container) $this->registerLogger($container); $this->registerDatabase($container); $this->registerCache($container); + $this->registerEvent($container); $this->registerAdminConfig($container); $this->registerBaseUrl($container); From d8c1e401edf41a3a599598b87164fa24b8f62d84 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Thu, 22 Sep 2022 15:41:57 -0400 Subject: [PATCH 52/92] refactor(event): use a builder to build an admin scoped event dispatcher --- .../Admin/Action/ElfinderConnectorAction.php | 2 +- .../Admin/Action/Object/SaveAction.php | 2 +- .../Admin/Action/Object/UpdateAction.php | 2 +- .../ServiceProvider/AdminServiceProvider.php | 44 ++++++++ .../Charcoal/Event/EventDispatcherBuilder.php | 104 ++++++++++++++++++ .../Charcoal/Event/EventDispatcherTrait.php | 6 +- .../ServiceProvider/EventServiceProvider.php | 79 +------------ 7 files changed, 159 insertions(+), 80 deletions(-) create mode 100644 packages/event/src/Charcoal/Event/EventDispatcherBuilder.php diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index 61ea974ea..d744a4956 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -914,7 +914,7 @@ public function setDependencies(Container $container) $this->filesystemConfig = $container['filesystem/config']; $this->filesystems = $container['filesystems']; - $this->setEventDispatcher($container['event/dispatcher']); + $this->setEventDispatcher($container['admin/event/dispatcher']); } /** diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php index d7049a6fb..24d5a5121 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php @@ -274,6 +274,6 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->setEventDispatcher($container['event/dispatcher']); + $this->setEventDispatcher($container['admin/event/dispatcher']); } } diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php index 41db5f4d8..2e8f231d6 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php @@ -228,6 +228,6 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->setEventDispatcher($container['event/dispatcher']); + $this->setEventDispatcher($container['admin/event/dispatcher']); } } diff --git a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php index cc9ad9e20..814fabc7e 100644 --- a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php +++ b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php @@ -4,6 +4,8 @@ // From Pimple use Charcoal\Admin\AssetsConfig; +use Charcoal\Event\EventDispatcher; +use Charcoal\Event\EventDispatcherBuilder; use Pimple\Container; use Pimple\ServiceProviderInterface; use Assetic\Asset\AssetReference; @@ -83,6 +85,7 @@ public function register(Container $container) $this->registerAuthExtensions($container); $this->registerViewExtensions($container); $this->registerAssetsManager($container); + $this->registerAdminEventDispatcher($container); // Register Access-Control-List (acl) $container->register(new AclServiceProvider()); @@ -548,4 +551,45 @@ protected function registerFactoryServices(Container $container) ]); }; } + + /** + * @param Container $container Pimple DI container. + * @return void + */ + protected function registerAdminEventDispatcher(Container $container) + { + /** + * @param Container $container + * @return array + */ + $container['admin/event/listeners'] = function (Container $container): array { + return ($container['admin/config']->get('events.listeners') ?? []); + }; + + /** + * Subscribers are classes that implements `\League\Event\ListenerSubscriber` + * It allows to subscribe many grouped listeners at once. + * + * @param Container $container + * @return array + */ + $container['admin/event/subscribers'] = function (Container $container): array { + return ($container['admin/config'] ->get('events.subscribers') ?? []); + }; + + /** + * Build an event dispatcher using admin config. + * + * @param Container $container + * @return EventDispatcher + */ + $container['admin/event/dispatcher'] = function (Container $container): EventDispatcher { + /** @var EventDispatcherBuilder $eventDispatcherBuilder */ + $eventDispatcherBuilder = $container['event/dispatcher/builder']; + return $eventDispatcherBuilder->build( + $container['admin/event/listeners'], + $container['admin/event/subscribers'] + ); + }; + } } diff --git a/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php b/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php new file mode 100644 index 000000000..cf0af6468 --- /dev/null +++ b/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php @@ -0,0 +1,104 @@ +container = $container; + } + + /** + * @param array $listeners + * @param array $subscribers + * @return EventDispatcher + */ + public function build(array $listeners = [], array $subscribers = []): EventDispatcher + { + $dispatcher = new EventDispatcher(); + $dispatcher->setLogger($this->container['logger']); + + $this->registerEventListeners($dispatcher, $listeners); + $this->registerListenerSubscribers($dispatcher, $subscribers); + + return $dispatcher; + } + + /** + * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface + * @param array $listenersByEvent Array of EventListenerInterface attached to event. + * @return void + */ + private function registerEventListeners(EventDispatcherInterface $dispatcher, array $listenersByEvent) + { + foreach ($listenersByEvent as $event => $listeners) { + if (!is_iterable($listeners)) { + throw new InvalidArgumentException(sprintf( + 'Expected iterable map of event listeners for [%s]', + $event + )); + } + + foreach ($listeners as $listener => $options) { + if (!is_string($listener)) { + throw new InvalidArgumentException(sprintf( + 'Expected event listener class string as map key for [%s]', + $event + )); + } + + $listener = $this->container['event/listener/factory']->create($listener); + + $priority = ($options['priority'] ?? 0); + $once = ($options['once'] ?? false); + + if ($once) { + $dispatcher->subscribeOnceTo($event, $listener, $priority); + } else { + $dispatcher->subscribeTo($event, $listener, $priority); + } + } + } + } + + /** + * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface + * @param array> $subscribers Pimple DI container + * @return void + */ + private function registerListenerSubscribers(EventDispatcherInterface $dispatcher, array $subscribers) + { + foreach ($subscribers as $subscriber) { + if (!is_string($subscriber) || !class_exists($subscriber)) { + throw new InvalidArgumentException(sprintf( + 'Expected event subscriber as class string, received %s', + (is_string($subscriber) ? $subscriber : gettype($subscriber)) + )); + } + + $subscriber = $this->container['event/listener-subscriber/factory']->create($subscriber); + + $dispatcher->subscribeListenersFrom($subscriber); + } + } +} diff --git a/packages/event/src/Charcoal/Event/EventDispatcherTrait.php b/packages/event/src/Charcoal/Event/EventDispatcherTrait.php index b2777a759..833831994 100644 --- a/packages/event/src/Charcoal/Event/EventDispatcherTrait.php +++ b/packages/event/src/Charcoal/Event/EventDispatcherTrait.php @@ -47,10 +47,10 @@ protected function dispatchEvent(object $event): object /** * @param array $events - * @return void + * @return array */ - protected function dispatchEvents(array $events) + protected function dispatchEvents(array $events): array { - array_map([$this, 'dispatchEvent'], $events); + return array_map([$this, 'dispatchEvent'], $events); } } diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index c53d2934a..6f4c49e03 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -2,15 +2,13 @@ namespace Charcoal\Event\ServiceProvider; -use Charcoal\Event\EventDispatcher; +use Charcoal\Event\EventDispatcherBuilder; use Charcoal\Event\EventListenerInterface; use Charcoal\Factory\FactoryInterface; use Charcoal\Factory\GenericFactory; -use InvalidArgumentException; use League\Event\ListenerSubscriber; use Pimple\Container; use Pimple\ServiceProviderInterface; -use Psr\EventDispatcher\EventDispatcherInterface; /** * Event Service Provider. Configures and provides a PDO service to a container. @@ -24,17 +22,11 @@ class EventServiceProvider implements ServiceProviderInterface public function register(Container $container) { /** - * @param Container $container Pimple DI container. - * @return EventDispatcherInterface + * @param Container $container A service container. + * @return EventDispatcherBuilder */ - $container['event/dispatcher'] = function (Container $container): EventDispatcherInterface { - $dispatcher = new EventDispatcher(); - $dispatcher->setLogger($container['logger']); - - $this->registerEventListeners($dispatcher, $container); - $this->registerListenerSubscribers($dispatcher, $container); - - return $dispatcher; + $container['event/dispatcher/builder'] = function (Container $container) { + return new EventDispatcherBuilder($container); }; /** @@ -92,65 +84,4 @@ public function register(Container $container) ]); }; } - - /** - * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface - * @param Container $container Pimple DI container - * @return void - */ - private function registerEventListeners(EventDispatcherInterface $dispatcher, Container $container) - { - /** - * @var array> $container['event/listeners'] - */ - foreach ($container['event/listeners'] as $event => $listeners) { - if (!is_iterable($listeners)) { - throw new InvalidArgumentException(sprintf( - 'Expected iterable map of event listeners for [%s]', - $event - )); - } - - foreach ($listeners as $listener => $options) { - if (!is_string($listener)) { - throw new InvalidArgumentException(sprintf( - 'Expected event listener class string as map key for [%s]', - $event - )); - } - - $listener = $container['event/listener/factory']->create($listener); - - $priority = ($options['priority'] ?? 0); - $once = ($options['once'] ?? false); - - if ($once) { - $dispatcher->subscribeOnceTo($event, $listener, $priority); - } else { - $dispatcher->subscribeTo($event, $listener, $priority); - } - } - } - } - - /** - * @param EventDispatcherInterface $dispatcher Psr-14 Event Dispatcher Interface - * @param Container $container Pimple DI container - * @return void - */ - private function registerListenerSubscribers(EventDispatcherInterface $dispatcher, Container $container) - { - foreach ($container['event/subscribers'] as $subscriber) { - if (!is_string($subscriber) || !class_exists($subscriber)) { - throw new InvalidArgumentException(sprintf( - 'Expected event subscriber as class string, received %s', - (is_string($subscriber) ? $subscriber : gettype($subscriber)) - )); - } - - $subscriber = $container['event/listener-subscriber/factory']->create($subscriber); - - $dispatcher->subscribeListenersFrom($subscriber); - } - } } From 56769d38f159f759f1a0f2bf243515c781d20b85 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 23 Sep 2022 10:56:46 -0400 Subject: [PATCH 53/92] style: apply suggestions from code review Co-authored-by: Chauncey McAskill --- .../src/Charcoal/Event/EventDispatcherBuilder.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php b/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php index cf0af6468..b2a02b5b7 100644 --- a/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php +++ b/packages/event/src/Charcoal/Event/EventDispatcherBuilder.php @@ -8,21 +8,14 @@ use Psr\EventDispatcher\EventDispatcherInterface; /** - * Builder + * Event Dispatcher Builder * - * Helps in the process of building an Event Dispatcher. + * Creates a new event dispatcher and registers the given listeners and subscribers. */ class EventDispatcherBuilder { - /** - * A Pimple dependency-injection container to fulfill the required services. - * @var Container $container - */ protected Container $container; - /** - * @param Container $container The DI container. - */ public function __construct(Container $container) { $this->container = $container; From a887c73c8512006277fca12fc5460afac7976617 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 14:08:14 -0400 Subject: [PATCH 54/92] feat(object): add an ObjectServiceProvider for object package services --- packages/app/composer.json | 1 + .../ServiceProvider/AppServiceProvider.php | 2 ++ .../Charcoal/Object/ObjectServiceProvider.php | 27 +++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 packages/object/src/Charcoal/Object/ObjectServiceProvider.php diff --git a/packages/app/composer.json b/packages/app/composer.json index 9eb42a435..958f05263 100644 --- a/packages/app/composer.json +++ b/packages/app/composer.json @@ -32,6 +32,7 @@ "charcoal/config": "^4.0.1", "charcoal/event": "^3.2", "charcoal/factory": "^4.0.1", + "charcoal/object": "^4.1", "charcoal/translator": "^4.0.1", "charcoal/view": "^4.0.1", "monolog/monolog": "^1.17", diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index 3859433ac..b831ad0aa 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -38,6 +38,7 @@ use Charcoal\App\Template\TemplateInterface; use Charcoal\App\Template\WidgetInterface; use Charcoal\App\Template\WidgetBuilder; +use Charcoal\Object\ObjectServiceProvider; use Charcoal\View\Twig\DebugHelpers as TwigDebugHelpers; use Charcoal\View\Twig\HelpersInterface as TwigHelpersInterface; use Charcoal\View\Twig\UrlHelpers as TwigUrlHelpers; @@ -78,6 +79,7 @@ public function register(Container $container) $container->register(new ScriptServiceProvider()); $container->register(new TranslatorServiceProvider()); $container->register(new ViewServiceProvider()); + $container->register(new ObjectServiceProvider()); $this->registerKernelServices($container); $this->registerHandlerServices($container); diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php new file mode 100644 index 000000000..8407cf2cd --- /dev/null +++ b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php @@ -0,0 +1,27 @@ +registerRevisionServices($pimple); + } + + private function registerRevisionServices(Container $pimple) + { + $pimple['revision/service'] = function (Container $pimple): RevisionService { + return new RevisionService([ + 'config' => $pimple['admin/config'], + 'model/factory' => $pimple['model/factory'], + ]); + }; + } +} From 5e3702390287bdb2d69838519be92de9db43bba3 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 14:11:53 -0400 Subject: [PATCH 55/92] refactor(revision): refactor revision object and interface to use ModelInterface as subject --- .../object/src/Charcoal/Object/ObjectRevision.php | 13 +++++++------ .../Charcoal/Object/ObjectRevisionInterface.php | 14 ++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/packages/object/src/Charcoal/Object/ObjectRevision.php b/packages/object/src/Charcoal/Object/ObjectRevision.php index 4f847c572..7773b74b1 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevision.php +++ b/packages/object/src/Charcoal/Object/ObjectRevision.php @@ -2,6 +2,7 @@ namespace Charcoal\Object; +use Charcoal\Model\ModelInterface; use InvalidArgumentException; use DateTime; use DateTimeInterface; @@ -291,10 +292,10 @@ public function getDataDiff() * 2. Load the current item from DB * 3. Create diff from (1) and (2). * - * @param RevisionableInterface $obj The object to create the revision from. + * @param ModelInterface $obj The object to create the revision from. * @return ObjectRevision Chainable */ - public function createFromObject(RevisionableInterface $obj) + public function createFromObject(ModelInterface $obj) { $prevRev = $this->lastObjectRevision($obj); @@ -382,10 +383,10 @@ public function recursiveDiff(array $array1, array $array2) /** * @todo Should return NULL if source does not exist. * - * @param RevisionableInterface $obj The object to load the last revision of. + * @param ModelInterface $obj The object to load the last revision of. * @return ObjectRevision The last revision for the give object. */ - public function lastObjectRevision(RevisionableInterface $obj) + public function lastObjectRevision(ModelInterface $obj) { $rev = $this->modelFactory()->create(self::class); @@ -410,11 +411,11 @@ public function lastObjectRevision(RevisionableInterface $obj) * * @todo Should return NULL if source does not exist. * - * @param RevisionableInterface $obj Target object. + * @param ModelInterface $obj Target object. * @param integer $revNum The revision number to load. * @return ObjectRevision */ - public function objectRevisionNum(RevisionableInterface $obj, $revNum) + public function objectRevisionNum(ModelInterface $obj, $revNum) { $rev = $this->modelFactory()->create(self::class); diff --git a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php index 9206ee66c..f95d75d81 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php @@ -2,6 +2,8 @@ namespace Charcoal\Object; +use Charcoal\Model\ModelInterface; + /** * Defines a changeset of an object implementing {@see \Charcoal\Object\RevisionableInterface}. * @@ -104,10 +106,10 @@ public function getDataDiff(); * 2. Load the current item from DB * 3. Create diff from (1) and (2). * - * @param RevisionableInterface $obj The object to create the revision from. + * @param ModelInterface $obj The object to create the revision from. * @return ObjectRevision Chainable */ - public function createFromObject(RevisionableInterface $obj); + public function createFromObject(ModelInterface $obj); /** * @param array $dataPrev Optional. The previous revision data. @@ -126,17 +128,17 @@ public function createDiff(array $dataPrev, array $dataObj); public function recursiveDiff(array $array1, array $array2); /** - * @param RevisionableInterface $obj The object to load the last revision of. + * @param ModelInterface $obj The object to load the last revision of. * @return ObjectRevision The last revision for the give object. */ - public function lastObjectRevision(RevisionableInterface $obj); + public function lastObjectRevision(ModelInterface $obj); /** * Retrieve a specific object revision, by revision number. * - * @param RevisionableInterface $obj Target object. + * @param ModelInterface $obj Target object. * @param integer $revNum The revision number to load. * @return ObjectRevision */ - public function objectRevisionNum(RevisionableInterface $obj, $revNum); + public function objectRevisionNum(ModelInterface $obj, $revNum); } From dc17ab954fe557d7307a9b1a09e687241841c036 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 14:12:23 -0400 Subject: [PATCH 56/92] feat(revision): remove revision generation from Content.php --- packages/object/src/Charcoal/Object/Content.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/object/src/Charcoal/Object/Content.php b/packages/object/src/Charcoal/Object/Content.php index 32cf80941..57eea1018 100644 --- a/packages/object/src/Charcoal/Object/Content.php +++ b/packages/object/src/Charcoal/Object/Content.php @@ -197,11 +197,6 @@ protected function preUpdate(array $properties = null) { parent::preUpdate($properties); - // Content is revisionable - if ($this['revisionEnabled']) { - $this->generateRevision(); - } - // Timestampable propertiees $this->setLastModified('now'); From 342c91fcd0d32c4d26f49bcc0ce5d64dd2592be7 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 14:13:05 -0400 Subject: [PATCH 57/92] feat(revision): add a revision service to handle all revision related operations --- .../src/Charcoal/Object/RevisionService.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 packages/object/src/Charcoal/Object/RevisionService.php diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionService.php new file mode 100644 index 000000000..a2c9a5a75 --- /dev/null +++ b/packages/object/src/Charcoal/Object/RevisionService.php @@ -0,0 +1,89 @@ +config = $dependencies['config']; + $this->setModelFactory($dependencies['model/factory']); + } + + public function generateRevision(ModelInterface $model): ?ObjectRevisionInterface + { + if (!$this->canCreateRevision($model)) { + return null; + } + + // TODO config should be a config class. + $config = $this->findRevisionsConfig($model); + + $revisionObject = isset($config['revision_class']) + ? $this->createRevisionObject($config['revision_class']) + : $this->createRevisionObject(); + $revisionObject->createFromObject($model); + + if (!empty($revisionObject->getDataDiff())) { + $revisionObject->save(); + } + + return $revisionObject; + } + + public function createRevisionObject(string $objectRevisionClass = ObjectRevision::class): ObjectRevisionInterface + { + return $this->modelFactory()->create($objectRevisionClass); + } + + public function canCreateRevision(ModelInterface $model): bool + { + if (!$this->config->get('revision_enabled')) { + return false; + } + + $revisionConfig = $this->findRevisionsConfig($model); + + return ($revisionConfig['enabled'] ?? true); + } + + public function findRevisionsConfig(ModelInterface $model): array + { + $class = get_class($model); + if (in_array($class, $this->getRevisionableClasses())) { + return $this->getRevisionsConfig($class); + } + + foreach ($this->getRevisionableClasses() as $revisonable) { + if ($model instanceof $revisonable) { + $this->getRevisionsConfig($revisonable); + } + } + } + + public function getRevisionsConfig(?string $class = null): array + { + $revisions = $this->config->get('revisions'); + + return $class ? $revisions[$class] : $revisions; + } + + public function getRevisionableClasses(): array + { + return array_keys($this->getRevisionsConfig()); + } +} From 448a78032d08965957df7ae2c9a4485afd1368e4 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 14:14:00 -0400 Subject: [PATCH 58/92] feat(revision): add a listener to hook revision generation --- .../Object/GenerateRevisionListener.php | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/object/src/Charcoal/Object/GenerateRevisionListener.php diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php new file mode 100644 index 000000000..7431cf4a7 --- /dev/null +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -0,0 +1,30 @@ +getObject(); + + $this->revisionService->generateRevision($model); + } + + public function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->revisionService = $container->get('revision/service'); + } +} From 8bffba819d7e7e13478f9f23410e9b9310acbaee Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 16:07:55 -0400 Subject: [PATCH 59/92] feat(revision): add RevisionConfig.php to cast revisions config nodes to --- .../src/Charcoal/Object/RevisionConfig.php | 19 ++++++++++ .../src/Charcoal/Object/RevisionService.php | 35 +++++++++++++------ 2 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 packages/object/src/Charcoal/Object/RevisionConfig.php diff --git a/packages/object/src/Charcoal/Object/RevisionConfig.php b/packages/object/src/Charcoal/Object/RevisionConfig.php new file mode 100644 index 000000000..7f88362b4 --- /dev/null +++ b/packages/object/src/Charcoal/Object/RevisionConfig.php @@ -0,0 +1,19 @@ +findRevisionsConfig($model); - $revisionObject = isset($config['revision_class']) - ? $this->createRevisionObject($config['revision_class']) - : $this->createRevisionObject(); + $revisionObject = $this->createRevisionObject($config->get('revisionClass')); $revisionObject->createFromObject($model); if (!empty($revisionObject->getDataDiff())) { @@ -58,32 +55,48 @@ public function canCreateRevision(ModelInterface $model): bool $revisionConfig = $this->findRevisionsConfig($model); + // If we did not find a config of the value of the config is false, we don't want to revision. + if (!$revisionConfig) { + return false; + } + return ($revisionConfig['enabled'] ?? true); } - public function findRevisionsConfig(ModelInterface $model): array + public function findRevisionsConfig(ModelInterface $model): ?RevisionConfig { $class = get_class($model); if (in_array($class, $this->getRevisionableClasses())) { return $this->getRevisionsConfig($class); } - foreach ($this->getRevisionableClasses() as $revisonable) { - if ($model instanceof $revisonable) { - $this->getRevisionsConfig($revisonable); + // Allows ancestor level configuration. + foreach ($this->getRevisionableClasses() as $class) { + if ($model instanceof $class) { + return $this->getRevisionsConfig($class); } } + + return null; } - public function getRevisionsConfig(?string $class = null): array + private function getRevisionsConfig(string $class): RevisionConfig { $revisions = $this->config->get('revisions'); + $revisionsData = $revisions[$class]; + + // If a config is a boolean instead of a data array, it means we only want to affect the enabled state. + if (is_bool($revisionsData)) { + $revisionsData = [ + 'enabled' => $revisionsData + ]; + } - return $class ? $revisions[$class] : $revisions; + return new RevisionConfig($revisionsData); } public function getRevisionableClasses(): array { - return array_keys($this->getRevisionsConfig()); + return array_keys($this->config->get('revisions')); } } From 205258cdaffcfb2a8423a9a1f9a5481fcf80d81e Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Mon, 26 Sep 2022 17:45:52 -0400 Subject: [PATCH 60/92] feat(revision): add `properties` and `propertyBlacklist` options for Revisions `properties` limits the scope of revision to only some properties while `propertyBlacklist` ensures that some properties are not processed into the revision --- .../object/src/Charcoal/Object/ObjectRevision.php | 5 +++-- .../Charcoal/Object/ObjectRevisionInterface.php | 3 ++- .../object/src/Charcoal/Object/RevisionConfig.php | 9 ++++++++- .../src/Charcoal/Object/RevisionService.php | 15 ++++++++++++++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/object/src/Charcoal/Object/ObjectRevision.php b/packages/object/src/Charcoal/Object/ObjectRevision.php index 7773b74b1..93f91ab80 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevision.php +++ b/packages/object/src/Charcoal/Object/ObjectRevision.php @@ -293,9 +293,10 @@ public function getDataDiff() * 3. Create diff from (1) and (2). * * @param ModelInterface $obj The object to create the revision from. + * @param array|null $properties List of properties to revision. * @return ObjectRevision Chainable */ - public function createFromObject(ModelInterface $obj) + public function createFromObject(ModelInterface $obj, ?array $properties = null) { $prevRev = $this->lastObjectRevision($obj); @@ -308,7 +309,7 @@ public function createFromObject(ModelInterface $obj) $this->setRevUser($obj['lastModifiedBy']); } - $this->setDataObj($obj->data()); + $this->setDataObj($obj->data($properties)); $this->setDataPrev($prevRev->getDataObj()); $diff = $this->createDiff(); diff --git a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php index f95d75d81..de052fafe 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php @@ -107,9 +107,10 @@ public function getDataDiff(); * 3. Create diff from (1) and (2). * * @param ModelInterface $obj The object to create the revision from. + * @param array|null $properties List of properties to revision. * @return ObjectRevision Chainable */ - public function createFromObject(ModelInterface $obj); + public function createFromObject(ModelInterface $obj, ?array $properties = null); /** * @param array $dataPrev Optional. The previous revision data. diff --git a/packages/object/src/Charcoal/Object/RevisionConfig.php b/packages/object/src/Charcoal/Object/RevisionConfig.php index 7f88362b4..71a2dab1b 100644 --- a/packages/object/src/Charcoal/Object/RevisionConfig.php +++ b/packages/object/src/Charcoal/Object/RevisionConfig.php @@ -10,10 +10,17 @@ * The config loaded when creating a revision for a model. * The config is generated from the `revisions` key in the config and can be customized per model. * - * `'revisions' : {'Namespace\\Model: {...}'}` : here the `...` represents the data used to create the config. + * {'revisions' : {'Namespace\\Model: {...}'}} here the `...` represents the data used to create the config. */ class RevisionConfig extends AbstractConfig { protected bool $enabled = true; protected string $revisionClass = ObjectRevision::class; + protected array $properties = []; + protected array $propertyBlacklist = [ + 'created', + 'lastModified', + 'createdBy', + 'lastModifiedBy', + ]; } diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionService.php index f619be0f0..f075a3636 100644 --- a/packages/object/src/Charcoal/Object/RevisionService.php +++ b/packages/object/src/Charcoal/Object/RevisionService.php @@ -32,8 +32,21 @@ public function generateRevision(ModelInterface $model): ?ObjectRevisionInterfac $config = $this->findRevisionsConfig($model); + $revisionProperties = array_keys($model->data()); + + if (count($config['properties'])) { + $revisionProperties = array_intersect_key($revisionProperties, $config['properties']); + } + + if (count($config['propertyBlacklist'])) { + $revisionProperties = array_filter( + $revisionProperties, + fn($n) => !in_array($n, $config['propertyBlacklist']) + ); + } + $revisionObject = $this->createRevisionObject($config->get('revisionClass')); - $revisionObject->createFromObject($model); + $revisionObject->createFromObject($model, $revisionProperties); if (!empty($revisionObject->getDataDiff())) { $revisionObject->save(); From 8d84a4eb2c42eb2b931e5766a4f5a07a004c4fe5 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 27 Sep 2022 18:07:38 -0400 Subject: [PATCH 61/92] refactor(revision): refactor revision config and service to allow for more flexible configuration --- .../Charcoal/Object/ObjectServiceProvider.php | 17 +- .../src/Charcoal/Object/RevisionConfig.php | 154 ++++++++++++++++-- .../Charcoal/Object/RevisionModelConfig.php | 83 ++++++++++ .../src/Charcoal/Object/RevisionService.php | 98 +++++------ 4 files changed, 283 insertions(+), 69 deletions(-) create mode 100644 packages/object/src/Charcoal/Object/RevisionModelConfig.php diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php index 8407cf2cd..55e57f84b 100644 --- a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php +++ b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php @@ -17,10 +17,23 @@ public function register(Container $pimple) private function registerRevisionServices(Container $pimple) { + $pimple['revision/config'] = function (Container $pimple): RevisionConfig { + $configData = $pimple['config']->get('revisions'); + + // If the config data is a boolean, it means we only want to affect the enabled state. + if (is_bool($configData)) { + $configData = [ + 'enabled' => $configData, + ]; + } + + return new RevisionConfig($configData); + }; + $pimple['revision/service'] = function (Container $pimple): RevisionService { return new RevisionService([ - 'config' => $pimple['admin/config'], - 'model/factory' => $pimple['model/factory'], + 'revision/config' => $pimple['revision/config'], + 'model/factory' => $pimple['model/factory'], ]); }; } diff --git a/packages/object/src/Charcoal/Object/RevisionConfig.php b/packages/object/src/Charcoal/Object/RevisionConfig.php index 71a2dab1b..c7b9155ef 100644 --- a/packages/object/src/Charcoal/Object/RevisionConfig.php +++ b/packages/object/src/Charcoal/Object/RevisionConfig.php @@ -3,24 +3,156 @@ namespace Charcoal\Object; use Charcoal\Config\AbstractConfig; +use Charcoal\Model\ModelInterface; /** * Revision Config * - * The config loaded when creating a revision for a model. - * The config is generated from the `revisions` key in the config and can be customized per model. - * - * {'revisions' : {'Namespace\\Model: {...}'}} here the `...` represents the data used to create the config. + * Configuration for the project's revision system */ class RevisionConfig extends AbstractConfig { protected bool $enabled = true; protected string $revisionClass = ObjectRevision::class; - protected array $properties = []; - protected array $propertyBlacklist = [ - 'created', - 'lastModified', - 'createdBy', - 'lastModifiedBy', - ]; + // TODO implement the limit feature. + protected ?int $limitPerModel = null; + protected array $models = []; + // Exclude properties from the revision process. + protected array $excludedProperties = []; + + /** + * @param ModelInterface $model + * @return ?RevisionModelConfig + */ + public function buildModelConfig(ModelInterface $model): ?RevisionModelConfig + { + $class = get_class($model); + + if (isset($models[$class])) { + return $this->prepareRevisionModelConfig($model, $models[$class]); + } + + // If the exact class is not defined in the revisions models key, try to find options from inheritance. + foreach ($this->models as $class => $revisionOptions) { + if ($model instanceof $class) { + return $this->prepareRevisionModelConfig($model, $revisionOptions); + } + } + + return null; + } + + /** + * @param ModelInterface $model + * @param array|boolean $revisionOptions + * @return RevisionModelConfig + */ + private function prepareRevisionModelConfig(ModelInterface $model, $revisionOptions): RevisionModelConfig + { + // If a config is a boolean instead of a data array, it means we only want to affect the enabled state. + if (is_bool($revisionOptions)) { + $revisionOptions = [ + 'enabled' => $revisionOptions, + ]; + } + + $extraOptions = [ + 'excludedProperties' => $this->getExcludedProperties(), + ]; + + // Extract excludedProperties options from the model's ancestors. + foreach ($this->models as $class => $modelConfig) { + if ($model instanceof $class) { + // keep only excludedProperties from ancestors + $modelConfig = array_intersect_key($modelConfig, array_flip(['excludedProperties'])); + $extraOptions = array_merge_recursive($extraOptions, $modelConfig); + } + } + + return new RevisionModelConfig(array_merge($revisionOptions, $extraOptions)); + } + + /** + * @return bool + */ + public function isEnabled(): bool + { + return $this->enabled; + } + + /** + * @return string + */ + public function getRevisionClass(): string + { + return $this->revisionClass; + } + + /** + * @param string $revisionClass RevisionClass for RevisionConfig. + * @return self + */ + public function setRevisionClass(string $revisionClass): self + { + $this->revisionClass = $revisionClass; + + return $this; + } + + /** + * @return int|null + */ + public function getLimitPerModel(): ?int + { + return $this->limitPerModel; + } + + /** + * @param int|null $limitPerModel LimitPerModel for RevisionConfig. + * @return self + */ + public function setLimitPerModel(?int $limitPerModel): self + { + $this->limitPerModel = $limitPerModel; + + return $this; + } + + /** + * @return array + */ + public function getModels(): array + { + return $this->models; + } + + /** + * @param array $models Models for RevisionConfig. + * @return self + */ + public function setModels(array $models): self + { + $this->models = $models; + + return $this; + } + + /** + * @return array + */ + public function getExcludedProperties(): array + { + return $this->excludedProperties; + } + + /** + * @param array $excludedProperties ExcludedProperties for RevisionConfig. + * @return self + */ + public function setExcludedProperties(array $excludedProperties): self + { + $this->excludedProperties = $excludedProperties; + + return $this; + } } diff --git a/packages/object/src/Charcoal/Object/RevisionModelConfig.php b/packages/object/src/Charcoal/Object/RevisionModelConfig.php new file mode 100644 index 000000000..ddd39e743 --- /dev/null +++ b/packages/object/src/Charcoal/Object/RevisionModelConfig.php @@ -0,0 +1,83 @@ +enabled; + } + + /** + * @return array + */ + public function getProperties(): array + { + return $this->properties; + } + + public function hasProperties(): bool + { + return !!count($this->properties); + } + + /** + * @return array + */ + public function getExcludedProperties(): array + { + return $this->excludedProperties; + } + + public function hasExcludedProperties(): bool + { + return !!count($this->excludedProperties); + } + + /** + * @return array + */ + public function getIncludedProperties(): array + { + return $this->includedProperties; + } + + public function hasIncludedProperties(): bool + { + return !!count($this->includedProperties); + } + + /** + * @return string + */ + public function getRevisionClass(): string + { + return $this->revisionClass; + } +} diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionService.php index f075a3636..899bf2864 100644 --- a/packages/object/src/Charcoal/Object/RevisionService.php +++ b/packages/object/src/Charcoal/Object/RevisionService.php @@ -2,7 +2,6 @@ namespace Charcoal\Object; -use Charcoal\Admin\Config; use Charcoal\Model\ModelFactoryTrait; use Charcoal\Model\ModelInterface; @@ -16,43 +15,62 @@ class RevisionService { use ModelFactoryTrait; - private Config $config; + private RevisionConfig $revisionConfig; + private array $modelRevisionConfig; public function __construct(array $dependencies) { - $this->config = $dependencies['config']; + $this->revisionConfig = $dependencies['revision/config']; $this->setModelFactory($dependencies['model/factory']); } public function generateRevision(ModelInterface $model): ?ObjectRevisionInterface { - if (!$this->canCreateRevision($model)) { + // Bail early + if ( + !$this->revisionConfig->isEnabled() || + !$this->canCreateRevision($model) + ) { return null; } - $config = $this->findRevisionsConfig($model); + $modelConfig = $this->getModelRevisionConfig($model); + $revisionProperties = $this->parseRevisionProperties($model); - $revisionProperties = array_keys($model->data()); + $revisionObject = $this->createRevisionObject($modelConfig->getRevisionClass()); + $revisionObject->createFromObject($model, $revisionProperties); - if (count($config['properties'])) { - $revisionProperties = array_intersect_key($revisionProperties, $config['properties']); + if (!empty($revisionObject->getDataDiff())) { + $revisionObject->save(); } - if (count($config['propertyBlacklist'])) { - $revisionProperties = array_filter( - $revisionProperties, - fn($n) => !in_array($n, $config['propertyBlacklist']) - ); + return $revisionObject; + } + + public function parseRevisionProperties(ModelInterface $model): array + { + $modelConfig = $this->getModelRevisionConfig($model); + $properties = array_keys($model->data()); + + if ($modelConfig->hasProperties()) { + return array_intersect_key($properties, $modelConfig->getProperties()); } - $revisionObject = $this->createRevisionObject($config->get('revisionClass')); - $revisionObject->createFromObject($model, $revisionProperties); + if ($modelConfig->hasExcludedProperties()) { + $excludedProperties = $modelConfig->getExcludedProperties(); - if (!empty($revisionObject->getDataDiff())) { - $revisionObject->save(); + if ($modelConfig->hasIncludedProperties()) { + $includedProperties = $modelConfig->getIncludedProperties(); + $excludedProperties = array_filter($excludedProperties, fn($e) => in_array($e, $includedProperties)); + } + + return array_filter( + $properties, + fn($n) => !in_array($n, $excludedProperties) + ); } - return $revisionObject; + return $properties; } public function createRevisionObject(string $objectRevisionClass = ObjectRevision::class): ObjectRevisionInterface @@ -62,54 +80,22 @@ public function createRevisionObject(string $objectRevisionClass = ObjectRevisio public function canCreateRevision(ModelInterface $model): bool { - if (!$this->config->get('revision_enabled')) { - return false; - } - - $revisionConfig = $this->findRevisionsConfig($model); + $revisionConfig = $this->getModelRevisionConfig($model); // If we did not find a config of the value of the config is false, we don't want to revision. if (!$revisionConfig) { return false; } - return ($revisionConfig['enabled'] ?? true); + return $revisionConfig->isEnabled(); } - public function findRevisionsConfig(ModelInterface $model): ?RevisionConfig + private function getModelRevisionConfig(ModelInterface $model): ?RevisionModelConfig { - $class = get_class($model); - if (in_array($class, $this->getRevisionableClasses())) { - return $this->getRevisionsConfig($class); + if (!isset($this->modelRevisionConfig[get_class($model)])) { + $this->modelRevisionConfig[get_class($model)] = $this->revisionConfig->buildModelConfig($model); } - // Allows ancestor level configuration. - foreach ($this->getRevisionableClasses() as $class) { - if ($model instanceof $class) { - return $this->getRevisionsConfig($class); - } - } - - return null; - } - - private function getRevisionsConfig(string $class): RevisionConfig - { - $revisions = $this->config->get('revisions'); - $revisionsData = $revisions[$class]; - - // If a config is a boolean instead of a data array, it means we only want to affect the enabled state. - if (is_bool($revisionsData)) { - $revisionsData = [ - 'enabled' => $revisionsData - ]; - } - - return new RevisionConfig($revisionsData); - } - - public function getRevisionableClasses(): array - { - return array_keys($this->config->get('revisions')); + return $this->modelRevisionConfig[get_class($model)]; } } From 8f2bd66bfb4ebe09db53c35219687116bfe62bc7 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Wed, 28 Sep 2022 09:34:10 -0400 Subject: [PATCH 62/92] fix(revision): fix included properties and properties options --- packages/object/src/Charcoal/Object/RevisionService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionService.php index 899bf2864..087afd70f 100644 --- a/packages/object/src/Charcoal/Object/RevisionService.php +++ b/packages/object/src/Charcoal/Object/RevisionService.php @@ -53,7 +53,7 @@ public function parseRevisionProperties(ModelInterface $model): array $properties = array_keys($model->data()); if ($modelConfig->hasProperties()) { - return array_intersect_key($properties, $modelConfig->getProperties()); + return array_intersect($properties, $modelConfig->getProperties()); } if ($modelConfig->hasExcludedProperties()) { @@ -61,7 +61,7 @@ public function parseRevisionProperties(ModelInterface $model): array if ($modelConfig->hasIncludedProperties()) { $includedProperties = $modelConfig->getIncludedProperties(); - $excludedProperties = array_filter($excludedProperties, fn($e) => in_array($e, $includedProperties)); + $excludedProperties = array_filter($excludedProperties, fn($e) => !in_array($e, $includedProperties)); } return array_filter( From 8eb4b4123390d87e71e97ef8d51f65e94b1df8bb Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Thu, 29 Sep 2022 15:04:22 -0400 Subject: [PATCH 63/92] feat(revision): use the revisions service everywhere and refactor it to require setting a model on the service - a ModelInterface instance must be set on the service - Remove RevisionableInterface.php and RevisionableTrait.php - use dependency locator for RevisionService --- .../Action/Object/RevertRevisionAction.php | 38 +-- .../Admin/Ui/ObjectRevisionsInterface.php | 2 +- .../Admin/Ui/ObjectRevisionsTrait.php | 11 +- .../FormGroup/ObjectRevisionsFormGroup.php | 8 + .../Admin/Widget/FormSidebarWidget.php | 17 +- .../Admin/Widget/ObjectRevisionsWidget.php | 16 ++ .../charcoal/object/object-revision.json | 4 +- .../object/src/Charcoal/Object/Content.php | 4 - .../Object/GenerateRevisionListener.php | 2 +- .../src/Charcoal/Object/ObjectRevision.php | 1 - .../Object/ObjectRevisionInterface.php | 2 +- .../Charcoal/Object/ObjectServiceProvider.php | 8 +- .../src/Charcoal/Object/RevisionService.php | 154 ++++++++++- .../Charcoal/Object/RevisionableInterface.php | 62 ----- .../src/Charcoal/Object/RevisionableTrait.php | 243 ------------------ 15 files changed, 215 insertions(+), 357 deletions(-) delete mode 100644 packages/object/src/Charcoal/Object/RevisionableInterface.php delete mode 100644 packages/object/src/Charcoal/Object/RevisionableTrait.php diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index 3b320b310..aed868789 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -2,14 +2,15 @@ namespace Charcoal\Admin\Action\Object; +use Charcoal\Object\RevisionService; use Exception; use InvalidArgumentException; // From PSR-7 +use Pimple\Container; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; // From 'charcoal-object' use Charcoal\Object\ObjectRevisionInterface; -use Charcoal\Object\RevisionableInterface; // From 'charcoal-admin' use Charcoal\Admin\AdminAction; use Charcoal\Admin\Ui\ObjectContainerInterface; @@ -45,6 +46,15 @@ class RevertRevisionAction extends AdminAction implements ObjectContainerInterfa */ protected $revNum; + private RevisionService $revisionService; + + protected function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->revisionService = $container->get('revision/service'); + } + /** * Retrieve the list of parameters to extract from the HTTP request. * @@ -62,9 +72,9 @@ protected function validDataFromRequest() /** * Set the revision number to restore. * - * @param integer $revNum The revision number to load. - * @throws InvalidArgumentException If the given revision is invalid. + * @param integer $revNum The revision number to load. * @return ObjectContainerInterface Chainable + * @throws InvalidArgumentException If the given revision is invalid. */ protected function setRevNum($revNum) { @@ -91,8 +101,8 @@ public function revNum() } /** - * @param RequestInterface $request A PSR-7 compatible Request instance. - * @param ResponseInterface $response A PSR-7 compatible Response instance. + * @param RequestInterface $request A PSR-7 compatible Request instance. + * @param ResponseInterface $response A PSR-7 compatible Response instance. * @return ResponseInterface */ public function run(RequestInterface $request, ResponseInterface $response) @@ -107,19 +117,11 @@ public function run(RequestInterface $request, ResponseInterface $response) '{{ errorMessage }}' => $failMessage ]); - $obj = $this->obj(); - if (!($obj instanceof RevisionableInterface)) { - $this->setSuccess(false); - - $this->addFeedback('error', strtr('{{ model }} does not support revisions', [ - '{{ model }}' => $this->getSingularLabelFromObj($obj), - ])); - - return $response->withStatus(400); - } - + $obj = $this->obj(); $revNum = $this->revNum(); - $revision = $obj->revisionNum($revNum); + $this->revisionService->setModel($obj); + + $revision = $this->revisionService->revisionFromNumber($revNum); if (!$revision['id']) { $this->setSuccess(false); @@ -139,7 +141,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(404); } - $result = $obj->revertToRevision($revNum); + $result = $this->revisionService->revertToRevision($revNum); if ($result) { $doneMessage = $translator->translate( diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php index 846efab13..cb5262de4 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsInterface.php @@ -12,5 +12,5 @@ interface ObjectRevisionsInterface /** * @return \Charcoal\Object\ObjectRevisionInterface[] */ - public function objectRevisions(); + public function objectRevisions(): array; } diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php index f30af4628..3e6766615 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php @@ -6,6 +6,7 @@ use Charcoal\Factory\FactoryInterface; // From 'charcoal-object' use Charcoal\Object\ObjectRevisionInterface; +use Charcoal\Object\RevisionService; /** * An implementation, as Trait, of the {@see \Charcoal\Admin\Ui\ObjectRevisionsInterface}. @@ -15,7 +16,7 @@ trait ObjectRevisionsTrait /** * @return ObjectRevisionInterface[] */ - public function objectRevisions() + public function objectRevisions(): array { if (!$this->objType() || !$this->objId()) { return []; @@ -24,7 +25,9 @@ public function objectRevisions() $obj = $this->modelFactory()->create($this->objType()); $obj->setId($this->objId()); - $lastRevision = $obj->latestRevision(); + $this->revisionService()->setModel($obj); + + $lastRevision = $this->revisionService()->latestRevision(); $propLabel = '%2$s'; $callback = function (ObjectRevisionInterface &$revision) use ($lastRevision, $obj, $propLabel) { @@ -67,7 +70,7 @@ public function objectRevisions() $revision->allowRevert = ($lastRevision['revNum'] !== $revision['revNum']); }; - return $obj->allRevisions($callback); + return $this->revisionService()->allRevisions($callback); } /** @@ -88,4 +91,6 @@ abstract public function objId(); * @return FactoryInterface */ abstract protected function modelFactory(); + + abstract protected function revisionService(): RevisionService; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php index 526d6d514..70c01bd7e 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php @@ -3,6 +3,7 @@ namespace Charcoal\Admin\Widget\FormGroup; // From 'pimple/pimple' +use Charcoal\Object\RevisionService; use Pimple\Container; // From 'charcoal-core' use Charcoal\Model\ModelFactoryTrait; @@ -37,6 +38,12 @@ class ObjectRevisionsFormGroup extends AbstractFormGroup implements */ public $widgetId; + private RevisionService $revisionService; + + protected function revisionService(): RevisionService + { + return $this->revisionService; + } /** * @param string $widgetId The widget identifier. @@ -110,6 +117,7 @@ protected function setDependencies(Container $container) parent::setDependencies($container); $this->setModelFactory($container['model/factory']); + $this->revisionService = $container['revision/service']; $this->objType = $container['request']->getParam('obj_type'); $this->objId = $container['request']->getParam('obj_id'); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index d2189706b..d96ca5ca3 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -2,7 +2,7 @@ namespace Charcoal\Admin\Widget; -use Charcoal\Object\RevisionableInterface; +use Charcoal\Object\RevisionService; use Charcoal\User\AuthAwareInterface; use InvalidArgumentException; // From Pimple @@ -163,6 +163,8 @@ class FormSidebarWidget extends AdminWidget implements */ private $requiredGlobalAclPermissions = []; + private RevisionService $revisionService; + /** * @param array|ArrayInterface $data Class data. * @return FormSidebarWidget Chainable @@ -552,8 +554,10 @@ public function isObjRevisionable() return $this->isObjRevisionable; } - if ($obj instanceof RevisionableInterface && $obj['revisionEnabled']) { - $this->isObjRevisionable = !!count($obj->allRevisions()); + $this->revisionService->setModel($obj); + + if ($this->revisionService->revisionEnabled()) { + $this->isObjRevisionable = !!count($this->revisionService->allRevisions()); } } } @@ -894,4 +898,11 @@ protected function isAssoc(array $array) return !!array_filter($array, 'is_string', ARRAY_FILTER_USE_KEY); } + + protected function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->revisionService = $container['revision/service']; + } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php index cb482ded4..25049a3cf 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php @@ -8,6 +8,8 @@ use Charcoal\Admin\AdminWidget; use Charcoal\Admin\Ui\ObjectRevisionsInterface; use Charcoal\Admin\Ui\ObjectRevisionsTrait; +use Charcoal\Object\RevisionService; +use Pimple\Container; /** * Class ObjectRevisionWidget @@ -27,6 +29,20 @@ class ObjectRevisionsWidget extends AdminWidget implements */ protected $objId; + private RevisionService $revisionService; + + protected function setDependencies(Container $container) + { + parent::setDependencies($container); + + $this->revisionService = $container['revision/service']; + } + + protected function revisionService(): RevisionService + { + return $this->revisionService; + } + /** * @return boolean */ diff --git a/packages/object/metadata/admin/charcoal/object/object-revision.json b/packages/object/metadata/admin/charcoal/object/object-revision.json index 14fd32a23..f5f2954b2 100644 --- a/packages/object/metadata/admin/charcoal/object/object-revision.json +++ b/packages/object/metadata/admin/charcoal/object/object-revision.json @@ -39,7 +39,7 @@ "form": { "type": "charcoal/admin/widget/object-form", "form_ident": "default", - "target_type": "charcoal/object/object-revision" + "obj_type": "charcoal/object/object-revision" } }, "layout": { @@ -57,7 +57,7 @@ "form": { "type": "charcoal/admin/widget/table", "collection_ident": "default", - "target_type": "charcoal/object/object-revision" + "obj_type": "charcoal/object/object-revision" } }, "layout": { diff --git a/packages/object/src/Charcoal/Object/Content.php b/packages/object/src/Charcoal/Object/Content.php index 57eea1018..2b53f1bf0 100644 --- a/packages/object/src/Charcoal/Object/Content.php +++ b/packages/object/src/Charcoal/Object/Content.php @@ -15,8 +15,6 @@ use Charcoal\Object\ContentInterface; use Charcoal\Object\AuthorableInterface; use Charcoal\Object\AuthorableTrait; -use Charcoal\Object\RevisionableInterface; -use Charcoal\Object\RevisionableTrait; use Charcoal\Object\TimestampableInterface; use Charcoal\Object\TimestampableTrait; @@ -26,11 +24,9 @@ class Content extends AbstractModel implements AuthorableInterface, ContentInterface, - RevisionableInterface, TimestampableInterface { use AuthorableTrait; - use RevisionableTrait; use TranslatorAwareTrait; use TimestampableTrait; diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php index 7431cf4a7..7339f0971 100644 --- a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -18,7 +18,7 @@ public function __invoke(object $event) /** @var ModelInterface $model */ $model = $event->getObject(); - $this->revisionService->generateRevision($model); + $this->revisionService->setModel($model)->generateRevision(); } public function setDependencies(Container $container) diff --git a/packages/object/src/Charcoal/Object/ObjectRevision.php b/packages/object/src/Charcoal/Object/ObjectRevision.php index 93f91ab80..675ec5bba 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevision.php +++ b/packages/object/src/Charcoal/Object/ObjectRevision.php @@ -15,7 +15,6 @@ use Charcoal\Model\ModelFactoryTrait; // From 'charcoal-object' use Charcoal\Object\ObjectRevisionInterface; -use Charcoal\Object\RevisionableInterface; /** * Represents the changeset of an object. diff --git a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php index de052fafe..349c18108 100644 --- a/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php +++ b/packages/object/src/Charcoal/Object/ObjectRevisionInterface.php @@ -5,7 +5,7 @@ use Charcoal\Model\ModelInterface; /** - * Defines a changeset of an object implementing {@see \Charcoal\Object\RevisionableInterface}. + * Defines a changeset of an object implementing {@see \Charcoal\Object\ModelInterface}. * * {@see \Charcoal\Object\ObjectRevision} for a basic implementation. */ diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php index 55e57f84b..2d0e12978 100644 --- a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php +++ b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php @@ -3,6 +3,7 @@ namespace Charcoal\Object; use Pimple\Container; +use Pimple\Psr11\ServiceLocator; use Pimple\ServiceProviderInterface; /** @@ -31,10 +32,9 @@ private function registerRevisionServices(Container $pimple) }; $pimple['revision/service'] = function (Container $pimple): RevisionService { - return new RevisionService([ - 'revision/config' => $pimple['revision/config'], - 'model/factory' => $pimple['model/factory'], - ]); + $services = new ServiceLocator($pimple, ['revision/config', 'model/factory', 'logger']); + + return new RevisionService($services); }; } } diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionService.php index 087afd70f..b8c5ba9fc 100644 --- a/packages/object/src/Charcoal/Object/RevisionService.php +++ b/packages/object/src/Charcoal/Object/RevisionService.php @@ -2,42 +2,66 @@ namespace Charcoal\Object; +use Charcoal\Loader\CollectionLoader; use Charcoal\Model\ModelFactoryTrait; use Charcoal\Model\ModelInterface; +use Pimple\Psr11\ServiceLocator; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerAwareTrait; /** * Revision Service * * Service handling revision generation and retrieval. * Can be implemented in a listener + * + * Revisions need to act on a ModelInterface, + * So to use the revision service, one have to set a ModelInterface beforehand. + * Failure to do so will result in a */ class RevisionService { use ModelFactoryTrait; + use LoggerAwareTrait; private RevisionConfig $revisionConfig; private array $modelRevisionConfig; + private ModelInterface $model; - public function __construct(array $dependencies) + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function __construct(ServiceLocator $locator) { - $this->revisionConfig = $dependencies['revision/config']; - $this->setModelFactory($dependencies['model/factory']); + $this->revisionConfig = $locator->get('revision/config'); + $this->setModelFactory($locator->get('model/factory')); + $this->setLogger($locator->get('logger')); } - public function generateRevision(ModelInterface $model): ?ObjectRevisionInterface + public function __invoke(ModelInterface $model): self { + $this->setModel($model); + + return $this; + } + + public function generateRevision(): ?ObjectRevisionInterface + { + $model = $this->getModel(); + // Bail early if ( !$this->revisionConfig->isEnabled() || - !$this->canCreateRevision($model) + !$this->revisionEnabled() ) { return null; } - $modelConfig = $this->getModelRevisionConfig($model); - $revisionProperties = $this->parseRevisionProperties($model); + $revisionProperties = $this->parseRevisionProperties(); + $revisionObject = $this->createRevisionObject(); - $revisionObject = $this->createRevisionObject($modelConfig->getRevisionClass()); $revisionObject->createFromObject($model, $revisionProperties); if (!empty($revisionObject->getDataDiff())) { @@ -47,10 +71,67 @@ public function generateRevision(ModelInterface $model): ?ObjectRevisionInterfac return $revisionObject; } - public function parseRevisionProperties(ModelInterface $model): array + public function latestRevision(): ObjectRevisionInterface + { + $model = $this->getModel(); + $revision = $this->createRevisionObject(); + + return $revision->lastObjectRevision($model); + } + + /** + * @return ObjectRevisionInterface[] + */ + public function allRevisions(callable $callback = null): array + { + $model = $this->getModel(); + $loader = $this->createRevisionObjectCollectionLoader(); + + $loader + ->addOrder('revTs', 'desc') + ->addFilters([ + [ + 'property' => 'targetType', + 'value' => $model->objType(), + ], + [ + 'property' => 'targetId', + 'value' => $model->id(), + ], + ]); + + if ($callback !== null) { + $loader->setCallback($callback); + } + + $revisions = $loader->load(); + + return $revisions->objects(); + } + + public function revertToRevision(int $number): bool + { + $model = $this->getModel(); + $revision = $this->revisionFromNumber($number); + + if (!$revision->id()) { + return false; + } + + if (isset($model['lastModifiedBy'])) { + $model['lastModifiedBy'] = $revision->getRevUser(); + } + + $model->setData($revision->getDataObj()); + + return $model->update(); + } + + public function parseRevisionProperties(): array { + $model = $this->getModel(); $modelConfig = $this->getModelRevisionConfig($model); - $properties = array_keys($model->data()); + $properties = array_keys($model->data()); if ($modelConfig->hasProperties()) { return array_intersect($properties, $modelConfig->getProperties()); @@ -73,13 +154,40 @@ public function parseRevisionProperties(ModelInterface $model): array return $properties; } - public function createRevisionObject(string $objectRevisionClass = ObjectRevision::class): ObjectRevisionInterface + public function getObjectRevisionClass(): string { - return $this->modelFactory()->create($objectRevisionClass); + $modelConfig = $this->getModelRevisionConfig(); + + return $modelConfig->getRevisionClass(); } - public function canCreateRevision(ModelInterface $model): bool + public function createRevisionObjectCollectionLoader(): CollectionLoader { + return new CollectionLoader([ + 'logger' => $this->logger, + 'factory' => $this->modelFactory(), + 'model' => $this->getRevisionObjectPrototype($this->getObjectRevisionClass()), + ]); + } + + public function getRevisionObjectPrototype(): ObjectRevisionInterface + { + return $this->modelFactory()->get($this->getObjectRevisionClass()); + } + + public function createRevisionObject(): ObjectRevisionInterface + { + return $this->modelFactory()->create($this->getObjectRevisionClass()); + } + + public function revisionFromNumber(int $number): ObjectRevisionInterface + { + return $this->createRevisionObject()->objectRevisionNum($this->getModel(), $number); + } + + public function revisionEnabled(): bool + { + $model = $this->getModel(); $revisionConfig = $this->getModelRevisionConfig($model); // If we did not find a config of the value of the config is false, we don't want to revision. @@ -90,12 +198,30 @@ public function canCreateRevision(ModelInterface $model): bool return $revisionConfig->isEnabled(); } - private function getModelRevisionConfig(ModelInterface $model): ?RevisionModelConfig + private function getModelRevisionConfig(): ?RevisionModelConfig { + $model = $this->getModel(); + if (!isset($this->modelRevisionConfig[get_class($model)])) { $this->modelRevisionConfig[get_class($model)] = $this->revisionConfig->buildModelConfig($model); } return $this->modelRevisionConfig[get_class($model)]; } + + public function getModel(): ModelInterface + { + if (!isset($this->model)) { + throw new \InvalidArgumentException('Setting a `ModelInterface` is imperative to use the RevisionService'); + } + + return $this->model; + } + + public function setModel(ModelInterface $model): self + { + $this->model = $model; + + return $this; + } } diff --git a/packages/object/src/Charcoal/Object/RevisionableInterface.php b/packages/object/src/Charcoal/Object/RevisionableInterface.php deleted file mode 100644 index 6b60497ed..000000000 --- a/packages/object/src/Charcoal/Object/RevisionableInterface.php +++ /dev/null @@ -1,62 +0,0 @@ -revisionEnabled = !!$enabled; - return $this; - } - - /** - * @return boolean - */ - public function getRevisionEnabled() - { - return $this->revisionEnabled; - } - - /** - * Create a revision collection loader. - * - * @return CollectionLoader - */ - public function createRevisionObjectCollectionLoader() - { - $loader = new CollectionLoader([ - 'logger' => $this->logger, - 'factory' => $this->modelFactory(), - 'model' => $this->getRevisionObjectPrototype(), - ]); - - return $loader; - } - - /** - * Create a revision object. - * - * @return ObjectRevisionInterface - */ - public function createRevisionObject() - { - $rev = $this->modelFactory()->create($this->getObjectRevisionClass()); - - return $rev; - } - - /** - * Retrieve the revision object prototype. - * - * @return ObjectRevisionInterface - */ - public function getRevisionObjectPrototype() - { - $proto = $this->modelFactory()->get($this->getObjectRevisionClass()); - - return $proto; - } - - /** - * Set the class name of the object revision model. - * - * @param string $className The class name of the object revision model. - * @throws InvalidArgumentException If the class name is not a string. - * @return AbstractPropertyDisplay Chainable - */ - protected function setObjectRevisionClass($className) - { - if (!is_string($className)) { - throw new InvalidArgumentException( - 'Route class name must be a string.' - ); - } - - $this->objectRevisionClass = $className; - return $this; - } - - /** - * Retrieve the class name of the object revision model. - * - * @return string - */ - public function getObjectRevisionClass() - { - return $this->objectRevisionClass; - } - - /** - * Alias of {@see self::getObjectRevisionClass()}. - * - * @return string - */ - public function objectRevisionClass() - { - return $this->getObjectRevisionClass(); - } - - /** - * @see \Charcoal\Object\ObjectRevision::create_fromObject() - * @return ObjectRevision - */ - public function generateRevision() - { - $rev = $this->createRevisionObject(); - - $rev->createFromObject($this); - if (!empty($rev->getDataDiff())) { - $rev->save(); - } - - return $rev; - } - - /** - * @see \Charcoal\Object\ObejctRevision::lastObjectRevision - * @return ObjectRevision - */ - public function latestRevision() - { - $rev = $this->createRevisionObject(); - $rev = $rev->lastObjectRevision($this); - - return $rev; - } - - /** - * @see \Charcoal\Object\ObejctRevision::objectRevisionNum() - * - * @todo Should return NULL if source does not exist. - * - * @param integer $revNum The revision number. - * @return ObjectRevision - */ - public function revisionNum($revNum) - { - $rev = $this->createRevisionObject(); - $rev = $rev->objectRevisionNum($this, intval($revNum)); - - return $rev; - } - - /** - * Retrieves all revisions for the current objet - * - * @param callable $callback Optional object callback. - * @return array - */ - public function allRevisions(callable $callback = null) - { - $loader = $this->createRevisionObjectCollectionLoader(); - $loader - ->addOrder('revTs', 'desc') - ->addFilters([ - [ - 'property' => 'targetType', - 'value' => $this->objType(), - ], - [ - 'property' => 'targetId', - 'value' => $this->id(), - ], - ]); - - if ($callback !== null) { - $loader->setCallback($callback); - } - - $revisions = $loader->load(); - return $revisions->objects(); - } - - /** - * @param integer $revNum The revision number to revert to. - * @throws InvalidArgumentException If revision number is invalid. - * @return boolean Success / Failure. - */ - public function revertToRevision($revNum) - { - if (!$revNum) { - throw new InvalidArgumentException( - 'Invalid revision number' - ); - } - - $rev = $this->revisionNum(intval($revNum)); - - if (!$rev->id()) { - return false; - } - - if (isset($obj['lastModifiedBy'])) { - $obj['lastModifiedBy'] = $rev->getRevUser(); - } - - $this->setData($rev->getDataObj()); - $this->update(); - - return true; - } - - /** - * Retrieve the object model factory. - * - * @return \Charcoal\Factory\FactoryInterface - */ - abstract public function modelFactory(); - - /** - * @return \Charcoal\Model\MetadataInterface - */ - abstract public function metadata(); -} From 59845ded813cc4844249bdd55c4ebc4ef909fd3f Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 30 Sep 2022 10:20:42 -0400 Subject: [PATCH 64/92] refactor(revisions): rename `RevisionService` to `RevisionsManager` --- .../Charcoal/Admin/Action/Object/RevertRevisionAction.php | 4 ++-- .../admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php | 4 ++-- .../Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php | 6 +++--- .../admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php | 4 ++-- .../src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php | 6 +++--- .../src/Charcoal/Object/GenerateRevisionListener.php | 2 +- .../object/src/Charcoal/Object/ObjectServiceProvider.php | 4 ++-- .../Object/{RevisionService.php => RevisionsManager.php} | 8 ++++---- 8 files changed, 19 insertions(+), 19 deletions(-) rename packages/object/src/Charcoal/Object/{RevisionService.php => RevisionsManager.php} (97%) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index aed868789..91c82008d 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -2,7 +2,7 @@ namespace Charcoal\Admin\Action\Object; -use Charcoal\Object\RevisionService; +use Charcoal\Object\RevisionsManager; use Exception; use InvalidArgumentException; // From PSR-7 @@ -46,7 +46,7 @@ class RevertRevisionAction extends AdminAction implements ObjectContainerInterfa */ protected $revNum; - private RevisionService $revisionService; + private RevisionsManager $revisionService; protected function setDependencies(Container $container) { diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php index 3e6766615..45b33dfa5 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php @@ -6,7 +6,7 @@ use Charcoal\Factory\FactoryInterface; // From 'charcoal-object' use Charcoal\Object\ObjectRevisionInterface; -use Charcoal\Object\RevisionService; +use Charcoal\Object\RevisionsManager; /** * An implementation, as Trait, of the {@see \Charcoal\Admin\Ui\ObjectRevisionsInterface}. @@ -92,5 +92,5 @@ abstract public function objId(); */ abstract protected function modelFactory(); - abstract protected function revisionService(): RevisionService; + abstract protected function revisionService(): RevisionsManager; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php index 70c01bd7e..3ba6a6198 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php @@ -3,7 +3,7 @@ namespace Charcoal\Admin\Widget\FormGroup; // From 'pimple/pimple' -use Charcoal\Object\RevisionService; +use Charcoal\Object\RevisionsManager; use Pimple\Container; // From 'charcoal-core' use Charcoal\Model\ModelFactoryTrait; @@ -38,9 +38,9 @@ class ObjectRevisionsFormGroup extends AbstractFormGroup implements */ public $widgetId; - private RevisionService $revisionService; + private RevisionsManager $revisionService; - protected function revisionService(): RevisionService + protected function revisionService(): RevisionsManager { return $this->revisionService; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index d96ca5ca3..11cb758cb 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -2,7 +2,7 @@ namespace Charcoal\Admin\Widget; -use Charcoal\Object\RevisionService; +use Charcoal\Object\RevisionsManager; use Charcoal\User\AuthAwareInterface; use InvalidArgumentException; // From Pimple @@ -163,7 +163,7 @@ class FormSidebarWidget extends AdminWidget implements */ private $requiredGlobalAclPermissions = []; - private RevisionService $revisionService; + private RevisionsManager $revisionService; /** * @param array|ArrayInterface $data Class data. diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php index 25049a3cf..b7887eb16 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php @@ -8,7 +8,7 @@ use Charcoal\Admin\AdminWidget; use Charcoal\Admin\Ui\ObjectRevisionsInterface; use Charcoal\Admin\Ui\ObjectRevisionsTrait; -use Charcoal\Object\RevisionService; +use Charcoal\Object\RevisionsManager; use Pimple\Container; /** @@ -29,7 +29,7 @@ class ObjectRevisionsWidget extends AdminWidget implements */ protected $objId; - private RevisionService $revisionService; + private RevisionsManager $revisionService; protected function setDependencies(Container $container) { @@ -38,7 +38,7 @@ protected function setDependencies(Container $container) $this->revisionService = $container['revision/service']; } - protected function revisionService(): RevisionService + protected function revisionService(): RevisionsManager { return $this->revisionService; } diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php index 7339f0971..bfcf6eb6e 100644 --- a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -11,7 +11,7 @@ */ class GenerateRevisionListener extends AbstractEventListener { - protected RevisionService $revisionService; + protected RevisionsManager $revisionService; public function __invoke(object $event) { diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php index 2d0e12978..e9451a5f7 100644 --- a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php +++ b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php @@ -31,10 +31,10 @@ private function registerRevisionServices(Container $pimple) return new RevisionConfig($configData); }; - $pimple['revision/service'] = function (Container $pimple): RevisionService { + $pimple['revision/service'] = function (Container $pimple): RevisionsManager { $services = new ServiceLocator($pimple, ['revision/config', 'model/factory', 'logger']); - return new RevisionService($services); + return new RevisionsManager($services); }; } } diff --git a/packages/object/src/Charcoal/Object/RevisionService.php b/packages/object/src/Charcoal/Object/RevisionsManager.php similarity index 97% rename from packages/object/src/Charcoal/Object/RevisionService.php rename to packages/object/src/Charcoal/Object/RevisionsManager.php index b8c5ba9fc..75cce4b9e 100644 --- a/packages/object/src/Charcoal/Object/RevisionService.php +++ b/packages/object/src/Charcoal/Object/RevisionsManager.php @@ -11,16 +11,16 @@ use Psr\Log\LoggerAwareTrait; /** - * Revision Service + * Revisions Manager * * Service handling revision generation and retrieval. - * Can be implemented in a listener + * Can be implemented in an event listener * * Revisions need to act on a ModelInterface, * So to use the revision service, one have to set a ModelInterface beforehand. - * Failure to do so will result in a + * Failure to do so will result in an \InvalidArgumentException being thrown. */ -class RevisionService +class RevisionsManager { use ModelFactoryTrait; use LoggerAwareTrait; From a191404e7511391c4f83d23101fbd8636571ad14 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 30 Sep 2022 10:24:59 -0400 Subject: [PATCH 65/92] refactor(revisions): rename revisions config and container keys with the plural form of `Revision` --- .../Admin/Action/Object/RevertRevisionAction.php | 2 +- .../Widget/FormGroup/ObjectRevisionsFormGroup.php | 2 +- .../src/Charcoal/Admin/Widget/FormSidebarWidget.php | 2 +- .../Charcoal/Admin/Widget/ObjectRevisionsWidget.php | 2 +- .../src/Charcoal/Object/GenerateRevisionListener.php | 2 +- .../src/Charcoal/Object/ObjectServiceProvider.php | 12 ++++++++---- .../{RevisionConfig.php => RevisionsConfig.php} | 6 +++--- .../object/src/Charcoal/Object/RevisionsManager.php | 4 ++-- 8 files changed, 18 insertions(+), 14 deletions(-) rename packages/object/src/Charcoal/Object/{RevisionConfig.php => RevisionsConfig.php} (97%) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index 91c82008d..0f8b85f69 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -52,7 +52,7 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container->get('revision/service'); + $this->revisionService = $container->get('revisions/manager'); } /** diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php index 3ba6a6198..55bc1a978 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php @@ -117,7 +117,7 @@ protected function setDependencies(Container $container) parent::setDependencies($container); $this->setModelFactory($container['model/factory']); - $this->revisionService = $container['revision/service']; + $this->revisionService = $container['revisions/manager']; $this->objType = $container['request']->getParam('obj_type'); $this->objId = $container['request']->getParam('obj_id'); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index 11cb758cb..407fb886b 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -903,6 +903,6 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container['revision/service']; + $this->revisionService = $container['revisions/manager']; } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php index b7887eb16..8b52e753e 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php @@ -35,7 +35,7 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container['revision/service']; + $this->revisionService = $container['revisions/manager']; } protected function revisionService(): RevisionsManager diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php index bfcf6eb6e..7abed4340 100644 --- a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -25,6 +25,6 @@ public function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container->get('revision/service'); + $this->revisionService = $container->get('revisions/manager'); } } diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php index e9451a5f7..1b17e2a44 100644 --- a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php +++ b/packages/object/src/Charcoal/Object/ObjectServiceProvider.php @@ -18,7 +18,7 @@ public function register(Container $pimple) private function registerRevisionServices(Container $pimple) { - $pimple['revision/config'] = function (Container $pimple): RevisionConfig { + $pimple['revisions/config'] = function (Container $pimple): RevisionsConfig { $configData = $pimple['config']->get('revisions'); // If the config data is a boolean, it means we only want to affect the enabled state. @@ -28,11 +28,15 @@ private function registerRevisionServices(Container $pimple) ]; } - return new RevisionConfig($configData); + return new RevisionsConfig($configData); }; - $pimple['revision/service'] = function (Container $pimple): RevisionsManager { - $services = new ServiceLocator($pimple, ['revision/config', 'model/factory', 'logger']); + $pimple['revisions/manager'] = function (Container $pimple): RevisionsManager { + $services = new ServiceLocator($pimple, [ + 'revisions/config', + 'model/factory', + 'logger' + ]); return new RevisionsManager($services); }; diff --git a/packages/object/src/Charcoal/Object/RevisionConfig.php b/packages/object/src/Charcoal/Object/RevisionsConfig.php similarity index 97% rename from packages/object/src/Charcoal/Object/RevisionConfig.php rename to packages/object/src/Charcoal/Object/RevisionsConfig.php index c7b9155ef..4d849c430 100644 --- a/packages/object/src/Charcoal/Object/RevisionConfig.php +++ b/packages/object/src/Charcoal/Object/RevisionsConfig.php @@ -6,11 +6,11 @@ use Charcoal\Model\ModelInterface; /** - * Revision Config + * Revisions Config * - * Configuration for the project's revision system + * Configuration for the project's revisions system */ -class RevisionConfig extends AbstractConfig +class RevisionsConfig extends AbstractConfig { protected bool $enabled = true; protected string $revisionClass = ObjectRevision::class; diff --git a/packages/object/src/Charcoal/Object/RevisionsManager.php b/packages/object/src/Charcoal/Object/RevisionsManager.php index 75cce4b9e..24c5051ce 100644 --- a/packages/object/src/Charcoal/Object/RevisionsManager.php +++ b/packages/object/src/Charcoal/Object/RevisionsManager.php @@ -25,7 +25,7 @@ class RevisionsManager use ModelFactoryTrait; use LoggerAwareTrait; - private RevisionConfig $revisionConfig; + private RevisionsConfig $revisionConfig; private array $modelRevisionConfig; private ModelInterface $model; @@ -35,7 +35,7 @@ class RevisionsManager */ public function __construct(ServiceLocator $locator) { - $this->revisionConfig = $locator->get('revision/config'); + $this->revisionConfig = $locator->get('revisions/config'); $this->setModelFactory($locator->get('model/factory')); $this->setLogger($locator->get('logger')); } From d0807442fbbe29abfda70510d13a0ba47e7113c3 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 30 Sep 2022 11:13:36 -0400 Subject: [PATCH 66/92] refactor(revisions): rename `ObjectServiceProvider` to `RevisionServiceProvider` --- .../Charcoal/App/ServiceProvider/AppServiceProvider.php | 4 ++-- ...ctServiceProvider.php => RevisionServiceProvider.php} | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) rename packages/object/src/Charcoal/Object/{ObjectServiceProvider.php => RevisionServiceProvider.php} (81%) diff --git a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php index b831ad0aa..84ee46cd7 100644 --- a/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php +++ b/packages/app/src/Charcoal/App/ServiceProvider/AppServiceProvider.php @@ -38,7 +38,7 @@ use Charcoal\App\Template\TemplateInterface; use Charcoal\App\Template\WidgetInterface; use Charcoal\App\Template\WidgetBuilder; -use Charcoal\Object\ObjectServiceProvider; +use Charcoal\Object\RevisionServiceProvider; use Charcoal\View\Twig\DebugHelpers as TwigDebugHelpers; use Charcoal\View\Twig\HelpersInterface as TwigHelpersInterface; use Charcoal\View\Twig\UrlHelpers as TwigUrlHelpers; @@ -79,7 +79,7 @@ public function register(Container $container) $container->register(new ScriptServiceProvider()); $container->register(new TranslatorServiceProvider()); $container->register(new ViewServiceProvider()); - $container->register(new ObjectServiceProvider()); + $container->register(new RevisionServiceProvider()); $this->registerKernelServices($container); $this->registerHandlerServices($container); diff --git a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php b/packages/object/src/Charcoal/Object/RevisionServiceProvider.php similarity index 81% rename from packages/object/src/Charcoal/Object/ObjectServiceProvider.php rename to packages/object/src/Charcoal/Object/RevisionServiceProvider.php index 1b17e2a44..38adc231c 100644 --- a/packages/object/src/Charcoal/Object/ObjectServiceProvider.php +++ b/packages/object/src/Charcoal/Object/RevisionServiceProvider.php @@ -7,16 +7,11 @@ use Pimple\ServiceProviderInterface; /** - * Object Service Provider + * Revision Service Provider */ -class ObjectServiceProvider implements ServiceProviderInterface +class RevisionServiceProvider implements ServiceProviderInterface { public function register(Container $pimple) - { - $this->registerRevisionServices($pimple); - } - - private function registerRevisionServices(Container $pimple) { $pimple['revisions/config'] = function (Container $pimple): RevisionsConfig { $configData = $pimple['config']->get('revisions'); From 3a83d57151ee9f8a49a3e7114022dfd8aa5b1cd6 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 30 Sep 2022 11:30:21 -0400 Subject: [PATCH 67/92] refactor(revisions): fix method comments --- .../src/Charcoal/Object/RevisionModelConfig.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/object/src/Charcoal/Object/RevisionModelConfig.php b/packages/object/src/Charcoal/Object/RevisionModelConfig.php index ddd39e743..d7c1c41ac 100644 --- a/packages/object/src/Charcoal/Object/RevisionModelConfig.php +++ b/packages/object/src/Charcoal/Object/RevisionModelConfig.php @@ -26,16 +26,13 @@ class RevisionModelConfig extends AbstractConfig // include a property that was excluded by a parent. protected array $includedProperties = []; - /** - * @return bool - */ public function isEnabled(): bool { return $this->enabled; } /** - * @return array + * @return string[] */ public function getProperties(): array { @@ -48,7 +45,7 @@ public function hasProperties(): bool } /** - * @return array + * @return string[] */ public function getExcludedProperties(): array { @@ -61,7 +58,7 @@ public function hasExcludedProperties(): bool } /** - * @return array + * @return string[] */ public function getIncludedProperties(): array { @@ -70,11 +67,11 @@ public function getIncludedProperties(): array public function hasIncludedProperties(): bool { - return !!count($this->includedProperties); + return (bool)$this->includedProperties; } /** - * @return string + * @return class-string */ public function getRevisionClass(): string { From 019f2ea3cf2fa43e7f26c68bd38e8d1e22dccca3 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 11:20:46 -0400 Subject: [PATCH 68/92] refactor(revision): uniformize functions names for RevisionsManager --- .../Admin/Action/Object/RevertRevisionAction.php | 2 +- .../src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php | 4 ++-- .../src/Charcoal/Admin/Widget/FormSidebarWidget.php | 4 ++-- .../object/src/Charcoal/Object/RevisionsManager.php | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index 0f8b85f69..8709efb22 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -121,7 +121,7 @@ public function run(RequestInterface $request, ResponseInterface $response) $revNum = $this->revNum(); $this->revisionService->setModel($obj); - $revision = $this->revisionService->revisionFromNumber($revNum); + $revision = $this->revisionService->getRevisionFromNumber($revNum); if (!$revision['id']) { $this->setSuccess(false); diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php index 45b33dfa5..dd38525b8 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php @@ -27,7 +27,7 @@ public function objectRevisions(): array $this->revisionService()->setModel($obj); - $lastRevision = $this->revisionService()->latestRevision(); + $lastRevision = $this->revisionService()->getLatestRevision(); $propLabel = '%2$s'; $callback = function (ObjectRevisionInterface &$revision) use ($lastRevision, $obj, $propLabel) { @@ -70,7 +70,7 @@ public function objectRevisions(): array $revision->allowRevert = ($lastRevision['revNum'] !== $revision['revNum']); }; - return $this->revisionService()->allRevisions($callback); + return $this->revisionService()->getAllRevisions($callback); } /** diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index 407fb886b..e2bdd4faf 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -556,8 +556,8 @@ public function isObjRevisionable() $this->revisionService->setModel($obj); - if ($this->revisionService->revisionEnabled()) { - $this->isObjRevisionable = !!count($this->revisionService->allRevisions()); + if ($this->revisionService->isRevisionEnabled()) { + $this->isObjRevisionable = !!count($this->revisionService->getAllRevisions()); } } } diff --git a/packages/object/src/Charcoal/Object/RevisionsManager.php b/packages/object/src/Charcoal/Object/RevisionsManager.php index 24c5051ce..e080a16c2 100644 --- a/packages/object/src/Charcoal/Object/RevisionsManager.php +++ b/packages/object/src/Charcoal/Object/RevisionsManager.php @@ -54,7 +54,7 @@ public function generateRevision(): ?ObjectRevisionInterface // Bail early if ( !$this->revisionConfig->isEnabled() || - !$this->revisionEnabled() + !$this->isRevisionEnabled() ) { return null; } @@ -71,7 +71,7 @@ public function generateRevision(): ?ObjectRevisionInterface return $revisionObject; } - public function latestRevision(): ObjectRevisionInterface + public function getLatestRevision(): ObjectRevisionInterface { $model = $this->getModel(); $revision = $this->createRevisionObject(); @@ -82,7 +82,7 @@ public function latestRevision(): ObjectRevisionInterface /** * @return ObjectRevisionInterface[] */ - public function allRevisions(callable $callback = null): array + public function getAllRevisions(callable $callback = null): array { $model = $this->getModel(); $loader = $this->createRevisionObjectCollectionLoader(); @@ -112,7 +112,7 @@ public function allRevisions(callable $callback = null): array public function revertToRevision(int $number): bool { $model = $this->getModel(); - $revision = $this->revisionFromNumber($number); + $revision = $this->getRevisionFromNumber($number); if (!$revision->id()) { return false; @@ -180,12 +180,12 @@ public function createRevisionObject(): ObjectRevisionInterface return $this->modelFactory()->create($this->getObjectRevisionClass()); } - public function revisionFromNumber(int $number): ObjectRevisionInterface + public function getRevisionFromNumber(int $number): ObjectRevisionInterface { return $this->createRevisionObject()->objectRevisionNum($this->getModel(), $number); } - public function revisionEnabled(): bool + public function isRevisionEnabled(): bool { $model = $this->getModel(); $revisionConfig = $this->getModelRevisionConfig($model); From 227795574cb7277077a48d8d12f5090d7092c8fe Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 11:21:43 -0400 Subject: [PATCH 69/92] refactor(revision): remove unnecessary exception --- packages/object/src/Charcoal/Object/RevisionsManager.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/object/src/Charcoal/Object/RevisionsManager.php b/packages/object/src/Charcoal/Object/RevisionsManager.php index e080a16c2..d6bc5e61d 100644 --- a/packages/object/src/Charcoal/Object/RevisionsManager.php +++ b/packages/object/src/Charcoal/Object/RevisionsManager.php @@ -211,10 +211,6 @@ private function getModelRevisionConfig(): ?RevisionModelConfig public function getModel(): ModelInterface { - if (!isset($this->model)) { - throw new \InvalidArgumentException('Setting a `ModelInterface` is imperative to use the RevisionService'); - } - return $this->model; } From 3cce5e420ef678dceefaf6729123a68b6dbd9414 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 30 Sep 2022 16:07:30 -0400 Subject: [PATCH 70/92] fix(revisions): fix remaining revisionService renaming --- .../Admin/Action/Object/RevertRevisionAction.php | 10 +++++----- .../src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php | 8 ++++---- .../Widget/FormGroup/ObjectRevisionsFormGroup.php | 8 ++++---- .../src/Charcoal/Admin/Widget/FormSidebarWidget.php | 10 +++++----- .../Charcoal/Admin/Widget/ObjectRevisionsWidget.php | 8 ++++---- .../src/Charcoal/Object/GenerateRevisionListener.php | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php index 8709efb22..5bb035f48 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/RevertRevisionAction.php @@ -46,13 +46,13 @@ class RevertRevisionAction extends AdminAction implements ObjectContainerInterfa */ protected $revNum; - private RevisionsManager $revisionService; + private RevisionsManager $revisionManager; protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container->get('revisions/manager'); + $this->revisionManager = $container->get('revisions/manager'); } /** @@ -119,9 +119,9 @@ public function run(RequestInterface $request, ResponseInterface $response) $obj = $this->obj(); $revNum = $this->revNum(); - $this->revisionService->setModel($obj); + $this->revisionManager->setModel($obj); - $revision = $this->revisionService->getRevisionFromNumber($revNum); + $revision = $this->revisionManager->getRevisionFromNumber($revNum); if (!$revision['id']) { $this->setSuccess(false); @@ -141,7 +141,7 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(404); } - $result = $this->revisionService->revertToRevision($revNum); + $result = $this->revisionManager->revertToRevision($revNum); if ($result) { $doneMessage = $translator->translate( diff --git a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php index dd38525b8..f03edf9fe 100644 --- a/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php +++ b/packages/admin/src/Charcoal/Admin/Ui/ObjectRevisionsTrait.php @@ -25,9 +25,9 @@ public function objectRevisions(): array $obj = $this->modelFactory()->create($this->objType()); $obj->setId($this->objId()); - $this->revisionService()->setModel($obj); + $this->revisionManager()->setModel($obj); - $lastRevision = $this->revisionService()->getLatestRevision(); + $lastRevision = $this->revisionManager()->getLatestRevision(); $propLabel = '%2$s'; $callback = function (ObjectRevisionInterface &$revision) use ($lastRevision, $obj, $propLabel) { @@ -70,7 +70,7 @@ public function objectRevisions(): array $revision->allowRevert = ($lastRevision['revNum'] !== $revision['revNum']); }; - return $this->revisionService()->getAllRevisions($callback); + return $this->revisionManager()->getAllRevisions($callback); } /** @@ -92,5 +92,5 @@ abstract public function objId(); */ abstract protected function modelFactory(); - abstract protected function revisionService(): RevisionsManager; + abstract protected function revisionManager(): RevisionsManager; } diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php index 55bc1a978..9c1309345 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormGroup/ObjectRevisionsFormGroup.php @@ -38,11 +38,11 @@ class ObjectRevisionsFormGroup extends AbstractFormGroup implements */ public $widgetId; - private RevisionsManager $revisionService; + private RevisionsManager $revisionManager; - protected function revisionService(): RevisionsManager + protected function revisionManager(): RevisionsManager { - return $this->revisionService; + return $this->revisionManager; } /** @@ -117,7 +117,7 @@ protected function setDependencies(Container $container) parent::setDependencies($container); $this->setModelFactory($container['model/factory']); - $this->revisionService = $container['revisions/manager']; + $this->revisionManager = $container['revisions/manager']; $this->objType = $container['request']->getParam('obj_type'); $this->objId = $container['request']->getParam('obj_id'); diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index e2bdd4faf..bec6645f5 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -163,7 +163,7 @@ class FormSidebarWidget extends AdminWidget implements */ private $requiredGlobalAclPermissions = []; - private RevisionsManager $revisionService; + private RevisionsManager $revisionManager; /** * @param array|ArrayInterface $data Class data. @@ -554,10 +554,10 @@ public function isObjRevisionable() return $this->isObjRevisionable; } - $this->revisionService->setModel($obj); + $this->revisionManager->setModel($obj); - if ($this->revisionService->isRevisionEnabled()) { - $this->isObjRevisionable = !!count($this->revisionService->getAllRevisions()); + if ($this->revisionManager->isRevisionEnabled()) { + $this->isObjRevisionable = !!count($this->revisionManager->getAllRevisions()); } } } @@ -903,6 +903,6 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container['revisions/manager']; + $this->revisionManager = $container['revisions/manager']; } } diff --git a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php index 8b52e753e..e8299def0 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/ObjectRevisionsWidget.php @@ -29,18 +29,18 @@ class ObjectRevisionsWidget extends AdminWidget implements */ protected $objId; - private RevisionsManager $revisionService; + private RevisionsManager $revisionManager; protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container['revisions/manager']; + $this->revisionManager = $container['revisions/manager']; } - protected function revisionService(): RevisionsManager + protected function revisionManager(): RevisionsManager { - return $this->revisionService; + return $this->revisionManager; } /** diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php index 7abed4340..aaaf64bdf 100644 --- a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -11,20 +11,20 @@ */ class GenerateRevisionListener extends AbstractEventListener { - protected RevisionsManager $revisionService; + protected RevisionsManager $revisionManager; public function __invoke(object $event) { /** @var ModelInterface $model */ $model = $event->getObject(); - $this->revisionService->setModel($model)->generateRevision(); + $this->revisionManager->setModel($model)->generateRevision(); } public function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionService = $container->get('revisions/manager'); + $this->revisionManager = $container->get('revisions/manager'); } } From 6915a219260e438c1b7df3acf31bbbceb0cdb981 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 1 Nov 2022 15:32:45 -0400 Subject: [PATCH 71/92] refactor(event): add StoppableEventTrait and refactor Event with it --- packages/event/src/Charcoal/Event/Event.php | 21 +--------- .../Charcoal/Event/StoppableEventTrait.php | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 packages/event/src/Charcoal/Event/StoppableEventTrait.php diff --git a/packages/event/src/Charcoal/Event/Event.php b/packages/event/src/Charcoal/Event/Event.php index 30099eedd..0b86986e1 100644 --- a/packages/event/src/Charcoal/Event/Event.php +++ b/packages/event/src/Charcoal/Event/Event.php @@ -9,24 +9,5 @@ */ class Event implements StoppableEventInterface { - private bool $propagationStopped = false; - - /** - * @inheritDoc - */ - public function isPropagationStopped(): bool - { - return !!$this->propagationStopped; - } - - /** - * Stop the propagation of the event to further listeners. - * The remainder of the subscribed listeners won't be dispatched - * - * @return void - */ - protected function stopPropagation() - { - $this->propagationStopped = true; - } + use StoppableEventTrait; } diff --git a/packages/event/src/Charcoal/Event/StoppableEventTrait.php b/packages/event/src/Charcoal/Event/StoppableEventTrait.php new file mode 100644 index 000000000..6e6717da3 --- /dev/null +++ b/packages/event/src/Charcoal/Event/StoppableEventTrait.php @@ -0,0 +1,38 @@ +propagationStopped; + } + + /** + * Stop the propagation of the event to further listeners. + * The remainder of the subscribed listeners won't be dispatched + * + * @return void + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } +} From de71935f0c93dcca8d9aa28d3b0f1d2c9b13227d Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 1 Nov 2022 15:34:30 -0400 Subject: [PATCH 72/92] feat(event): add interruptable contract for events which provides events with a mean to interrupt the event chain and provide a reason for it --- .../Event/InterruptableEventInterface.php | 19 ++++++++ .../Event/InterruptableEventTrait.php | 43 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 packages/event/src/Charcoal/Event/InterruptableEventInterface.php create mode 100644 packages/event/src/Charcoal/Event/InterruptableEventTrait.php diff --git a/packages/event/src/Charcoal/Event/InterruptableEventInterface.php b/packages/event/src/Charcoal/Event/InterruptableEventInterface.php new file mode 100644 index 000000000..fbb4cfa16 --- /dev/null +++ b/packages/event/src/Charcoal/Event/InterruptableEventInterface.php @@ -0,0 +1,19 @@ +reason = $reason; + + $this->interrupted = true; + } + + public function isInterrupted(): bool + { + return $this->interrupted; + } + + /** + * @return string|Stringable + */ + public function reason() + { + return $this->reason; + } +} From 2d6f1321be36eb833f76b992d6e1ec0b3491ae08 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 1 Nov 2022 15:35:59 -0400 Subject: [PATCH 73/92] refactor(event): move model related events to the `core` package --- .../src/Charcoal/Model/Events/AbstractModelEvent.php} | 8 ++++++-- packages/core/src/Charcoal/Model/Events/WasSaved.php | 10 ++++++++++ packages/core/src/Charcoal/Model/Events/WasUpdated.php | 10 ++++++++++ packages/core/src/Charcoal/Model/Events/WillSave.php | 10 ++++++++++ packages/core/src/Charcoal/Model/Events/WillUpdate.php | 10 ++++++++++ .../src/Charcoal/Event/Events/Object/WasSaved.php | 10 ---------- .../Charcoal/Event/Events/Object/WasSavedOrUpdated.php | 10 ---------- .../src/Charcoal/Event/Events/Object/WasUpdated.php | 10 ---------- .../src/Charcoal/Event/Events/Object/WillSave.php | 10 ---------- .../Charcoal/Event/Events/Object/WillSaveOrUpdate.php | 10 ---------- .../src/Charcoal/Event/Events/Object/WillUpdate.php | 10 ---------- 11 files changed, 46 insertions(+), 62 deletions(-) rename packages/{event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php => core/src/Charcoal/Model/Events/AbstractModelEvent.php} (65%) create mode 100644 packages/core/src/Charcoal/Model/Events/WasSaved.php create mode 100644 packages/core/src/Charcoal/Model/Events/WasUpdated.php create mode 100644 packages/core/src/Charcoal/Model/Events/WillSave.php create mode 100644 packages/core/src/Charcoal/Model/Events/WillUpdate.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasSaved.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasSavedOrUpdated.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WasUpdated.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillSave.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillSaveOrUpdate.php delete mode 100644 packages/event/src/Charcoal/Event/Events/Object/WillUpdate.php diff --git a/packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php b/packages/core/src/Charcoal/Model/Events/AbstractModelEvent.php similarity index 65% rename from packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php rename to packages/core/src/Charcoal/Model/Events/AbstractModelEvent.php index 17812261e..6e3f19864 100644 --- a/packages/event/src/Charcoal/Event/Events/Object/AbstractObjectEvent.php +++ b/packages/core/src/Charcoal/Model/Events/AbstractModelEvent.php @@ -1,15 +1,19 @@ Date: Tue, 1 Nov 2022 15:37:31 -0400 Subject: [PATCH 74/92] refactor(event): move model events dispatch from actions to StorableTrait --- .../Admin/Action/Object/SaveAction.php | 19 ------------- .../Admin/Action/Object/UpdateAction.php | 19 ------------- .../src/Charcoal/Source/StorableTrait.php | 28 ++++++++++++++----- 3 files changed, 21 insertions(+), 45 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php index 24d5a5121..435360624 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php @@ -16,12 +16,6 @@ use Charcoal\Property\DescribablePropertyInterface; // From 'charcoal-object' use Charcoal\Object\AuthorableInterface; -// From 'charcoal-event' -use Charcoal\Event\EventDispatcherTrait; -use Charcoal\Event\Events\Object\WasSaved; -use Charcoal\Event\Events\Object\WasSavedOrUpdated; -use Charcoal\Event\Events\Object\WillSave; -use Charcoal\Event\Events\Object\WillSaveOrUpdate; /** * Action: Create an object and insert into storage. @@ -44,8 +38,6 @@ */ class SaveAction extends AbstractSaveAction { - use EventDispatcherTrait; - /** * Data for the target model. * @@ -217,12 +209,8 @@ public function run(RequestInterface $request, ResponseInterface $response) } } - $this->dispatchEvents([new WillSave($obj), new WillSaveOrUpdate($obj)]); - $result = $obj->save(); - $this->dispatchEvents([new WasSaved($obj), new WasSavedOrUpdated($obj)]); - if ($result) { $this->setObj($obj); @@ -269,11 +257,4 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(500); } } - - protected function setDependencies(Container $container) - { - parent::setDependencies($container); - - $this->setEventDispatcher($container['admin/event/dispatcher']); - } } diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php index 2e8f231d6..53da97f63 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php @@ -11,12 +11,6 @@ use Charcoal\Model\ModelValidator; // From 'charcoal-object' use Charcoal\Object\AuthorableInterface; -// From 'charcoal-event' -use Charcoal\Event\EventDispatcherTrait; -use Charcoal\Event\Events\Object\WasSavedOrUpdated; -use Charcoal\Event\Events\Object\WasUpdated; -use Charcoal\Event\Events\Object\WillSaveOrUpdate; -use Charcoal\Event\Events\Object\WillUpdate; /** * Action: Save an object and update copy in storage. @@ -38,8 +32,6 @@ */ class UpdateAction extends AbstractSaveAction { - use EventDispatcherTrait; - /** * Data for the target model. * @@ -192,12 +184,8 @@ public function run(RequestInterface $request, ResponseInterface $response) $obj->setLastModifiedBy($this->getAuthorIdent()); } - $this->dispatchEvents([new WillUpdate($obj), new WillSaveOrUpdate($obj)]); - $result = $obj->update(); - $this->dispatchEvents([new WasUpdated($obj), new WasSavedOrUpdated($obj)]); - if ($result) { $this->addFeedback('success', $this->translator()->translate('Object has been successfully updated.')); $this->addFeedback('success', strtr($this->translator()->translate('Updated Object: {{ objId }}'), [ @@ -223,11 +211,4 @@ public function run(RequestInterface $request, ResponseInterface $response) return $response->withStatus(500); } } - - protected function setDependencies(Container $container) - { - parent::setDependencies($container); - - $this->setEventDispatcher($container['admin/event/dispatcher']); - } } diff --git a/packages/core/src/Charcoal/Source/StorableTrait.php b/packages/core/src/Charcoal/Source/StorableTrait.php index 6cd5df751..632b2c484 100644 --- a/packages/core/src/Charcoal/Source/StorableTrait.php +++ b/packages/core/src/Charcoal/Source/StorableTrait.php @@ -2,10 +2,14 @@ namespace Charcoal\Source; -use RuntimeException; -use InvalidArgumentException; -// From 'charcoal-factory' +use Charcoal\App\Facade\Event; use Charcoal\Factory\FactoryInterface; +use Charcoal\Model\Events\WasSaved; +use Charcoal\Model\Events\WasUpdated; +use Charcoal\Model\Events\WillSave; +use Charcoal\Model\Events\WillUpdate; +use InvalidArgumentException; +use RuntimeException; /** * Provides an object with storage interaction. @@ -223,8 +227,9 @@ final public function loadFromQuery($query, array $binds = []) */ final public function save() { + $event = Event::dispatch(new WillSave($this)); $pre = $this->preSave(); - if ($pre === false) { + if ($pre === false || $event->isInterrupted()) { $this->logger->error(sprintf( 'Can not save object "%s:%s"; cancelled by %s::preSave()', $this->objType(), @@ -247,8 +252,9 @@ final public function save() $this->setId($ret); } + $event = Event::dispatch(new WasSaved($this)); $post = $this->postSave(); - if ($post === false) { + if ($post === false || $event->isInterrupted()) { $this->logger->error(sprintf( 'Saved object "%s:%s" but %s::postSave() failed', $this->objType(), @@ -269,8 +275,12 @@ final public function save() */ final public function update(array $keys = null) { + /** @var WillUpdate $event */ + $event = Event::dispatch(new WillUpdate($this)); + + // TODO: remove call to preUpdate $pre = $this->preUpdate($keys); - if ($pre === false) { + if ($pre === false || $event->isInterrupted()) { $this->logger->error(sprintf( 'Can not update object "%s:%s"; cancelled by %s::preUpdate()', $this->objType(), @@ -291,8 +301,12 @@ final public function update(array $keys = null) return false; } + /** @var WasUpdated $event */ + $event = Event::dispatch(new WasUpdated($this)); + + // TODO: remove call to postUpdate $post = $this->postUpdate($keys); - if ($post === false) { + if ($post === false || $event->isInterrupted()) { $this->logger->warning(sprintf( 'Updated object "%s:%s" but %s::postUpdate() failed', $this->objType(), From 7d7c47ec36cba9a06f244beb639954c5161c429d Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Thu, 3 Nov 2022 11:08:24 -0400 Subject: [PATCH 75/92] docs(revision): add some documentation for the reformed revision system --- packages/object/README.md | 105 +++++++++++++----- .../Charcoal/Object/RevisionModelConfig.php | 2 +- 2 files changed, 79 insertions(+), 28 deletions(-) diff --git a/packages/object/README.md b/packages/object/README.md index 8b1447fc9..f90feb529 100644 --- a/packages/object/README.md +++ b/packages/object/README.md @@ -16,7 +16,7 @@ Object definition (Content and UserData), behaviors and tools. - [Category](#category) - [Hierarchical](#hierarchical) - [Publishable](#publishable) - - [Revisionable](#revisionable) + - [Revisionable](#revision-manager) - [Routable](#routable) - [Helpers](#helpers) - [ObjectDraft](#objectdraft) @@ -128,7 +128,6 @@ The **UserData** class should be used for all objects that are expected to be en - [Category](#category) - [Hierarchical](#hierarchical) - [Publishable](#publishable) -- [Revisionable](#revisionable) - [Routable](#routable) ### Archivable @@ -225,26 +224,78 @@ _The archivable behavior is not yet documented. It is still under heavy developm > Default metadata is defined in `metadata/charcoal/object/publishable-interface.json`. -### Revisionable +### Revision Manager -Revisionable objects implement `\Charcoal\Object\Revision\RevisionableInterface`, which can be easily implemented by using `\Charcoal\Object\Revision\RevisionableTrait`. +The Revision Manager is a service that handles every related tasks with keeping revisions of objects implementing `\Charcoal\Model\ModelInterface`. -Revisionable objects create _revisions_ which logs the changes between an object's versions, as _diffs_. +The manager creates _revisions_ which logs the changes between an object's versions, as _diffs_. + +The `\Charcoal\Object\Listener` is a listener available to map a Model to a revision generation. **API** -- `setRevisionEnabled(bool$enabled)` - `revisionEnabled()` - `revisionObject()` - `generateRevision()` - `latestRevision()` -- `revisionNum(integer $revNum)` +- `revisionForNumber(integer $revNum)` - `allRevisions(callable $callback = null)` - `revertToRevision(integer $revNum)` -**Properties (metadata)** +**USAGE** + +```PHP +$revisionMangager->setModel($model)->generateRevision(); +``` + +The revision manager also looks for a configuration in the app config keyed `revisions`. +This config gives projects control over the revision system like disabling the revisions or specifying what models to enable +revisions for. The following example can be used to enable revisions for all content models in a project: + +```JSON +{ + "revisions": { + "enabled": true, + "excludedProperties": [ + "created", + "lastModified", + "createdBy", + "lastModifiedBy", + "active", + "locked", + "requiredAclPermissions", + "position" + ], + "models": { + "Charcoal\\Object\\Content": {} + } + } +} +``` + +**Config options** + +| Key | Description | Type | Default Value | +|--------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------|----------------------------------| +| **enabled** | Enable or not the revisions. | `bool` | `true` | +| **revisionClass** | Change the revision object class. | `string` | `Charcoal\Object\ObjectRevision` | +| **limitPerModel** (TODO) | Define a limit of revisions per model. | `int|null` | `null` | +| **models** | Specify which models to enable revisions for and and what options to apply. See next table. | `object` | `[]` | +| **excludedProperties** | Exclude properties from the revision process. | `string[]` | `[]` | + + +**Models options** + +| Key | Description | Type | Default Value | +|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|----------------------------------| +| **enabled** | Enable or not the revisions for the model. Important to note that class inheritance is respected, meaning enabling revisions for a high order model will also enable revisions for it's children. | `bool` | `true` | +| **revisionClass** | Change the revision object class for a specific model and it's children. | `string` | `Charcoal\Object\ObjectRevision` | +| **limitPerModel** (TODO) | Define a limit of revisions per model. | `int|null` | `null` | +| **properties** | Limits the revision process to only these properties, disregarding property exclusions and inclusions. | `string[]` | `[]` | +| **excludedProperties** | Exclude properties from the revision process. | `string[]` | `[]` | +| **includedProperties** | Include properties in the revision process. By default, all properties are included, so this can be used to include a property that was excluded by a parent. | `string[]` | `[]` | + -_The revisionable behavior does not implement any properties as all logic & data is self-contained in the revisions._ ### Routable @@ -262,16 +313,16 @@ Upon every `update` in _storage_, a revisionable object creates a new *revision* **Revision properties** -| Property | Type | Default | Description | -| ------------------ | ------------ | ---------- | ----------- | -| **target_type** | `string` | `null` | The object type of the target object. -| **target_id** | `string` | `null` | The object idenfiier of the target object. -| **rev_num** | `integer` | `null` | Revision number, (auto-generated). -| **ref_ts** | `date-time` | | -| **rev_user** | `string` | `null` | -| **data_prev** | `structure` | | -| **data_obj** | `structure` | | -| **data_diff** | `structure` | | +| Property | Type | Default | Description | +|-----------------|-------------|---------|--------------------------------------------| +| **target_type** | `string` | `null` | The object type of the target object. | +| **target_id** | `string` | `null` | The object idenfiier of the target object. | +| **rev_num** | `integer` | `null` | Revision number, (auto-generated). | +| **ref_ts** | `date-time` | | | +| **rev_user** | `string` | `null` | | +| **data_prev** | `structure` | | | +| **data_obj** | `structure` | | | +| **data_diff** | `structure` | | | **Revision methods** @@ -286,14 +337,14 @@ It is possible, (typically from the charcoal admin backend), to create *schedule **Schedule properties** -| Property | Type | Default | Description | -| ------------------ | ------------ | ---------- | ----------- | -| **target_type** | `string` | `null` | The object type of the target object. -| **target_id** | `string` | `null` | The object idenfiier of the target object. -| **scheduled_date** | `date-time` | `null` | -| **data_diff** | `structure` | `[]` | -| **processed** | `boolean` | `false` | -| **processed_date** | +| Property | Type | Default | Description | +|--------------------|-------------|---------|--------------------------------------------| +| **target_type** | `string` | `null` | The object type of the target object. | +| **target_id** | `string` | `null` | The object idenfiier of the target object. | +| **scheduled_date** | `date-time` | `null` | | +| **data_diff** | `structure` | `[]` | | +| **processed** | `boolean` | `false` | | +| **processed_date** | | | | **Schedule methods (API)** diff --git a/packages/object/src/Charcoal/Object/RevisionModelConfig.php b/packages/object/src/Charcoal/Object/RevisionModelConfig.php index d7c1c41ac..25c268301 100644 --- a/packages/object/src/Charcoal/Object/RevisionModelConfig.php +++ b/packages/object/src/Charcoal/Object/RevisionModelConfig.php @@ -22,7 +22,7 @@ class RevisionModelConfig extends AbstractConfig protected array $properties = []; // Exclude properties from the revision process. protected array $excludedProperties = []; - // Include properties from the revision process. By default, all properties are included, so this can be used to + // Include properties in the revision process. By default, all properties are included, so this can be used to // include a property that was excluded by a parent. protected array $includedProperties = []; From ad160f441976d6a43315ddeb15c6ac9c674eb062 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 11:40:15 -0400 Subject: [PATCH 76/92] Apply suggestions from code review Co-authored-by: Chauncey McAskill --- .../src/Charcoal/Event/InterruptableEventInterface.php | 2 +- .../object/src/Charcoal/Object/GenerateRevisionListener.php | 2 +- packages/object/src/Charcoal/Object/RevisionsManager.php | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/event/src/Charcoal/Event/InterruptableEventInterface.php b/packages/event/src/Charcoal/Event/InterruptableEventInterface.php index fbb4cfa16..3a46c4427 100644 --- a/packages/event/src/Charcoal/Event/InterruptableEventInterface.php +++ b/packages/event/src/Charcoal/Event/InterruptableEventInterface.php @@ -15,5 +15,5 @@ public function isInterrupted(): bool; /** * @return string|Stringable */ - public function reason(); + public function getReasonForInterruption(); } diff --git a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php index aaaf64bdf..ba2a744fc 100644 --- a/packages/object/src/Charcoal/Object/GenerateRevisionListener.php +++ b/packages/object/src/Charcoal/Object/GenerateRevisionListener.php @@ -7,7 +7,7 @@ use Pimple\Container; /** - * Listener + * Event Listener: Generates a revision for the event's related model. */ class GenerateRevisionListener extends AbstractEventListener { diff --git a/packages/object/src/Charcoal/Object/RevisionsManager.php b/packages/object/src/Charcoal/Object/RevisionsManager.php index d6bc5e61d..4d6d81938 100644 --- a/packages/object/src/Charcoal/Object/RevisionsManager.php +++ b/packages/object/src/Charcoal/Object/RevisionsManager.php @@ -127,6 +127,9 @@ public function revertToRevision(int $number): bool return $model->update(); } + /** + * @return string[] + */ public function parseRevisionProperties(): array { $model = $this->getModel(); @@ -154,6 +157,9 @@ public function parseRevisionProperties(): array return $properties; } + /** + * @return class-string + */ public function getObjectRevisionClass(): string { $modelConfig = $this->getModelRevisionConfig(); From 5ad2122bd296bd177d00e7524b3147ce7f2beba8 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 1 Nov 2022 14:51:20 -0400 Subject: [PATCH 77/92] feat(proxies): implement static proxies system using the facade model - add a simple bootstrapping mechanic - add Facade - add Event Facade --- packages/app/src/Charcoal/App/App.php | 18 ++++ .../App/Bootstrap/RegisterFacades.php | 24 ++++++ .../app/src/Charcoal/App/Facade/Event.php | 29 +++++++ .../app/src/Charcoal/App/Facade/Facade.php | 84 +++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php create mode 100644 packages/app/src/Charcoal/App/Facade/Event.php create mode 100644 packages/app/src/Charcoal/App/Facade/Facade.php diff --git a/packages/app/src/Charcoal/App/App.php b/packages/app/src/Charcoal/App/App.php index 800e88fd9..f17a0b538 100644 --- a/packages/app/src/Charcoal/App/App.php +++ b/packages/app/src/Charcoal/App/App.php @@ -39,6 +39,15 @@ class App extends SlimApp implements */ private $routeManager; + /** + * List of bootstrap classes. + * + * @var string[] + */ + protected array $bootstrappers = [ + \Charcoal\App\Bootstrap\RegisterFacades::class + ]; + /** * Getter for creating/returning the unique instance of this class. * @@ -128,6 +137,8 @@ private function setup() $dotenv = Dotenv::createImmutable($config->basePath()); $dotenv->safeLoad(); + $this->bootstrap(); + // Setup routes $this->routeManager()->setupRoutes(); @@ -143,6 +154,13 @@ private function setup() $this->setupMiddlewares(); } + private function bootstrap() + { + foreach ($this->bootstrappers as $bootstrapper) { + (new $bootstrapper())->bootstrap($this); + } + } + /** * Retrieve (create, if necessary) the application's route manager. * diff --git a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php new file mode 100644 index 000000000..0ad3d5ec3 --- /dev/null +++ b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php @@ -0,0 +1,24 @@ +getContainer()[$key]; + return static::$resolvedServices[$key]; + } + + public static function clearResolvedContainerService(string $key) + { + unset(static::$resolvedServices[$key]); + } + + public static function clearResolvedContainerServices() + { + static::$resolvedServices = []; + } + + /** + * Handle dynamic, static calls to the object. + * + * @param string $method + * @param array $args + * @return mixed + * + * @throws RuntimeException + */ + public static function __callStatic(string $method, array $args = []) + { + $instance = static::getRoot(); + + if (! $instance) { + throw new RuntimeException(sprintf('The facade [%s]\'s root is not set.', get_called_class())); + } + + return $instance->$method(...$args); + } +} From 2f13dddac8abc853d6d5355d49bba337e789500a Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Thu, 3 Nov 2022 11:31:22 -0400 Subject: [PATCH 78/92] Apply suggestions from code review Co-authored-by: Chauncey McAskill --- .../app/src/Charcoal/App/Facade/Event.php | 9 ++++---- .../app/src/Charcoal/App/Facade/Facade.php | 23 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/app/src/Charcoal/App/Facade/Event.php b/packages/app/src/Charcoal/App/Facade/Event.php index ed0cea3ec..444e4adfb 100644 --- a/packages/app/src/Charcoal/App/Facade/Event.php +++ b/packages/app/src/Charcoal/App/Facade/Event.php @@ -7,10 +7,10 @@ use League\Event\ListenerSubscriber; /** - * Event Facade + * Facade: Event Dispatcher * - * Alias for the 'admin/event/dispatcher' container key. - * Give access to the admin event dispatcher. + * Alias for the 'admin/event/dispatcher' container service. + * Provides access to the admin event dispatcher. * * @method static object dispatch(object $event) * @method static void dispatchGeneratedEvents(EventGenerator $generator) @@ -18,7 +18,8 @@ * @method static void subscribeOnceTo(string $event, callable $listener, int $priority = ListenerPriority::NORMAL) * @method static void subscribeListenersFrom(ListenerSubscriber $subscriber) * - * @see \Charcoal\Event\EventDispatcher, \League\Event\EventDispatcher + * @see \Charcoal\Event\EventDispatcher + * @see \League\Event\EventDispatcher */ class Event extends Facade { diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index 804063333..9612c6b5f 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -13,6 +13,9 @@ class Facade { protected static App $app; + /** + * @var array + */ protected static array $resolvedServices = []; public static function setApp(App $app) @@ -29,20 +32,14 @@ public static function getRoot() } /** - * Get the container key the facade is providing alias for. - * - * @return string + * Get the container service key the facade is providing alias for. */ - protected static function getContainerKey(): string + protected static function getContainerServiceKey(): string { - throw new RuntimeException(sprintf('The facade [%s] is not providing a container key.', get_called_class())); + throw new RuntimeException(sprintf('The facade [%s] does not provide a container service key.', get_called_class())); } - /** - * @param $key - * @return mixed - */ - protected static function resolveContainerService($key) + protected static function resolveContainerService(string $key): object { if (isset(static::$resolvedServices[$key])) { return static::$resolvedServices[$key]; @@ -75,8 +72,10 @@ public static function __callStatic(string $method, array $args = []) { $instance = static::getRoot(); - if (! $instance) { - throw new RuntimeException(sprintf('The facade [%s]\'s root is not set.', get_called_class())); + if (!$instance) { + throw new RuntimeException( + sprintf('The facade [%s] root is not defined', get_called_class()) + ); } return $instance->$method(...$args); From bac35571d29d80232801c1cb044325b52c782c50 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Thu, 3 Nov 2022 11:43:50 -0400 Subject: [PATCH 79/92] refactor(facade): improve facade terminology --- .../App/Bootstrap/RegisterFacades.php | 4 +-- .../app/src/Charcoal/App/Facade/Event.php | 2 +- .../app/src/Charcoal/App/Facade/Facade.php | 34 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php index 0ad3d5ec3..e61910dd8 100644 --- a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php +++ b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php @@ -18,7 +18,7 @@ class RegisterFacades */ public function bootstrap(App $app) { - Facade::clearResolvedContainerServices(); - Facade::setApp($app); + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeApp($app); } } diff --git a/packages/app/src/Charcoal/App/Facade/Event.php b/packages/app/src/Charcoal/App/Facade/Event.php index 444e4adfb..d66251f34 100644 --- a/packages/app/src/Charcoal/App/Facade/Event.php +++ b/packages/app/src/Charcoal/App/Facade/Event.php @@ -23,7 +23,7 @@ */ class Event extends Facade { - protected static function getContainerKey(): string + protected static function getFacadeName(): string { return 'admin/event/dispatcher'; } diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index 9612c6b5f..bec931793 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -16,9 +16,9 @@ class Facade /** * @var array */ - protected static array $resolvedServices = []; + protected static array $resolvedInstances = []; - public static function setApp(App $app) + public static function setFacadeApp(App $app) { static::$app = $app; } @@ -26,37 +26,39 @@ public static function setApp(App $app) /** * @return mixed */ - public static function getRoot() + public static function getFacadeInstance() { - return static::resolveContainerService(static::getContainerKey()); + return static::resolveFacadeInstance(static::getFacadeName()); } /** * Get the container service key the facade is providing alias for. */ - protected static function getContainerServiceKey(): string + protected static function getFacadeName(): string { - throw new RuntimeException(sprintf('The facade [%s] does not provide a container service key.', get_called_class())); + throw new RuntimeException( + sprintf('The facade [%s] does not provide a container service key.', get_called_class()) + ); } - protected static function resolveContainerService(string $key): object + protected static function resolveFacadeInstance(string $key): object { - if (isset(static::$resolvedServices[$key])) { - return static::$resolvedServices[$key]; + if (isset(static::$resolvedInstances[$key])) { + return static::$resolvedInstances[$key]; } - static::$resolvedServices[$key] = static::$app->getContainer()[$key]; - return static::$resolvedServices[$key]; + static::$resolvedInstances[$key] = static::$app->getContainer()[$key]; + return static::$resolvedInstances[$key]; } - public static function clearResolvedContainerService(string $key) + public static function clearResolvedFacadeInstance(string $key) { - unset(static::$resolvedServices[$key]); + unset(static::$resolvedInstances[$key]); } - public static function clearResolvedContainerServices() + public static function clearResolvedFacadeInstances() { - static::$resolvedServices = []; + static::$resolvedInstances = []; } /** @@ -70,7 +72,7 @@ public static function clearResolvedContainerServices() */ public static function __callStatic(string $method, array $args = []) { - $instance = static::getRoot(); + $instance = static::getFacadeInstance(); if (!$instance) { throw new RuntimeException( From 7fa1eb0d9f25a4afddf5f8b4ebad533a1cab3ce8 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 10:56:39 -0400 Subject: [PATCH 80/92] Apply suggestions from code review Co-authored-by: Chauncey McAskill --- .../app/src/Charcoal/App/Bootstrap/RegisterFacades.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php index e61910dd8..c1c6f25c5 100644 --- a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php +++ b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php @@ -5,18 +5,12 @@ use Charcoal\App\App; use Charcoal\App\Facade\Facade; -/** - * Class RegisterFacades - */ class RegisterFacades { /** - * Bootstrap the charcoal application. - * - * @param App $app - * @return void + * Bootstrap the facades with the Charcoal application. */ - public function bootstrap(App $app) + public function bootstrap(App $app): void { Facade::clearResolvedFacadeInstances(); Facade::setFacadeApp($app); From df78a3b16f2d3cfa88afbb624273b0ffddfb177a Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 11:01:00 -0400 Subject: [PATCH 81/92] refactor(facade): make `facade` abstract --- packages/app/src/Charcoal/App/Facade/Facade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index bec931793..74dbe6938 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -10,7 +10,7 @@ * * The facade class acts as a shortcut to a container service. */ -class Facade +abstract class Facade { protected static App $app; /** From 773938a80bae72b3433d04912720ff626a0b176d Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 4 Nov 2022 11:09:24 -0400 Subject: [PATCH 82/92] refactor(facade): use the container as resolver instead of the passing the app itself --- .../app/src/Charcoal/App/Bootstrap/RegisterFacades.php | 2 +- packages/app/src/Charcoal/App/Facade/Facade.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php index c1c6f25c5..126c87a4b 100644 --- a/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php +++ b/packages/app/src/Charcoal/App/Bootstrap/RegisterFacades.php @@ -13,6 +13,6 @@ class RegisterFacades public function bootstrap(App $app): void { Facade::clearResolvedFacadeInstances(); - Facade::setFacadeApp($app); + Facade::setFacadeResolver($app->getContainer()); } } diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index 74dbe6938..af822aa26 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -2,7 +2,7 @@ namespace Charcoal\App\Facade; -use Charcoal\App\App; +use Psr\Container\ContainerInterface; use RuntimeException; /** @@ -12,15 +12,15 @@ */ abstract class Facade { - protected static App $app; + protected static ContainerInterface $resolver; /** * @var array */ protected static array $resolvedInstances = []; - public static function setFacadeApp(App $app) + public static function setFacadeResolver(ContainerInterface $resolver) { - static::$app = $app; + static::$resolver = $resolver; } /** @@ -47,7 +47,7 @@ protected static function resolveFacadeInstance(string $key): object return static::$resolvedInstances[$key]; } - static::$resolvedInstances[$key] = static::$app->getContainer()[$key]; + static::$resolvedInstances[$key] = static::$resolver[$key]; return static::$resolvedInstances[$key]; } From b48dd17a691caa8e044b8ec4c93b061d6296d8f0 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 8 Nov 2022 15:32:49 -0500 Subject: [PATCH 83/92] fix(event): fix missing renaming --- packages/event/src/Charcoal/Event/InterruptableEventTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/event/src/Charcoal/Event/InterruptableEventTrait.php b/packages/event/src/Charcoal/Event/InterruptableEventTrait.php index 1eac86847..bed190cc7 100644 --- a/packages/event/src/Charcoal/Event/InterruptableEventTrait.php +++ b/packages/event/src/Charcoal/Event/InterruptableEventTrait.php @@ -36,7 +36,7 @@ public function isInterrupted(): bool /** * @return string|Stringable */ - public function reason() + public function getReasonForInterruption() { return $this->reason; } From eb3699b6be9f99c9afda1b4b7a8ab93ce9a3e83e Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 8 Nov 2022 15:37:08 -0500 Subject: [PATCH 84/92] fix(event): rename event services container keys to be prefixed with `app/` --- .../Admin/Action/ElfinderConnectorAction.php | 2 +- .../ServiceProvider/AdminServiceProvider.php | 44 ------------------- .../app/src/Charcoal/App/Facade/Event.php | 4 +- .../ServiceProvider/EventServiceProvider.php | 38 ++++++++++++++++ 4 files changed, 41 insertions(+), 47 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index d744a4956..efc1397cd 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -914,7 +914,7 @@ public function setDependencies(Container $container) $this->filesystemConfig = $container['filesystem/config']; $this->filesystems = $container['filesystems']; - $this->setEventDispatcher($container['admin/event/dispatcher']); + $this->setEventDispatcher($container['app/event/dispatcher']); } /** diff --git a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php index 814fabc7e..cc9ad9e20 100644 --- a/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php +++ b/packages/admin/src/Charcoal/Admin/ServiceProvider/AdminServiceProvider.php @@ -4,8 +4,6 @@ // From Pimple use Charcoal\Admin\AssetsConfig; -use Charcoal\Event\EventDispatcher; -use Charcoal\Event\EventDispatcherBuilder; use Pimple\Container; use Pimple\ServiceProviderInterface; use Assetic\Asset\AssetReference; @@ -85,7 +83,6 @@ public function register(Container $container) $this->registerAuthExtensions($container); $this->registerViewExtensions($container); $this->registerAssetsManager($container); - $this->registerAdminEventDispatcher($container); // Register Access-Control-List (acl) $container->register(new AclServiceProvider()); @@ -551,45 +548,4 @@ protected function registerFactoryServices(Container $container) ]); }; } - - /** - * @param Container $container Pimple DI container. - * @return void - */ - protected function registerAdminEventDispatcher(Container $container) - { - /** - * @param Container $container - * @return array - */ - $container['admin/event/listeners'] = function (Container $container): array { - return ($container['admin/config']->get('events.listeners') ?? []); - }; - - /** - * Subscribers are classes that implements `\League\Event\ListenerSubscriber` - * It allows to subscribe many grouped listeners at once. - * - * @param Container $container - * @return array - */ - $container['admin/event/subscribers'] = function (Container $container): array { - return ($container['admin/config'] ->get('events.subscribers') ?? []); - }; - - /** - * Build an event dispatcher using admin config. - * - * @param Container $container - * @return EventDispatcher - */ - $container['admin/event/dispatcher'] = function (Container $container): EventDispatcher { - /** @var EventDispatcherBuilder $eventDispatcherBuilder */ - $eventDispatcherBuilder = $container['event/dispatcher/builder']; - return $eventDispatcherBuilder->build( - $container['admin/event/listeners'], - $container['admin/event/subscribers'] - ); - }; - } } diff --git a/packages/app/src/Charcoal/App/Facade/Event.php b/packages/app/src/Charcoal/App/Facade/Event.php index d66251f34..475140e5f 100644 --- a/packages/app/src/Charcoal/App/Facade/Event.php +++ b/packages/app/src/Charcoal/App/Facade/Event.php @@ -9,7 +9,7 @@ /** * Facade: Event Dispatcher * - * Alias for the 'admin/event/dispatcher' container service. + * Alias for the 'app/event/dispatcher' container service. * Provides access to the admin event dispatcher. * * @method static object dispatch(object $event) @@ -25,6 +25,6 @@ class Event extends Facade { protected static function getFacadeName(): string { - return 'admin/event/dispatcher'; + return 'app/event/dispatcher'; } } diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index 6f4c49e03..0776917f8 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -2,6 +2,7 @@ namespace Charcoal\Event\ServiceProvider; +use Charcoal\Event\EventDispatcher; use Charcoal\Event\EventDispatcherBuilder; use Charcoal\Event\EventListenerInterface; use Charcoal\Factory\FactoryInterface; @@ -83,5 +84,42 @@ public function register(Container $container) } ]); }; + + // The App event services + // ========================================================================== + + /** + * @param Container $container + * @return array + */ + $container['app/event/listeners'] = function (Container $container): array { + return ($container['admin/config']->get('events.listeners') ?? []); + }; + + /** + * Subscribers are classes that implements `\League\Event\ListenerSubscriber` + * It allows to subscribe many grouped listeners at once. + * + * @param Container $container + * @return array + */ + $container['app/event/subscribers'] = function (Container $container): array { + return ($container['admin/config'] ->get('events.subscribers') ?? []); + }; + + /** + * Build an event dispatcher using admin config. + * + * @param Container $container + * @return EventDispatcher + */ + $container['app/event/dispatcher'] = function (Container $container): EventDispatcher { + /** @var EventDispatcherBuilder $eventDispatcherBuilder */ + $eventDispatcherBuilder = $container['event/dispatcher/builder']; + return $eventDispatcherBuilder->build( + $container['app/event/listeners'], + $container['app/event/subscribers'] + ); + }; } } From e1e57d35889ea8d7f551b8522484045ae84aa743 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Tue, 8 Nov 2022 15:51:45 -0500 Subject: [PATCH 85/92] chore(composer): update composer lock file --- composer.lock | 192 ++++++++++++++++++++++++++------------------------ 1 file changed, 100 insertions(+), 92 deletions(-) diff --git a/composer.lock b/composer.lock index 635598c7c..9308f1af3 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": "3bc44ca00298e3f4add3926db355a4a3", + "content-hash": "e0a55d1d0b9ecf249aa8e3d364e574d4", "packages": [ { "name": "barryvdh/elfinder-flysystem-driver", @@ -392,16 +392,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.4.1", + "version": "2.4.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379" + "reference": "67c26b443f348a51926030c83481b85718457d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/69568e4293f4fa993f3b0e51c9723e1e17c41379", - "reference": "69568e4293f4fa993f3b0e51c9723e1e17c41379", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d", + "reference": "67c26b443f348a51926030c83481b85718457d3d", "shasum": "" }, "require": { @@ -491,7 +491,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.4.1" + "source": "https://github.com/guzzle/psr7/tree/2.4.3" }, "funding": [ { @@ -507,7 +507,7 @@ "type": "tidelift" } ], - "time": "2022-08-28T14:45:39+00:00" + "time": "2022-10-26T14:07:24+00:00" }, { "name": "intervention/image", @@ -804,16 +804,16 @@ }, { "name": "league/event", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/thephpleague/event.git", - "reference": "6d6d88d3c398f4e32995fccd4ec50a5bdaef131b" + "reference": "221867a61087ee265ca07bd39aa757879afca820" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/event/zipball/6d6d88d3c398f4e32995fccd4ec50a5bdaef131b", - "reference": "6d6d88d3c398f4e32995fccd4ec50a5bdaef131b", + "url": "https://api.github.com/repos/thephpleague/event/zipball/221867a61087ee265ca07bd39aa757879afca820", + "reference": "221867a61087ee265ca07bd39aa757879afca820", "shasum": "" }, "require": { @@ -857,22 +857,22 @@ ], "support": { "issues": "https://github.com/thephpleague/event/issues", - "source": "https://github.com/thephpleague/event/tree/3.0.0" + "source": "https://github.com/thephpleague/event/tree/3.0.2" }, - "time": "2020-09-29T17:42:28+00:00" + "time": "2022-10-29T09:31:25+00:00" }, { "name": "league/flysystem", - "version": "1.1.9", + "version": "1.1.10", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "094defdb4a7001845300334e7c1ee2335925ef99" + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/094defdb4a7001845300334e7c1ee2335925ef99", - "reference": "094defdb4a7001845300334e7c1ee2335925ef99", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1", + "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1", "shasum": "" }, "require": { @@ -945,7 +945,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/1.1.9" + "source": "https://github.com/thephpleague/flysystem/tree/1.1.10" }, "funding": [ { @@ -953,7 +953,7 @@ "type": "other" } ], - "time": "2021-12-09T09:40:50+00:00" + "time": "2022-10-04T09:16:37+00:00" }, { "name": "league/flysystem-cached-adapter", @@ -1252,16 +1252,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.6.4", + "version": "v6.6.5", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b" + "reference": "8b6386d7417526d1ea4da9edb70b8352f7543627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b", - "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/8b6386d7417526d1ea4da9edb70b8352f7543627", + "reference": "8b6386d7417526d1ea4da9edb70b8352f7543627", "shasum": "" }, "require": { @@ -1285,8 +1285,8 @@ "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", "league/oauth2-google": "Needed for Google XOAUTH2 authentication", "psr/log": "For optional PSR-3 debug logging", - "stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)" + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" }, "type": "library", "autoload": { @@ -1318,7 +1318,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.5" }, "funding": [ { @@ -1326,7 +1326,7 @@ "type": "github" } ], - "time": "2022-08-22T09:22:00+00:00" + "time": "2022-10-07T12:23:10+00:00" }, { "name": "phpoption/phpoption", @@ -2667,16 +2667,16 @@ }, { "name": "vlucas/phpdotenv", - "version": "v5.4.1", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", - "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", + "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", "shasum": "" }, "require": { @@ -2691,15 +2691,19 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.4.1", "ext-filter": "*", - "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" }, "suggest": { "ext-filter": "Required to use the boolean validator." }, "type": "library", "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": true + }, "branch-alias": { - "dev-master": "5.4-dev" + "dev-master": "5.5-dev" } }, "autoload": { @@ -2731,7 +2735,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" }, "funding": [ { @@ -2743,7 +2747,7 @@ "type": "tidelift" } ], - "time": "2021-12-12T23:22:04+00:00" + "time": "2022-10-16T01:01:54+00:00" } ], "packages-dev": [ @@ -2799,16 +2803,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.235.12", + "version": "3.241.0", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "e0ebecfb0284dc44dc99a172cd9c68c40739904c" + "reference": "9a08ac83249a2e6d07c624802cbf961f7269a691" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e0ebecfb0284dc44dc99a172cd9c68c40739904c", - "reference": "e0ebecfb0284dc44dc99a172cd9c68c40739904c", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9a08ac83249a2e6d07c624802cbf961f7269a691", + "reference": "9a08ac83249a2e6d07c624802cbf961f7269a691", "shasum": "" }, "require": { @@ -2887,9 +2891,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.235.12" + "source": "https://github.com/aws/aws-sdk-php/tree/3.241.0" }, - "time": "2022-09-20T18:18:07+00:00" + "time": "2022-11-08T19:16:53+00:00" }, { "name": "cache/adapter-common", @@ -4031,16 +4035,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.38", + "version": "2.0.39", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd" + "reference": "f3a0e2b715c40cf1fd270d444901b63311725d63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/b03536539f43a4f9aa33c4f0b2f3a1c752088fcd", - "reference": "b03536539f43a4f9aa33c4f0b2f3a1c752088fcd", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/f3a0e2b715c40cf1fd270d444901b63311725d63", + "reference": "f3a0e2b715c40cf1fd270d444901b63311725d63", "shasum": "" }, "require": { @@ -4121,7 +4125,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/2.0.38" + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.39" }, "funding": [ { @@ -4137,20 +4141,20 @@ "type": "tidelift" } ], - "time": "2022-09-02T17:04:26+00:00" + "time": "2022-10-24T10:49:03+00:00" }, { "name": "phpstan/phpstan", - "version": "1.8.5", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20" + "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6598a5ff12ca4499a836815e08b4d77a2ddeb20", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", + "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", "shasum": "" }, "require": { @@ -4180,7 +4184,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.5" + "source": "https://github.com/phpstan/phpstan/tree/1.9.1" }, "funding": [ { @@ -4196,20 +4200,20 @@ "type": "tidelift" } ], - "time": "2022-09-07T16:05:32+00:00" + "time": "2022-11-04T13:35:59+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.17", + "version": "9.2.18", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8" + "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8", - "reference": "aa94dc41e8661fe90c7316849907cba3007b10d8", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/12fddc491826940cf9b7e88ad9664cf51f0f6d0a", + "reference": "12fddc491826940cf9b7e88ad9664cf51f0f6d0a", "shasum": "" }, "require": { @@ -4265,7 +4269,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.18" }, "funding": [ { @@ -4273,7 +4277,7 @@ "type": "github" } ], - "time": "2022-08-30T12:24:04+00:00" + "time": "2022-10-27T13:35:33+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4518,16 +4522,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.24", + "version": "9.5.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5" + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", - "reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/851867efcbb6a1b992ec515c71cdcf20d895e9d2", + "reference": "851867efcbb6a1b992ec515c71cdcf20d895e9d2", "shasum": "" }, "require": { @@ -4549,14 +4553,14 @@ "phpunit/php-timer": "^5.0.2", "sebastian/cli-parser": "^1.0.1", "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.5", + "sebastian/comparator": "^4.0.8", "sebastian/diff": "^4.0.3", "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.3", + "sebastian/exporter": "^4.0.5", "sebastian/global-state": "^5.0.1", "sebastian/object-enumerator": "^4.0.3", "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.1", + "sebastian/type": "^3.2", "sebastian/version": "^3.0.2" }, "suggest": { @@ -4600,7 +4604,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.26" }, "funding": [ { @@ -4610,9 +4614,13 @@ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-08-30T07:42:16+00:00" + "time": "2022-10-28T06:00:21+00:00" }, { "name": "psr/simple-cache", @@ -5766,16 +5774,16 @@ }, { "name": "symfony/console", - "version": "v4.4.45", + "version": "v4.4.48", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "28b77970939500fb04180166a1f716e75a871ef8" + "reference": "8e70c1cab07ac641b885ce80385b9824a293c623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/28b77970939500fb04180166a1f716e75a871ef8", - "reference": "28b77970939500fb04180166a1f716e75a871ef8", + "url": "https://api.github.com/repos/symfony/console/zipball/8e70c1cab07ac641b885ce80385b9824a293c623", + "reference": "8e70c1cab07ac641b885ce80385b9824a293c623", "shasum": "" }, "require": { @@ -5836,7 +5844,7 @@ "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/console/tree/v4.4.45" + "source": "https://github.com/symfony/console/tree/v4.4.48" }, "funding": [ { @@ -5852,20 +5860,20 @@ "type": "tidelift" } ], - "time": "2022-08-17T14:50:19+00:00" + "time": "2022-10-26T16:02:45+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.12", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2d67c1f9a1937406a9be3171b4b22250c0a11447" + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2d67c1f9a1937406a9be3171b4b22250c0a11447", - "reference": "2d67c1f9a1937406a9be3171b4b22250c0a11447", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", "shasum": "" }, "require": { @@ -5900,7 +5908,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.12" + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" }, "funding": [ { @@ -5916,7 +5924,7 @@ "type": "tidelift" } ], - "time": "2022-08-02T13:48:16+00:00" + "time": "2022-09-21T19:53:16+00:00" }, { "name": "symfony/polyfill-php73", @@ -6161,16 +6169,16 @@ }, { "name": "symfony/stopwatch", - "version": "v5.4.5", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "4d04b5c24f3c9a1a168a131f6cbe297155bc0d30" + "reference": "6df7a3effde34d81717bbef4591e5ffe32226d69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/4d04b5c24f3c9a1a168a131f6cbe297155bc0d30", - "reference": "4d04b5c24f3c9a1a168a131f6cbe297155bc0d30", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/6df7a3effde34d81717bbef4591e5ffe32226d69", + "reference": "6df7a3effde34d81717bbef4591e5ffe32226d69", "shasum": "" }, "require": { @@ -6203,7 +6211,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.5" + "source": "https://github.com/symfony/stopwatch/tree/v5.4.13" }, "funding": [ { @@ -6219,7 +6227,7 @@ "type": "tidelift" } ], - "time": "2022-02-18T16:06:09+00:00" + "time": "2022-09-28T13:19:49+00:00" }, { "name": "symfony/yaml", @@ -6390,16 +6398,16 @@ }, { "name": "twig/twig", - "version": "v3.4.2", + "version": "v3.4.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077" + "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", - "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58", + "reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58", "shasum": "" }, "require": { @@ -6450,7 +6458,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.4.2" + "source": "https://github.com/twigphp/Twig/tree/v3.4.3" }, "funding": [ { @@ -6462,7 +6470,7 @@ "type": "tidelift" } ], - "time": "2022-08-12T06:47:24+00:00" + "time": "2022-09-28T08:42:51+00:00" } ], "aliases": [], From 4e3eecd7357b663f0d197fc82141cafdcd0d439c Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 11 Nov 2022 15:03:24 -0500 Subject: [PATCH 86/92] tests(app): fixing phpunit tests failures caused be refactoring revisions, event and facade --- .../Admin/Widget/FormSidebarWidget.php | 6 +- .../Charcoal/Admin/Action/LoginActionTest.php | 1 + .../Charcoal/Admin/ContainerProvider.php | 8 +++ .../app/src/Charcoal/App/Facade/Facade.php | 4 +- .../Cms/ContainerIntegrationTrait.php | 4 ++ .../CoreContainerIntegrationTrait.php | 4 ++ .../tests/Charcoal/CoreContainerProvider.php | 2 + .../tests/Charcoal/Mock/BadStorableMock.php | 55 ++++------------- .../Charcoal/Source/StorableTraitTest.php | 60 +++++++++++-------- packages/email/tests/bootstrap.php | 4 ++ .../ServiceProvider/EventServiceProvider.php | 13 +++- .../EventServiceProviderTest.php | 12 ---- .../tests/Charcoal/Object/ContentTest.php | 3 - .../tests/Charcoal/Object/ObjectRouteTest.php | 5 ++ .../Property/ContainerIntegrationTrait.php | 6 ++ 15 files changed, 99 insertions(+), 88 deletions(-) delete mode 100644 packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php diff --git a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php index bec6645f5..02941c3c1 100644 --- a/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php +++ b/packages/admin/src/Charcoal/Admin/Widget/FormSidebarWidget.php @@ -163,7 +163,7 @@ class FormSidebarWidget extends AdminWidget implements */ private $requiredGlobalAclPermissions = []; - private RevisionsManager $revisionManager; + private ?RevisionsManager $revisionManager; /** * @param array|ArrayInterface $data Class data. @@ -549,7 +549,7 @@ public function isObjRevisionable() $this->isObjRevisionable = false; } else { $obj = $this->form()->obj(); - if (!$obj->id()) { + if (!$obj->id() || $this->revisionManager === null) { $this->isObjRevisionable = false; return $this->isObjRevisionable; } @@ -903,6 +903,6 @@ protected function setDependencies(Container $container) { parent::setDependencies($container); - $this->revisionManager = $container['revisions/manager']; + $this->revisionManager = ($container['revisions/manager'] ?? null); } } diff --git a/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php b/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php index 8c814c806..0e0449d25 100644 --- a/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php +++ b/packages/admin/tests/Charcoal/Admin/Action/LoginActionTest.php @@ -2,6 +2,7 @@ namespace Charcoal\Tests\Admin\Action; +use Charcoal\App\Facade\Facade; use PDO; use ReflectionClass; diff --git a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php index dd0f8b434..05c4dda44 100644 --- a/packages/admin/tests/Charcoal/Admin/ContainerProvider.php +++ b/packages/admin/tests/Charcoal/Admin/ContainerProvider.php @@ -2,6 +2,7 @@ namespace Charcoal\Tests\Admin; +use Charcoal\App\Facade\Facade; use PDO; // From Mockery @@ -92,6 +93,9 @@ public function registerDebug(Container $container) */ public function registerBaseServices(Container $container) { + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); + $this->registerDebug($container); $this->registerConfig($container); $this->registerDatabase($container); @@ -390,6 +394,7 @@ public function registerCache(Container $container) public function registerEvent(Container $container) { $container['event/dispatcher'] = new EventDispatcher(); + $container['app/event/dispatcher'] = $container['event/dispatcher']; } /** @@ -554,6 +559,9 @@ public function registerEmailFactory(Container $container) */ public function registerActionDependencies(Container $container) { + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); + $this->registerDebug($container); $this->registerLogger($container); $this->registerDatabase($container); diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index af822aa26..196405442 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -12,13 +12,13 @@ */ abstract class Facade { - protected static ContainerInterface $resolver; + protected static \ArrayAccess $resolver; /** * @var array */ protected static array $resolvedInstances = []; - public static function setFacadeResolver(ContainerInterface $resolver) + public static function setFacadeResolver(\ArrayAccess $resolver) { static::$resolver = $resolver; } diff --git a/packages/cms/tests/Charcoal/Cms/ContainerIntegrationTrait.php b/packages/cms/tests/Charcoal/Cms/ContainerIntegrationTrait.php index 46ee1cedd..1880f36c9 100644 --- a/packages/cms/tests/Charcoal/Cms/ContainerIntegrationTrait.php +++ b/packages/cms/tests/Charcoal/Cms/ContainerIntegrationTrait.php @@ -6,6 +6,7 @@ use Charcoal\App\AppContainer as Container; // From 'charcoal-cms/tests' +use Charcoal\App\Facade\Facade; use Charcoal\Tests\Cms\ContainerProvider; /** @@ -109,5 +110,8 @@ private function setupContainer() $this->container = $container; $this->containerProvider = $provider; + + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); } } diff --git a/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php b/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php index 57ce03768..dd4c5b46d 100644 --- a/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php +++ b/packages/core/tests/Charcoal/CoreContainerIntegrationTrait.php @@ -3,6 +3,7 @@ namespace Charcoal\Tests; // From Pimple +use Charcoal\App\Facade\Facade; use Pimple\Container; // From 'charcoal-core/tests' @@ -68,5 +69,8 @@ private function setupContainer() $this->container = $container; $this->containerProvider = $provider; + + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); } } diff --git a/packages/core/tests/Charcoal/CoreContainerProvider.php b/packages/core/tests/Charcoal/CoreContainerProvider.php index 6de374c33..be057796d 100644 --- a/packages/core/tests/Charcoal/CoreContainerProvider.php +++ b/packages/core/tests/Charcoal/CoreContainerProvider.php @@ -2,6 +2,7 @@ namespace Charcoal\Tests; +use Charcoal\Event\ServiceProvider\EventServiceProvider; use PDO; // From PSR-3 @@ -52,6 +53,7 @@ public function registerBaseServices(Container $container) $this->registerSource($container); $this->registerLogger($container); $this->registerCache($container); + $container->register(new EventServiceProvider()); } /** diff --git a/packages/core/tests/Charcoal/Mock/BadStorableMock.php b/packages/core/tests/Charcoal/Mock/BadStorableMock.php index 40765d358..5cd154c1e 100644 --- a/packages/core/tests/Charcoal/Mock/BadStorableMock.php +++ b/packages/core/tests/Charcoal/Mock/BadStorableMock.php @@ -9,48 +9,19 @@ /** * */ -class BadStorableMock extends StorableMock +class BadStorableMock extends GenericModel { - const FAIL_AFTER = false; - const FAIL_BEFORE = true; + private bool $failAfter = false; + private bool $failBefore = false; - /** - * Whether to fail before or after an event. - * - * @var boolean - */ - private $fail = self::FAIL_BEFORE; - - /** - * Create new storable mock. - * - * @param boolean $fail TRUE to fail on pre-event, FALSE to fail on post-event. - */ - public function __construct($fail = self::FAIL_BEFORE) + public function failBefore() { - $this->fail = (bool)$fail; - - parent::__construct(); + $this->failBefore = true; } - /** - * Create new storable mock to fail on before events. - * - * @return static - */ - public static function createToFailBefore() - { - return new self(self::FAIL_BEFORE); - } - - /** - * Create new storable mock to fail on after events. - * - * @return static - */ - public static function createToFailAfter() + public function failAfter() { - return new self(self::FAIL_AFTER); + $this->failAfter = true; } /** @@ -61,7 +32,7 @@ public static function createToFailAfter() */ protected function preSave() { - return $this->fail; + return $this->failBefore; } /** @@ -72,7 +43,7 @@ protected function preSave() */ protected function postSave() { - return !$this->fail; + return $this->failAfter; } /** @@ -84,7 +55,7 @@ protected function postSave() */ protected function preUpdate(array $keys = null) { - return $this->fail; + return $this->failBefore; } /** @@ -96,7 +67,7 @@ protected function preUpdate(array $keys = null) */ protected function postUpdate(array $keys = null) { - return !$this->fail; + return $this->failAfter; } /** @@ -107,7 +78,7 @@ protected function postUpdate(array $keys = null) */ protected function preDelete() { - return $this->fail; + return $this->failBefore; } /** @@ -118,6 +89,6 @@ protected function preDelete() */ protected function postDelete() { - return !$this->fail; + return $this->failAfter; } } diff --git a/packages/core/tests/Charcoal/Source/StorableTraitTest.php b/packages/core/tests/Charcoal/Source/StorableTraitTest.php index 83ae6750d..998054ee4 100644 --- a/packages/core/tests/Charcoal/Source/StorableTraitTest.php +++ b/packages/core/tests/Charcoal/Source/StorableTraitTest.php @@ -2,6 +2,9 @@ namespace Charcoal\Tests\Source; +use Charcoal\Model\Service\MetadataLoader; +use Charcoal\Model\Service\ModelLoaderBuilder; +use Charcoal\Tests\Mock\GenericModel; use InvalidArgumentException; use RuntimeException; @@ -27,12 +30,13 @@ */ class StorableTraitTest extends AbstractTestCase { + use \Charcoal\Tests\CoreContainerIntegrationTrait; use ReflectionsTrait; /** * The tested class. * - * @var StorableMock + * @var GenericModel */ public $obj; @@ -43,7 +47,17 @@ class StorableTraitTest extends AbstractTestCase */ protected function setUp(): void { - $this->obj = new StorableMock(); + $container = $this->getContainer(); + + $this->factory = $container['model/factory']; + $this->obj = $this->factory->get(GenericModel::class); + + $source = $this->obj->source(); + if (!$source->tableExists()) { + $source->createTable(); + } + + // $this->obj = new StorableMock(); } /** @@ -210,7 +224,8 @@ public function testSourceFactory() public function testMissingSourceFactory() { $this->expectException(RuntimeException::class); - $this->callMethod($this->obj, 'sourceFactory'); + $obj = new StorableMock(); + $this->callMethod($obj, 'sourceFactory'); } /** @@ -233,24 +248,19 @@ public function testSource() { $obj = $this->obj; - /** 1. Default state is NULL */ - $this->assertNull($this->getPropertyValue($obj, 'source')); - - /** 2. Create repository if state is NULL */ + /** 1. Create repository if state is NULL */ $src1 = $obj->source(); $this->assertInstanceOf(SourceInterface::class, $src1); - $this->assertSame($src1, $this->getPropertyValue($obj, 'source')); - /** 3. Mutated state */ + /** 2. Mutated state */ $src2 = $this->createSource(); $that = $obj->setSource($src2); $this->assertSame($src2, $obj->source()); - $this->assertSame($src2, $this->getPropertyValue($obj, 'source')); - /** 4. Storable can create a repository */ + /** 3. Storable can create a repository */ $this->assertInstanceOf(SourceInterface::class, $this->callMethod($obj, 'createSource')); - /** 5. Chainable */ + /** 4. Chainable */ $this->assertSame($that, $obj); } @@ -278,13 +288,13 @@ public function testSave() $this->assertTrue($obj->save()); /** 2. Fail Early */ - $obj = BadStorableMock::createToFailBefore(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->failBefore(); $this->assertFalse($obj->save()); /** 3. Fail Early */ - $obj = BadStorableMock::createToFailAfter(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->failAfter(); $this->assertFalse($obj->save()); } @@ -312,13 +322,13 @@ public function testUpdate() $this->assertTrue($obj->update()); /** 2. Fail Early */ - $obj = BadStorableMock::createToFailBefore(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->failBefore(); $this->assertFalse($obj->update()); /** 3. Fail Early */ - $obj = BadStorableMock::createToFailAfter(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->failAfter(); $this->assertFalse($obj->update()); } @@ -346,13 +356,15 @@ public function testDelete() $this->assertTrue($obj->delete()); /** 2. Fail Early */ - $obj = BadStorableMock::createToFailBefore(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->setId('123'); + $obj->failBefore(); $this->assertFalse($obj->delete()); /** 3. Fail Early */ - $obj = BadStorableMock::createToFailAfter(); - $obj->setSource($src); + $obj = $this->factory->create(BadStorableMock::class); + $obj->setId('123'); + $obj->failAfter(); $this->assertFalse($obj->delete()); } } diff --git a/packages/email/tests/bootstrap.php b/packages/email/tests/bootstrap.php index 5f58b07cf..a3869fca4 100644 --- a/packages/email/tests/bootstrap.php +++ b/packages/email/tests/bootstrap.php @@ -2,6 +2,7 @@ use Charcoal\App\AppConfig; use Charcoal\App\AppContainer; +use Charcoal\App\Facade\Facade; use Charcoal\Config\GenericConfig; if (($_ENV['TEST_MODE'] ?? '') === 'PACKAGE') { @@ -39,3 +40,6 @@ $GLOBALS['container'] = new AppContainer([ 'config' => $config ]); + +Facade::clearResolvedFacadeInstances(); +Facade::setFacadeResolver($GLOBALS['container']); diff --git a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php index 0776917f8..c4b38a699 100644 --- a/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php +++ b/packages/event/src/Charcoal/Event/ServiceProvider/EventServiceProvider.php @@ -23,7 +23,7 @@ class EventServiceProvider implements ServiceProviderInterface public function register(Container $container) { /** - * @param Container $container A service container. + * @param Container $container A service container. * @return EventDispatcherBuilder */ $container['event/dispatcher/builder'] = function (Container $container) { @@ -93,6 +93,10 @@ public function register(Container $container) * @return array */ $container['app/event/listeners'] = function (Container $container): array { + if (!$container->offsetExists('admin/config')) { + return []; + } + return ($container['admin/config']->get('events.listeners') ?? []); }; @@ -104,7 +108,11 @@ public function register(Container $container) * @return array */ $container['app/event/subscribers'] = function (Container $container): array { - return ($container['admin/config'] ->get('events.subscribers') ?? []); + if (!$container->offsetExists('admin/config')) { + return []; + } + + return ($container['admin/config']->get('events.subscribers') ?? []); }; /** @@ -116,6 +124,7 @@ public function register(Container $container) $container['app/event/dispatcher'] = function (Container $container): EventDispatcher { /** @var EventDispatcherBuilder $eventDispatcherBuilder */ $eventDispatcherBuilder = $container['event/dispatcher/builder']; + return $eventDispatcherBuilder->build( $container['app/event/listeners'], $container['app/event/subscribers'] diff --git a/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php b/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php deleted file mode 100644 index 3207eb676..000000000 --- a/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php +++ /dev/null @@ -1,12 +0,0 @@ -assertNull($this->obj['createdBy']); $this->assertNull($this->obj['lastModifiedBy']); - - // Revisionable properties - $this->assertTrue($this->obj['revisionEnabled']); } /** diff --git a/packages/object/tests/Charcoal/Object/ObjectRouteTest.php b/packages/object/tests/Charcoal/Object/ObjectRouteTest.php index 9451fe128..d3c168cd6 100644 --- a/packages/object/tests/Charcoal/Object/ObjectRouteTest.php +++ b/packages/object/tests/Charcoal/Object/ObjectRouteTest.php @@ -2,6 +2,8 @@ namespace Charcoal\Tests\Object; +use Charcoal\App\Facade\Facade; +use Charcoal\Event\ServiceProvider\EventServiceProvider; use DateTime; // From Pimple @@ -170,8 +172,11 @@ private function container() $containerProvider->registerBaseServices($container); $containerProvider->registerModelFactory($container); $containerProvider->registerModelCollectionLoader($container); + $container->register(new EventServiceProvider()); $this->container = $container; + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); } return $this->container; diff --git a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php index 980467f17..df3ee878b 100644 --- a/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php +++ b/packages/property/tests/Charcoal/Property/ContainerIntegrationTrait.php @@ -3,6 +3,8 @@ namespace Charcoal\Tests\Property; // From Pimple +use Charcoal\App\Facade\Facade; +use Charcoal\Event\ServiceProvider\EventServiceProvider; use Pimple\Container; // From 'charcoal-property/tests' use Charcoal\Tests\Property\ContainerProvider; @@ -64,8 +66,12 @@ private function setupContainer() $provider->registerPropertyFactory($container); $provider->registerModelFactory($container); $provider->registerModelCollectionLoader($container); + $container->register(new EventServiceProvider()); $this->container = $container; $this->containerProvider = $provider; + + Facade::clearResolvedFacadeInstances(); + Facade::setFacadeResolver($container); } } From d20153edffcdb5ae63fec0431a8a64d3ee3c26c1 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 11 Nov 2022 15:13:32 -0500 Subject: [PATCH 87/92] chore(composer): update composer lock file --- composer.lock | 96 +++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/composer.lock b/composer.lock index 9308f1af3..ddfc35471 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": "e0a55d1d0b9ecf249aa8e3d364e574d4", + "content-hash": "dfb33ddbf6308b7c1c3f715d971f9c6a", "packages": [ { "name": "barryvdh/elfinder-flysystem-driver", @@ -2200,16 +2200,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", - "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -2224,7 +2224,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2262,7 +2262,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -2278,20 +2278,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", - "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -2306,7 +2306,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2345,7 +2345,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -2361,20 +2361,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", - "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -2383,7 +2383,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2428,7 +2428,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -2444,7 +2444,7 @@ "type": "tidelift" } ], - "time": "2022-05-10T07:21:04+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", @@ -2803,16 +2803,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.241.0", + "version": "3.242.1", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "9a08ac83249a2e6d07c624802cbf961f7269a691" + "reference": "9bfd85f696fff6a9b7810f1361751ad33a9b23d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9a08ac83249a2e6d07c624802cbf961f7269a691", - "reference": "9a08ac83249a2e6d07c624802cbf961f7269a691", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9bfd85f696fff6a9b7810f1361751ad33a9b23d1", + "reference": "9bfd85f696fff6a9b7810f1361751ad33a9b23d1", "shasum": "" }, "require": { @@ -2891,9 +2891,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.241.0" + "source": "https://github.com/aws/aws-sdk-php/tree/3.242.1" }, - "time": "2022-11-08T19:16:53+00:00" + "time": "2022-11-11T19:59:24+00:00" }, { "name": "cache/adapter-common", @@ -4145,16 +4145,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.9.1", + "version": "1.9.2", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f" + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", - "reference": "a59c8b5bfd4a236f27efc8b5ce72c313c2b54b5f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d6fdf01c53978b6429f1393ba4afeca39cc68afa", + "reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa", "shasum": "" }, "require": { @@ -4184,7 +4184,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.1" + "source": "https://github.com/phpstan/phpstan/tree/1.9.2" }, "funding": [ { @@ -4200,7 +4200,7 @@ "type": "tidelift" } ], - "time": "2022-11-04T13:35:59+00:00" + "time": "2022-11-10T09:56:11+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5928,16 +5928,16 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", - "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/9e8ecb5f92152187c4799efd3c96b78ccab18ff9", + "reference": "9e8ecb5f92152187c4799efd3c96b78ccab18ff9", "shasum": "" }, "require": { @@ -5946,7 +5946,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5987,7 +5987,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.27.0" }, "funding": [ { @@ -6003,20 +6003,20 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.26.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1" + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1", - "reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", + "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", "shasum": "" }, "require": { @@ -6025,7 +6025,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.26-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -6066,7 +6066,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" }, "funding": [ { @@ -6082,7 +6082,7 @@ "type": "tidelift" } ], - "time": "2022-05-24T11:49:31+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/service-contracts", From cc2d8c8b01407f52afb1141aa542b1b0c17d7511 Mon Sep 17 00:00:00 2001 From: Joel Alphonso Date: Fri, 11 Nov 2022 15:17:54 -0500 Subject: [PATCH 88/92] tests(event): add test placeholder --- .../ServiceProvider/EventServiceProviderTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php diff --git a/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php b/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php new file mode 100644 index 000000000..3207eb676 --- /dev/null +++ b/packages/event/tests/Charcoal/Event/ServiceProvider/EventServiceProviderTest.php @@ -0,0 +1,12 @@ + Date: Sat, 12 Nov 2022 13:57:14 -0500 Subject: [PATCH 89/92] refactor(facade): Improve type-hinting Changed: - Replaced ArrayAccess with Pimple Container as resolver for stricter binding and future transition to PSR-11. - Added missing types `void` and `object` to various methods. - Moved `RuntimeException` if instance is not an object from `__callStatic()` to `resolveFacadeInstance` --- .../app/src/Charcoal/App/Facade/Facade.php | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/app/src/Charcoal/App/Facade/Facade.php b/packages/app/src/Charcoal/App/Facade/Facade.php index 196405442..07b7bbb9f 100644 --- a/packages/app/src/Charcoal/App/Facade/Facade.php +++ b/packages/app/src/Charcoal/App/Facade/Facade.php @@ -2,7 +2,7 @@ namespace Charcoal\App\Facade; -use Psr\Container\ContainerInterface; +use Pimple\Container; use RuntimeException; /** @@ -12,21 +12,22 @@ */ abstract class Facade { - protected static \ArrayAccess $resolver; + protected static Container $resolver; + /** * @var array */ protected static array $resolvedInstances = []; - public static function setFacadeResolver(\ArrayAccess $resolver) + public static function setFacadeResolver(Container $resolver): void { static::$resolver = $resolver; } /** - * @return mixed + * @return object */ - public static function getFacadeInstance() + public static function getFacadeInstance(): object { return static::resolveFacadeInstance(static::getFacadeName()); } @@ -36,9 +37,10 @@ public static function getFacadeInstance() */ protected static function getFacadeName(): string { - throw new RuntimeException( - sprintf('The facade [%s] does not provide a container service key.', get_called_class()) - ); + throw new RuntimeException(sprintf( + 'The facade [%s] does not provide a container service key.', + get_called_class() + )); } protected static function resolveFacadeInstance(string $key): object @@ -47,16 +49,25 @@ protected static function resolveFacadeInstance(string $key): object return static::$resolvedInstances[$key]; } - static::$resolvedInstances[$key] = static::$resolver[$key]; + $instance = static::$resolver[$key]; + if (!is_object($instance)) { + throw new RuntimeException(sprintf( + 'The facade [%s] instance must be an object, received %s', + get_called_class(), + gettype($instance) + )); + } + + static::$resolvedInstances[$key] = $instance; return static::$resolvedInstances[$key]; } - public static function clearResolvedFacadeInstance(string $key) + public static function clearResolvedFacadeInstance(string $key): void { unset(static::$resolvedInstances[$key]); } - public static function clearResolvedFacadeInstances() + public static function clearResolvedFacadeInstances(): void { static::$resolvedInstances = []; } @@ -64,22 +75,12 @@ public static function clearResolvedFacadeInstances() /** * Handle dynamic, static calls to the object. * - * @param string $method - * @param array $args + * @param string $method + * @param mixed[] $args * @return mixed - * - * @throws RuntimeException */ public static function __callStatic(string $method, array $args = []) { - $instance = static::getFacadeInstance(); - - if (!$instance) { - throw new RuntimeException( - sprintf('The facade [%s] root is not defined', get_called_class()) - ); - } - - return $instance->$method(...$args); + return static::getFacadeInstance()->$method(...$args); } } From ff5e12e1e4b5ff4b964d7c50c971cf356a18de73 Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Sat, 12 Nov 2022 14:07:35 -0500 Subject: [PATCH 90/92] refactor(elfinder): Improve FileWasUploaded integration Moved anonymous function on "upload.presave" event to a method, `dispatchEventOnUploadPreSave`, for easier customization. --- .../Admin/Action/ElfinderConnectorAction.php | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php index efc1397cd..bf79dcb66 100644 --- a/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/ElfinderConnectorAction.php @@ -190,17 +190,13 @@ public function setupElfinder(array $extraOptions = []) define('ELFINDER_IMG_PARENT_URL', (string)$this->baseUrl(ElfinderTemplate::ELFINDER_ASSETS_REL_PATH)); } - $extraOptions = array_merge($extraOptions, ['bind' => [ - 'upload.presave' => [function (&$thash, &$name, $src) { - if (!$src || !file_exists($src)) { - return false; - } - - $this->getEventDispatcher()->dispatch(new FileWasUploaded($src)); - - return true; - }] - ]]); + $extraOptions = array_merge($extraOptions, [ + 'bind' => [ + 'upload.presave' => [ + ':dispatchEventOnUploadPreSave', + ], + ], + ]); $options = $this->buildConnectorOptions($extraOptions); @@ -684,6 +680,27 @@ protected function translateFilesystemName(string $ident): ?string return null; } + /** + * Dispatches an event on `upload.presave`. + * + * @param string $path The target path. + * @param string $name The target name. + * @param string $src The temporary file name. + * @param object $elfinder The elFinder instance. + * @param object $volume The current volume instance. + * @return void|bool|array + */ + public function dispatchEventOnUploadPreSave(&$path, &$name, $src, $elfinder, $volume) + { + if (!$src || !file_exists($src)) { + return false; + } + + $this->getEventDispatcher()->dispatch(new FileWasUploaded($src)); + + return true; + } + /** * Sanitizes a file name on `upload.presave`. * From 8f99c4ad6c29788b1bf2c39061ed0c81c4735446 Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Fri, 18 Nov 2022 16:51:20 -0500 Subject: [PATCH 91/92] Remove unused PHP imports Co-authored-by: Xavier Aymond --- packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php | 1 - packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php | 1 - packages/property/src/Charcoal/Property/ImageProperty.php | 1 - 3 files changed, 3 deletions(-) diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php index 435360624..6c189bace 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/SaveAction.php @@ -4,7 +4,6 @@ use Exception; use PDOException; -use Pimple\Container; // From PSR-7 use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php index 53da97f63..10b9a87ba 100644 --- a/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php +++ b/packages/admin/src/Charcoal/Admin/Action/Object/UpdateAction.php @@ -3,7 +3,6 @@ namespace Charcoal\Admin\Action\Object; use Exception; -use Pimple\Container; // From PSR-7 use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; diff --git a/packages/property/src/Charcoal/Property/ImageProperty.php b/packages/property/src/Charcoal/Property/ImageProperty.php index a588dc91e..970d30e0c 100644 --- a/packages/property/src/Charcoal/Property/ImageProperty.php +++ b/packages/property/src/Charcoal/Property/ImageProperty.php @@ -10,7 +10,6 @@ // From 'charccoal-translator' use Charcoal\Translator\Translation; // From 'charcoal-property' -use Charcoal\Property\Event\PropertyEvent; use Charcoal\Property\FileProperty; /** From 310eb8b5b69ddb40f957c17000d7248fa327bc3c Mon Sep 17 00:00:00 2001 From: Chauncey McAskill Date: Fri, 18 Nov 2022 16:57:21 -0500 Subject: [PATCH 92/92] Rename parameter name in RevisionServiceProvider Despite `$pimple` being the correct parameter name [1], using `$container` falls in line with all other occurrences in service providers and elsewhere. The inconsistent parameter can pose problems with PHP 8's named arguments and for static analysis tools like PHPStan and Psalm which will flag this as an error. This can be corrected in a future changeset that replaces all type-hints of Pimple's Container with the PSR-11 Container interface. [1]: https://github.com/silexphp/Pimple/blob/v3.5.0/src/Pimple/ServiceProviderInterface.php#L43 Co-authored-by: Xavier Aymond --- .../src/Charcoal/Object/RevisionServiceProvider.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/object/src/Charcoal/Object/RevisionServiceProvider.php b/packages/object/src/Charcoal/Object/RevisionServiceProvider.php index 38adc231c..df50c5e5d 100644 --- a/packages/object/src/Charcoal/Object/RevisionServiceProvider.php +++ b/packages/object/src/Charcoal/Object/RevisionServiceProvider.php @@ -11,10 +11,10 @@ */ class RevisionServiceProvider implements ServiceProviderInterface { - public function register(Container $pimple) + public function register(Container $container) { - $pimple['revisions/config'] = function (Container $pimple): RevisionsConfig { - $configData = $pimple['config']->get('revisions'); + $container['revisions/config'] = function (Container $container): RevisionsConfig { + $configData = $container['config']->get('revisions'); // If the config data is a boolean, it means we only want to affect the enabled state. if (is_bool($configData)) { @@ -26,8 +26,8 @@ public function register(Container $pimple) return new RevisionsConfig($configData); }; - $pimple['revisions/manager'] = function (Container $pimple): RevisionsManager { - $services = new ServiceLocator($pimple, [ + $container['revisions/manager'] = function (Container $container): RevisionsManager { + $services = new ServiceLocator($container, [ 'revisions/config', 'model/factory', 'logger'