Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ide Link Bundle Laravel Package

vincecore/ide-link-bundle

PHP package that adds IDE deep links to error pages, logs, and stack traces, so you can jump from the browser or terminal straight to the exact file and line in PhpStorm, VS Code, or another editor. Configurable URL schemes and simple setup make debugging faster.

View on GitHub
Deep Wiki
Context7

Getting Started

What it actually solves. When your app runs in a VM/Vagrant/Docker container, stack traces contain container paths (/var/www/app/src/Foo.php). Your IDE is on the host (/Users/you/code/app/src/Foo.php). The exception page's "open in editor" links point at files that don't exist on the host, so clicking a frame does nothing. vincecore/ide-link-bundle is a small Symfony bundle that rewrites those paths before the error page renders them.

Reality check before you install anything. This is a Symfony bundle from 2016 with 2 stars and no dependents (score 0.01). It targets Symfony 2/3-era APIs (getRequest(), YAML service definitions, non-autowired services). It will not composer require cleanly into a modern Symfony app without a fork, and it is not a Laravel package at all — no service provider, no auto-discovery, no config/ file. Treat it as a reference implementation of a 30-line idea, not a dependency.

Where to look first (in the repo, in this order):

  1. README.md — the config keys for the two path prefixes (container → host).
  2. DependencyInjection/*Extension.php — the real config schema; READMEs from this era are usually stale.
  3. Resources/config/services.yml — tells you whether it hooks kernel.exception, a Twig extension, or the exception controller.
  4. EventListener/ or Twig/ — the actual rewrite logic. This is the only file you actually care about.

First use case (Laravel). Skip the package; port the idea. You want: in local only, take every frame in the exception payload and str_replace(containerPrefix, hostPrefix, $frame['file']).

// config/ide-link.php
return [
    'enabled' => env('APP_DEBUG') && env('IDE_LINK_HOST_PREFIX'),
    'map' => [
        '/var/www/app' => env('IDE_LINK_HOST_PREFIX'), // /Users/you/code/app
    ],
];

Then map frames inside a decorating exception handler:

class IdeLinkHandler extends Handler
{
    public function __construct(private Handler $inner) {}

    protected function convertExceptionToArray(Throwable $e): array
    {
        $data = parent::convertExceptionToArray($e);
        $data['trace'] = collect($data['trace'])->map(function ($frame) {
            if (!empty($frame['file'])) {
                $frame['file'] = app(PathMapper::class)->map($frame['file']);
            }
            return $frame;
        })->all();
        return $data;
    }
}

Bind PathMapper as a singleton, keep it a pure string -> string function, and you have effectively reimplemented the bundle in one afternoon with no abandoned-dependency risk.

Implementation Patterns

Prefix-mapping table, longest-match-wins. The core operation is a replacement of path prefixes, not substrings. Build the map as an array, sort keys by length descending, walk it, return on first match. Naive str_replace across the whole HTML is what the 2016-era code likely did and it's the wrong shape — it will happily corrupt a filename that happens to contain your container prefix.

final class PathMapper
{
    /** @param array<string,string> $map containerPrefix => hostPrefix */
    public function __construct(private array $map, private bool $enabled) {}

    public function map(string $path): string
    {
        if (!$this->enabled) return $path;
        $keys = array_keys($this->map);
        usort($keys, fn ($a, $b) => strlen($b) <=> strlen($a));
        foreach ($keys as $from) {
            if (str_starts_with($path, rtrim($from, '/'))) {
                return rtrim($this->map[$from], '/') . substr($path, strlen(rtrim($from, '/')));
            }
        }
        return $path;
    }
}

Symfony equivalent (if you're actually in Symfony). The bundle almost certainly registers a kernel.exception listener and mutates the FlattenException traces. The key is listener priority: it must run before whatever renders the trace (priority > 0), otherwise the exception page has already been built from the original frames. The same longest-prefix logic applies; in Symfony the config is typically a two-entry hash of /var/www → /Users/you, plus a debug-only guard.

Multiple environments, one map. Developers flip between Docker, Vagrant, and plain php artisan serve on the host. Drive the map from env (IDE_LINK_HOST_PREFIX, IDE_LINK_CONTAINER_PREFIX) rather than hardcoding it in a config file — the host prefix is per-developer, the container prefix is per-project. Commit the container prefix; let each dev set the host one in .env.

IDE scheme is a separate concern. Rewriting the path only makes the link correct; it doesn't make it open. phpstorm://open?file=...&line=... and vscode://file/... have different escaping rules, and on macOS/Linux you often need a small ~/bin handler that converts the URL back into a CLI call. Keep the scheme handling in your layout/JS, out of the path mapper.

Integration point in Laravel. Ignition (Spatie) replaced Whoops and has its own frame rendering — if you're on a recent Laravel, hook Spatie\Ignition\Ignition::make()->... or register a SolutionProvider, or override Handler::render/convertExceptionToArray as above. Confirm which renderer is active before writing the mapper; mapping the wrong payload is a silent no-op.

Gotchas and Tips

  • This won't install on a modern Symfony, and never on Laravel. Composer will fight you on symfony/symfony constraints from 2016. Fork and port, or copy the logic. Do not add it to composer.json and hope.
  • Path rewriting is cosmetic only. The exception page often reads the file from disk to show the source snippet. If the container path doesn't exist in the container after mapping (it won't, if you map to a host path), the code preview vanishes. Either mount the same tree on both sides, or map only in the link generation and leave the read path alone.
  • Symlinks defeat realpath(). PHP reports the realpath of an included file, so if your project is symlinked inside the container, your configured prefix won't match the file's actual reported path. Check the actual getFile() values before writing the map — don't guess.
  • Windows hosts. Backslashes, drive letters, and file:///C:/... triples-of-slashes. The 2016 code predates most people caring about this; test it.
  • Case sensitivity. macOS default volumes are case-insensitive, Linux containers aren't. A prefix that "works" locally can fail in CI, but CI isn't rendering exception pages, so this rarely bites — still, don't rely on case-insensitive matching in your mapper.
  • Guard on debug mode. Never serve host filesystem paths in production stack traces. Gate the whole mapper on APP_DEBUG / kernel.debug and return the input unchanged otherwise.
  • Debugging a "link still broken" report: dump the frame file value from the raw exception before mapping, then the mapped value. Nine times out of ten the configured prefix is off by a trailing slash or the code is running from a different docroot than you assumed (/app vs /var/www/html).
  • Listener priority is the #1 silent failure in the Symfony version. Register with kernel.exception at a priority higher than the exception renderer, and verify by temporarily logging inside the listener.
  • Extension point: keep the mapper injectable with an interface (PathMapperInterface) so teams can swap in a container-aware resolver or a filesystem-abstraction lookup later. That's the one design improvement worth making over the original.
  • When to walk away: if your team uses Laravel Sail/DDEV with a bind mount, host and container paths often already align enough that you only need the mapper for /var/www/html → project root — a five-line config, not a Symfony bundle.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package