快乐两小时

这是我的一个博客

PHP桌面应用打包笔记

- 发布于 PHP 来自

前言

该笔记针对 windows 环境

准备工作

  • PHP For Windows 下载(我这里选择 php-8.2.19-nts-Win32-vs16-x64)->解压->配置好ini(不会自行百度)->开启FFI拓展->配置好环境
  • composer 配置composer环境
  • Enigma Virtual Box (这是一个封装压缩成单文件的应用)
  • Inno Setup (这是一个打包为安装包应用,有中文版的自行百度)

1. 压缩PHP

运行Enigma Virtual Box应用

  1. 主程序:选择你的php-win.exe文件

  2. 新增->添加文件夹[递归] :选择你的PHP文件夹,这里我选择的是C:\environment\php-8.2.19-nts(请根据你的实际情况选择)

  3. 文件选项:勾选->压缩文件和勾选->启动文件虚拟化

  4. 执行封包(你会拿到一个单文件),这里我的是php-win_boxed.exe

2. 创建桌面php应用

  • 新建文件夹php-webui(根据你的实际情况新建)

  • 进入文件夹php-webui(根据你的实际情况新建)

执行composer(这里我选择webui的php桌面拓展)

composer require kingbes/webui
  • 新建文件 index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>phpwebui</title>
    <!-- You must add webu.js; otherwise, the interaction cannot be performed -->
    <script src="webui.js"></script>
</head>

<body>
    <p>hello world for php webui</p>
    <button onclick="btn()">btn</button>
    <script>
        function btn() {
            hello('hello').then(function (res) {
                console.log(res)
            })
        }
    </script>
</body>

</html>
  • 新建入口文件index.php
<?php
require "./vendor/autoload.php";

use Kingbes\Webui;
use Kingbes\JavaScript;

// 实例
$Webui = new Webui;
// html字符串
$html = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . "index.html");
// 创建一个窗口
$window = $Webui->newWindow();
// 绑定js函数
$bind = $Webui->bind($window, "hello", function ($event, JavaScript $js) {
    // 获取第一个参数为字符串
    $arg_one = $js->getString($event);
    var_dump($arg_one);

    // 返回字符串
    $js->returnString($event, "nihao");
});
// 显示窗口
$Webui->show($window, $html);
// 等待
$Webui->wait();
// 释放所有
$Webui->clean();

  • 将封包好的php-win_boxed.exe文件复制到php-webui文件夹中

执行指令

.\php-win_boxed.exe index.php

3. 将应用打包为安装包

执行应用 Inno Setup

  • 选择->用[脚本向导]创建新的脚本文件->下一步
  • 填写信息:应用程序信息(更具你的信息填写即可)->下一步(应用程序文件夹,不用管)->下一步
  • 应用程序文件:主程序执行文件(选择你的 php-win_boxed.exe 文件,我这里选择是C:\project\php\php-webui\php-win.exe)->添加文件夹(选择你新建的 php-webui,包含你的子文件)->下一步
  • 创建程序快捷方式:勾选(允许用户创建桌面快捷方式)->下一步
  • 应用程序文档:(根据你的实际情况填)->下一步
  • 安装程序安装模式: 选择->管理员安装模式(为所有用户安装)-下一步
  • ...剩下的根据你的实际情况填写
  • Inno Setup 预处理器:勾选->使用 #define 编译指令->下一步
  • 完成

接下来会弹出 是否立即编译脚本 ,选择否

然后修改一下代码

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppExeName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Parameters:"index.php"; 

再编译即可

假设你要添加自己的 ico图标 你就把图标文件添加到你的 php-webui文件夹中 然后修改以下代码(我的ico图标文件夹是favicon.ico,根据你的实际情况)

Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppExeName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Parameters:"index.php"; IconFilename:"{app}\favicon.ico"

PHP的FFI拓展的使用

- 发布于 PHP 来自

从 编 译 C 动 态 库 到 php 的 FFI 拓 展 使 用

要求 版本
FFI *

没有安装 FFI 拓展,自行安装

编写C代码

新建 demo.c 文件

// 包含c的stdio库(根据实际情况添加文件头)
#include <stdio.h>

//---------------- 设置导出名 `EXPORT` (全大写可加下划线、可自定义,例如 ASD_API)
#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
// ---------------

// 导出名 函数返回类型 函数名(类型 参数...)
EXPORT int cAdd(int a, int b)
{
    return a + b;
}

编译C动态库

  • linux 环境

使用gcc编译动态库

gcc 参数后面指定你要编译的文件,这里是 demo.c。根据你的实际情况修改

-shared 参数表示生成动态库。

-o 参数后面指定输出的动态库文件名,这里是 demo.so。根据你实际情况修改(必须是so文件)

-std=c++11 参数表示这个是c++文件,编译文件格式为cc或者cpp,这里使用c所以没使用

gcc demo.c -shared -o demo.so
  • windows 环境

这里使用 tcc 编译器 根据实际情况下载 win64或者win32,我这里选择 tcc-0.9.27-win64-bin.zip,解压,设置环境变量

tcc.exe 编译器运行文件

-shared 参数表示生成动态库,参数后面是要编译的C文件,这里是demo.c。(根据实际情况修改)

-o 参数后面指定输出的动态库文件名,这里是 demo.dll。根据你实际情况修改(必须是dll文件)

-x c++ 参数表示这个是c++文件,编译文件格式为cc或者cpp,这里使用c所以没使用

tcc.exe -shared demo.c -o demo.dll

PHP调用C动态库

新建C头文件 demo.h

int cAdd(int a, int b);

新建php文件 demo.php

<?php

// 严格模式
declare(strict_types=1);

// 头文件 内容
$header_file = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . "demo.h");

// 动态库文件路径 linux为so文件 windows为dll文件
$library_file = __DIR__ . DIRECTORY_SEPARATOR . "demo.so";

// 创建 FFI 对象
$ffi = FFI::cdef($header_file, $library_file);

// 调用函数cAdd
$add = $ffi->cAdd(1, 2);

// 打印结果
var_dump($add);

PHP与C的类型转换

php c
int int
string const char *
float float
double double
bool bool
null NULL
object struct
void void
mixed 所有

下面是一些用法

int

C文件

...忽略

// 返回类型为int 
// 参数a为int类型
// 参数b为int类型
EXPORT int cAdd(int a, int b)
{
    return a + b;
}

h文件头内容

// 返回类型为int 
// 参数a为int类型
// 参数b为int类型
int cAdd(int a, int b);

php文件

<?php

...忽略

// 我传递相同类型即可
$ffi->cAdd(1, 1);

C的struct结构体类型

C文件

...忽略

// 结构体
typedef struct demo_t {
    int mynum;
    const char* str;
} demo_t;

// 返回类型为结构体 
// 参数t为结构体类型
EXPORT demo_t cStruct(demo_t* t)
{
    ...
    return t;
}

h文件头内容

// 结构体
typedef struct demo_t {
    int mynum;
    const char* str;
} demo_t;
// 返回类型为结构体 
// 参数t为结构体类型
demo_t cStruct(demo_t* t);

php文件

<?php

...忽略

// 直接创建 C 的结构体,$demo_t得到是一个php对象类型
// $ffi->new可以创建C的任意数据类型
$demo_t = $ffi->new('struct demo_t'); 

// $demo_t得到一个object
/* object(FFI\CData:struct demo_t)#3 (2) {
    ["mynum"]=>
    int(0)
    ["str"]=>
    NULL
} */

// 根据类型 赋值 即可
$demo_t->mynum = 12;

$demo_t->str = "hello";

// 参数传递
$obj = $ffi->cStruct($demo_t);

// 最后$obj也会是php的对象类型

面对PHP没用的类型可以用使用 FFI->new 函数创建

  • 下面例子

C文件

// 枚举
typedef enum {
    my_num_one = 0,
    my_num_two = 1,
} my_enums;

php文件


// 创建C的int类型 $c_int = $ffi->new('int'); // 赋值 $c_int = 1; // C的枚举 $c_enum_one = $ffi->new('my_num_one'); // 拿c枚举中的my_num_one,或者直接传递 int // 创建C的long类型 $c_long = $ffi->new('long'); // 创建C的long long类型 $c_long_long = $ffi->new('long long') // 创建C的int数组0~10 $c_int_array = $ffi->new('int[10]') for ($i = 0; $i < count($c_int_array); $i++) { $c_int_array[$i] = $i; }

C的函数类型传参

C文件

...忽略

// 传递一个函数且参数int a
EXPORT void cClosure(void (*func)(int a))
{
    ...
}

h文件头内容

void cClosure(void (*func)(int a));

php文件

// 新建php函数,参数a为int
function func(int $a){
    ...
}

// 创建c函数 第一个参数为int
$callback = $ffi->new('void (*)(int *)');

// 赋值
$callback = function($c_int){
    return func($c_int);
};

// 调用C
$ffi->cClosure($callback);

linux 编译安装 FFI 拓展

当然这个是确保已经安装了 php 环境下

从官方下载PHP源码,解压,进入ext/ffi 目录

apt install build-essential

安装 FFI 依赖库

apt install libffi-dev

执行phpize文件

phpize

配置编译选项。--with-php-config=php-config文件

./configure --with-php-config=/usr/local/bin/php-config

编译和安装

make && make install

修改php的ini配置文件

extension=ffi
ffi.enable=true

检查是否安装成功

php -m | grep FFI

# 出现 FFI 表示安装成功
FFI

windows 环境开启 FFI

当然这个是确保已经安装了 php 环境下

修改php的ini配置文件

extension=ffi
ffi.enable=true

检查是否安装成功

php -m | grep FFI
# 出现 FFI 表示安装成功
FFI