Skip to content

Commit

Permalink
Merge pull request #27179 from totten/master-phpstorm-setting
Browse files Browse the repository at this point in the history
PhpStorm - Add a corybantic avalanche of type-hints
  • Loading branch information
seamuslee001 committed Aug 27, 2023
2 parents 9eca1f7 + c2053df commit 6821fd8
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 1 deletion.
43 changes: 43 additions & 0 deletions tools/extensions/phpstorm/Civi/PhpStorm/Api3Generator.php
@@ -0,0 +1,43 @@
<?php

namespace Civi\PhpStorm;

use Civi\Core\Service\AutoService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* @service civi.phpstorm.api3
*/
class Api3Generator extends AutoService implements EventSubscriberInterface {

public static function getSubscribedEvents() {
return [
'civi.phpstorm.flush' => '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();
}

}
44 changes: 44 additions & 0 deletions tools/extensions/phpstorm/Civi/PhpStorm/Api4Generator.php
@@ -0,0 +1,44 @@
<?php

namespace Civi\PhpStorm;

use Civi\Core\Service\AutoService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* @service civi.phpstorm.api4
*/
class Api4Generator extends AutoService implements EventSubscriberInterface {

public static function getSubscribedEvents() {
return [
'civi.phpstorm.flush' => '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();
}

}
50 changes: 50 additions & 0 deletions tools/extensions/phpstorm/Civi/PhpStorm/EventGenerator.php
@@ -0,0 +1,50 @@
<?php

namespace Civi\PhpStorm;

use Civi\Core\CiviEventDispatcher;
use Civi\Core\CiviEventDispatcherInterface;
use Civi\Core\CiviEventInspector;
use Civi\Core\Service\AutoService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* @service civi.phpstorm.event
*/
class EventGenerator extends AutoService implements EventSubscriberInterface {

public static function getSubscribedEvents() {
return [
'civi.phpstorm.flush' => 'generate',
];
}

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', ...$all);

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();
}

}
24 changes: 24 additions & 0 deletions tools/extensions/phpstorm/Civi/PhpStorm/PhpStormMetadata.php
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions tools/extensions/phpstorm/Civi/PhpStorm/SettingsGenerator.php
@@ -0,0 +1,28 @@
<?php

namespace Civi\PhpStorm;

use Civi\Core\Service\AutoService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* @service civi.phpstorm.settings
*/
class SettingsGenerator extends AutoService implements EventSubscriberInterface {

public static function getSubscribedEvents() {
return ['civi.phpstorm.flush' => '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();
}

}
1 change: 1 addition & 0 deletions tools/extensions/phpstorm/info.xml
Expand Up @@ -34,5 +34,6 @@
<mixin>mgd-php@1.0.0</mixin>
<mixin>setting-php@1.0.0</mixin>
<mixin>smarty-v2@1.0.1</mixin>
<mixin>scan-classes@1.0.0</mixin>
</mixins>
</extension>
11 changes: 10 additions & 1 deletion tools/extensions/phpstorm/phpstorm.php
Expand Up @@ -27,13 +27,22 @@ 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) {
$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)) {
Expand Down

0 comments on commit 6821fd8

Please sign in to comment.