小破孩的小站 - PHP
type: channel
link: https://blog.8688web.com/index.php/tag/PHP/
items: 【PHP】一个流行的 PHP 日期时间处理库 nesbot/carbon
items: PHP8.0 + 版本按数据处理类型分类的新增函数整理表格，包含函数名、对应版本、功能说明及可直接运行的用法示例
items: 【Elasticsearch】Elasticsearch 与数据库实时同步方案
items: 【Elasticsearch】IK 分词器安装指南和PHP使用实例
items: 【Elasticsearch】 PHP 使用ES示例demo
items: 【PHP】境外微信支付生成平台证书 - wechatpay
items: 【PHP】bc系列函数（高精度数学运算）
items: 【PHP】ThinkPHP8数据库迁移示例
items: 【PHP】ThinkPHP8 数据库迁移与数据填充完全指南
items: 【ThinkPHP】创建有背景的微信小程序二维码

【PHP】一个流行的 PHP 日期时间处理库 nesbot/carbon
type: item
date: 2025-12-16T09:01:00+08:00
description: 官方文档：https://carbon.nesbot.com/docs/安装方法1. 基本安装# 在项目根目录执行 composer require nesbot/carbon2. 指定版本安装# 安装特定版本 composer require nesbot/carbon:^2.0 # 安装开发版本 composer require nesbot/carbon --dev基本使用方法1. 引入 Carbonuse Carbon\Carbon; // 获取当前时间 $now = Carbon::now(); echo $now; // 2023-10-27 14:30:00 // 创建指定时间 $date = Carbon::create(2023, 12, 25, 10, 30, 0);2. 常用方法示例use Carbon\Carbon; // 格式化输出 echo Carbon::now()->format('Y-m-d H:i:s'); echo Carbon::now()->toDateString(); // 2023-10-27 echo Carbon::now()->toDateTimeString(); // 2023-10-27 14:30:00 // 日期操作 $tomorrow = Carbon::now()->addDay(); $yesterday = Carbon::now()->subDay(); $nextWeek = Carbon::now()->addWeek(); // 日期比较 $date1 = Carbon::create(2023, 10, 27); $date2 = Carbon::create(2023, 10, 28); if ($date1->lt($date2)) { // less than echo "date1 在 date2 之前"; } // 时区设置 $date = Carbon::now('Asia/Shanghai'); $date->setTimezone('America/New_York');3. 本地化（中文支持）# 安装语言包 composer require carbonphp/carbon-doctrine-typesuse Carbon\Carbon; // 设置中文 Carbon::setLocale('zh'); echo Carbon::now()->diffForHumans(); // 刚刚 echo Carbon::now()->subMinutes(5)->diffForHumans(); // 5分钟前配置文件（可选）在 Laravel 项目中，可以在 config/app.php 中配置时区：'timezone' => 'Asia/Shanghai', 'locale' => 'zh-CN',验证安装创建测试文件验证安装：<?php require 'vendor/autoload.php'; use Carbon\Carbon; echo "当前时间：" . Carbon::now() . "\n"; echo "明天：" . Carbon::now()->addDay() . "\n"; echo "一周前：" . Carbon::now()->subWeek()->diffForHumans() . "\n";常见问题解决版本兼容性问题# 查看当前PHP版本支持的Carbon版本 php -v # 如果PHP版本较旧，使用Carbon 1.x composer require nesbot/carbon:^1.39内存问题# 清理composer缓存 composer clear-cache代理设置# 如果下载慢，使用国内镜像 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/在框架中的使用LaravelCarbon 已集成在 Laravel 中，可直接使用：// 在Laravel中直接使用 $date = now(); // Carbon实例 echo $date->format('Y-m-d');其他框架// Slim, Symfony等框架 require __DIR__ . '/vendor/autoload.php'; use Carbon\Carbon; $carbon = new Carbon();这样就成功安装并可以使用 Carbon 来处理日期时间了。

PHP8.0 + 版本按数据处理类型分类的新增函数整理表格，包含函数名、对应版本、功能说明及可直接运行的用法示例
type: item
date: 2025-12-11T15:23:08+08:00
description: 分类函数名PHP版本功能说明用法示例字符串处理str_contains()8.0检查字符串是否包含指定子串（区分大小写），返回布尔值$str = "Hello PHP8"; var_dump(str_contains($str, "PHP8")); // bool(true)var_dump(str_contains($str, "php8")); // bool(false)字符串处理str_starts_with()8.0检查字符串是否以指定子串开头（区分大小写），返回布尔值$str = "2025-12-11"; var_dump(str_starts_with($str, "2025")); // bool(true)var_dump(str_starts_with($str, "2024")); // bool(false)字符串处理str_ends_with()8.0检查字符串是否以指定子串结尾（区分大小写），返回布尔值$str = "test.txt"; var_dump(str_ends_with($str, ".txt")); // bool(true)var_dump(str_ends_with($str, ".php")); // bool(false)字符串处理mb_str_split()8.0多字节安全的字符串分割，支持指定分割长度（解决str_split多字节乱码）$str = "你好PHP8"; print_r(mb_str_split($str)); // [0]=>你 [1]=>好 [2]=>P [3]=>H [4]=>P [5]=>8print_r(mb_str_split($str, 2)); // [0]=>你好 [1]=>PH [2]=>P8字符串处理strlcpy()8.1安全的字符串拷贝（限制长度，含终止符），返回源字符串总长度$dest = ''; $src = "Hello World";$len = strlcpy($dest, $src, 6); echo $dest; // Helloecho $len; // 11字符串处理mb_str_pad()8.3多字节安全的字符串填充（解决str_pad多字节长度计算错误）$str = "你好"; echo mb_str_pad($str, 5, "-", STR_PAD_RIGHT); // 你好---数组处理array_is_list()8.1检查数组是否为“列表”（索引从0开始、连续递增的整数索引），返回布尔值$list = [1,2,3]; $notList = [0=>1,2=>3];var_dump(array_is_list($list)); // bool(true) var_dump(array_is_list($notList)); // bool(false)数组处理array_find()8.3返回数组中第一个满足回调条件的元素，无则返回null$nums = [1,2,3,4,5]; $even = array_find($nums, fn($n) => $n % 2 === 0); echo $even; // 2数组处理array_find_key()8.3返回数组中第一个满足回调条件的键，无则返回null$users = ['a'=>18, 'b'=>22, 'c'=>19]; $key = array_find_key($users, fn($age) => $age > 20); echo $key; // b数组处理array_any()8.3检查数组是否存在至少一个满足回调条件的元素，返回布尔值$nums = [1,3,5]; var_dump(array_any($nums, fn($n) => $n%2===0)); // bool(false)$nums[]=8; var_dump(array_any($nums, fn($n) => $n%2===0)); // bool(true)数组处理array_all()8.3检查数组所有元素是否都满足回调条件，返回布尔值$nums = [2,4,6]; var_dump(array_all($nums, fn($n) => $n%2===0)); // bool(true)$nums[]=9; var_dump(array_all($nums, fn($n) => $n%2===0)); // bool(false)数值处理fdiv()8.0浮点数除法（优雅处理除0场景，返回INF/-INF/NaN，替代普通除法）var_dump(fdiv(10,2)); // float(5)var_dump(fdiv(10,0)); // float…

【Elasticsearch】Elasticsearch 与数据库实时同步方案
type: item
date: 2025-11-14T11:14:00+08:00
description: 在宝塔环境中实现 Elasticsearch 与数据库的实时同步，主要有以下几种方案：一、同步方案概览方案实时性复杂度数据一致性适用场景应用层双写最高中等最终一致新建项目，可修改代码Logstash JDBC分钟级低延迟一致已有项目，增量同步Canal秒级高最终一致MySQL 数据库，要求高实时性数据库触发器实时高强一致数据库层面同步二、方案一：应用层双写（推荐）实现原理在业务代码中同时写入数据库和 Elasticsearch。PHP 实现示例<?php /** * 应用层双写方案 * 在写入数据库的同时写入 Elasticsearch */ require '/www/wwwroot/your-site/vendor/autoload.php'; use Elasticsearch\ClientBuilder; class DualWriteService { private $esClient; private $db; public function __construct() { // 初始化 ES 客户端 $this->esClient = ClientBuilder::create() ->setHosts(['localhost:9200']) ->build(); // 初始化数据库连接 $this->initDatabase(); } private function initDatabase() { $host = 'localhost'; $dbname = 'your_database'; $username = 'your_username'; $password = 'your_password'; try { $this->db = new PDO( "mysql:host={$host};dbname={$dbname};charset=utf8mb4", $username, $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); } catch (PDOException $e) { throw new Exception("数据库连接失败: " . $e->getMessage()); } } /** * 添加文章（双写） */ public function addArticle($articleData) { $dbResult = $this->writeToDatabase($articleData); if ($dbResult['success']) { $esResult = $this->writeToElasticsearch($articleData, $dbResult['id']); if (!$esResult['success']) { // ES 写入失败，记录日志或加入重试队列 $this->logSyncFailure('add', $dbResult['id'], $articleData); } return $dbResult; } return $dbResult; } /** * 更新文章（双写） */ public function updateArticle($id, $updateData) { $dbResult = $this->updateDatabase($id, $updateData); if ($dbResult['success']) { $esResult = $this->updateElasticsearch($id, $updateData); if (!$esResult['success']) { $this->logSyncFailure('update', $id, $updateData); } } return $dbResult; } /** * 删除文章（双写） */ public function deleteArticle($id) { $dbResult = $this->deleteFromDatabase($id); if ($dbResult['success']) { $esResult = $this->deleteFromElasticsearch($id); if (!$esResult['success']) { $this->logSyncFailure('delete', $id); } } return $dbResult; } /** * 写入数据库 */ private function writeToD…

【Elasticsearch】IK 分词器安装指南和PHP使用实例
type: item
date: 2025-11-13T11:38:00+08:00
description: IK 分词器安装指南是的，IK 分词器需要单独安装。 Elasticsearch 默认只提供标准分词器，对于中文分词效果不佳，IK 分词器是专门针对中文优化的分词插件。一、IK 分词器安装方法方法 1：手动安装（推荐）步骤 1：下载对应版本的 IK 分词器# 进入 Elasticsearch 插件目录 cd /www/server/elasticsearch/plugins # 创建 ik 目录 mkdir ik cd ik # 下载对应版本的 IK 分词器 # 注意：版本号必须与 Elasticsearch 版本一致！ # 查看 ES 版本：curl -X GET "localhost:9200/" wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip # 解压 unzip elasticsearch-analysis-ik-7.17.0.zip # 删除 zip 文件 rm -f elasticsearch-analysis-ik-7.17.0.zip # 返回插件目录上级，确保目录结构正确 cd .. ls -la ik/步骤 2：重启 Elasticsearch# 通过宝塔面板重启 # 或者使用命令 systemctl restart elasticsearch步骤 3：验证安装# 验证 IK 分词器是否安装成功 curl -X GET "localhost:9200/_cat/plugins?v" # 测试分词效果 curl -X POST "localhost:9200/_analyze" -H 'Content-Type: application/json' -d' { "analyzer": "ik_smart", "text": "中华人民共和国" }'正常响应：{ "tokens" : [ { "token" : "中华人民共和国", "start_offset" : 0, "end_offset" : 7, "type" : "CN_WORD", "position" : 0 } ] }方法 2：使用 Elasticsearch 插件命令安装# 进入 Elasticsearch 安装目录 cd /www/server/elasticsearch # 使用插件管理器安装 ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip # 重启 Elasticsearch systemctl restart elasticsearch二、宝塔面板特有的安装方法通过宝塔终端操作打开宝塔面板 → 终端执行安装命令：# 切换到 elasticsearch 用户（宝塔环境可能需要） sudo -u elasticsearch /bin/bash # 进入 ES 插件目录 cd /www/server/elasticsearch/plugins # 下载并安装 IK 分词器 wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip unzip elasticsearch-analysis-ik-7.17.0.zip -d ik rm -f elasticsearch-analysis-ik-7.17.0.zip # 退出 elasticsearch 用户 exit # 重启 Elasticsearch 服务 systemctl restart elasticsearch宝塔文件管理器操作下载 IK 分词器：访问 IK 分词器 Releases下载对应版本的 ZIP 文件通过宝塔文件管理器上传：进入 /www/server/elasticsearch/plugins/创建 ik 文件夹上传 ZIP 文件到 ik 文件夹解压并删除 ZIP 文件重启服务：宝塔面板 → 软件商店 → Elasticsearch → 重启三、IK 分词器版本对应关系Elasticsearch 版本IK 分词器版本7.17.x7.17.07.16.x7.16.07.15.x7.15.07.14.x7.14.07.13.x7.13.07.12.x7.12.07.…

【Elasticsearch】 PHP 使用ES示例demo
type: item
date: 2025-11-13T11:13:00+08:00
description: 1. 使用 Composer 安装# 安装 Composer（如果未安装） curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer # 创建项目目录 mkdir elasticsearch-php-demo cd elasticsearch-php-demo # 初始化 Composer composer init # 安装 Elasticsearch PHP 客户端 composer require elasticsearch/elasticsearch2. 手动安装（不使用 Composer）<?php // 手动引入 Elasticsearch 客户端 require_once 'path/to/elasticsearch-php/autoload.php';示例 1：基础连接和索引操作<?php require 'vendor/autoload.php'; use Elasticsearch\ClientBuilder; class ElasticsearchDemo { private $client; public function __construct() { // 创建 Elasticsearch 客户端 $this->client = ClientBuilder::create() ->setHosts(['localhost:9200']) // ES 服务器地址 ->setRetries(2) // 重试次数 ->build(); // 检查连接 if (!$this->checkConnection()) { throw new Exception("无法连接到 Elasticsearch"); } } /** * 检查 Elasticsearch 连接 */ private function checkConnection() { try { return $this->client->ping(); } catch (Exception $e) { echo "连接失败: " . $e->getMessage() . "\n"; return false; } } /** * 创建索引 */ public function createIndex($indexName) { $params = [ 'index' => $indexName, 'body' => [ 'settings' => [ 'number_of_shards' => 2, 'number_of_replicas' => 1 ], 'mappings' => [ 'properties' => [ 'title' => [ 'type' => 'text', 'analyzer' => 'standard' ], 'content' => [ 'type' => 'text', 'analyzer' => 'standard' ], 'author' => [ 'type' => 'keyword' ], 'publish_date' => [ 'type' => 'date' ], 'views' => [ 'type' => 'integer' ] ] ] ] ]; try { $response = $this->client->indices()->create($params); echo "索引 {$indexName} 创建成功\n"; return $response; } catch (Exception $e) { echo "创建索引失败: " . $e->getMessage() . "\n"; return false; } } /** * 删除索引 */ public function deleteIndex($indexName) { $params = ['index' => $indexName]; try { // 检查索引是否存在 if ($this->client->indices()->exists($params)) { $response = $this->client->indices()->delete($params); echo "索引 {$indexName} 删除成功\n"; return $response; } else { echo "索引 {$indexName} 不存在\n"; return false; } } catch (Exception $e) { echo "删除索引失败: " . $e->getMessage()…

【PHP】境外微信支付生成平台证书 - wechatpay
type: item
date: 2025-09-05T17:56:00+08:00
description: 使用方法 $wechatPayPlatformCertInstance = new \addons\unidrink\library\pay\overseas\wechat\Certificate(); try { $platformCerts = $wechatPayPlatformCertInstance->getPlatformCerts(); if (!empty($platformCerts)) { // 获取最新的证书 $latestCert = $platformCerts[0]; // 返回成功信息 return json([ 'code' => 1, 'msg' => '平台证书生成成功', 'data' => [ 'serial_no' => $latestCert['serial_no'], 'effective_time' => $latestCert['effective_time'], 'expire_time' => $latestCert['expire_time'], 'cert_saved_path' => $wechatPayPlatformCertInstance->getCertPath() . $latestCert['serial_no'] . '.pem', ] ]); } else { throw new Exception('未获取到平台证书'); } } catch (Exception $e) { // 错误处理 return json([ 'code' => 0, 'msg' => '平台证书生成失败: ' . $e->getMessage(), 'data' => [] ]); } 下面是类 <?php namespace addons\unidrink\library\pay\overseas\wechat; use think\Exception; use think\Log; use WeChatPay\Crypto\Rsa; use WeChatPay\Formatter; use WeChatPay\Util\PemUtil; class Certificate { // 配置参数 private $config; // 证书保存路径 private $certPath; // 平台证书列表 private $platformCerts = []; /** * 构造函数 * @param array $config 微信支付配置 */ public function __construct($config = []) { // 默认配置 $defaultConfig = [ 'mch_id' => '', // 商户号 'serial_no' => '', // 商户API证书序列号 'private_key' => ROOT_PATH . 'public/cert/apiclient_key.pem', // 商户私钥内容或路径 'api_v3_key' => '', // APIv3密钥 'cert_path' => ROOT_PATH . 'public/cert/wechatpay/', // 证书保存目录 'ca_cert_path' => ROOT_PATH . 'public/cert/cacert.pem' // CA证书路径 ]; $this->config = array_merge($defaultConfig, $config); $this->certPath = $this->config['cert_path']; // 验证APIv3密钥长度 $this->validateApiV3Key(); // 创建证书目录并确保权限 $this->prepareDirectories(); } /** * 准备目录并确保权限 */ private function prepareDirectories() { // 创建证书目录 if (!is_dir($this->certPath)) { if (!mkdir($this->certPath, 0755, true)) { throw new Exception('无法创建证书目录: ' . $this->certPath); } } // 检查证书目录权限 if (!is_writable($this->certPath)) { throw new Exception('证书目录不可写: ' . $this->certPath); } // 确保CA证书目录可写 $caCertDir = dirname($this->config['ca_cert_path']); if (!i…

【PHP】bc系列函数（高精度数学运算）
type: item
date: 2025-08-18T14:28:06+08:00
description: PHP bc系列函数（高精度数学运算）简介：bc系列函数用于处理任意精度的十进制计算，适用于金融、科学计算等对精度要求极高的场景。需启用bcmath扩展（可通过phpinfo()检查是否启用）。特点：所有参数和返回值均为字符串类型（避免精度丢失）支持自定义小数位数（scale参数）提供基本运算、比较、幂运算等完整功能一、基础运算函数1. 加法：bcadd()// 语法：bcadd(左操作数, 右操作数, 保留小数位数) echo bcadd('123.456', '78.9', 2); // 202.36（123.456 + 78.9 = 202.356，保留2位小数） echo bcadd('100', '200', 0); // 300（整数相加）2. 减法：bcsub()// 语法：bcsub(被减数, 减数, 保留小数位数) echo bcsub('100.5', '30.25', 1); // 70.2（100.5 - 30.25 = 70.25，保留1位小数） echo bcsub('50', '75', 0); // -25（负数结果）3. 乘法：bcmul()// 语法：bcmul(乘数1, 乘数2, 保留小数位数) echo bcmul('2.5', '4.2', 2); // 10.50（2.5 × 4.2 = 10.5，保留2位小数） echo bcmul('100', '0.01', 0); // 1（整数与小数相乘）4. 除法：bcdiv()// 语法：bcdiv(被除数, 除数, 保留小数位数) echo bcdiv('10', '3', 3); // 3.333（10 ÷ 3 ≈ 3.3333...，保留3位） var_dump(bcdiv('5', '0', 0)); // false（除数为0时返回false）5. 取模（余数）：bcmod()// 语法：bcmod(被除数, 除数) echo bcmod('10', '3'); // 1（10 ÷ 3 余数为1） echo bcmod('7.5', '2.5'); // 0.0（7.5 是 2.5 的整数倍）二、比较函数bccomp()// 语法：bccomp(值1, 值2, 比较精度) // 返回值：1（值1 > 值2）、0（相等）、-1（值1 < 值2） echo bccomp('10', '10', 0); // 0（相等） echo bccomp('10.5', '10.49', 1); // 1（10.5 > 10.49，精确到1位小数） echo bccomp('5.999', '6', 2); // -1（5.999 < 6，精确到2位小数）三、幂运算与开方1. 幂运算：bcpow()// 语法：bcpow(底数, 指数, 保留小数位数) echo bcpow('2', '3', 0); // 8（2³ = 8） echo bcpow('1.2', '2', 2); // 1.44（1.2² = 1.44）2. 幂运算取模：bcpowmod()// 语法：bcpowmod(底数, 指数, 模数, 保留小数位数) // 作用：(base^exponent) % modulus，比先算幂再取模更高效 echo bcpowmod('3', '3', '5', 0); // 2（3³=27，27%5=2）3. 开平方：bcsqrt()// 语法：bcsqrt(数值, 保留小数位数) echo bcsqrt('25', 0); // 5（√25 = 5） echo bcsqrt('2', 4); // 1.4142（√2 ≈ 1.4142，保留4位） var_dump(bcsqrt('-4', 0)); // false（负数无法开平方）四、全局配置函数bcscale()// 功能：设置所有bc函数的默认小数位数（全局生效） bcscale(2); // 全局默认保留2位小数 echo bcadd('1', '2'); // 3.00（使用全局配置） echo bcsub('5', '3.5'); // 1.50（自动补全2位小数） echo bcadd('1', '2', 0); // 3（局部参数优先级高于全局）关键注意事项参数类型：必须传入字符串（如'123.45'），而非数字（避免自动转换导致精度丢失）。返回值：始终为字符串，需转换类型时用(float)或(int)。扩展依赖：需在php.ini中启用extension=bcmath（重启服务生效）。精度优先级：函数的scale参数 > bcscale()全局配置。通过这些函数，PHP可以实现高精度计算，解决普通数值类型精度不足的问题。

【PHP】ThinkPHP8数据库迁移示例
type: item
date: 2025-07-26T16:27:00+08:00
description: 以下是一个包含 各种字段类型、约束条件、索引和特殊配置 的ThinkPHP8数据库迁移示例（以products产品表为例），覆盖常见场景：迁移文件示例（database/migrations/20250726153000_create_products_table.php）<?php use think\migration\Migrator; use think\migration\db\Column; class CreateProductsTable extends Migrator { /** * 执行迁移（创建表结构） */ public function change() { // 创建products表，配置表级参数 $table = $this->table('products', [ 'engine' => 'InnoDB', // 数据库引擎 'charset' => 'utf8mb4', // 字符集 'collation' => 'utf8mb4_unicode_ci', // 排序规则 'comment' => '产品表（包含多种字段类型示例）', // 表注释 'auto_increment' => 1000, // 自增ID起始值（MySQL支持） ]); // 字段定义（覆盖各种场景） $table // 1. 自增主键（整数型，无符号，自增） ->addColumn('id', 'integer', [ 'identity' => true, // 自增 'unsigned' => true, // 无符号（只存正数） 'comment' => '产品ID（主键）' ]) // 2. 字符串（有限长度，非空，唯一索引） ->addColumn('sku', 'string', [ 'limit' => 50, // 长度限制 'null' => false, // 非空（默认） 'comment' => '产品SKU（唯一标识）', 'unique' => true // 唯一约束 ]) // 3. 字符串（长文本，可空，带默认值） ->addColumn('name', 'string', [ 'limit' => 255, 'null' => false, 'default' => '', // 默认空字符串 'comment' => '产品名称' ]) // 4. 文本类型（无长度限制，可空） ->addColumn('description', 'text', [ 'null' => true, // 允许为空 'comment' => '产品详细描述' ]) // 5. 小数类型（高精度，带默认值） ->addColumn('price', 'decimal', [ 'precision' => 10, // 总位数 'scale' => 2, // 小数位数 'default' => 0.00, // 默认0.00 'comment' => '产品售价' ]) // 6. 整数类型（无符号，默认值，索引） ->addColumn('stock', 'integer', [ 'unsigned' => true, // 无符号（只存正数） 'default' => 0, // 默认库存0 'comment' => '库存数量', 'index' => true // 普通索引 ]) // 7. 枚举类型（固定可选值） ->addColumn('status', 'enum', [ 'values' => ['draft', 'active', 'disabled'], // 可选值 'default' => 'draft', // 默认草稿状态 'comment' => '产品状态（draft:草稿, active:上架, disabled:下架）' ]) // 8. 布尔类型（tinyint，默认true） ->addColumn('is_recommend', 'boolean', [ 'default' => true, // 默认true（1） 'comment' => '是否推荐（1:是, 0:否）' ]) // 9. 外键关联（整数，无符号，级联操作） ->addColumn('category_id', 'integer', [ 'unsigned' => true, 'null' => false, 'comment' => '所属分类ID', 'index' => true // 普通索引 ]) // 10. JSON类型（存储复杂结构，可空） ->addColumn('tags', 'json', [ 'null' => true, 'comment' => '产品…

【PHP】ThinkPHP8 数据库迁移与数据填充完全指南
type: item
date: 2025-07-26T10:25:00+08:00
description: ThinkPHP8 数据库迁移与数据填充完全指南（官方文档补充版）一、安装与配置1. 安装迁移扩展composer require topthink/think-migration2. 配置文件说明配置文件位于 config/database.php默认迁移表名：think_migration支持多数据库连接3. 环境要求PHP >= 8.1ThinkPHP >= 8.0建议使用 InnoDB 引擎（支持事务）二、迁移文件操作1. 创建迁移文件# 创建基础迁移文件 php think migrate:create CreateUsersTable # 创建带表名的迁移文件（自动生成基础结构） php think migrate:create AddEmailToUsersTable --table=users # 指定数据库连接 php think migrate:create CreateLogsTable --connection=log2. 迁移文件结构生成的迁移文件位于 database/migrations，示例：<?php use think\migration\Migrator; use think\migration\db\Column; class CreateUsersTable extends Migrator { /** * 执行迁移（向上操作） */ public function up() { $table = $this->table('users'); $table->addColumn('name', 'string', ['limit' => 50]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addTimestamps() // 自动添加 create_time 和 update_time 字段 ->create(); } /** * 回滚迁移（向下操作） */ public function down() { $this->dropTable('users'); } }三、常用字段类型字段类型描述示例用法integer整数类型->addColumn('age', 'integer')biginteger大整数类型->addColumn('user_id', 'biginteger')string字符串类型->addColumn('name', 'string', ['limit' => 50])text长文本类型->addColumn('content', 'text')datetime日期时间类型->addColumn('created_at', 'datetime')timestamp时间戳类型->addColumn('updated_at', 'timestamp')boolean布尔类型->addColumn('status', 'boolean', ['default' => 0])decimal高精度小数->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])enum枚举类型->addColumn('gender', 'enum', ['values' => ['male', 'female', 'other']])四、表结构操作1. 创建表public function up() { $table = $this->table('users', [ 'engine' => 'InnoDB', 'charset' => 'utf8mb4', 'comment' => '用户表', 'collation' => 'utf8mb4_unicode_ci' ]); $table->addColumn('id', 'integer', ['identity' => true]) // 自增ID（默认主键） ->addColumn('username', 'string', ['limit' => 30]) ->addColumn('email', 'string', ['limit' => 100, 'unique' => true]) ->addColumn('password', 'string', ['limit' => 100]) ->addColumn('status', 'boolean', ['default' => 1]) ->addIndex(['username']) // 普通索引 ->ad…

【ThinkPHP】创建有背景的微信小程序二维码
type: item
date: 2025-05-17T11:21:27+08:00
description: <?php namespace app\common\lib\wechat; use think\Exception; use think\facade\Cache; use think\facade\Request; use think\Image; use think\facade\Db; class QrCode extends Basic { // 小程序页面路径 private string $path = "pages/myCenter/invite"; // 小程序码保存路径 private string $savePath = "./storage/qrcode/"; // 二维码参数 private string $param = ""; public function __construct(string $path = "", string $field = "", string $param = "", int $width = 430) { if (!empty($path) && empty($field)) { $this->path = $path; } if (!empty($path) && !empty($field) && !empty($param)) { $this->path = "{$path}?{$field}={$param}"; } $this->param = $param; } /** * 生成小程序码并可选择合并背景图 * * @param string $backgroundPath 背景图路径 * @param int $qrWidth 二维码在背景图上的宽度位置 * @param int $qrHeight 二维码在背景图上的高度位置 * @return string 生成后的二维码URL */ public function setQrcode(string $backgroundPath = '', int $qrWidth = 160, int $qrHeight = 530): string { try { // 获取访问令牌 $accessToken = Cache::get('accesstoken'); if (empty($accessToken)) { $accessToken = (new AccessToken())->getAccesToken(); Cache::set('accesstoken', $accessToken, 7200); // 假设token有效期为2小时 } // 请求小程序码 $url = "https://api.weixin.qq.com/wxa/getwxacode?access_token={$accessToken}"; $data = [ 'path' => $this->path, 'scene' => 'type=qrcode', 'width' => 430, ]; $result = $this->curlPost($url, $data, 'POST'); if ($result === false || isset($result['errcode'])) { throw new Exception("Failed to get QR code: " . json_encode($result)); } // 保存二维码到文件 $fileName = md5($this->param); $directory = $this->savePath . date('Ymd') . '/'; if (!$this->createDirectory($directory)) { throw new Exception("目录创建失败: {$directory}"); } $filePath = "{$directory}{$fileName}.png"; if (!file_put_contents($filePath, $result)) { throw new Exception("文件写入失败: {$filePath}"); } // 生成完整URL $fullDomain = (new \app\common\lib\data\Str())->getFullDomain(); $qrcodeUrl = $fullDomain . ltrim($filePath, '.'); // 合并背景图（如果提供） if (!empty($backgroundPath)) { return $this->mergeWithBackground($backgroundPath, $filePath, $…