From 7043f4e45e5a0d37e800493182888c753d641f3c Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 16:57:27 -0700 Subject: [PATCH 1/9] phpstorm - Enable 'scan-classes' --- tools/extensions/phpstorm/info.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/extensions/phpstorm/info.xml b/tools/extensions/phpstorm/info.xml index dc9f4ec61010..d1b1d4efd158 100644 --- a/tools/extensions/phpstorm/info.xml +++ b/tools/extensions/phpstorm/info.xml @@ -34,5 +34,6 @@ mgd-php@1.0.0 setting-php@1.0.0 smarty-v2@1.0.1 + scan-classes@1.0.0 From 6d39767e62c381ea1bd836f24e0e57eb378c0e4e Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 18:57:32 -0700 Subject: [PATCH 2/9] Use `hook_managed` to implement `civi.phpstorm.flush` --- tools/extensions/phpstorm/phpstorm.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/extensions/phpstorm/phpstorm.php b/tools/extensions/phpstorm/phpstorm.php index 320245c99cd2..1a8f9937bce5 100644 --- a/tools/extensions/phpstorm/phpstorm.php +++ b/tools/extensions/phpstorm/phpstorm.php @@ -34,6 +34,15 @@ function phpstorm_civicrm_container(\Symfony\Component\DependencyInjection\Conta $container->addCompilerPass(new \Civi\PhpStorm\PhpStormCompilePass(), PassConfig::TYPE_AFTER_REMOVING, 2000); } +function phpstorm_civicrm_managed(&$entities, $modules) { + // We don't currently have an event for extensions to join the "system flush" operation. Apply Skullduggery method. + // This gives a useful baseline event for most generators -- but it's _not_ for "services", and each generator may be supplemented + // by other events. + if ($modules === NULL && !defined('CIVICRM_TEST')) { + Civi::dispatcher()->dispatch('civi.phpstorm.flush'); + } +} + function phpstorm_civicrm_uninstall() { $dir = phpstorm_metadata_dir(); if (file_exists($dir)) { From 056ff7655bb1621cfa62d2dbcadb0860e1a2ad26 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 18:52:05 -0700 Subject: [PATCH 3/9] (NFC) phpstorm.php --- tools/extensions/phpstorm/phpstorm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/extensions/phpstorm/phpstorm.php b/tools/extensions/phpstorm/phpstorm.php index 1a8f9937bce5..fa27ce20e058 100644 --- a/tools/extensions/phpstorm/phpstorm.php +++ b/tools/extensions/phpstorm/phpstorm.php @@ -27,7 +27,7 @@ function phpstorm_metadata_dir(): string { * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/ */ function phpstorm_civicrm_config(&$config): void { - _phpstorm_civix_civicrm_config($config); + _phpstorm_civix_civicrm_config($config); } function phpstorm_civicrm_container(\Symfony\Component\DependencyInjection\ContainerBuilder $container) { From 3c30e721de6924ecd6548c0c1b863f029e622167 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 17:41:58 -0700 Subject: [PATCH 4/9] PhpStormMetadata - Define registerArgumentsSet() and addExpectedArguments() --- .../Civi/PhpStorm/PhpStormMetadata.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/PhpStormMetadata.php b/tools/extensions/phpstorm/Civi/PhpStorm/PhpStormMetadata.php index 854e8643eff0..260f0ebd2e4d 100644 --- a/tools/extensions/phpstorm/Civi/PhpStorm/PhpStormMetadata.php +++ b/tools/extensions/phpstorm/Civi/PhpStorm/PhpStormMetadata.php @@ -44,6 +44,30 @@ public function __construct(string $name, string $attribution) { $this->buffer = ''; } + public function registerArgumentsSet(string $name, ...$args) { + $escapedName = var_export($name, 1); + $escapedArgs = implode(', ', array_map(function($arg) { + return var_export($arg, 1); + }, $args)); + $this->buffer .= "registerArgumentsSet($escapedName, $escapedArgs);\n"; + return $this; + } + + /** + * @param string $for + * Ex: '\Civi\Core\SettingsBag::get()' + * @param int $index + * The positional offset among the arguments + * @param string $argumentSet + * Name of the argument set. (This should already be defined by `registerArgumentsSet()`.) + * @return $this + */ + public function addExpectedArguments(string $for, int $index, string $argumentSet) { + $escapedSet = var_export($argumentSet, 1); + $this->buffer .= "expectedArguments($for, $index, argumentsSet($escapedSet));\n"; + return $this; + } + /** * @param string $for * @param array $map From e20befbefc352ef1169cfe0841e7d10cebf1beb4 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 17:43:18 -0700 Subject: [PATCH 5/9] phpstorm - Generate hints about CiviCRM settings --- .../Civi/PhpStorm/SettingsGenerator.php | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tools/extensions/phpstorm/Civi/PhpStorm/SettingsGenerator.php diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/SettingsGenerator.php b/tools/extensions/phpstorm/Civi/PhpStorm/SettingsGenerator.php new file mode 100644 index 000000000000..fde4baa2be12 --- /dev/null +++ b/tools/extensions/phpstorm/Civi/PhpStorm/SettingsGenerator.php @@ -0,0 +1,28 @@ + 'generate']; + } + + public function generate() { + $metadata = \Civi\Core\SettingsMetadata::getMetadata(); + $methods = ['get', 'getDefault', 'getExplicit', 'getMandatory', 'hasExplicit', 'revert', 'set']; + $builder = new PhpStormMetadata('settings', __CLASS__); + $builder->registerArgumentsSet('settingNames', ...array_keys($metadata)); + foreach ($methods as $method) { + $builder->addExpectedArguments('\Civi\Core\SettingsBag::' . $method . '()', 0, 'settingNames'); + } + $builder->write(); + } + +} From a8d33773b9d56af2ca6a44cf6633c6c16b23c1dd Mon Sep 17 00:00:00 2001 From: Coleman Watts Date: Fri, 25 Aug 2023 19:14:23 -0700 Subject: [PATCH 6/9] phpstorm - Generate hints about civicrm_api4() --- .../phpstorm/Civi/PhpStorm/Api4Generator.php | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tools/extensions/phpstorm/Civi/PhpStorm/Api4Generator.php diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/Api4Generator.php b/tools/extensions/phpstorm/Civi/PhpStorm/Api4Generator.php new file mode 100644 index 000000000000..62a6f6598db9 --- /dev/null +++ b/tools/extensions/phpstorm/Civi/PhpStorm/Api4Generator.php @@ -0,0 +1,44 @@ + 'generate', + 'hook_civicrm_post::CustomGroup' => 'generate', + ]; + } + + public function generate() { + /* + * FIXME: PHPSTORM_META doesn't seem to support compound dynamic arguments + * so even if you give it separate lists like + * ``` + * expectedArguments(\civicrm_api4('Contact'), 1, 'a', 'b'); + * expectedArguments(\civicrm_api4('Case'), 1, 'c', 'd'); + * ``` + * It doesn't differentiate them and always offers a,b,c,d for every entity. + * If they ever fix that upstream we could fetch a different list of actions per entity, + * but for now there's no point. + */ + + $entities = \Civi\Api4\Entity::get(FALSE)->addSelect('name')->execute()->column('name'); + $actions = ['get', 'save', 'create', 'update', 'delete', 'replace', 'revert', 'export', 'autocomplete', 'getFields', 'getActions', 'checkAccess']; + + $builder = new PhpStormMetadata('api4', __CLASS__); + $builder->registerArgumentsSet('api4Entities', ...$entities); + $builder->registerArgumentsSet('api4Actions', ...$actions); + $builder->addExpectedArguments('\civicrm_api4()', 0, 'api4Entities'); + $builder->addExpectedArguments('\civicrm_api4()', 1, 'api4Actions'); + $builder->write(); + } + +} From c9380d75f8726aa69f32b1159330fb720a8f1c02 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 19:29:20 -0700 Subject: [PATCH 7/9] phpstorm - Generate hints about civicrm_api3() --- .../phpstorm/Civi/PhpStorm/Api3Generator.php | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tools/extensions/phpstorm/Civi/PhpStorm/Api3Generator.php diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/Api3Generator.php b/tools/extensions/phpstorm/Civi/PhpStorm/Api3Generator.php new file mode 100644 index 000000000000..777e3d19892e --- /dev/null +++ b/tools/extensions/phpstorm/Civi/PhpStorm/Api3Generator.php @@ -0,0 +1,43 @@ + 'generate', + ]; + } + + public function generate() { + /* + * FIXME: PHPSTORM_META doesn't seem to support compound dynamic arguments + * so even if you give it separate lists like + * ``` + * expectedArguments(\civicrm_api4('Contact'), 1, 'a', 'b'); + * expectedArguments(\civicrm_api4('Case'), 1, 'c', 'd'); + * ``` + * It doesn't differentiate them and always offers a,b,c,d for every entity. + * If they ever fix that upstream we could fetch a different list of actions per entity, + * but for now there's no point. + */ + + $entities = \civicrm_api3('entity', 'get', []); + $actions = ['create', 'delete', 'get', 'getactions', 'getcount', 'getfield', 'getfields', 'getlist', 'getoptions', 'getrefcount', 'getsingle', 'getunique', 'getvalue', 'replace', 'validate']; + + $builder = new PhpStormMetadata('api3', __CLASS__); + $builder->registerArgumentsSet('api3Entities', ...$entities['values']); + $builder->registerArgumentsSet('api3Actions', ...$actions); + $builder->addExpectedArguments('\civicrm_api3()', 0, 'api3Entities'); + $builder->addExpectedArguments('\civicrm_api3()', 1, 'api3Actions'); + $builder->write(); + } + +} From f816f7fba8498aaded98219ccb445ca542867827 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 19:44:59 -0700 Subject: [PATCH 8/9] phpstorm - Generate hints about Civi::dispatcher() --- .../phpstorm/Civi/PhpStorm/EventGenerator.php | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php b/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php new file mode 100644 index 000000000000..561e95ec968e --- /dev/null +++ b/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php @@ -0,0 +1,37 @@ + 'generate', + ]; + } + + public function generate() { + $inspector = new CiviEventInspector(); + + $builder = new PhpStormMetadata('events', __CLASS__); + $builder->registerArgumentsSet('events', ...array_keys($inspector->getAll())); + + foreach ([CiviEventDispatcher::class, CiviEventDispatcherInterface::class] as $class) { + foreach (['dispatch', 'addListener', 'removeListener', 'getListeners', 'hasListeners'] as $method) { + $builder->addExpectedArguments(sprintf("\\%s::%s()", $class, $method), 0, 'events'); + } + } + + $builder->write(); + } + +} From c2053dfb70d7a71fa50b274211761b0e04e698f2 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 19:59:34 -0700 Subject: [PATCH 9/9] phpstorm - Generate event hints for "hook_civicrm_pre::Entity" (and similar) --- .../phpstorm/Civi/PhpStorm/EventGenerator.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php b/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php index 561e95ec968e..81202ff2ffcb 100644 --- a/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php +++ b/tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php @@ -22,8 +22,21 @@ public static function getSubscribedEvents() { public function generate() { $inspector = new CiviEventInspector(); + $entities = \Civi\Api4\Entity::get(FALSE)->addSelect('name')->execute()->column('name'); + $specialEvents = ['hook_civicrm_post', 'hook_civicrm_pre', 'civi.api4.validate']; + foreach ($entities as $entity) { + foreach ($specialEvents as $specialEvent) { + $entityEvents [] = "$specialEvent::$entity"; + } + } + // PHP 7.4 can simplify: + // $entityEvents = array_map(fn($pair) => implode('::', $pair), \CRM_Utils_Array::product([$entities, $specialEvents])); + + + $all = array_merge(array_keys($inspector->getAll()), $entityEvents); + $builder = new PhpStormMetadata('events', __CLASS__); - $builder->registerArgumentsSet('events', ...array_keys($inspector->getAll())); + $builder->registerArgumentsSet('events', ...$all); foreach ([CiviEventDispatcher::class, CiviEventDispatcherInterface::class] as $class) { foreach (['dispatch', 'addListener', 'removeListener', 'getListeners', 'hasListeners'] as $method) {