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

Laravel Xdg Laravel Package

owenvoke/laravel-xdg

A Laravel package for working with the XDG Base Directory specification, helping resolve standard config, data, cache, and state directories across platforms. It provides convenient helpers for using XDG paths within Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

owenvoke/laravel-xdg wires the XDG Base Directory Specification into Laravel. Instead of scattering storage_path(), config_path() and hardcoded ~/.myapp strings around a CLI or desktop-ish Laravel app, you resolve config/cache/data/state/runtime directories the way Linux (and other Unix) tooling expects. v1.5.0 only widens the framework constraint to include Laravel 12 — no API or behaviour changes — so existing setups keep working after composer update.

  1. composer require owenvoke/laravel-xdg — the service provider is auto-discovered; no manual registration needed on a standard Laravel skeleton.
  2. Publish the config if you need to change the application name used to namespace the directories (php artisan vendor:publish --tag=xdg-config or the equivalent tag listed in the README — check vendor/owenvoke/laravel-xdg/config for the exact key name).
  3. Confirm the resolved values before writing code against them:
php artisan tinker
>>> xdg_config_home();        // ~/.config
>>> xdg_config_home('myapp'); // ~/.config/myapp

The first use case is almost always a CLI/daemon built on Laravel (Laravel Zero, an artisan-driven sync tool, a worker) where you want the SQLite database, logs, lock files and a user-editable config.json to land in ~/.local/share/myapp, ~/.local/state/myapp and ~/.config/myapp rather than inside the checked-out project directory.

Implementation Patterns

Resolve paths at runtime, not at config-load time. The package is a thin resolver over the XDG_* environment variables with spec fallbacks ($XDG_CONFIG_HOME → ~/.config, $XDG_CACHE_HOME → ~/.cache, $XDG_DATA_HOME → ~/.local/share, $XDG_STATE_HOME → ~/.local/state, $XDG_RUNTIME_DIR → no default). Treat the accessors as functions to call, not values to snapshot.

Rebase Laravel in a service provider. Do it in register()/boot() of your own provider so it runs before anything touches storage:

public function register(): void
{
    $this->app->useStoragePath(xdg_data_home('myapp').'/storage');
}

public function boot(): void
{
    config([
        'database.connections.sqlite.database' => xdg_data_home('myapp').'/database.sqlite',
        'logging.channels.single.path'          => xdg_state_home('myapp').'/logs/app.log',
        'cache.stores.file.path'                => xdg_cache_home('myapp').'/cache',
    ]);
}

Read and write user config deliberately. xdg_config_home('myapp').'/config.json' is the file a user is supposed to edit; xdg_data_home() is where you keep things they shouldn't. Keeping that split makes later "reset my settings" instructions trivial.

Handle the search-path variants. XDG defines XDG_CONFIG_DIRS and XDG_DATA_DIRS as colon-separated lists with system defaults (/etc/xdg, /usr/local/share:/usr/share). Use the package's *_dirs accessors rather than concatenating yourself if you support system-wide defaults layered under the per-user directory.

Isolate it in tests. Because the values come from env, set them per test to a temp dir and let the resolver do the rest:

putenv('XDG_CONFIG_HOME='.($tmp = sys_get_temp_dir().'/xdg-test'));
$this->app->forgetInstance(...); // if you cache the resolved value anywhere

Guard the platform. If the same codebase ships to macOS or Windows, wrap the calls behind your own AppPaths class that branches: XDG on Linux, ~/Library/Application Support/{app} on macOS, %APPDATA% on Windows. Keep that branch in one place so the rest of the app calls AppPaths::config() and never a raw helper.

Gotchas and Tips

  • config:cache will freeze the absolute paths. If you bake xdg_data_home() results into config/*.php at load time, php artisan config:cache on machine A produces a cached config with machine A's home directory. Resolve in a provider after the cache is loaded (as above) or via config() overrides in boot(), and never ship a cached config with these paths.
  • Relative values must be ignored. The spec says relative XDG_* values are invalid and the implementation should fall back to the default. Don't putenv('XDG_CONFIG_HOME=foo') and expect foo to be honoured — and don't rely on the package to normalise a trailing slash for you; rtrim($path, '/') before concatenation.
  • XDG_RUNTIME_DIR is the fragile one. It's unset in cron jobs, CI containers, SSH sessions without a login manager, and typically absent on macOS. It has no spec default, so any code depending on it needs an explicit fallback (sys_get_temp_dir() keyed by UID) and a permissions check — it's meant to be 0700 and cleared on logout, so don't persist anything durable there.
  • HOME changes with the invoking user. Running the same command via sudo, su, systemd units or a scheduler resolves a different ~. Be explicit about which user's config you intend, and prefer resolving inside the command rather than at container boot.
  • Application-name namespacing is a footgun. Keep the name stable and lower-case, sanitised of /, and consistent between the XDG helpers and wherever you build paths by hand. Changing it in a later release silently orphans users' existing config/data directories — if you must change it, add a migration that moves the old directory.
  • Don't call accessors too early. They may read published config; resolving before the provider boots (e.g. inside a config/ file's return array, or another package's register() running earlier) gives you defaults you didn't intend. If ordering matters, check bootstrap/providers.php and register your path-rebasing provider near the top.
  • Laravel 12 support is the only change in v1.5.0. No deprecations, no breaking changes, no new API. If you're on Laravel 10/11 just take the patch; if you're pinning owenvoke/laravel-xdg explicitly, keep the constraint loose (^1.4) so the widened framework support actually reaches you. Re-run your test matrix on Laravel 12 because the framework's own path/storage internals are what you're overriding.
  • Extension points. The published config's application name is the intended knob; beyond that, prefer wrapping the helpers in your own path resolver rather than monkey-patching the provider — it keeps your macOS/Windows branching and your test-time temp-dir overrides in a single class.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin