Array of project root paths, or empty array if not available */ public static function getProjectRoots(): array { $ideProjectRoots = getenv('IDE_PROJECT_ROOTS'); if ($ideProjectRoots === false || $ideProjectRoots === '') { return []; } // IDE_PROJECT_ROOTS is typically a colon-separated list (Unix) or semicolon-separated (Windows) $separator = str_contains($ideProjectRoots, ';') ? ';' : ':'; $roots = explode($separator, $ideProjectRoots); // Filter out empty values and normalize paths return array_filter( array_map('trim', $roots), fn(string $root) => $root !== '' && is_dir($root) ); } /** * Convert absolute file path to relative path based on project roots * * If PhpStorm is detected and project roots are available, returns the shortest * relative path. Otherwise returns the absolute path. * * @param string $filePath Absolute file path * @return string Relative path if project root found, otherwise absolute path */ public static function getRelativePath(string $filePath): string { if (!self::isPhpStorm()) { return $filePath; } $projectRoots = self::getProjectRoots(); if (empty($projectRoots)) { return $filePath; } $filePath = realpath($filePath); if ($filePath === false) { return $filePath; } $shortestPath = $filePath; $shortestLength = strlen($filePath); foreach ($projectRoots as $root) { $root = realpath($root); if ($root === false) { continue; } // Check if file is within this project root if (str_starts_with($filePath, $root . DIRECTORY_SEPARATOR) || $filePath === $root) { $relative = substr($filePath, strlen($root) + 1); if (strlen($relative) < $shortestLength) { $shortestPath = $relative; $shortestLength = strlen($relative); } } } return $shortestPath; } }