From 282a504e1add7a513e9b61f38aa8ae6760cf675e Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Fri, 25 Aug 2023 04:37:21 -0700 Subject: [PATCH] Add extension "phpstorm" --- .../phpstorm/Civi/PhpStorm/Generator.php | 68 ++++++ tools/extensions/phpstorm/info.xml | 38 ++++ tools/extensions/phpstorm/phpstorm.civix.php | 200 ++++++++++++++++++ tools/extensions/phpstorm/phpstorm.php | 25 +++ 4 files changed, 331 insertions(+) create mode 100644 tools/extensions/phpstorm/Civi/PhpStorm/Generator.php create mode 100644 tools/extensions/phpstorm/info.xml create mode 100644 tools/extensions/phpstorm/phpstorm.civix.php create mode 100644 tools/extensions/phpstorm/phpstorm.php diff --git a/tools/extensions/phpstorm/Civi/PhpStorm/Generator.php b/tools/extensions/phpstorm/Civi/PhpStorm/Generator.php new file mode 100644 index 0000000000..d555b4850d --- /dev/null +++ b/tools/extensions/phpstorm/Civi/PhpStorm/Generator.php @@ -0,0 +1,68 @@ +getPath('[civicrm.files]/.phpstorm.meta.php'); + + $data = static::renderMetadata(static::findServices($container)); + file_put_contents($file, $data); + } + + private static function findServices(ContainerBuilder $c): array { + $aliases = $c->getAliases(); + $services = []; + foreach ($c->getServiceIds() as $serviceId) { + $definition = isset($aliases[$serviceId]) + ? $c->getDefinition($aliases[$serviceId]) : $c->getDefinition($serviceId); + + $class = $definition->getClass(); + if ($class) { + $services[$serviceId] = $class; + } + else { + // fprintf(STDERR, "INCOMPLETE: Service \"%s\" does not declare a type.\n", $serviceId); + $services[$serviceId] = 'mixed'; + } + } + return $services; + } + + private static function renderMetadata(array $services): string { + ob_start(); + try { + printf('<' . "?php\n"); + printf("namespace PHPSTORM_META {\n"); + printf("override(\Civi::service(), map(\n"); + echo str_replace('\\\\', '\\', var_export($services, 1)); + // PhpStorm 2022.3.1: 'Civi\\Foo' doesn't work, but 'Civi\Foo' does. + printf(");\n"); + printf("}\n"); + } + finally { + return ob_get_clean(); + } + } + +} diff --git a/tools/extensions/phpstorm/info.xml b/tools/extensions/phpstorm/info.xml new file mode 100644 index 0000000000..38449206f2 --- /dev/null +++ b/tools/extensions/phpstorm/info.xml @@ -0,0 +1,38 @@ + + + phpstorm + PhpStorm + Generate API metadata for PhpStormE + AGPL-3.0 + + Tim Otten + totten@civicrm.org + + + http://FIXME + http://FIXME + http://FIXME + http://www.gnu.org/licenses/agpl-3.0.html + + 2023-08-25 + 1.0 + alpha + + 5.66 + + This is a new, undeveloped module + + + + + + CRM/PhpStorm + 23.02.1 + crmPhpstorm + + + diff --git a/tools/extensions/phpstorm/phpstorm.civix.php b/tools/extensions/phpstorm/phpstorm.civix.php new file mode 100644 index 0000000000..d17889aee2 --- /dev/null +++ b/tools/extensions/phpstorm/phpstorm.civix.php @@ -0,0 +1,200 @@ +getUrl(self::LONG_NAME), '/'); + } + return CRM_Core_Resources::singleton()->getUrl(self::LONG_NAME, $file); + } + + /** + * Get the path of a resource file (in this extension). + * + * @param string|NULL $file + * Ex: NULL. + * Ex: 'css/foo.css'. + * @return string + * Ex: '/var/www/example.org/sites/default/ext/org.example.foo'. + * Ex: '/var/www/example.org/sites/default/ext/org.example.foo/css/foo.css'. + */ + public static function path($file = NULL) { + // return CRM_Core_Resources::singleton()->getPath(self::LONG_NAME, $file); + return __DIR__ . ($file === NULL ? '' : (DIRECTORY_SEPARATOR . $file)); + } + + /** + * Get the name of a class within this extension. + * + * @param string $suffix + * Ex: 'Page_HelloWorld' or 'Page\\HelloWorld'. + * @return string + * Ex: 'CRM_Foo_Page_HelloWorld'. + */ + public static function findClass($suffix) { + return self::CLASS_PREFIX . '_' . str_replace('\\', '_', $suffix); + } + +} + +use CRM_Phpstorm_ExtensionUtil as E; + +/** + * (Delegated) Implements hook_civicrm_config(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config + */ +function _phpstorm_civix_civicrm_config($config = NULL) { + static $configured = FALSE; + if ($configured) { + return; + } + $configured = TRUE; + + $extRoot = __DIR__ . DIRECTORY_SEPARATOR; + $include_path = $extRoot . PATH_SEPARATOR . get_include_path(); + set_include_path($include_path); + // Based on , this does not currently require mixin/polyfill.php. +} + +/** + * Implements hook_civicrm_install(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_install + */ +function _phpstorm_civix_civicrm_install() { + _phpstorm_civix_civicrm_config(); + // Based on , this does not currently require mixin/polyfill.php. +} + +/** + * (Delegated) Implements hook_civicrm_enable(). + * + * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_enable + */ +function _phpstorm_civix_civicrm_enable(): void { + _phpstorm_civix_civicrm_config(); + // Based on , this does not currently require mixin/polyfill.php. +} + +/** + * Inserts a navigation menu item at a given place in the hierarchy. + * + * @param array $menu - menu hierarchy + * @param string $path - path to parent of this item, e.g. 'my_extension/submenu' + * 'Mailing', or 'Administer/System Settings' + * @param array $item - the item to insert (parent/child attributes will be + * filled for you) + * + * @return bool + */ +function _phpstorm_civix_insert_navigation_menu(&$menu, $path, $item) { + // If we are done going down the path, insert menu + if (empty($path)) { + $menu[] = [ + 'attributes' => array_merge([ + 'label' => $item['name'] ?? NULL, + 'active' => 1, + ], $item), + ]; + return TRUE; + } + else { + // Find an recurse into the next level down + $found = FALSE; + $path = explode('/', $path); + $first = array_shift($path); + foreach ($menu as $key => &$entry) { + if ($entry['attributes']['name'] == $first) { + if (!isset($entry['child'])) { + $entry['child'] = []; + } + $found = _phpstorm_civix_insert_navigation_menu($entry['child'], implode('/', $path), $item); + } + } + return $found; + } +} + +/** + * (Delegated) Implements hook_civicrm_navigationMenu(). + */ +function _phpstorm_civix_navigationMenu(&$nodes) { + if (!is_callable(['CRM_Core_BAO_Navigation', 'fixNavigationMenu'])) { + _phpstorm_civix_fixNavigationMenu($nodes); + } +} + +/** + * Given a navigation menu, generate navIDs for any items which are + * missing them. + */ +function _phpstorm_civix_fixNavigationMenu(&$nodes) { + $maxNavID = 1; + array_walk_recursive($nodes, function($item, $key) use (&$maxNavID) { + if ($key === 'navID') { + $maxNavID = max($maxNavID, $item); + } + }); + _phpstorm_civix_fixNavigationMenuItems($nodes, $maxNavID, NULL); +} + +function _phpstorm_civix_fixNavigationMenuItems(&$nodes, &$maxNavID, $parentID) { + $origKeys = array_keys($nodes); + foreach ($origKeys as $origKey) { + if (!isset($nodes[$origKey]['attributes']['parentID']) && $parentID !== NULL) { + $nodes[$origKey]['attributes']['parentID'] = $parentID; + } + // If no navID, then assign navID and fix key. + if (!isset($nodes[$origKey]['attributes']['navID'])) { + $newKey = ++$maxNavID; + $nodes[$origKey]['attributes']['navID'] = $newKey; + $nodes[$newKey] = $nodes[$origKey]; + unset($nodes[$origKey]); + $origKey = $newKey; + } + if (isset($nodes[$origKey]['child']) && is_array($nodes[$origKey]['child'])) { + _phpstorm_civix_fixNavigationMenuItems($nodes[$origKey]['child'], $maxNavID, $nodes[$origKey]['attributes']['navID']); + } + } +} diff --git a/tools/extensions/phpstorm/phpstorm.php b/tools/extensions/phpstorm/phpstorm.php new file mode 100644 index 0000000000..450c7b3ea2 --- /dev/null +++ b/tools/extensions/phpstorm/phpstorm.php @@ -0,0 +1,25 @@ +