“等等,你能用 Laravel 编原生移动 App?从什么时候开始的?”
当你第一次听说 NativePHP 时,脑子里大概率会冒出这个问题。但请相信我,这绝不是在做梦。而且最棒的部分来了:现在你可以不用花一分钱,就直接用 Laravel 打造原生的 iOS 和 Android 应用!
为什么这事儿意义重大?
试想一下这个场景:你是一名 Laravel 开发者,玩 PHP 玩得炉火纯青。结果有一天,你的老板或客户突然要求你开发一个移动 App。通常情况下,你不得不:
- 从零开始学习 Swift 或 Kotlin。
- 雇个移动端开发(那成本……你懂的)。
- 使用 React Native 或 Flutter,并投入大量时间去学习全新的生态系统。
NativePHP 的出现彻底改变了游戏规则——它让你能直接利用现有的 Laravel 技能来构建移动应用。而且从第 3 版(代号 NativePHP Air)开始,所有的功能在 MIT 协议下完全免费且开源。
第 3 版(Version 3)有哪些新特性?
1. 模块化插件系统
这是最核心的改变。在此之前,所有的原生功能都被打包在一个巨大的包里,导致生成的 App 非常臃肿。现在呢?你只需要安装你真正需要的插件。
例如,如果你只需要访问摄像头和文件系统:
composer require nativephp/mobile-camera
composer require nativephp/mobile-file
简单,对吧?你的 App 能保持轻量、构建速度飞快,而且因为没有那些乱七八糟的冗余功能,提交 App Store 审核时也会更顺畅。
2. 免费 vs. 付费插件
NativePHP 团队制定了一套非常合理的平衡策略。他们将核心的基础插件完全免费提供(基于 MIT 协议):
🆓 免费插件:
- Browser —— 在 App 内打开网页链接
- Camera —— 访问设备摄像头
- Device —— 获取设备信息
- Dialog —— 弹窗告警与提示
- File —— 文件管理
- Microphone —— 音频录制
- Network —— 检查网络连接状态
- Share —— 分享内容到其他应用
- System —— 获取系统信息
💎 付费插件(一次性付费,终身使用):
- Biometrics —— 指纹与面部识别
- Geolocation —— GPS 与定位服务
- Firebase Push Notifications —— 推送通知
- Scanner —— 二维码与条形码扫描
- Secure Storage —— 加密存储敏感数据
付费插件的价格大约在每个 99 美元左右。关键在于,这是一次性付费且不限项目数量。哪怕你写了 10 个 App,也只需要付一次钱。
动手实操!
基础安装
首先,创建一个新的 Laravel 项目:
laravel new awesome-app
cd awesome-app
composer require nativephp/mobile
搞定!现在你已经打好了移动 App 的地基。
安装所需插件
假设你想开发一个具备摄像头访问和照片分享功能的 App:
composer require nativephp/mobile-camera
composer require nativephp/mobile-share
注册对应插件:
php artisan native:plugin:register nativephp/mobile-camera
php artisan native:plugin:register nativephp/mobile-share
示例:构建一个拍照并分享的功能
让我们来实现一个简单的功能:调用摄像头拍一张照片,然后将其分享到其他应用。
在 Livewire 组件中,代码如下:
<?php
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Facades\Camera;
use Native\Mobile\Facades\Share;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\Camera\PhotoTaken;
class PhotoSharer extends Component
{
public $photoPath = null;
public function takePhoto()
{
// 以高质量打开摄像头
Camera::takePicture()
->quality(0.9)
->dispatch();
}
#[OnNative(PhotoTaken::class)]
public function handlePhoto(string $path)
{
// 保存捕获的照片路径
$this->photoPath = $path;
// 给用户反馈
$this->dispatch('photo-saved', path: $path);
}
public function sharePhoto()
{
if (!$this->photoPath) {
return;
}
// 分享照片到其他应用
Share::files([$this->photoPath])
->message('Check out this awesome photo!')
->dispatch();
}
public function render()
{
return view('livewire.photo-sharer');
}
}
Blade View:
<div class="container mx-auto p-4">
<h1 class="text-2xl font-bold mb-4">Photo Sharer</h1>
<div class="space-y-4">
<!-- Take Photo Button -->
<button
wire:click="takePhoto"
class="w-full bg-blue-500 text-white py-3 px-6 rounded-lg hover:bg-blue-600">
📸 Take Photo
</button>
<!-- Photo Preview -->
@if($photoPath)
<div class="bg-gray-100 p-4 rounded-lg">
<img src="{{ $photoPath }}" alt="Photo" class="w-full rounded">
<button
wire:click="sharePhoto"
class="mt-4 w-full bg-green-500 text-white py-3 px-6 rounded-lg hover:bg-green-600">
📤 Share Photo
</button>
</div>
@endif
</div>
</div>
简单,对吧?你依然是在用熟悉的 Livewire 模式开发,只是顺手加了点 NativePHP 的门面(Facades)而已。
示例:使用付费插件(Firebase 推送通知)
现在我们来看看付费插件的例子。假设你已经购买了用于推送通知的 Firebase 插件。
安装插件:
composer require nativephp/mobile-firebase
php artisan native:plugin:register nativephp/mobile-firebase
Firebase 配置步骤:
- 在 Firebase 控制台 创建一个新项目。
- 下载 Android 版的
google-services.json和 iOS 版的GoogleService-Info.plist。 - 将这两个文件直接放入你的项目根目录。
- NativePHP 会在构建应用时自动编译并配置它们。
注册推送通知的代码如下:
<?php
namespace App\Livewire;
use Livewire\Component;
use Native\Mobile\Facades\PushNotifications;
use Native\Mobile\Attributes\OnNative;
use Native\Mobile\Events\PushNotification\TokenGenerated;
class NotificationSettings extends Component
{
public $notificationToken = null;
public $isEnrolled = false;
public function enableNotifications()
{
// 请求权限并注册推送通知
PushNotifications::enroll();
}
#[OnNative(TokenGenerated::class)]
public function handleTokenGenerated(string $token)
{
// Token 生成成功!
$this->notificationToken = $token;
$this->isEnrolled = true;
// 将 token 发送到后端进行存储
$this->sendTokenToServer($token);
}
private function sendTokenToServer(string $token)
{
// 发送到你的后端 API
auth()->user()->update([
'fcm_token' => $token,
'platform' => $this->detectPlatform()
]);
}
private function detectPlatform()
{
// 检测平台(iOS 或 Android)
return \Native\Mobile\Facades\System::platform();
}
public function render()
{
return view('livewire.notification-settings');
}
}
页面:
<div class="p-4">
<div class="bg-white rounded-lg shadow p-6">
<h2 class="text-xl font-bold mb-4">Notification Settings</h2>
@if(!$isEnrolled)
<div class="mb-4">
<p class="text-gray-600 mb-4">
Enable notifications to receive updates directly on your device!
</p>
<button
wire:click="enableNotifications"
class="w-full bg-blue-500 text-white py-3 px-6 rounded-lg hover:bg-blue-600">
🔔 Enable Notifications
</button>
</div>
@else
<div class="bg-green-50 p-4 rounded-lg">
<p class="text-green-800 font-semibold mb-2">
✅ Notifications Enabled
</p>
<p class="text-sm text-gray-600">
You'll receive notifications for important updates.
</p>
</div>
@endif
</div>
</div>
从后台发送通知:
<?php
namespace App\Services;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
class PushNotificationService
{
private $messaging;
public function __construct()
{
$factory = (new Factory)->withServiceAccount(config('firebase.credentials'));
$this->messaging = $factory->createMessaging();
}
public function sendToUser($user, $title, $body, $data = [])
{
if (!$user->fcm_token) {
return false;
}
$message = CloudMessage::withTarget('token', $user->fcm_token)
->withNotification([
'title' => $title,
'body' => $body,
])
->withData($data);
try {
$this->messaging->send($message);
return true;
} catch (\Exception $e) {
\Log::error('Push notification failed: ' . $e->getMessage());
return false;
}
}
}
在Controller中使用:
use App\Services\PushNotificationService;
class OrderController extends Controller
{
public function __construct(
private PushNotificationService $pushService
) {}
public function confirmOrder(Order $order)
{
$order->update(['status' => 'confirmed']);
// 向客户发送通知
$this->pushService->sendToUser(
$order->user,
'Order Confirmed',
"Order #{$order->id} has been confirmed and is being processed!",
['order_id' => $order->id]
);
return response()->json(['success' => true]);
}
}
飞跃式体验:让测试回归简单
NativePHP 最酷的功能之一莫过于 Jump。你只需要在手机上(iOS 或 Android)安装这个专门的 App,就能直接测试你的 Laravel 应用,完全不需要经历漫长的打包过程,也不用安装那些沉重的开发者工具(比如 Xcode 或 Android Studio)。
使用方法极其简单:
php artisan native:jump
# 或者
./native jump
然后,只需用手机上的 Jump App 扫描出现的二维码。Boom! 你的 Laravel 应用就会直接在你的设备上跑起来。
为什么 Jump 功能如此惊艳:
- 无需 Mac 即可测试 iOS 应用:即使你用的是 Windows 或 Linux,也能在 iPhone 上看到运行效果。
- 无需安装 Xcode 或 Android Studio:跳过那些动辄几十 GB 的开发环境配置。
- 极速反馈:本地代码的修改会立即同步到手机端。
- 完全免费:你可以免费测试所有插件(包括那些高级付费插件)。
- 演示神器:开发完一个功能,立刻就能在真机上演示给客户看。
开发属于你自己的插件
这才是 NativePHP 真正强大的地方。如果官方提供的功能无法满足你的需求,你完全可以利用原生能力开发自己的插件。
自动化插件脚手架
最简单的方法是使用 NativePHP 内置的命令来生成模板:
php artisan native:plugin:create
这个命令会询问你的插件名称、命名空间以及你想要实现的功能,随后它会自动生成一套完整的插件结构。
插件结构解析
my-plugin/
├── composer.json # 包元数据
├── nativephp.json # 插件清单
├── src/
│ ├── MyPluginServiceProvider.php
│ ├── MyPlugin.php # 主类
│ ├── Facades/
│ │ └── MyPlugin.php
│ ├── Events/
│ │ └── SomethingHappened.php
│ └── Commands/ # 生命周期钩子命令
├── resources/
│ ├── android/src/ # Kotlin 代码
│ ├── ios/Sources/ # Swift 代码
│ └── js/ # JavaScript 库
composer.json 文件
这一点至关重要:你必须在插件的 composer.json 中将 type(类型)字段设置为 nativephp-plugin:
{
"name": "vendor/my-plugin",
"type": "nativephp-plugin",
"extra": {
"laravel": {
"providers": ["Vendor\\MyPlugin\\MyPluginServiceProvider"]
},
"nativephp": {
"manifest": "nativephp.json"
}
}
}
nativephp.json 文件
这是插件的清单文件(Manifest) ,用于声明原生的相关配置:
{
"namespace": "MyPlugin",
"bridge_functions": [
{
"name": "MyPlugin.DoSomething",
"ios": "MyPluginFunctions.DoSomething",
"android": "com.myvendor.plugins.myplugin.MyPluginFunctions.DoSomething"
}
],
"events": ["Vendor\\MyPlugin\\Events\\SomethingHappened"],
"android": {
"permissions": ["android.permission.CAMERA"],
"dependencies": {
"implementation": ["com.google.mlkit:barcode-scanning:17.2.0"]
}
},
"ios": {
"info_plist": {
"NSCameraUsageDescription": "摄像头将用于扫描二维码"
},
"dependencies": {
"pods": [{"name": "GoogleMLKit/BarcodeScanning", "version": "~> 4.0"}]
}
}
}
Android 包名命名(Package Naming)
至关重要: 在 Android/Kotlin 开发中,你必须在每个文件的顶部声明包名:
// 资源文件路径:resources/android/src/MyPluginFunctions.kt
package com.myvendor.plugins.myplugin
import com.nativephp.mobile.bridge.BridgeFunction
import com.nativephp.mobile.bridge.BridgeResponse
object MyPluginFunctions {
class DoSomething : BridgeFunction {
override fun execute(parameters: Map<String, Any>): Map<String, Any> {
return BridgeResponse.success(mapOf("status" to "done"))
}
}
}
包路径必须与清单文件(Manifest)中定义的路径完全匹配。
简单插件示例 —— 震动功能(Vibration)
让我们来创建一个简单的插件,实现设备的震动功能。
Swift (iOS) —— resources/ios/Sources/VibrationFunctions.swift:
import Foundation
import UIKit
@objc public class VibrationFunctions: NSObject {
@objc public static func vibrate(_ type: String) -> [String: Any] {
switch type {
case "light":
let generator = UIImpactFeedbackGenerator(style: .light)
generator.impactOccurred()
case "medium":
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
case "heavy":
let generator = UIImpactFeedbackGenerator(style: .heavy)
generator.impactOccurred()
default:
UINotificationFeedbackGenerator().notificationOccurred(.success)
}
return ["success": true, "type": type]
}
}
Kotlin (Android) — resources/android/src/VibrationFunctions.kt :
package com.myvendor.vibration
import android.content.Context
import android.os.VibrationEffect
import android.os.Vibrator
import com.nativephp.mobile.bridge.BridgeFunction
import com.nativephp.mobile.bridge.BridgeResponse
object VibrationFunctions {
class Vibrate : BridgeFunction {
override fun execute(parameters: Map<String, Any>): Map<String, Any> {
val context = parameters["context"] as? Context
?: return BridgeResponse.error("Context not available")
val type = parameters["type"] as? String ?: "medium"
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
val duration = when (type) {
"light" -> 50L
"medium" -> 100L
"heavy" -> 150L
else -> 100L
}
val amplitude = when (type) {
"light" -> 50
"medium" -> 100
"heavy" -> 150
else -> VibrationEffect.DEFAULT_AMPLITUDE
}
vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude))
return BridgeResponse.success(mapOf("type" to type))
}
}
}
清单文件 nativephp.json:
{
"namespace": "Vibration",
"bridge_functions": [
{
"name": "Vibration.vibrate",
"ios": "VibrationFunctions.vibrate",
"android": "com.myvendor.vibration.VibrationFunctions.Vibrate"
}
],
"android": {
"permissions": ["android.permission.VIBRATE"]
}
}
PHP 类 — src/Vibration.php :
<?php
namespace MyVendor\VibrationPlugin;
use Native\Mobile\Bridge;
class Vibration
{
public function vibrate(string $type = 'medium'): array
{
return Bridge::call('Vibration.vibrate', [
'type' => $type
]);
}
}
Facade —— src/Facades/Vibration.php:
<?php
namespace MyVendor\VibrationPlugin\Facades;
use Illuminate\Support\Facades\Facade;
class Vibration extends Facade
{
protected static function getFacadeAccessor()
{
return \MyVendor\VibrationPlugin\Vibration::class;
}
}
Service Provider — src/VibrationServiceProvider.php :
<?php
namespace MyVendor\VibrationPlugin;
use Illuminate\Support\ServiceProvider;
class VibrationServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Vibration::class, function () {
return new Vibration();
});
}
public function boot()
{
// 如果需要,可以在这里添加引导逻辑
}
}
使用:
use MyVendor\VibrationPlugin\Facades\Vibration;
// 在 Livewire 组件或控制器中
public function onButtonClick()
{
Vibration::vibrate('heavy');
// 执行其他操作...
}
本地开发调试
在开发过程中,为了能实时测试插件的改动,你需要将插件以**路径仓库(Path Repository)**的形式添加到应用的 composer.json 中:
{
"repositories": [
{"type": "path", "url": "../packages/my-plugin"}
]
}
然后require一下:
composer require vendor/my-plugin
对 PHP 代码的修改会立即生效(实时重载)。但如果你修改了原生代码(Swift 或 Kotlin) ,则需要重新构建:
php artisan native:run
对于清单文件(Manifest)或原生代码的重大改动,建议强制重新安装:
php artisan native:install --force
注册插件
通过 Composer 安装插件后,你必须对其进行注册,这样它才能被正式编译到原生构建(Native Builds)中。
首次设置:
php artisan vendor:publish --tag=nativephp-plugins-provider
这将会创建app/Providers/NativeServiceProvider.php.
注册一个插件
php artisan native:plugin:register vendor/my-plugin
这会自动将插件的服务提供者(Service Provider)添加到你的 plugins() 数组中:
public function plugins(): array
{
return [
\Vendor\MyPlugin\MyPluginServiceProvider::class,
];
}
列出所有插件
# 显示已注册的插件
php artisan native:plugin:list
# 显示所有已安装的插件(包括未注册的)
php artisan native:plugin:list --all
移除一个插件
php artisan native:plugin:register vendor/my-plugin --remove
验证你的插件
在构建之前,验证一下你的插件:
php artisan native:plugin:validate
这能捕捉到清单文件(Manifest)中的错误、缺失的原生代码,或是函数声明不匹配的问题。
为单页应用(SPA)准备的 JavaScript 库
如果你的 Laravel 项目前端使用的是 Vue 或 React,插件也可以提供专门的 JavaScript 库进行适配。你只需要在 resources/js/ 目录下创建一个文件:
// 资源文件路径:resources/js/vibration.js
const baseUrl = '/_native/api/call';
async function bridgeCall(method, params = {}) {
const response = await fetch(baseUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ method, params })
});
return response.json();
}
export async function vibrate(type = 'medium') {
return bridgeCall('Vibration.vibrate', { type });
}
使用者可以直接导入:
import { vibrate } from '@myvendor/vibration-plugin';
await vibrate('heavy');
使用 JavaScript 框架(Vue/React/Inertia)
NativePHP 不仅仅局限于传统的服务端渲染,它也完美支持现代 JavaScript 框架。如果你习惯于构建单页应用(SPA),可以通过 NativePHP 提供的 JS 桥接层直接与原生系统交互。 Vue 示例:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { camera, on, off, Events } from '#nativephp'
const photoPath = ref(null)
const isLoading = ref(false)
const takePhoto = async () => {
isLoading.value = true
await camera.takePicture()
.quality(0.9)
.remember()
}
const handlePhotoTaken = (event) => {
photoPath.value = event.path
isLoading.value = false
}
onMounted(() => {
on(Events.Camera.PhotoTaken, handlePhotoTaken)
})
onUnmounted(() => {
off(Events.Camera.PhotoTaken, handlePhotoTaken)
})
</script>
<template>
<div class="photo-capture">
<button
@click="takePhoto"
:disabled="isLoading"
class="btn-primary">
{{ isLoading ? 'Loading...' : '📸 Take Photo' }}
</button>
<img
v-if="photoPath"
:src="photoPath"
alt="Captured photo"
class="preview-image" />
</div>
</template>
提示与最佳实践
1. 在合适的时机申请权限
千万不要在 App 刚启动时就申请所有权限。 这种做法会让用户感到突兀,极大地增加被拒绝的可能性。
// ❌ 不要这样做
public function mount()
{
Camera::requestPermission();
Microphone::requestPermission();
Geolocation::requestPermission();
}
// ✅ 在用户需要该功能时再请求
public function startRecording()
{
// 先检查权限
if (!Microphone::hasPermission()) {
// 解释为什么需要该权限
$this->showPermissionExplanation();
return;
}
// 然后再请求
Microphone::record();
}
2. 正确地处理错误
use Native\Mobile\Facades\Camera;
public function capturePhoto()
{
try {
Camera::takePicture()
->quality(0.9)
->dispatch();
} catch (\Exception $e) {
// 记录错误日志
\Log::error('Camera error: ' . $e->getMessage());
// 给用户反馈
session()->flash('error', 'Failed to open camera. Please try again.');
}
}
3. 性能优化
虽然 NativePHP 插件运行在原生层,性能表现已经非常出色,但你仍然需要注意以下几点,以确保应用在大规模使用时依然丝滑:
// ✅ 批量操作
Share::files([
'photo1.jpg',
'photo2.jpg',
'photo3.jpg'
])->dispatch();
// ❌ 避免在循环中进行多次调用
foreach ($photos as $photo) {
Share::files([$photo])->dispatch(); // 太浪费资源了!
}
4. 测试
虽然 Jump 极其方便,能让你快速验证逻辑,但千万不要忽略**实际打包(Actual Builds)**的测试:
# 使用 Jump 进行测试(快速,适用于开发)
php artisan native:jump
# 构建用于彻底测试
# (需要 Bifrost 或自行设置)
变现你的 App
如果你正打算用 NativePHP 构建移动应用并赚取收益,这里有几种变现方案可供选择:
1. 应用内购买 (In-App Purchases)
你可以利用第三方插件,或者直接通过原生层集成 iOS 的 StoreKit 和 Android 的 Google Play Billing。
2. 订阅模式 (Subscription Model)
这是 SaaS(软件即服务)类应用的绝佳选择。你可以将移动端与 Laravel Cashier 结合使用:
use Laravel\Cashier\Cashier;
public function subscribe()
{
$user = auth()->user();
$user->newSubscription('premium', 'price_monthly')
->create($paymentMethod);
// 更新 App 中的状态
$this->isPremium = true;
}
3. 广告集成 (Ads Integration)
你可以使用社区提供的 AdMob 插件,或者通过自定义插件自行接入。
竞品对比:选择最适合你的武器
NativePHP vs. React Native
NativePHP:
- ✅ 驾轻就熟:直接使用你已经掌握的 PHP 技能。
- ✅ 全家桶支持:完美调用 Laravel 生态(Eloquent、Queue 队列等)。
- ✅ 极简配置:安装与环境搭建更简单。
- ❌ 社区较小:目前开发者群体还在成长中。
- ❌ 生态差异:现成插件的数量暂时少于 RN。
React Native:
- ✅ 生态繁荣:庞大的社区支持。
- ✅ 库文件丰富:几乎能找到任何你想要的第三方库。
- ❌ 学习门槛:必须掌握 JavaScript 和 React。
- ❌ 配置繁琐:原生环境的配置和调试相对复杂。
NativePHP vs. Flutter
NativePHP:
- ✅ 专注 PHP:无需离开 Laravel/PHP 舒适区。
- ✅ 逻辑共享:移动端与 Web 端可以共用大量后端代码。
- ❌ 性能上限:在高频高性能需求下,略逊于 Flutter。
Flutter:
- ✅ 顶级性能:渲染速度极快,体验丝滑。
- ✅ UI 高度一致:跨端视觉效果完全同步。
- ❌ 全新语言:必须从头学习 Dart 语言。
- ❌ 前后端分离:移动端是一个完全独立的体系,无法直接复用 Laravel 逻辑。
🗺️ 发展路线图:NativePHP 的未来
NativePHP 团队雄心勃勃,已经规划了清晰的未来:
- 开放插件市场 (Plugin Marketplace) :开发者可以上架并出售自己编写的插件。
- Mimi AI:专属 AI 助手,通过对话就能帮你生成 NativePHP 应用。
- 持续扩充插件库:官方将不断增加新的原生功能支持。
- 桌面端持续发力:在移动端突破的同时,已有的桌面端支持也将得到进一步强化。
🏁 结语
NativePHP 移动版宣布免费,对于想要进军移动端却不想从零学习新技术的 Laravel 开发者来说,这绝对是改变游戏规则的时刻。
为什么你现在就该尝试?
- 完全免费:核心框架和 9 款核心插件全部免费开放。
- 死磕 PHP:无需为了做个 App 去学 Swift 或 Kotlin。
- 插件化架构:模块化设计,扩展性极强。
- Jump 极速预览:无需漫长编译,手机扫码即可看到改动。
- Laravel 力量:整个 Laravel 生态都是你坚实的后盾。
- 生产环境就绪:已经有大量应用在正式环境平稳运行。
🔥 还在等什么?现在就开始你的第一个 NativePHP 项目吧!:
# 创建新项目
laravel new my-app
cd my-app
# 安装 NativePHP
composer require nativephp/mobile
# 在手机上安装 Jump
# 从 bifrost.nativephp.com/jump 下载
# 开始开发
php artisan native:jump
这不仅仅是一个技术的终点,更是一个属于 Laravel 开发者的大航海时代的起点。
2026 年,NativePHP 3 (Air) 的发布彻底推倒了 Web 与原生应用之间的高墙。你现在掌握的,是这个时代最高效的“跨界超能力”。