blessedzulu / nativephp-deeplinks
Deep-link hardening for NativePHP Mobile: scope Android App Links to specific path prefixes and preserve array query params (Livewire #[Url]) through the WebView bridge. Build-time only, no native SDK.
Package info
github.com/blessedzulu/nativephp-deeplinks
Type:nativephp-plugin
pkg:composer/blessedzulu/nativephp-deeplinks
Requires
- php: ^8.2
- illuminate/support: ^11.0|^12.0|^13.0
- nativephp/mobile: ^3.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0
README
Deep-link hardening for NativePHP Mobile. Two build-time fixes for Android deep links - no native SDK, no runtime cost.
You set up deep links so that links to your site open your app instead of the browser. On Android, NativePHP gets you most of the way there - but two things quietly go wrong:
- Your app claims your entire website. Not just the pages you meant - every link to your domain (blog posts, marketing pages, the lot) starts trying to open the app.
- Deep links that carry list data arrive empty. A link with array parameters - like a Livewire
#[Url] public array- opens the app in the wrong state, with those values silently dropped.
This plugin fixes both, automatically, when you build. You don't call anything. iOS already handles both correctly, so it's left alone.
Features
- Scope which paths open your app - claim
/calculators,/toolsand friends, and let every other URL open in the browser where it belongs - Keep array query params intact through the Android WebView bridge (Livewire
#[Url] public array, add-a-row inputs, anything withfoo[0][bar]keys) - Zero runtime cost - it adjusts the generated native project at build time; nothing extra ships in your app
- Safe by design - every change is idempotent and guarded, so a NativePHP upgrade degrades to "unpatched" with a warning, never a broken build
- Config-driven - one config file (or a single env var); no native files to edit by hand
Requirements
- PHP 8.2+
- Laravel 11, 12 or 13
- NativePHP Mobile 3.0+
- Android (iOS needs neither fix)
Installation
composer require blessedzulu/nativephp-deeplinks php artisan vendor:publish --tag=nativephp-deeplinks-config
The plugin is auto-discovered by NativePHP (run php artisan native:plugin:list to confirm). Its work runs automatically on native:run and native:package, so there's nothing to call yourself.
Configuration
Tell it which paths should open your app, in config/nativephp-deeplinks.php:
return [ // Paths the Android app should claim. A glob ('/calculators/*') or a bare // prefix ('/calculators') both work. Leave empty to keep NativePHP's // default, where the app claims your whole domain. 'paths' => ['/calculators', '/tools', '/planners'], // Keep array query params intact through the WebView bridge. Leave this on. 'preserve_array_query' => true, ];
Prefer env? Set the paths there instead:
NATIVEPHP_DEEPLINK_PATHS=/calculators,/tools,/planners
Note: this plugin only handles path scoping and query handling. You still need NativePHP's own
NATIVEPHP_DEEPLINK_HOST, plus your server-hostedassetlinks.json(Android) andapple-app-site-association(iOS), for deep links to verify in the first place.
The two fixes, in plain English
1. Your app shouldn't claim your whole website
Out of the box, NativePHP registers your Android app for every path on your verified domain (android:pathPrefix="/"). So once a user installs the app, tapping any link to your site - a blog post, your pricing page, an old marketing URL - offers to open the app, even for pages the app doesn't have.
This plugin rewrites the generated AndroidManifest.xml to register only the path prefixes you list in paths. Everything else stays in the browser, which is what you want.
2. Array query params get mangled on Android
When a deep link opens your app, Android's WebView rebuilds the request's query string using Android's own URL parser - and that parser mishandles bracketed keys like foo[0][bar], then re-serialises them without re-encoding. The result: array query parameters get corrupted on the way in.
The usual casualty is a Livewire #[Url] public array property - think an "add a row" form where each row serialises as allowances[0][type]=fixed. Scalar params survive the trip; array rows arrive empty, so the page loads with the wrong state. This plugin patches the generated Android code so the original, correctly-encoded query reaches PHP exactly as a browser would send it.
iOS needs neither fix: path scoping comes from the paths array in your apple-app-site-association file, and Swift preserves the encoded query already.
How it works
A post_compile hook runs after NativePHP generates the native project and before Gradle builds it, working on the generated Android source. Every transform is idempotent (safe to run again) and guarded: if an anchor it expects has moved in a newer NativePHP, it logs a warning and skips that transform rather than failing your build. In other words, an upstream change degrades to "unpatched" loudly, instead of breaking things silently.
Compatibility
Built for NativePHP Mobile ^3.0 (developed against 3.3.x). If NativePHP fixes these upstream - the query decoding is a genuine bug - this plugin simply becomes a no-op you can remove.
License
MIT © Blessed Zulu