小破孩的小站 - ThinkPHP
type: channel
link: http://8688pic.com/index.php/category/tp/
items: 【PHP】境外微信支付生成平台证书 - wechatpay
items: 【PHP】ThinkPHP8数据库迁移示例
items: 【PHP】ThinkPHP8 数据库迁移与数据填充完全指南
items: 【ThinkPHP】创建有背景的微信小程序二维码
items: 【PHP】PHP实现无限级树形分类 树形分类 如果子集的pid不是空，父级id不存在，则子集不展示
items: 【PHP】清空MySQL数据，索引重置
items: 【PHP】ThinkPHP6.1 参数验证中间件
items: 【PHP】发送腾讯云短信
items: 【PHP】打印猿&蜂打打 开放平台 完整对接
items: 【PHP】给富文本内容的图片，视频，文件 拼接当前网址域名

【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】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, $…

【PHP】PHP实现无限级树形分类 树形分类 如果子集的pid不是空，父级id不存在，则子集不展示
type: item
date: 2025-05-07T16:31:33+08:00
description: /** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/12/17 22:27 * @param $arr * @param $id * @param $pid * @return array * @Description:树形分类 如果子集的pid不是空，父级id不存在，则子集不展示 */ public function getTreeByDateDelChile($arr,$id,$pid) { $items = []; foreach($arr as $v){ $items[$v[$id]] = $v; } $tree = []; foreach($items as $k => $item){ if($item[$pid] &&!isset($items[$item[$pid]])){ unset($items[$k]); continue; } if(isset($items[$item[$pid]])){ $items[$item[$pid]]['child'][] = &$items[$k]; }else{ $tree[] = &$items[$k]; } } return $tree; } /** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/9/14 16:59 * @param array $arr * @param string $children * @return array * @Description:将树形数组转换成一维数组 */ public function setManyTosingle(array $arr = [], string $children = 'child'): array { $result = []; foreach ($arr as $item) { // 先将当前节点添加到结果数组中 $newItem = $item; if (isset($newItem[$children])) { unset($newItem[$children]); } $result[] = $newItem; // 递归处理子节点 if (isset($item[$children]) && is_array($item[$children])) { $result = array_merge($result, $this->setManyTosingle($item[$children], $children)); } } return $result; }

【PHP】清空MySQL数据，索引重置
type: item
date: 2025-05-06T10:23:08+08:00
description: <?php // 定义排除表的常量 const EXCLUDE_TABLES = [ 'web_admin_company', 'web_admin_func', 'web_admin_role', 'web_admin_user', 'web_app_config', 'web_china_city', 'web_china_city_area', 'web_china_city_backup', 'web_company_config', 'web_shop_category', 'web_tppay', // 'web_shop_goods_tmpserver', ]; class SomeClass { public function truncateTables() { try { // 开启事务 Db::startTrans(); // 获取所有表名 $tables = Db::query('SHOW TABLES'); $tableNames = array_map('current', $tables); foreach ($tableNames as $tableName) { if (!in_array($tableName, EXCLUDE_TABLES)) { // 使用参数化查询清空表并重置索引 Db::execute("TRUNCATE TABLE `$tableName`"); echo "表 {$tableName} 已清空 "; } } // 提交事务 Db::commit(); } catch (\Exception $e) { // 回滚事务 Db::rollback(); // 记录错误日志 error_log("发生错误: " . $e->getMessage()); echo "发生错误: " . $e->getMessage(); } } }

【PHP】ThinkPHP6.1 参数验证中间件
type: item
date: 2025-03-13T14:54:00+08:00
description: public function handle($request, \Closure $next) { try { // 获取并清理参数 $params = array_filter(array_map(function ($value) { return is_string($value) ? trim($value) : $value; }, $request->param()), function ($value) { return is_numeric($value) || !empty($value); }); unset($params['controller'], $params['function']); if (empty($params)) return $next($request); // 设置请求属性，方便后续使用 $request->checkParam = $params; // 获取应用名、控制器和操作名 $appName = app('http')->getName(); $controller = Request::instance()->controller(true); $action = Request::instance()->action(true); // 动态构建验证器路径 $controllerParts = explode('.', $controller); $validatePathParts = array_merge([$appName, 'validate'], $controllerParts); $lastKey = array_key_last($validatePathParts); $validatePathParts[$lastKey] = ucfirst((string) $validatePathParts[$lastKey]); // $validatePath = implode('\\', array_map('ucfirst', $validatePathParts)); $validatePath = 'app\\'.implode('\\', $validatePathParts); // 检查验证器是否存在及场景是否定义 if (!class_exists($validatePath) || !$this->sceneExists($validatePath, $action)) { return $next($request); } // 验证数据 $validateInstance = new $validatePath; if (!$validateInstance->scene($action)->check($params)) { throw new Exception($validateInstance->getError()); } } catch (Exception $e) { return show(100, $e->getMessage()); } return $next($request); } /** * 检查指定验证场景是否存在 * * @param string $validateClass 验证类名 * @param string $scene 场景名 * @return bool */ protected function sceneExists(string $validateClass, string $scene): bool { return (new $validateClass)->hasScene($scene); }

【PHP】发送腾讯云短信
type: item
date: 2025-03-13T14:48:00+08:00
description: 优化空间很大，先用着，能用<?php namespace app\common\lib\sms\tencent; //缓存 use think\facade\Cache; use TencentCloud\Common\Credential; use TencentCloud\Common\Profile\ClientProfile; use TencentCloud\Common\Profile\HttpProfile; use TencentCloud\Common\Exception\TencentCloudSDKException; use TencentCloud\Sms\V20210111\SmsClient; use TencentCloud\Sms\V20210111\Models\SendSmsRequest; class Sms{ public $SecretID = "......................"; public $SecretKey = "......................."; public $SmsSdkAppId = "..........."; public $TemplateId = "........."; public $SignName = "............"; public $code; public $phone; public function __construct($phone = '', $code = '', $tempID = '') { $this->phone = $phone; $this->code = $code; if(!empty($tempID)){ $this->TemplateId = $tempID; } } public function send(){ try { //控制台 >API密钥管理页面获取 SecretID 和 SecretKey $cred = new Credential($this->SecretID, $this->SecretKey); //实例化一个http选项 [可选] $httpProfile = new HttpProfile(); $httpProfile->setEndpoint("sms.tencentcloudapi.com"); //实例化一个client选项 [可选] $clientProfile = new ClientProfile(); $clientProfile->setHttpProfile($httpProfile); /** * 实例化以sms为例的client对象, [第三个参数 可选] * * 第二个参数是地域信息,可以直接填 ap-guangzhou */ $client = new SmsClient($cred, "ap-beijing", $clientProfile); // 实例化一个sms发送短信请求对象,每个接口都会对应一个request对象。 $req = new SendSmsRequest(); //生成随机验证码 // $code = rand(11111, 99999); // $params = array( // //接收方手机号，带上+86 示例：+8613711112222 // "PhoneNumberSet" => array((string)$this->phone), // //短信应用ID:在 [短信控制台] 添加应用后生成的实际SdkAppId // "SmsSdkAppId" => (string)$this->SmsSdkAppId, // //短信签名内容:[不理解可以看文章里的截图] // "SignName" => (string)$this->SignName, // //模板ID:必须填写已审核通过的模板 // "TemplateId" => (string)$this->TemplateId, // //我的模板中有两个参数 第一个是验证码参数 第二个是有效时间 若无模板参数，则设置为空 // "TemplateParamSet" => array((string)$this->code, '10'), // //SecretID // // "SenderId" => (string)$this->SecretID // ); $params = array( "PhoneNumberSet" => array( (string)$this->phone ), "SmsSdkAppId" => (string)$this->SmsSdkAppId, "SignName" => (str…

【PHP】打印猿&蜂打打 开放平台 完整对接
type: item
date: 2025-03-13T14:44:59+08:00
description: 基础类<?php namespace app\common\lib\printman; use think\facade\Log; class Basic { #APPID public $AppId; #密钥 public $AppSecret; #API地址 public $ApiUrl; #打印机ID public $PrinterId; public function __construct($AppId, $AppSecret, $PrinterId) { $this->AppId = $AppId; $this->AppSecret = $AppSecret; $this->ApiUrl = "https://iot-app-prod.fengdada.cn/mk/api"; $this->PrinterId = $PrinterId; } public function encode($BizData, $nonce) { // global $AppSecret; $jsonBytes = mb_convert_encoding($BizData , 'utf-8'); $bizData = strval($jsonBytes); $sign_ori = $bizData . $nonce . $this->AppSecret; $md5_hash = md5($sign_ori, true); $sign = base64_encode($md5_hash); return $sign; } public function generate_verification_code() { $verification_code = ""; for ($i = 0; $i < 6; $i++) { $verification_code .= strval(rand(0, 9)); } return $verification_code; } public function requests_post($url, $data, $headers) { $ch = curl_init(); curl_setopt($ch, CURLOPT_CAINFO, "cacert-2023-01-10.pem"); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return $response; } public function DoMyPost($URL, $json_BizData) { $ts = round(microtime(true) * 1000); $nonce = $this->generate_verification_code(); $sign = $this->encode($json_BizData, $nonce); $data = array( "bizData" => $json_BizData, "nonce" => $nonce, "appId" => $this->AppId, "timestamp" => strval($ts) ); $headers = array( 'Content-Type: application/json', 'sign:'.$sign ); // $response = $this->requests_post($URL, json_encode($data), $headers); $response = $this->curlRequest($URL, "POST",json_encode($data),true,false, $headers); Log::write("打印机日志:".print_r($response,true),'printman'); if(empty($response)) { echo "Error: no response received"; }else{ return $response; } } // 云打印验证码 public function PrintCaptcha() { $URL = $this->ApiUrl."/print/captcha…

【PHP】给富文本内容的图片，视频，文件 拼接当前网址域名
type: item
date: 2025-03-13T14:12:10+08:00
description: /** * @Author:小破孩 * @Email:3584685883@qq.com * @Time:2024/11/18 15:20 * @param $text * @param $domain * @return string|string[]|null * @Description:给服务文本拼接当前网址域名 */ public function addDomainToPaths($text, $domain){ // 匹配图片路径 $text = preg_replace('/<img.*?src="([^"]+)"/i', '<img src="' . $domain . '$1"', $text); // 匹配视频路径 $text = preg_replace('/<video.*?src="([^"]+)"/i', '<video src="' . $domain . '$1"', $text); // 匹配文件路径（可根据具体文件类型的链接特征进行修改） $text = preg_replace('/<a.*?href="([^"]+)"/i', '<a href="' . $domain . '$1"', $text); return $text; }