You've used NativePHP's built-in facades. Now it's time to build your own.
Plugins let you extend NativePHP with custom native functionality. Today we're building a Haptics plugin that goes beyond the basic Device::vibrate() — with patterns, intensities, and platform-specific feedback types.
In this post:
#Plugin Structure
A NativePHP plugin is just a Composer package:
my-haptics-plugin/├── composer.json├── nativephp.json # The magic manifest├── src/│ ├── Haptics.php│ ├── HapticsServiceProvider.php│ └── Facades/Haptics.php└── resources/ ├── android/src/.../HapticsFunctions.kt └── ios/Sources/HapticsFunctions.swift
#The Manifest File
The nativephp.json manifest wires everything together:
{ "namespace": "Haptics", "bridge_functions": [ { "name": "Haptics.Impact", "android": "com.yourname.haptics.HapticsFunctions.Impact", "ios": "HapticsFunctions.Impact", "description": "Trigger impact haptic feedback" }, { "name": "Haptics.Notification", "android": "com.yourname.haptics.HapticsFunctions.Notification", "ios": "HapticsFunctions.Notification", "description": "Trigger notification haptic" } ], "android": { "permissions": ["android.permission.VIBRATE"] }, "ios": { "min_version": "13.0" }}
A full list of all the available configuration options is contained in the Docs.
#PHP Layer
The PHP code in your plugin just needs to make use of the special nativephp_call function ("the god method"), which is available when running inside the context of a NativePHP shell app.
(As nativephp_call is not available in normal PHP installations, you should wrap it in a function_exists() check.)
This function receives the name of the native function you want to call (the name defined in your nativephp.json above) and its parameters as a JSON string as the second parameter:
<?php namespace YourName\Haptics; class Haptics{ public function impact(string $style = 'medium'): bool { if (function_exists('nativephp_call')) { $result = nativephp_call('Haptics.Impact', json_encode([ 'style' => $style, ])); if ($result) { $decoded = json_decode($result, true); return $decoded['success'] ?? false; } } return false; } public function notification(string $type = 'success'): bool { if (function_exists('nativephp_call')) { $result = nativephp_call('Haptics.Notification', json_encode([ 'type' => $type, ])); if ($result) { $decoded = json_decode($result, true); return $decoded['success'] ?? false; } } return false; }}
#Native Code
It's entirely up to you to decide how that function call from PHP is handled on the native side: you decide how the parameters get processed and what native functionality to execute.
When your plugin has been registered into your application, this native code gets compiled into your application and is then available to use.
#iOS (Swift)
import UIKit enum HapticsFunctions { class Impact: BridgeFunction { func execute(parameters: [String: Any]) throws -> [String: Any] { let style = parameters["style"] as? String ?? "medium" let feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle = { switch style { case "light": return .light case "heavy": return .heavy default: return .medium } }() let generator = UIImpactFeedbackGenerator(style: feedbackStyle) generator.impactOccurred() return BridgeResponse.success(["success": true]) } } class Notification: BridgeFunction { func execute(parameters: [String: Any]) throws -> [String: Any] { let type = parameters["type"] as? String ?? "success" let feedbackType: UINotificationFeedbackGenerator.FeedbackType = { switch type { case "warning": return .warning case "error": return .error default: return .success } }() let generator = UINotificationFeedbackGenerator() generator.notificationOccurred(feedbackType) return BridgeResponse.success(["success": true]) } }}
#Android (Kotlin)
package com.yourname.haptics import android.os.VibrationEffectimport android.os.Vibratorimport com.nativephp.mobile.bridge.BridgeFunctionimport com.nativephp.mobile.bridge.BridgeResponse object HapticsFunctions { class Impact(private val context: Context) : BridgeFunction { override fun execute(parameters: Map<String, Any>): Map<String, Any> { val style = parameters["style"] as? String ?: "medium" val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator val amplitude = when (style) { "light" -> 50 "heavy" -> 255 else -> 128 } vibrator.vibrate(VibrationEffect.createOneShot(10, amplitude)) return BridgeResponse.success(mapOf("success" to true)) } } // Similare implementation for the 'notification' style feedback}
#Using Your Plugin
Once installed, it works like any NativePHP facade. You can just call your PHP function from your controller, action or another part of your app and NativePHP takes care of calling the native code you created:
use YourName\Haptics\Facades\Haptics; // Impact feedbackHaptics::impact('heavy'); // Solid thudHaptics::impact('light'); // Gentle tap // Notification feedbackHaptics::notification('success'); // Quick double-tapHaptics::notification('error'); // Longer error buzz
#In a Livewire Component
public function submitForm(){ $this->validate(); Haptics::notification('success'); $this->dispatch('form-submitted');}
Congratulations! You just built your first native plugin in PHP. Your Laravel code can now trigger real haptic motors on real devices. 🎉
What will you build next?