关于php:php-打包-zip

php 应用原生的 ZipArchive 类来打包 zip。 <?phpnamespace App\Services;use Exception;use Illuminate\Support\Facades\Log;/** * Class Zip * * @package App\Services */class Zip{ /** * @param array $path_arr 待打包的文件门路汇合 * @param string $zip_path 压缩包门路 * * @return string */ public static function makeZip(array $path_arr, string $zip_path): string { $zip = new \ZipArchive(); try { if ($zip->open($zip_path, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) { foreach ($path_arr as $file) { if (!file_exists($file)) { continue; } $zip->addFile($file, basename($file)); } $zip->close(); return $zip_path; } } catch (\Throwable $e) { Log::error(sprintf("%s err %s", __METHOD__, $e->getMessage())); throw new Exception('打包出错了,请重试'); } throw new Exception('打包出错了,请重试'); }}

February 23, 2024 · 1 min · jiezi

关于php:使用Chatgpt编写的PHP数据库pdo操作类增删改查

摘要将PDO封装成PHP类进行调用有很多益处,包含: 1、封装性和抽象性: 通过将PDO封装到一个类中,您能够将数据库操作逻辑与应用程序的其余局部拆散开来,进步了代码的组织性和可维护性。这样,您只需在一个中央保护数据库连贯和查问逻辑,而不用在整个应用程序中分布数据库代码。 2、重用性: 将数据库操作封装成类使得这些操作能够在应用程序的不同局部重复使用,而无需反复编写雷同的代码。这有助于缩小代码冗余,提高效率。 3、安全性: 通过类的办法来执行数据库操作,能够轻松地施行预处理语句,从而缩小了SQL注入攻打的危险。类还能够提供错误处理机制,使您可能更容易地解决数据库谬误。 4、可扩展性: 应用类封装数据库操作,能够轻松地扩大和保护应用程序。如果须要增加新的数据库操作或更改现有的操作,只需批改类中的相应办法而不用更改应用程序的其余局部。 5、清晰的接口: 类提供了一个清晰的接口,使其余开发人员可能更容易地了解和应用数据库操作。这有助于团队合作和代码保护。 将PDO封装成PHP类能够进步代码的可维护性、可重用性和安全性,同时升高了代码的耦合度,使数据库操作更容易治理和扩大。这是一个良好的软件工程实际,特地实用于中大型和简单的应用程序。 类文件Database.php 以下是应用Chatgpt生成的操作类,然而我做了30%的批改和优化。 <?php /** * Title:PDO数据库操作类 * Author:TANKING * Blog:https://segmentfault.com/u/tanking * Date:2023-09-18 */ class Database { private $host; private $username; private $password; private $database; private $pdo; private $error = null; public function __construct($host, $username, $password, $database) { $this->host = $host; $this->username = $username; $this->password = $password; $this->database = $database; $this->connect(); } // 获取错误信息 public function getError() { return $this->error; } // 连贯数据库 private function connect() { $dsn = "mysql:host={$this->host};dbname={$this->database}"; try { $this->pdo = new PDO($dsn, $this->username, $this->password); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); } } // 插入数据 public function insert($table, $data) { try { $columns = implode(", ", array_keys($data)); $values = ":" . implode(", :", array_keys($data)); $sql = "INSERT INTO $table ($columns) VALUES ($values)"; $stmt = $this->pdo->prepare($sql); $result = $stmt->execute($data); if (!$result) { $this->error = $stmt->errorInfo(); } return $result; } catch (PDOException $e) { // 解决数据库异样 $this->error = $e->getMessage(); return FALSE; } } // 更新数据 public function update($table, $data, $where) { try { $set = ""; foreach ($data as $key => $value) { $set .= "$key = :$key, "; } $set = rtrim($set, ', '); $sql = "UPDATE $table SET $set WHERE $where"; $stmt = $this->pdo->prepare($sql); $result = $stmt->execute($data); if (!$result) { $this->error = $stmt->errorInfo(); } return $result; } catch (PDOException $e) { // 解决数据库异样 $this->error = $e->getMessage(); return FALSE; } } // 删除数据 public function delete($table, $where) { try { $sql = "DELETE FROM $table WHERE $where"; $stmt = $this->pdo->prepare($sql); $result = $stmt->execute(); if($stmt->rowCount() === 0) { // 没有受影响的记录 $this->error = '没有受影响的记录'; return FALSE; }else { return $result; } } catch (PDOException $e) { // 解决数据库异样 $this->error = $e->getMessage(); return FALSE; } } // 查问一条数据 public function queryOne($table, $conditions = []) { $whereClause = $this->buildWhereClause($conditions); $sql = "SELECT * FROM $table $whereClause LIMIT 1"; try { $stmt = $this->pdo->prepare($sql); $stmt->execute($conditions); return $stmt->fetch(PDO::FETCH_ASSOC); } catch (PDOException $e) { // 解决数据库异样 $this->error = $e->getMessage(); return FALSE; } } // 查问所有数据 public function queryAll($table, $conditions = []) { $whereClause = $this->buildWhereClause($conditions); $sql = "SELECT * FROM $table $whereClause"; try { $stmt = $this->pdo->prepare($sql); $stmt->execute($conditions); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (PDOException $e) { // 解决数据库异样 $this->error = $e->getMessage(); return FALSE; } } // 执行原生SQL语句 public function executeSQL($sql, $params = []) { try { $stmt = $this->pdo->prepare($sql); $result = $stmt->execute($params); if (!$result) { // 执行失败 $this->error = $stmt->errorInfo(); return FALSE; } return $stmt; } catch (PDOException $e) { // 解决数据库异样 $this->error = $stmt->errorInfo(); return FALSE; } } // 数据绑定 private function buildWhereClause($conditions) { if (empty($conditions)) { return ''; } $where = 'WHERE'; foreach ($conditions as $key => $value) { $where .= " $key = :$key AND"; } $where = rtrim($where, ' AND'); return $where; } }实例配置文件 Db.php ...

September 19, 2023 · 4 min · jiezi

关于php:php获取客户端ip地址及ip所在国家省份城市县区

摘要获取客户端ip地址,而后应用这个ip地址获取所在的国家、省份、城市,能够在网站中实现IP属地,公布地等性能。 本文的获取IP地址信息均采自网络上收费的IP查问网站,通过其API或者网页HTML解析出的ip地址信息。 代码<?php // 编码 header('Content-type:application/json'); $ip = $_GET['ip']; // 过滤空数据 if(!$ip) { $ipinfo = array( 'code' => 201, 'msg' => '未传入ip地址' ); echo json_encode($ipinfo,JSON_UNESCAPED_UNICODE); exit; } // 验证ipv4地址合法性 if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { $ipinfo = array( 'code' => 201, 'msg' => '这不是一个正确的ip地址' ); echo json_encode($ipinfo,JSON_UNESCAPED_UNICODE); exit; } // 申请接口 $methods = [ 'getMethod_1', 'getMethod_2', 'getMethod_3', 'getMethod_4', 'getMethod_5' ]; foreach ($methods as $method) { $response = json_decode($method($ip)); if ($response->code === 200) { // 如果申请胜利,输入申请后果并进行循环 echo $method($ip); break; } } if (!isset($response) || $response->code !== 200) { $ipinfo = array( 'code' => 201, 'msg' => '申请失败~' ); echo json_encode($ipinfo,JSON_UNESCAPED_UNICODE); exit; } // HTTP申请封装 function cUrlGetIP($url) { // cUrl $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $header[] = 'user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36'; curl_setopt($ch, CURLOPT_HTTPHEADER, $header); return curl_exec($ch); curl_close($ch); } // 中国34个省级行政区域 $provinces = array( "北京", "天津", "河北", "山西", "内蒙古", "辽宁", "吉林", "黑龙江", "上海", "江苏", "浙江", "安徽", "福建", "江西", "山东", "河南", "湖北", "湖南", "广东", "广西", "海南", "重庆", "四川", "贵州", "云南", "西藏", "陕西", "甘肃", "青海", "宁夏", "新疆", "香港", "澳门", "台湾" ); // 接口1 // http://ipshudi.com/{ip}.htm function getMethod_1($ip) { $response = file_get_contents('http://ipshudi.com/'.$ip.'.htm'); $str1 = substr($response, strripos($response, "归属地")); $str2 = substr($str1, 0, strrpos($str1, "运营商")); $str3 = substr($str2, strripos($str2, "<span>") + 6); $str4 = substr($str3, 0, strripos($str3, "</span>") + 6); // 提取国家 $country = substr($str4, 0, strpos($str4, ' ')); // 提取省份 $str5 = substr($str4, 0, strrpos($str4, " <a href")); $province = substr($str5, strpos($str5, ' ') + 1); // 提取城市 preg_match('/>([^<]+)</', $str4, $matches); $city = $matches[1]; // 提取县区 $str6 = substr($str4, strripos($str4, "</a>")); $district = preg_replace('/[^\x{4e00}-\x{9fa5}]+/u', '', $str6); // 判断是否获取胜利 if($country || $province || $city || $district) { // 拼接数组 $ipinfo = array( 'code' => 200, 'msg' => '获取胜利', 'ipinfo' => array( 'country' => $country, 'province' => $province, 'city' => $city, 'district' => $district, 'ip' => $ip ) ); }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } return json_encode($ipinfo,JSON_UNESCAPED_UNICODE); } // 接口2 // https://searchplugin.csdn.net/api/v1/ip/get?ip={ip} function getMethod_2($ip) { $response = cUrlGetIP('https://searchplugin.csdn.net/api/v1/ip/get?ip='.$ip); $code = json_decode($response,true)['code']; if($code == 200) { $str1 = json_decode($response,true)['data']['address']; // 国家 $country = explode(' ', $str1)[0]; // 省份 $province = explode(' ', $str1)[1]; // 城市 $city = explode(' ', $str1)[2]; // 县区 $district = ''; // 判断是否获取胜利 if($country || $province || $city || $district) { // 拼接数组 $ipinfo = array( 'code' => 200, 'msg' => '获取胜利', 'ipinfo' => array( 'country' => $country, 'province' => $province, 'city' => $city, 'district' => $district, 'ip' => json_decode($response,true)['data']['ip'] ) ); }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } return json_encode($ipinfo,JSON_UNESCAPED_UNICODE); } // 接口3 // https://ipchaxun.com/{ip}/ function getMethod_3($ip) { $response = cUrlGetIP('https://ipchaxun.com/'.$ip.'/'); $str1 = substr($response, strripos($response, "归属地") + 15); $str2 = substr($str1, 0, strrpos($str1, "运营商")); // 提取省份 global $provinces; foreach ($provinces as $province_) { if (strpos($str2, $province_) !== false) { $province = $province_; break; } } // 提取国家 $str3 = substr($str2, 0, strrpos($str2, $province)); $country = preg_replace('/[^\x{4e00}-\x{9fa5}]+/u', '', $str3); // 提取城市 $str4 = substr($str2, strripos($str2, "nofollow") + 10); $city = substr($str4, 0, strrpos($str4, "</a>")); // 提取县区 $str6 = substr($str2, strripos($str2, "</a>") + 4); $district = substr($str6, 0, strrpos($str6, "</span>")); // 判断是否获取胜利 if($country || $province || $city || $district) { // 拼接数组 $ipinfo = array( 'code' => 200, 'msg' => '获取胜利', 'ipinfo' => array( 'country' => $country, 'province' => $province, 'city' => $city, 'district' => $district, 'ip' => $ip ) ); }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } return json_encode($ipinfo,JSON_UNESCAPED_UNICODE); } // 接口4 // https://api.vvhan.com/api/getIpInfo?ip={ip} function getMethod_4($ip) { $response = cUrlGetIP('https://api.vvhan.com/api/getIpInfo?ip='.$ip); $success = json_decode($response,true)['success']; if($success == true) { $str1 = json_decode($response,true)['info']; // 国家 $country = $str1['country']; // 省份 $province = $str1['prov']; // 城市 $city = $str1['city']; // 县区 $district = ''; // 判断是否获取胜利 if($country || $province || $city || $district) { // 拼接数组 $ipinfo = array( 'code' => 200, 'msg' => '获取胜利', 'ipinfo' => array( 'country' => $country, 'province' => $province, 'city' => $city, 'district' => $district, 'ip' => $ip ) ); }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } return json_encode($ipinfo,JSON_UNESCAPED_UNICODE); } // 接口5 // https://c.runoob.com/wp-content/themes/toolrunoob2/option/ajax.php?type=checkIP&REMOTE_ADDR={ip} function getMethod_5($ip) { $response = cUrlGetIP('https://c.runoob.com/wp-content/themes/toolrunoob2/option/ajax.php?type=checkIP&REMOTE_ADDR='.$ip); $flag = json_decode($response,true)['flag']; if($flag == true) { $str1 = json_decode($response,true)['data']; // 国家 $country = $str1['country']; // 省份 $province = $str1['regionName']; // 城市 $city = $str1['city']; // 县区 $district = ''; // 判断是否获取胜利 if($country || $province || $city || $district) { // 拼接数组 $ipinfo = array( 'code' => 200, 'msg' => '获取胜利', 'ipinfo' => array( 'country' => $country, 'province' => $province, 'city' => $city, 'district' => $district, 'ip' => $ip ) ); }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } }else { $ipinfo = array( 'code' => 201, 'msg' => '获取失败' ); } return json_encode($ipinfo,JSON_UNESCAPED_UNICODE); }?>应用https://域名/getipInfo.php?ip=须要查问的IP地址 ...

September 7, 2023 · 4 min · jiezi

关于php:MysqlHelper一个便捷的MySQL导入导出的助手类库

MysqlHelper 是一个便捷的通过PHP导入和导出Mysql数据库表构造和数据的工具,能够疾速实现mysql的数据库的导入和导出. 个性简略易用: 仅依赖mysqlli扩大,开箱即用灵便操作: 兼容支流框架,应用更不便长期保护: 作者为自由职业者,保障我的项目的长期稳固和继续更新装置通过Composer导入类库 composer require zjkal/mysql-helper应用文档1. 实例化形式一: 惯例办法 use zjkal\MysqlHelper;$mysql = new MysqlHelper('root', 'root', 'testdatabase', '127.0.0.1', '3306', 'utf8mb4', 'wp_');形式二: 实例化后,通过setConfig办法设置数据库配置 $mysql = new MysqlHelper();$mysql->setConfig(['username' => 'root', 'password' => 'root', 'database' => 'testdatabase']);MysqlHelper针对罕用的框架做了兼容,能够间接应用框架的数据库配置, 比方ThinkPHP框架或Laravel框架 $mysql = new MysqlHelper();$config = config('database.connections.mysql');$mysql->setConfig($config);2. 导出数据//导出数据库(蕴含表构造和数据)$mysql->exportSqlFile('test.sql');//仅导出数据库表构造$mysql->exportSqlFile('test.sql', false);//导出指定表的构造和数据$mysql->exportSqlFile('test.sql', true, ['table1', 'table2']);3. 导入数据sql文件中的表前缀须要应用__PREFIX__占位符代替如果实例化时,曾经设置了数据库前缀,则能够不必传入第二个参数//导入数据库$mysql->importSqlFile('test.sql');//导入数据库,并主动替换表前缀$mysql->importSqlFile('test.sql', 'wp_');开源协定MysqlHelper遵循MIT开源协定, 意味着您无需任何受权, 即可收费将MysqlHelper利用到您的我的项目中

September 2, 2023 · 1 min · jiezi

关于php:PHP中的日期和时间处理函数详解

在 Web 开发中,解决日期和工夫是一个常见的工作。PHP 提供了一系列弱小的日期和工夫处理函数,例如strtotime、date和DateTimeImmutable::createFromFormat等。 这些函数使得在不同的工夫格局之间进行转换,进行日期和工夫计算以及格式化输入变得更加便捷。 本文将深入探讨这三个函数的用法和劣势。 1. strtotime 函数strtotime函数用于将人类可读的日期和工夫字符串转换为 Unix 工夫戳。它能够承受一个日期工夫字符串作为参数,并尝试解析它并将其转换为对应的 Unix 工夫戳。除了承受根本的日期工夫格局外,它还能够了解各种绝对工夫表达式。以下是strtotime函数的参数和作用: strtotime(string $datetime, ?int $baseTimestamp = null): int|false参数:string $datetime, ?int $baseTimestamp = null$datetime:须要解析的日期工夫字符串。$baseTimestamp:可选参数,示意用于计算绝对日期的根底工夫戳。返回值:解析胜利则返回对应的 Unix 工夫戳,解析失败则返回false。应用strtotime函数时,能够传递各种不同格局的日期工夫字符串,包含相对工夫(如"2023-08-06"、"15:30:00")以及绝对工夫(如"tomorrow"、"next week")。 函数会尝试依据传入的字符串进行正当的日期工夫转换,不便进行工夫的计算和比拟。 echo strtotime("2023-08-06 15:30:00"), PHP_EOL;echo strtotime("tomorrow"), PHP_EOL;echo strtotime("+1 day"), PHP_EOL;2. date 函数date函数用于将 Unix 工夫戳格式化为所需的日期和工夫字符串。它承受一个格局字符串和一个 Unix 工夫戳作为参数,而后返回一个格式化后的日期工夫字符串。以下是date函数的参数和作用: date(string $format, ?int $timestamp = null): string参数:string $format, ?int $timestamp = null$format:日期工夫格局字符串,其中蕴含各种格式化选项,用于定义输入的日期工夫款式。$timestamp:可选参数,示意须要格式化的 Unix 工夫戳。默认为time()函数的返回值,即以后的 Unix 工夫戳。返回值:依据指定格局返回格式化后的日期工夫字符串。date函数的第一个参数是日期格局字符串,其中蕴含各种格式化选项,例如"Y"代表年份,"m"代表月份,"d"代表日期,"H"代表小时,"i"代表分钟,"s"代表秒等。 // set the default timezone to use.date_default_timezone_set('UTC');// Prints something like: Mondayecho date("l");// Prints something like: Monday 8th of August 2005 03:12:46 PMecho date('l jS \of F Y h:i:s A');// Prints: July 1, 2000 is on a Saturdayecho "July 1, 2000 is on a " . date("l", mktime(0, 0, 0, 7, 1, 2000));/* use the constants in the format parameter */// prints something like: Wed, 25 Sep 2013 15:28:57 -0700echo date(DATE_RFC2822);// prints something like: 2000-07-01T00:00:00+00:00echo date(DATE_ATOM, mktime(0, 0, 0, 7, 1, 2000));通过组合这些选项,就能够创立出各种不同的日期和工夫格局。 ...

August 28, 2023 · 1 min · jiezi

关于php:EasyAdmin8『-ThinkPHP80-Laravel10x-webman-』-Layui后台管理系统

我的项目官网https://easyadmin8.top我的项目介绍EasyAdmin8 在 EasyAdmin 的根底上更新 ThinkPHP 框架到 8.0 ,PHP 最低版本要求不低于 8.0 ThinkPHP v8.0 和 layui v2.8.x 的疾速开发的后盾管理系统。 我的项目地址:https://github.com/wolf-leo/EasyAdmin8 https://gitee.com/wolf18/easyAdmin8 EasyAdmin8-Laravel 在 EasyAdmin 的根底上应用 Laravel 10.x 重构,PHP 最低版本要求不低于 8.1Laravel v10.x 和 layui v2.8.x 的疾速开发的后盾管理系统。 我的项目地址: https://github.com/wolf-leo/EasyAdmin8-Laravel https://gitee.com/wolf18/EasyAdmin8-Laravel EasyAdmin8-webman 在 EasyAdmin 的根底上应用 webman 最新版重构,PHP 最低版本要求不低于 8.0webman 和 layui v2.8.x 的疾速开发的后盾管理系统。 我的项目地址: https://github.com/wolf-leo/EasyAdmin8-webman https://gitee.com/wolf18/EasyAdmin8-webman 界面预览

August 27, 2023 · 1 min · jiezi

关于php:PHP实现读取指定目录下的所有文件

在php中读取指定目录下的文件次要用到了opendir和readdir函数 一:opendir(关上目录句柄)1:语法opendir(path,context);2:参数阐明参数形容path必须。规定要关上的目录门路。context可选。规定目录句柄的环境。context 是可批改目录流的行为的一套选项。3:返回值胜利则返回目录句柄资源。失败则返回 FALSE。如果门路不是非法目录,或者因为许可限度或文件系统谬误导致的目录不能关上,则抛出 E_WARNING 级别的谬误。您能够通过在函数名称前增加 '@' 来暗藏 opendir() 的谬误输入。 二:readdir(回目录中下一个文件的文件名)1:语法readdir(dir_handle);2:参数阐明参数形容dir_handle可选。指定之前由 opendir() 关上的目录句柄资源。如果该参数未指定,则应用最初一个由 opendir() 关上的链接。3:返回值胜利则返回文件名,失败则返回 FALSE。 三:php实现读取指定目录下所有文件/** * @param string $path [要读取的文件目录] */public function traverse($path = '.') { $currentDir = opendir($path); //opendir()返回一个目录句柄,失败返回false while(($file = readdir($currentDir)) !== false) { //readdir()返回关上目录句柄中的一个条目 $subDir = $path . DIRECTORY_SEPARATOR . $file; //构建子目录门路 if($file == '.' || $file == '..') { continue; } else if(is_dir($subDir)) { //如果是目录,进行递归 $this->traverse($subDir); } else { //如果是文件,调用clasbackFun办法(参数:文件门路,文件名) $this->callbackFun($path,$file); } }}

August 17, 2023 · 1 min · jiezi

关于php:什么是-Nodejs-的-crossenv-工具包

cross-env 是一个运行在 Node.js 环境中的工具包,它的次要作用是让咱们能够在命令行中设置环境变量,而不用放心跨操作系统的兼容问题。在 Unix 和 Windows 零碎中设置环境变量的形式是不同的,这就导致了咱们无奈写出一条在所有操作系统中都能够运行的设置环境变量的命令。cross-env 的呈现就是为了解决这个问题。 以一个十分常见的场景为例,让咱们来看一下在没有应用 cross-env 的状况下,如何在不同的操作系统中设置环境变量。在 Unix 零碎中,咱们通常会这样做: NODE_ENV=production node app.js而在 Windows 零碎中,咱们须要这样做: set NODE_ENV=production&&node app.js这样的差别使得咱们在编写跨平台脚本时遇到了艰难,因为咱们无奈保障所有的开发者和用户都在应用同一种操作系统。 这时,cross-env 就派上了用场。应用 cross-env,咱们能够这样设置环境变量: cross-env NODE_ENV=production node app.js这条命令无论在 Unix 还是在 Windows 零碎中,都能正确地设置 NODE_ENV 环境变量为 production。 cross-env 的工作原理是它在外部对操作系统进行了查看,并依据操作系统的类型来决定如何设置环境变量。这样,开发者就无需放心操作系统的差别,只须要专一于他们的利用程序代码。 cross-env 是一个十分玲珑的工具包,但它解决了一个十分理论的问题。在理论开发中,开发者常常须要依据环境变量来扭转他们的应用程序的行为。比方,在开发环境中,开发者可能心愿关上一些调试日志,而在生产环境中,他们可能心愿敞开这些日志。通过设置环境变量,开发者能够在不扭转代码的状况下,实现这种行为的切换。 不仅如此,环境变量还经常被用来存储敏感信息,如数据库明码、API 密钥等。这些信息不应该被硬编码在代码中,而是应该存储在环境变量中,以减少应用程序的安全性。 在应用 cross-env 时,咱们通常会在 package.json 文件的 scripts 字段中应用它,像上面这样: "scripts": { "start": "cross-env NODE_ENV=production node app.js"}而后,咱们就能够通过运行 npm start 来启动咱们的应用程序,而 NODE_ENV 环境变量会被设置为 production。

August 16, 2023 · 1 min · jiezi

关于php:Chapter-18自制可逆等位字符串加密解密编码解码

欢送来到「我是真的狗杂谈世界」,关注不迷路 背景最近做的我的项目屡次遇到了分享邀请的需要点,即须要在承受邀请时能辨认到邀请者的信息,又须要思考信息敏感性,没找到成熟的三方实现,于是本人思考实现了两套。 思路计划不能间接将邀请信息用于传递,须要对信息(个别是字符串,不是字符串也能够转换为字符串)进行加密解决,或者说编码解决。但同时须要满足一下要求: 要求可逆:加密(编码)后的密文该当能通过解密(解码)取得原文,否则就无奈取得邀请者信息了;长度:加密(编码)后的密文应该尽可能与原文长度相当,能够略多(如果能更少也好,不过那须要波及压缩了,不是明天的重点);内容:加密(编码)后的密文该当是可预知的字符汇合,如果可设置更好;算法可公开(意味着存在额定依赖):不单单依附一个固定算法,即使算法公开仍旧能保障安全性,否则算法一旦被破解也就没什么了;复杂度适当:太简单的个别对计算要求很高,对开发(自己)老本也高,能满足目前的我的项目需要即可(我相对不会说我懒);常见算法常见的一些加密解密、编码解码算法: 单向:md系列、sha1;对称:des、aes;非对称:rsa、dsa;简略编码:base64;移位法实质保护全量字符可能的程序,对给定字符串的每个字符依照其地位进行移位转换,失去后果;地位到字符移位偏移量通过一套内部输出的规定来指定;上述两步相当于将字符枚举、程序、偏移量规定三套作为算法变量交由用户管制,在不能齐全晓得三个信息的状况下,即使晓得算法,也能保障密文安全性;防篡改尽管用移位法能够保障用户无奈通过密文还原原文,但用户能够用大量无序密文来攻打,造成大量垃圾数据或暴力猜测到一些信息,因而还须要反对能校验密文是否非法的性能,也就是防篡改。通用的思路是额定加一点校验数据,由原文结构而成退出到密文中,解密(解码)时校验一遍作为验证即可,比方网络IP和TCP层的累加和就是这种思路。实现 public function encrypt(string $value): string { if (0 == $this->sortCount) { throw new EncrypterException('未设置字符排布阵列'); } if (0 == $this->shiftingCount) { throw new EncrypterException('未设置编码密钥'); } $encrypted = ''; for ($i = 0; $i < strlen($value); $i++) { if (!key_exists($value[$i], $this->sortList)) { throw new EncrypterException('发现不期待的字符'); } $index = $this->sortList[$value[$i]]; $index += $this->shiftingList[$i % $this->shiftingCount]; $index %= $this->sortCount; $encrypted .= $this->sort[$index]; } // 增加校验位 if ($this->checkSum) { $sum = 0; for ($i = 0; $i < strlen($value); $i++) { $sum += ord($value[$i]); } $sum %= $this->sortCount; $start = $this->sort[$sum]; $end = $this->sort[$this->sortCount - $sum - 1]; $encrypted = $start . $encrypted . $end; } return $encrypted; } public function decrypt(string $value): string { if (0 == $this->sortCount) { throw new EncrypterException('未设置字符排布阵列'); } if (0 == $this->shiftingCount) { throw new EncrypterException('未设置编码密钥'); } // 拿出校验位 if ($this->checkSum) { if (strlen($value) < 2) { throw new EncrypterException('解密校验失败'); } $start = $value[0]; $end = $value[strlen($value) - 1]; $value = substr($value, 1, -1); } $decrypted = ''; for ($i = 0; $i < strlen($value); $i++) { if (!key_exists($value[$i], $this->sortList)) { throw new EncrypterException('发现不期待的字符'); } $index = $this->sortList[$value[$i]]; $index -= $this->shiftingList[$i % $this->shiftingCount]; while ($index < 0) { $index += $this->sortCount; } $decrypted .= $this->sort[$index]; } // 校验校验位 if ($this->checkSum) { $sum = 0; for ($i = 0; $i < strlen($decrypted); $i++) { $sum += ord($decrypted[$i]); } $sum %= $this->sortCount; if ($start != $this->sort[$sum] || $end != $this->sort[$this->sortCount - $sum - 1]) { throw new EncrypterException('解密校验失败'); } } return $decrypted; }特点剖析须要晓得原文全副字符形成可能;原文和密文的字符形成是同一个汇合;密文与原文长度相等,即便退出校验位也仅多2位;实用于前端、用户可见可感知的传递场景;异或法实质利用a^b^b=a,也就是两次异或复位的个性(字符可转换成一个数值也就是一个多位的二进制,单位异或的个性在多位场景下同样成立)同样与原文、密文每个字符进行异或操作的字符应该与其地位规定无关,同移位法相干规定可读性因为异或后的值可能超过输出原值,字符转换时可能转换为十分用字符影响浏览,因而能够抉择嵌套一层base64加解密不便浏览。 ...

July 11, 2023 · 2 min · jiezi

关于php:基于Surprise协同过滤实现短视频推荐

前言后面一文介绍了通过根底的web我的项目构造实现简略的内容举荐,与其说那个是举荐不如说是一个排序算法。因为热度计算形式尽管解决了内容的时效品质动态化。然而绝对用户而言,大家看到的都是简直统一的内容(不一样也可能只是某工夫里某视频的排前或靠后),没有做到个性化的千人千面。        尽管如此,基于内容的热度举荐仍然有他独特的利用场景——热门榜单。所以只须要把这个性能换一个模块就能够了,将个性化举荐留给更善于做这方面的算法。        当然了,做举荐零碎的办法很多,平台层面的像spark和明天要讲的Surprise。办法层面能够用深度学习做,也能够用协同过滤,或综合一起等等。大厂可能就更欠缺了,在召回阶段就有很多通道,比方基于卷积截帧辨认视频内容,文本类似度计算和现有数据撑持,前面又通过荡涤,粗排,精排,重排等等流程,可能他们更多的是要保障平台内容的多样性。         那咱们这里仍然走入门理论应用为主,能让咱们的我的项目疾速对接上个性化举荐,以下就是在起因PHP我的项目构造上对接Surprise,实现用户和物品的类似度举荐。  环境python3.8Flask2.0pandas2.0mysql-connector-python     surpriseopenpyxlgunicorn Surprise介绍Surprise库是一款用于构建和剖析举荐零碎的工具库,他提供了多种举荐算法,包含基线算法、邻域办法、基于矩阵合成的算法(如SVD、PMF、SVD++、NMF)等。内置了多种相似性度量办法,如余弦相似性、均方差(MSD)、皮尔逊相关系数等。这些相似性度量办法能够用于评估用户之间的相似性,从而为举荐零碎提供重要的数据反对。  协同过滤数据集既然要基于工具库实现协同过滤举荐,天然就须要按该库的规范进行。Surprise也和大多数协同过滤框架相似,数据集只须要有用户对某个物品打分分值,如果本人没有能够在网上下载收费的Movielens或Jester,以下是我依据业务创立的表格,自行参考。 CREATE TABLE `short_video_rating` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` varchar(120) DEFAULT '', `item_id` int(11) DEFAULT '0', `rating` int(11) unsigned DEFAULT '0' COMMENT '评分', `scoring_set` json DEFAULT NULL COMMENT '行为汇合', `create_time` int(11) DEFAULT '0', `action_day_time` int(11) DEFAULT '0' COMMENT '更新当天工夫', `update_time` int(11) DEFAULT '0' COMMENT '更新工夫', `delete_time` int(11) DEFAULT '0' COMMENT '删除工夫', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8mb4 COMMENT='用户对视频评分表';业务介绍Web业务端通过接口或埋点,在用户操作的中央依据预设的规范记录评分记录。当打分表有数据后,用python将SQL记录转为表格再导入Surprise,依据不同的算法训练,最初依据接管的参数返回对应的举荐top列表。python局部由Flask启动的服务,与php进行http交互,前面将以片段代码阐明。 ...

July 4, 2023 · 7 min · jiezi

关于php:imi-v2148-发布大表分页和数据库迁移组件史诗级增强

更新阐明时隔将近一个月工夫,imi 再次发版,上面我将简略介绍这段时间里 imi 的变动。 MySQL 高性能分页查问(大表分页类)imi 的用户广泛反馈了一个问题,那就是当业务数据越来越多的状况下,后盾分页查问的速度是越来越慢。为解决用户迫切的需要,开发了这个性能。(文档) MySQL 分页查问的性能始终被人所诟病,既然 MySQL 不违心主动优化,那用户只能被迫手动优化。 分页查问性能优化的计划有很多,因为篇幅限度,不在这里具体阐明了。 原理: 先查出记录 id再依据 id 查问记录// 首先筹备好查问构建器$query = Db::query()->from('xxxtable');// 实例化大表分页类,字段名默认 id$pagination = new \Imi\Db\Mysql\Query\Pagination\BigTablePagination($query);// 指定主表主键字段名,如果波及多张表关联,这里须要指定主表的主键字段名$pagination = new \Imi\Db\Mysql\Query\Pagination\BigTablePagination($query, 'xxxtable.id');// 只查列表,返回值同 $query->select(),Result 对象$result = $pagination->select();// 获取数组$result->getArray();// 分页查问,返回值同 $query->paginate(),PaginationResult 对象$page = 1;$limit = 10;$result = $pagination->paginate($page, $limit);仅 MySQL 数据库反对数据库迁徙组件史诗级加强反对在生成模型时主动生成迁徙文件。(文档) 配置@app.beans: [ \Imi\Migration\Service\MigrationService::class => [ 'handler' => \Imi\Migration\Handler\FileMigrationHandler::class, // 迁徙处理器 'onGenerateModel' => true, // 是否在生成模型时主动生成迁徙文件 ],]上述配置是默认配置,不配置时主动启用。目录.migration 是寄存数据库迁徙文件和版本信息的目录,请勿将 .migration/version 提交到版本控制系统。 执行数据库迁徙执行前询问: vendor/bin/imi-swoole migration/migrate强制执行: ...

July 3, 2023 · 1 min · jiezi

关于php:PHPSwoole和Hyperf

PHP-FPM1.1 传统Nginx+FPM架构1.1.1 PHP-FPM 并发模型图1.1.1 一个HTTP申请的流转过程在网络应用场景下,PHP并没有像Golang那样实现http网络库,而是实现了FastCGI协定,而后与web服务器配合实现了http的解决,web服务器来解决http申请,而后将解析的后果再通过FastCGI协定转发给处理程序,处理程序解决实现后将后果返回给web服务器,web服务器再返回给用户。PHP-FPM是经典的多过程并发模型,Master/Worker模型。Master过程与Worker过程之间不会间接进行通信,Master过程只负责Fork和治理子过程,网络申请由子过程解决,一个Worker过程同时只能解决一个申请。Master通过共享内存获取worker过程的信息,比方worker过程以后状态、已解决申请数等,当master过程要杀掉一个worker过程时则通过发送信号的形式告诉worker过程。 1.1.2 PHP-FPM 初始化启动 及 Worker 申请解决PHP-FPM从初始化启动到Worker申请解决大略波及到以下步骤:fpm_init() Master读取php-fpm.conf文件初始化内存配置变量、创立管道、套接字、启动事件治理 fpm_run() Master过程fork出子过程后阻塞,worker进城去accept申请,执行php脚本(1)期待申请: worker过程阻塞在fcgi_accept_request()期待申请;(2)解析申请: fastcgi申请达到后被worker接管,而后开始接管并解析申请数据,直到request数据齐全达到;(3)申请初始化: 执行php_request_startup(),此阶段会调用每个扩大的:PHP_RINIT_FUNCTION();(4)编译、执行: 由php_execute_script()实现PHP脚本的编译、执行;(5)敞开申请: 申请实现后执行php_request_shutdown(),此阶段会调用每个扩大的:PHP_RSHUTDOWN_FUNCTION(),而后进入步骤(1)期待下一个申请。图1.1.2 php生命周期生成的语法树和opcode,同一个PHP脚本每次运行的后果都是一样的,在PHP-FPM模式下,每次申请都要解决一遍,是对系统资源极大的节约 1.2 Laravel框架的性能问题laravel是fpm社区外面十分受欢迎的一款web框架,联合composer治理开发组件,能够帮忙开发者在框架提供的多种优良组件(ORM,Router,Middleware,Artisan等)和解决方案在PSR-4标准下高效的进行Web服务端开发。所以有人说,如果Wordpress让php焕发第一春,那么Laravel+Composer让PHP焕发第二春。尽管laravel框架开发起来简略高效,中文社区也十分沉闷,生态丰盛。然而用laravel开发的利用性能问题却始终被重复提及。造成Laravel性能问题的次要起因有以下几点:框架代码的深度封装,导致把fpm模式下框架启动须要加载大量类和文件的性能问题放大(须要new太多类,每次new一个类都要autoloader找到类所在文件再require)每一次申请都会将所有的路由和配置全副加在到内存中,即使这次申请可能用不到(这其实也要归咎于fpm一次申请完结后就会销毁内存的个性)在框架启动时会调用composer autoload,依照classmap、prs-4、psr-0等标准生成所有类的引入门路所以针对Laravel进行性能优化的方向也就是针对以上几点来进行的:服务器启用 PHP OPcache 扩大缓存 opcodephp artisan route:cache php artisan config:cache等缓存命令通过 composer install --optimize-autoloader --no-dev 初始化我的项目依赖,以便减速 Composer 定位指定类对应的加载文件,同时不装置开发环境应用的依赖 1.3 FPM架构的局限并发模型带来的单台服务器性能局限只能开发HTTP服务器,如果须要WebSocket,TCP等其余协定的服务器怎么办服务解决中的内存状态无奈长久化,绝大多数只能开发无状态服务,有状态服务须要借助本地文件或者mmap形式来实现Swoole-PHP的高性能通信引擎扩大 2.1 什么是SwooleSwoole 是一个应用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程、高性能网络编程反对。提供了多种通信协议的网络服务器和客户端模块,能够不便疾速的实现 TCP/UDP服务、高性能Web、WebSocket服务、物联网、实时通信、游戏、微服务等,使 PHP 不再局限于传统的 Web 畛域。Swoole的呈现能够关上上述FPM所面临的局限Swoole是通过cli模式启动的常驻内存服务,通过本身提供的异步/协程服务端能够进步单机服务的并发性能(即使是同步I/O的服务性能也会有较大晋升)Swoole提供了多种通信协议的网络服务器,包含TCP、UDP、HTTP、WebSocket、MQTTSwoole提供该性能共享内存SwooleTable、过程间无锁计数器Atomic、过程间API等形式保障有状态服务过程间的同步底层hook原生PHP函数,使其可能更好的运行在协程server内(底层替换了ZendVM Stream的函数指针,所有应用php_stream进行socket操作均变成协程调度的异步IO) 2.2 如何应用Swoole2.2.1 协程格调HTTP服务端<?phpuse Swoole\Coroutine\Http\Server;use function Swoole\Coroutine\run; run(function () { $server = new Server('127.0.0.1', 9502, false);$server->handle('/test', function ($request, $response) { Co::sleep(1); $response->end("test");});$server->handle('/stop', function ($request, $response) use ($server) { $response->end("<h1>Stop</h1>"); $server->shutdown();});$server->start();}); ...

June 29, 2023 · 3 min · jiezi

关于php:关于项目初期数据量小的内容推荐的实现方法

前言当下,只有是一个初具规模的内容利用都具备个性化举荐零碎。比方购物类的会有举荐商品模块,搜寻条下有个性化的搜寻关键词或词条补全词,社交类的有博主举荐,视频或文章举荐等等。这些性能除了要有宏大的数据量,还要有健全的数据存储仓库建设计划,以及前面对数据的荡涤,排序,训练后的举荐模型算法。 然而,对于小公司或者说是小我的项目,在想法还未真正落地就设计大数据存储,举荐算法和一系列大型架构的计划,显然是不合乎业务型产品发展的失常法则的。我想那些大厂晚期开发应该也没有这么成熟的技术构造,都是一直迭代或者推倒重来一步步走过去的。 那么,在小我的项目晚期安顿了有对于举荐性能的那要如何解决呢? 如何做到下一次迭代在不重构的根底上增加协同过滤举荐? 上面就从视频举荐和用户举荐两个性能开展,用PHP和MySQL进行代码实现。 视频举荐这里次要通过一个内容热度值进行排序举荐,热度由内容品质和公布时间差决定,时差越长,热度越低,内容品质越高热度越高。而内容品质由视频点赞数,珍藏数和评论数外带权重决定,总体就是单位工夫内点赞,珍藏,评论越高,热度晋升,视频就越往前靠。绝对的就是公布工夫越久,热度就会逐渐升高,视频越往后靠。        另外咱们还要设计两个参数用于手动调节视频的热度,进步就只须要减少内容品质,所以额定加一个数能够说是初始值。升高能够对时间差增加一个指数,也就是时间差的次方,能够了解是重力,也就是随着工夫拉长,重力减少则热度成倍升高。 1. 公式1.1.  ”H“:视频热度值 1.2.  ”W“:视频品质,品质值自定(点赞数权重,珍藏数权重之和,或者点赞率(点赞量/浏览量),珍藏率(珍藏量/浏览量)之和)等。 1.3.  ”I“:初始值,能够手动调节热度或者用于前期用户账号的权重。比方零碎曾经有了成长体系,账号发育规定根本欠缺了,用于实时计算账号的权重调配的流量池,权重晋升,则前面公布的视频举荐力度大。 1.4.  ”T“:时间差,由以后工夫 - 公布工夫产生,加一的起因是避免时间差为0(分母为零),不过有审核机制下,这种状况并不存在。 1.5. ”G":热度衰减重力,这个也是用于手动调节热度设置的控制参数。不过前期如果增加了举报或者智能复审等环节,再随着诸如点赞和账户权重评估失控热度飙升的状况下,对视频违规或不良体现或长期状况进行减小举荐。默认值最好是1,这种状况就是在等同品质下新公布的越往前。  2. 表构造CREATE TABLE `hhyp_short_video` ( `id` int(11) NOT NULL AUTO_INCREMENT, `hsvn` varchar(255) DEFAULT '' COMMENT '短视频编号', `type` tinyint(1) DEFAULT '0' COMMENT '类型:1. 视频 2.图文', `user_id` int(11) DEFAULT '0', `video_url` varchar(255) DEFAULT '', `img_url` json DEFAULT NULL, `content` text COMMENT '内容', `market_goods_id` int(11) DEFAULT '0', `address_id` int(11) DEFAULT '0' COMMENT '地址ID', `lat` decimal(4,0) DEFAULT '0' COMMENT '纬度', `lng` decimal(4,0) DEFAULT '0' COMMENT '经度', `ip` varchar(100) CHARACTER SET utf8 DEFAULT '' COMMENT 'IP', `channel` tinyint(1) DEFAULT '0' COMMENT '渠道', `read_count` int(11) DEFAULT '0' COMMENT '浏览数', `like_count` int(11) DEFAULT '0' COMMENT '点赞数', `collect_count` int(11) DEFAULT '0' COMMENT '珍藏数', `comment_count` int(11) DEFAULT '0' COMMENT '评论数', `share_count` int(11) DEFAULT '0' COMMENT '分享数', `is_top` tinyint(1) DEFAULT '0' COMMENT '是否置顶:0.否 1.是', `status` int(11) DEFAULT '0' COMMENT '0. 审核中 10. 举荐 20. 下架', `hot_int` int(11) DEFAULT '1' COMMENT '热度初始值', `gravity` int(11) DEFAULT '1' COMMENT '热度重力衰减值', `audit_time` int(11) DEFAULT '0' COMMENT '审核工夫', `remark` text, `create_time` int(11) DEFAULT '0' COMMENT '创立工夫', `delete_time` int(11) DEFAULT '0', `update_time` int(11) DEFAULT '0', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=107 DEFAULT CHARSET=utf8mb4;3. 代码public static function getList($map, $page = 1, $size = 20) { // 数据指标权重配置 $wcfg = [ 'like_weight' => 4, 'collect_weight' => 3, 'comment_weight' => 1 ]; $timeUnit = 3600; // 单位小时 $where[] = ['delete_time', '=', 0]; $map = array_merge($where, $map); // 单位小时内,点赞/珍藏/评论越多热度越高,公布越久热度越低 $alog = "(like_count*%s+collect_count*%s+comment_count*%s+hot_int)/pow((UNIX_TIMESTAMP(NOW())-create_time)/%s, gravity)"; $hotIndex = sprintf($alog, $wcfg['like_weight'], $wcfg['collect_weight'], $wcfg['comment_weight'], $timeUnit); $field = ["id, hsvn,type,user_id,video_url,img_url,content,market_goods_id, like_count,collect_count,address_id,comment_count,share_count,create_time, $hotIndex as hot_index"]; $list = self::field($field) ->json(['img_url'], true) ->with([ 'user' => function ($query) { $query->withField('id, nickname, mobile, avatar'); }, 'marketGoods' => function ($query) { $query->withField('id, content,freight,user_id'); }, 'address' => function ($query) { $query->withField('id, mername'); } ]) ->where($map) ->page($page, $size) ->order("hot_index desc") ->select(); return $list; } ...

June 27, 2023 · 3 min · jiezi

关于php:如何运行-php-artisan-xxx-命令

Windows ① 关上 cmd 命令行工具② 进入到网站根目录,如 cd D:\xx\xx,如果以后盘符不是 D 盘,须要先切换盘符(运行 D:)③ 运行命令,如 php.exe artisan xxx如果 php 门路没有配置到环境变量中,须要指定 php.exe 的门路,如 D:\php\php.exe artisan xxx Linux ① 关上 shell 命令行工具② 进入到网站根目录,如 cd /var/www/xx/xx③ 切换到 php 的运行用户,如 su www,这一步很重要,如果默认应用 root 来运行命令,可能会导致前期权限运行有问题④ 运行命令,如 php artisan xxx如果第③步不能在 shell 模式切换到 php 的运行用户,也能够尝试应用 sudo -u www php artisan xxx 来运行命令

June 27, 2023 · 1 min · jiezi

关于php:盘点三种移动跨平台方案

 跨平台技术是前端人必备技能,明天就来为大家解读一下近几年业界支流的三大挪动端跨平台计划: Web 人造跨平台: Web App、PWA(Progressive Web Apps)、Hybrid App、PHA(Progress Hybrid App)都能够实现跨平台,WebView 是一种基于浏览器内核的跨平台解决方案,通过在挪动利用中嵌入一个浏览器组件,能够应用 HTML、CSS 和 JavaScript 来构建利用界面和逻辑。这种计划能够疾速开发利用,同时具备肯定的跨平台能力,因为 WebView 在不同的平台上基本上都有良好的反对。然而,因为 WebView 的性能和原生利用相比较差,且无奈齐全融入操作系统的个性,因而在一些对性能和用户体验要求较高的场景中,可能不太适宜应用。 容器化 Native 跨端: 这种计划次要基于原生利用的能力,通过将利用的业务逻辑封装成一个容器,在不同平台上进行适配和渲染,从而实现跨平台。这种计划通常应用一种对立的开发语言和框架,如React Native、Flutter、Ionic等,来编写应用逻辑,并通过桥接层将原生的API裸露给开发者。这样能够在肯定水平上实现代码的重用,并且性能较好,可能更好地融入操作系统的个性。然而因为底层依然应用了原生组件和性能,因而在一些特定平台的适配上可能须要额定的工作。 小程序跨平台计划: 小程序跨平台是一种绝对较新的跨平台计划,微信、支付宝等巨头的小程序框架不对立,于是有了FinClip、Taro、kbone等一系列跨小程序框架的计划。小程序提供了一种绝对关闭的开发环境和运行时,开发者能够应用一种对立的技术栈(如基于HTML、CSS和JavaScript的开发方式)来构建利用,并在不同平台上运行。 Web 生而跨平台 跨平台是 Web 与生俱来的劣势,浏览器和 WebView 都是 W3C 标准下的标准化 Web 容器,因而 Web 页面可能轻松投放到端外浏览器、端内 WebView、以及其它 App 提供的 WebView 中。 从老本角度来看,Web计划被认为是跨平台的不二之选,次要有以下几个起因: 1、对立的开发技术:Web开发应用的次要技术包含HTML、CSS和JavaScript,这些技术是跨平台的规范,简直所有的操作系统和设施都反对Web浏览器。开发者能够应用对立的开发技术,防止了学习和把握多个平台的特定技术和工具,升高了开发成本。 2、代码重用和保护:Web利用能够通过响应式设计和自适应布局,适应不同大小和分辨率的屏幕。这意味着开发者能够应用雷同的代码库和用户界面设计,在不同平台上构建统一的利用体验,防止了反复编写和保护多个平台的代码,缩小了开发成本和工作量。 3、部署和更新便当:Web利用基于浏览器运行,无需通过利用商店进行审核和公布,能够间接通过互联网进行部署和更新。这样,开发者能够更快地将利用推向市场,并在须要时疾速修复破绽或推出新性能,升高了部署和保护的老本。 4、跨平台兼容性:Web规范通过宽泛的测试和实际,具备较好的跨平台兼容性。在大多数状况下,Web利用在不同的浏览器和操作系统上都能够失常运行。这升高了针对不同平台进行适配和调试的老本,放慢了开发迭代的速度。 只管Web计划在老本方面具备劣势,但也须要留神它的一些限度,比方性能可能绝对较低、无奈间接拜访所有设施性能等。因而,抉择跨平台计划时,还须要综合思考其余因素,如性能要求、用户体验和性能需要,以及我的项目的特定状况。 容器化 Native 跨端 除 Web 人造跨端之外,另一种对立多端的思路是将 Native 定制成规范容器,让同一份代码跑在一个个规范容器中,例如: Android 容器:Native 壳 App iOS 容器:Native 壳 App Web 容器:Web Runtime React Native 跨 Android、iOS、Web、Windows 四端,Weex 跨 Android、iOS、Web 三端,Flutter 以相似的形式跨 Android、iOS、Web、Linux 四端。 从技术角度来看,RN 与 Weex 在 Native 容器中提供了 JavaScript 运行环境,以及布局引擎,渲染层都采纳 Native 控件,因而 UI 交互上依然存在零碎差别。而 Flutter 计划更彻底一些,连渲染层也换成了基于图形引擎自绘 UI 控件,从而保障 UI 交互的跨端一致性 然而,因为容器化 Native 的计划是从 Native 登程,没有跨端天才,除了要想方法反对 Web,还面临一个更难解决的问题——跨 App。 小程序跨平台计划 小程序跨平台计划是一种开发方法,容许开发者应用一套代码基于小程序框架构建利用,并在不同的平台上运行。上面介绍几种支流的小程序跨平台计划: 微信小程序: 微信小程序是最常见的小程序平台,开发者能够应用微信开发者工具和微信小程序框架(基于Vue.js)进行开发。微信小程序反对在微信客户端上运行,并提供了丰盛的原生组件和API,以及各种开发和调试工具。 支付宝小程序: 支付宝小程序是阿里巴巴旗下的小程序平台,与微信小程序相似。开发者能够应用支付宝开发者工具和支付宝小程序框架(基于React)进行开发。支付宝小程序也提供了一系列的原生组件和API,以及开发和调试工具。 FinClip 小程序平台: 国内各家的小程序框架并没有统一标准,各平台间小程序难以复用,于是就就有了 FinClip这样的技术来满足跨 App 投放利用的需要。 FinClip是一种小程序跨平台计划,它提供了一套集成化的解决方案,容许开发者将小程序嵌入到其余利用或网页中。并且 FinClip提供了丰盛的原生能力拜访接口,开发者能够通过FinClip Bridge API拜访设施的原生性能,如相机、地理位置、传感器等。这使得小程序能够取得更多的性能和更好的用户体验。 通过应用 FinClip小程序跨平台计划,开发者能够在不同的利用和平台共享同一套小程序代码,实现对立的开发和保护,同时取得原生能力拜访和跨域通信的劣势。这样能够进步开发效率、升高开发成本,并为用户提供更好的体验和性能。 这些小程序跨平台计划都提供了相应的开发工具和框架,开发者能够应用对立的开发语言(如JavaScript)和技术栈进行开发。开发者能够利用这些计划提供的组件库、API和工具,疾速构建跨平台的小程序利用,缩小反复开发和保护的工作量。同时,这些计划也提供了丰盛的原生性能拜访能力和开发调试工具,以便开发者更好地适配不同平台和提供优质的用户体验。 ...

June 26, 2023 · 1 min · jiezi

关于php:php怎么在线预览word文件php预览docdocxwps文件

PHP在线预览查看word文件文档PHP要实现在线Word预览只须要3步第一步: 筹备一个文件地址,如下:http://usdoc.cn/vw/文件模板.docx 第二步预览前置地址:http://vw.usdoc.cn/?src= 第三步开始预览http://vw.usdoc.cn/?src=http://usdoc.cn/vw/文件模板.docx

June 14, 2023 · 1 min · jiezi

关于php:静态编译swoolecli并调用rust的动态链接库

害 起因是上周因为发现php没有能间接获取硬件信息的类库或者api,而后用rust写了一个动态链接库调用。而后发了朋友圈后有人询问demo,索性写一个简略教程,省得工夫久了本人也忘了。(精通ffi的技术大牛请敞开此贴,纯属划水贴) note : 因为应用的是windows 上面步骤便以windows环境为例装置环境php环境ps: 因为我应用的swooel-cli 并没有附带ffi 便写一个繁难装置过程 php曾经有ffi扩大的请跳过 如果在linux的环境下应用swoole-cli的ffi那也就更不便了 网上教程一大把 请自行谷歌下载 cygwin 环境 # download cygwin 下载安装并装置 cygwin (用浏览器下载就行) wget https://cygwin.com/setup-x86_64.exe1.装置工具列表和依赖setup-x86_64.exe --no-desktop --no-shortcuts --no-startmenu --quiet-mode --disable-buggy-antivirus --site http://mirrors.ustc.edu.cn/cygwin/ --packages make,git,curl,wget,tar,libtool,bison,gcc-g++,autoconf,automake,openssl,libpcre2-devel,libssl-devel,libcurl-devel,libxml2-devel,libxslt-devel,libgmp-devel,ImageMagick,libpng-devel,libjpeg-devel,libfreetype-devel,libwebp-devel,libsqlite3-devel,zlib-devel,libbz2-devel,liblz4-devel,liblzma-devel,libzip-devel,libicu-devel,libonig-devel,libcares-devel,libsodium-devel,libyaml-devel,libMagick-devel,libzstd-devel,libbrotli-devel,libreadline-devel,libintl-devel,libpq-devel,libssh2-devel,libidn2-devel,gettext-devel,coreutils,openssl-devel,libffi-devel装置zip压缩包 setup-x86_64.exe --no-desktop --no-shortcuts --no-startmenu --quiet-mode --disable-buggy-antivirus --site http://mirrors.ustc.edu.cn/cygwin/ --packages zip unzip下载打包资源代码 git clone https://github.com/swoole/swoole-cli.git批改 sapi/scripts/cygwin/cygwin-config.sh文件 结尾加上--with-ffi ..../configure --prefix=/usr --disable-all \...--with-ffi \顺次执行以下命令即可 筹备re2c: bash ./sapi/scripts/cygwin/install-re2c.sh筹备扩大: bash ./sapi/scripts/cygwin/cygwin-config-ext.sh预处理: bash ./sapi/scripts/cygwin/cygwin-config.sh构建: bash ./sapi/scripts/cygwin/cygwin-build.sh打包: bash ./sapi/scripts/cygwin/cygwin-archive.shnote :打包实现后会在当前目录下生成 swoole-cli-{version}-cygwin-x64.zip 压缩包。 编写rust动静库1、cargo new --lib testdll 新建一个lib我的项目2、在 Cargo.toml 文件中加上 ...

June 14, 2023 · 1 min · jiezi

关于php:PHP文库网站开发会员充值功能代码

文档付费没有金币的状况须要提醒用户充值金币,实现后能力付金币下载文档 会员充值性能代码: <?php // 连贯数据库$servername = "localhost";$username = "username";$password = "password";$dbname = "myDB";$conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) { die("连贯失败: " . $conn->connect_error);} // 获取表单数据$member_id = $_POST['member_id']; // 会员ID$amount = $_POST['amount']; // 充值金额 // 插入数据到数据库 $sql = "INSERT INTO member_recharge (member_id, amount) VALUES ('$member_id', '$amount')"; if ($conn->query($sql) === TRUE) { echo "充值胜利!";} else { echo "Error: " . $sql . "<br>" . $conn->error;} // 敞开数据库连贯$conn->close();?>这个示例代码应用了MySQL数据库,首先连贯到数据库,而后从表单中获取会员ID和充值金额,最初将数据插入到名为member_recharge的表中。如果插入胜利,则输入“充值胜利!”,否则输入错误信息。 ...

June 9, 2023 · 1 min · jiezi

关于php:php-strncmp-源码分析

strncmp函数原型 源码剖析 版本PHP 5.6.401、Zend/zend_builtin_functions.c (内置函数)ZEND_FUNCTION(strncmp){ char *s1, *s2; int s1_len, s2_len; long len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssl", &s1, &s1_len, &s2, &s2_len, &len) == FAILURE) { return; } if (len < 0) { zend_error(E_WARNING, "Length must be greater than or equal to 0"); RETURN_FALSE; } RETURN_LONG(zend_binary_strncmp(s1, s1_len, s2, s2_len, len));}2、Zend/zend_operators.cZEND_API int zend_binary_strncmp(const char *s1, uint len1, const char *s2, uint len2, uint length) /* {{{ */{ int retval; if (s1 == s2) { return 0; } retval = memcmp(s1, s2, MIN(length, MIN(len1, len2))); if (!retval) { return (MIN(length, len1) - MIN(length, len2)); } else { return retval; }}3、memcmp 用法 ...

June 2, 2023 · 1 min · jiezi

关于php:yarnlock-文件的工作原理

yarn.lock 文件是 JavaScript 包管理器 Yarn 的外围组件之一,它记录了每个装置的包的确切版本号和依赖关系,并且在我的项目构建和部署中起着重要的作用。在本文中,咱们将深入探讨 yarn.lock 文件的作用、工作原理以及应用办法。 作用在探讨 yarn.lock 文件的作用之前,咱们先来看一下 NPM 和 Yarn 之间的区别。NPM 是 JavaScript 的默认包管理器,它通过 package.json 文件来治理包的版本和依赖关系。在装置一个包时,NPM 会从 package.json 文件中读取包的版本信息,并且尝试寻找合乎依赖关系的版本,而后将这些包装置到 node_modules 目录下。然而,因为 NPM 会主动地解析依赖关系并下载最新版本的包,所以同一个我的项目在不同的工夫和环境中,可能会装置不同的包版本,导致构建和部署呈现问题。 为了解决这个问题,Yarn 引入了 yarn.lock 文件,它的作用次要有以下几个方面: 1. 锁定包版本yarn.lock 文件会记录每个装置的包的确切版本号和依赖关系,因而能够确保在任何工夫和环境中,同一个我的项目所应用的包版本都是统一的。这有助于防止因为不同环境中的包版本不统一而导致的构建和部署问题,进步我的项目的稳定性和可靠性。 2. 进步装置速度因为 yarn.lock 文件记录了所有依赖包的版本信息,Yarn 在安装包时只须要读取这个文件,并下载所需的包版本,而不须要每次都去查问网络。这能够大大提高包的装置速度,尤其是在装置大型项目时。 3. 优化构建和部署yarn.lock 文件能够确保在构建和部署时应用的包版本是统一的,从而防止了因包版本不统一而引发的问题。此外,它还能够减速构建和部署过程,进步我的项目的效率和可靠性。 工作原理yarn.lock 文件的工作原理与 package-lock.json 文件相似,都是通过锁定包版本和依赖关系来确保每个我的项目应用的包版本是统一的。在 Yarn 中,当装置一个包时,Yarn 会首先查看 yarn.lock 文件,如果该文件存在并且包版本匹配,则间接下载相应的包,否则会更新 yarn.lock 文件并下载最新的包。 当生成 yarn.lock 文件时,Yarn 会查看 package.json 文件中的依赖关系,并计算每个依赖包的依赖关系。

May 29, 2023 · 1 min · jiezi

关于php:二分查找法学习记录

二分查找二分查找算法自身不难,这一章的重点不在二分查找算法自身,而是如何写出正确的代码。 关键字:清晰、谨严 定义不同的循环不变量,或者不一样的函数语义都会导致代码不一样。 二分查找法的两种实现形式定义两种不同的循环不变量来实现二分查找法: 第一种,也是比拟常见的一种:在 data[l,...,r] 范畴内查找 target。留神,data 的区间范畴是左闭右闭。 public function search($nums, $target){ // 在 data[l,...,r] 范畴内查找 $l = 0; $r = count($nums) - 1; while ($l <= $r) { $mid = floor(($l + $r) / 2); if ($target == $nums[$mid]) { return $mid; } if ($target < $nums[$mid]) { $r = $mid - 1; } else { $l = $mid + 1; } } return -1;}第二种,在 data[l,...,r) 的范畴中寻找 target。留神,data 的区间范畴是左闭右开。 ...

May 28, 2023 · 8 min · jiezi

关于php:PHP通过Modbus-Tcp实时获取设备数据

前言:最近接触了一个对于PLC工控的小我的项目,大略场景是,对方一个茶叶工厂。曾经通过各种设施组成了自动化的工控零碎。并且也让我的一个敌人做了茶园监控和茶园天气环境等的web页面展现,然而还没有工控设施的数据显示。 需要:工控设施曾经连贯到了一台作为上机位的电脑上,所以要获取设施数据。须要在同一局域网上,通过modbus tcp申请对方曾经凋谢的端口。拿到数据贮存到数据库,最初web界面只用按工夫程序获取数据库的数据。        因为拜访对方电脑须要他们提供受权,所以这里演示就以modbus的调试工具,以及前面PHP代码申请示例。  工具:1.  Modbus Slave: 从机端模仿软件,这里测试能够把他作为服务端,PHP为客户端就是取该机子的数据。2.   Modbus Poll: 主机仿真器,用于测试和调试Modbus从设施,这里测试也只是把他当做客户端应用。3.   ModScan32:  主机/从机模拟程序 ,当前介绍。4.   MThings:  一个国产免费软件, 既能够模仿主机设施 又能够模仿从机设备,当前介绍。 工具操作:Modbus Slave1. 创立TCP/IP连贯。(1). 点击connection->connection,弹出参数窗口,能够按上面确认。 (2). 配置函数,点击setup->slave definition,弹出参数窗口,默认OK就能够。 (3). 批改某项数据的值,双击对应的框,弹出后批改OK就能够。 2. 从机参数阐明:(1). ID, 机子的设施标识,是slave definition的slave ID(2). F, 以后节点的函数码,主机获取代码获取设置数据,须要指定的函数。 3. 查看发送和接收数据明细。 (1). 点击display,弹出面板。 留神modbus slave每次连贯只能维持10分钟可能是没有激活。  Modbus Poll1.  通过Tcp获取从机上的数据。(1).  连贯,点击connection->connection, 抉择TCP/IP。 (2). 批改为slave机子对应的IP地址和端口,点击保留。  (3). 连贯胜利后,查看读写定义,能够按指定slave配置批改。 (4). 连贯失败,Mbpoll面板会提醒红色字体。面板文字说明如下。Tx = 4示意向主站发送数据帧次数,图中为4次; Error = 0示意通信谬误次数,图中为0次; ID = 1示意模仿的Modbus子设施的设施地址,图中地址为1;F = 03示意所应用的Modbus性能码,图中为03性能码; SR = 1000ms示意扫描周期。红字局部,示意以后的谬误状态,“No Connection”示意未连贯状态。 (5). 查看读写数据。 ...

May 27, 2023 · 2 min · jiezi

关于php:php可以做搜索吗php搜索引擎的尝试

php是能够做搜索引擎的,如果你不想配置那些简单的插件、扩大,只是想独自用php可能搜寻几十万,百万甚至千万量级的文章,是齐全能够实现的。 先睹为快 百万数据索引演示 php搜索引擎 在做这个php搜寻性能之前,我也理解过以后的,比方elasticsearch等,一些曾经十分成熟搜寻软件的基本原理。 想理解搜索引擎的流程,能够移步我的其它文章,有具体解说。 这里大抵介绍一下我的php搜寻根本逻辑思维 当初的整个架构包含内容爬取、荡涤、入库、索引、搜寻。当然,索引性能,是能够独自拿进去的,如果你不想爬取网页,只是想搜寻本人的内容,定制改变一下是齐全能够的。 内容指定 爬取网页 能够爬取指定网页,只有把你想爬取的(可能是某一类的)网站链接,放在一个txt文件内,一般笔记本就足够,就能够很轻松地对这些链接进行多线程爬取。只有你的电脑性能达到中等,设置好爬取层数,每次能够爬取到几十万的网页,也不能爬取太深,不然,他人的服务器压力太大。 自定义内容 你也能够在数据库放入你本人想要搜寻的内容。 分词 想要搜寻,分词是必不可少的。php的分词工具也很多,如果你的畛域很垂直,那么词库的构建就很重要的,我的词库分类很丰盛,能够随时生成指定畛域的词库,这样分词的后果能力精准。 倒排索引 接下来就是依据分词后果,构建倒排索引,对于倒排索引的常识,能够自行理解,是一个大的知识点。基本上绝大部分的搜索引擎,基本原理都是基于倒排索引,而后在这根底上进行优化。当数据比拟少时,能够不必太关注数据结构算法,应用简略的存储逻辑就能够达到成果,速度也能保障。当数据达到百万、甚至千万时,就不得不去摸索更高效的数据结构了,在高级版本上,我应用了bitmap存储构造,这里的bitmap是指php的底层位运算,还是相当简单的,喋喋不休说不清,一般使用者也没必要理解这个,高级就行了~~ bitmap算法在大量数据时,并没有什么劣势,当然也不会比其它算法效率差,当数据及其宏大时,劣势霎时体现进去了,可能很大水平上节约存储空间。 搜寻 接下来是最外围的搜寻了。我不晓得怎么用语言很好地去形容其中的逻辑,因为过程及其不容易了解,也迭代了很多版本,根本的逻辑也是位运算。其中最大的问题是排序,怎么在大量数据下,把最合乎的后果排到后面。 其它 数据量十分大的时候,任何一个解决流程,都极有可能产生内存不够等问题,所有的致力,就是让这个搜索引擎达到一个成果:在一般的个人电脑上,可能索引千万级别的数据,而不必其它插件辅助。而且齐全做到了,只有电脑性能达到中等以上就能够。 整个过程不须要装置任何扩大,可间接运行的php文件。应用也很简略,本地搭建一个集成环境,源码扔进去,就能够应用了。

May 27, 2023 · 1 min · jiezi

关于php:用扩展的方式在-PHP-中使用-Kafka

前言:因为之前在 PHP 中应用 Kafka 是通过 composer 包的形式,因为 nmred/kafka-php 很久没有保护,并且网上相干问题的文章也比拟少。所以我这次换成 PHP 扩大 RdKafka 持续应用,次要介绍扩大装置和这种形式的基本操作。  装置:1. 下载地址。找到与本人环境匹配的就能够 2. 目录因为 php-rdkafka 依赖 librdkafka,linux 就须要先装置 librdkafka 后装置 php-rdkafka,而 windows 版本是如下几个文件,装置办法如下:(1). 将 librdkafka.dll 和 librdkafka.pdb 放入 PHP 装置的根目录下,而 php_rdkafka.dll 和 php_rdkafka.pdb 放入 PHP 装置目录的 ext 下。 (2). php.ini 配置文件增加 extension=php_rdkafka.dll,最初重启 PHP。 (3). php-m 或这 phpinfo (); 就能够查看到扩大了。 通过 get_declared_classes() 也能够查看到扩大里预设的函数了。 应用:1. 生产public function kafkaTest(){ $rk = new \RdKafka\Producer(); $rk->addBrokers("127.0.0.1:9092"); $topic = $rk->newTopic("shop"); $ret = []; for ($i = 0; $i < 5; $i++) { $content = "第" . $i . "次发送失败"; $message = ["mobile" => "15623652142", "content" => $content]; $payload = json_encode($message); // 指定向0号partition生产数据 $ret[]['produce_res'] = $topic->produce(0, 0, $payload, "sms_$i"); // 随机抉择partition //$topic->produce(RD_KAFKA_PARTITION_UA, 0, $payload); if ($rk->getOutQLen() > 0) { $ret[]['produce_poll'] = $rk->poll(500); } else { $ret[]['produce_poll'] = $rk->poll(0); } } dump($ret);} ...

May 27, 2023 · 2 min · jiezi

关于php:关于-PHP-启动-MongoDb-找不到指定模块问题

前言:最近有一个小 demo,须要通过 PHP 将用户行为记录贮存到 MongoDB,再用 Spark 做协同过滤。因为以前解决跨语言交互是通过消息中间件,这次本地应用 MongoDB 却弄出了几个问题。首先是本地装置了扩大,启动时报找不到模块谬误,再个时 PHP 进行的实例化应用时报 MongoClient 和一些函数不存在。最初也举荐一些方便快捷的 MongoDB 图形化工具。  环境:Windows10PHP7.4MongoDB 4.4.14 Serverphp_mongodb-1.12.1-7.4-nts-vc15-x64 装置:1. MongoDB Server 下载 2. php_mongodb-1.12.1-7.4-nts-vc15-x64 下载 3. navicat for mongodb 下载 问题:找不到指定模块 php_mongodbPHP 中增加扩大 php_mongodb 不正确,除了须要 php_mongodb.dll 还要 php_mongodb.pdb 复制到 php 装置目录下的 ext 中 最初在 php.ini 增加 extension=php_mongodb.dll,重启完后查看 php -m 或者浏览器中查看 phpinfo () 2. MongoClient 类不存在起因是这个是旧版本 php_mongo 扩大提供的类,新版本是通过前面演示代码的形式,如果不晓得扩大提供的新办法或函数能够通过一下形式查看。 (1). get_declared_classes () 查看扩大里预设的函数 工具应用:MongoDB Server(1).启动服务 Navicat for MongoDB(1). 连贯服务 ...

May 27, 2023 · 1 min · jiezi

关于php:php如何用正则匹配出百度云盘分享链接的提取码和链接

要应用正则表达式从百度云盘分享链接中提取提取码和链接,您能够应用以下代码: $link = '链接:https://pan.baidu.com/s/16y9Z5mTSE6gewStGDNndNQ 提取码:65nk 复制这段内容后关上百度网盘手机App,操作更不便哦'; $regex_link = '/(?<=链接:)[^\s]+/'; // 匹配"链接:"前面的非空白字符$regex_code = '/(?<=提取码:)\w+/'; // 匹配"提取码:"前面的字母数字字符 if (preg_match($regex_link, $link, $match_link) && preg_match($regex_code, $link, $match_code)) { echo "链接:{$match_link[0]}<br>"; echo "提取码:{$match_code[0]}"; } else { echo "没有找到链接或提取码。";}

May 26, 2023 · 1 min · jiezi

关于php:phpstorm开发项目中5种常用的使用方法

前言:有时因为各种起因重装了零碎,工具有时也重装,然而又没有把软件的设置导出来。最开始的工具配置过了段时间给忘记了,比方平时棘手的敞开窗口快捷键,composer,git等的设置。  办法总结:批改快捷方式点击”File”->”Settings”->”keyMap” 就能够看到那些快键键的各种分类,比方我常喜爱用”ctrl+w”敞开正在关上的窗口,所以就抉择”windows”分类下的Editor Tabs的”close”,邮件remove掉原来的,而后add手动增加一个即可。  2. 批改代码正文模板点击 ”File”-> “setting”->”File and code Templates” -> ”Includes” -> “PHP Function Doc Comment”,左边增加如下: /** * @Notes: 文件形容 * @Author: ZERO开发 * @Time: ${DATE} ${TIME} * @Interface ${NAME}${PARAM_DOC}#if (${TYPE_HINT} != "void") * @return ${TYPE_HINT}#end${THROWS_DOC}* @Return mixed  3. 增加composer3.1. 点击“Tools”->“composer”->“Manage dependencies”,点击“composer.phar”抉择本地装置过的composer,而后抉择一个装置的PHP版本。   4. 应用版本控制导入我的项目,比方git。4.1. 点击 “VCS’ -> “Checkout from version Control” -> “Git”,而后填写克隆的仓库地址曾经本地保留的我的项目名目录。  4.2. 第一次克隆可能须要输出用户名和明码,如果第一次输出谬误,前面克隆时不提醒从新输出,则可能是电脑对第一次输出的值进行了本地凭证保留。须要在winows凭证中批改账户明码,操作如下。 4.3.  关上“控制面板”-> “用户账号和家庭平安”。 4.4. 进入后,找到“windows凭证”点击治理进入。 4.5. 找到对应的地址,点击“编辑”或“删除”该记录,就能够从新克隆拉取直到胜利。 版本控制分支的“切换”,“创立”,“合并”。5.1. 切换,找到IDE的右下角,如果是Git,默认是显示Git master,点击该文字就能够关上仓库的所有分支,点击其中一个,“Checkout  As”就能够切换了。如果本地没有该分支则会创立一个。  5.2. 创立。如果须要在某个分支上再开一个分支,则切换胜利后,点击 ”New Bransh”命名一下就在本地新建了分支,本地并切换到该新建的分支下。 而后 ”ctrl+shift+k”关上推送窗口,将本地新建的分支推送到近程, 而后查看Remote Branches就会多了一个分支。 5.3. 合并。合并次要就是本地以后的分支与近程其中一个分支进行合并,所以只须要切换至本地开发的分支,而后点击“Remote Branches”下须要进行合并的分支,抉择“Merge into Current”即可。 ...

May 26, 2023 · 1 min · jiezi

关于php:PHP高并发高负载下的3种实战场景解决方法

前言:在理论开发我的项目中,产品一旦推广开来,总能遇到一些小问题。比方某个接口忽然就申请崩掉了,某个提交接口明明做了限度为什么就多出了好多反复的记录。还有是某个记录超过限度进行批改了,以下就以这几个小问题总结一下平时采取的解决办法。  场景:1.缓存生效场景,就比方某个接口做了数据缓存,缓存过期导致忽然某个时刻大量申请间接读数据库。解决办法设置redis缓存回调事件,订阅生效频道。所以这个也能够用来解决某些业务场景到期解决形式。 2.接口幂等性场景,就比方注册接口,通过手机号查问是否存在记录。但有时呈现网络提早用户连点等状况,会呈现数据库呈现几条一样的用户数据记录。 3.商品库存超卖场景,比方某个流动商品下单,多个用户同时下一个商品的订单,从而导致库存超卖的景象。解决办法能够应用乐观锁或者乐观锁解决此问题。  场景一,缓存生效回调。1. 设置Redis回调事件办法。(1). 关上Redis客户终端,输出命令非持久性的回调事件设置 config set notify-keyspace-events Ex (2). windows平台关上Redis装置目录中找到"redis.windows-service.conf",而后关上编辑找到notify-keyspace-events那一行,去掉"#",改为notify-keyspace-events “Ex"。 (3). 其中Redis还能够设置订阅键名的回调,比方订阅某个键名的del操作等,能够在conf中设置不同的,办法网上也有的。 2. 订阅redis某个库的键生效的频道名。能够在命令测试,也能够通过PHP代码订阅而后cli环境下运行脚本。 命令:subscribe __keyevent@0__:expired3. 设置有限期从新关上一个新的redis客户终端输出一个带有效期的键值对,如下(键名test_key_name, 工夫30s, 值ceshi) 命令:setex test_key_name 30 ceshi4. 查看键生效回调订阅的命令窗口是否呈现生效的键名。 5. 代码实现键名的生效事件订阅。<?php //设置php脚本执行工夫set_time_limit(0);//设置socket连贯超时工夫ini_set('default_socket_timeout', -1);class redisSubscribe { protected $config = [ "host" => "127.0.0.1", "password" => "6379" ]; protected $redis; public function __construct() { try { $this->redis = new \Redis(); $this->redis->pconnect($this->config['host'],$this->config['password']); } catch(\Exception $e) { echo "redis谬误:".$e->getMessage().PHP_EOL; } } // 一般音讯订阅 public function normal() { //申明频道名称 $channelName = "test"; try { $this->redis->subscribe([$channelName], function ($redis, $channel, $msg) { echo 'channel:' . $channel . ',message:' . $msg . PHP_EOL; file_put_contents('subscribe.log',"\n-".$msg."-\n",FILE_APPEND); }); } catch (\Exception $e) { echo $e->getMessage(); } } // 订阅Key生效事件的频道 public function keyNotify() { echo "wathc keyNotify start~~".PHP_EOL; // Key事件回调 //$channel = "__keyevent@0__:expired"; // 0号库的Key过期事件频道名 $channel = "__keyevent@*__:expired"; // 所有库的Key过期事件频道名 try { $this->redis->subscribe([$channel], function ($redis, $channel, $msg) { echo 'channel:' . $channel . '===========' . ',message:' . $msg . PHP_EOL; file_put_contents('subscribe.log',"\n-".$msg."-\n",FILE_APPEND); }); } catch (\Exception $e) { echo $e->getMessage(); } }}(new redisSubscribe())->keyNotify();?>6. 通过PHP-cli运行该脚本而后也能够setex一个短时间的键,而后查看命令是否输入该生效的键名。 ...

May 26, 2023 · 2 min · jiezi

关于php:OSS云文件列举分页功能的解决方案

前言:目前我的项目开发中上传性能很多都是应用云存储,其益处太多这里就不列举了。然而在上传胜利后,有些性能场景下须要对存储桶内的文件进行治理。天然把历史的文件列表展现进去就很有必要了。而后有列表就有分页,云存储又不像本地存储间接读文件夹就能够,所以我想了两种办法来解决。  办法:将上传的文件返回的地址都存到数据库中,取列表就分页的读表返回。应用OSS文档的文件列举办法,益处是不必建表等等,所以上面以这种形式演示。 阐明:Oss的SDK中提供一个办法listObjects,接管两个参数($bucket, $options)。别离示意存储桶名,基本参数集。$options是个数组,外面又有$prefix,$delimiter,$nextMarker,$maxkeys,参数形容见如下。 $prefix是本人在存储桶下寄存文件的文件夹门路,做分页最次要的是$nextMarker,$maxkeys这两个参数。一个是文件的终点就相当于分页start,一个是每次列举的最大个数就相当于page_size。  思路:实现这个分页是不带页码下拉式加载,所以不存在能够点击返回上一页,次要从$nextMarker,$maxkeys开始。    getNextMarker()办法获取上一次列举文件的标识,没有为空窜,有则返回上一次列表最初的那个文件门路。   将返回的标识存入缓存中,每次接口申请进来从缓存中读取一下上一次的文件标识。   将标识传入listObjects办法中$options参数的$nextMarker。就能够以上一次标识为终点列举前面的列表。   自定义一参数,如$page,(为0,$nextMarker则不取缓存间接传入空窜标识;下拉时则page累加传入缓存标识)。这样就可在接口申请时从最开始列举Oss文件。 代码实现:业务接口办法。/* * 列举专用(获取图片列表/获取文件列表) * @access private * @return array * */ private function getList($page = 0 , $dirname) { list($proctol,$domain) = explode("//", $this->oss->config['EndPoint']); // Oss对外域名 /*获取配置项分页页码,判断页数*/ if (!empty($this->ueconfig) && is_array($this->ueconfig)) { $pageSize = 20; } // 文件终点标识 $marker = ""; if($page > 0) { $marker = Cache::store('redis')->get($this->config['bucket']."-".$dirname."maker"); } // 获取配置文件中的页码 $pageSize = $this->ueconfig['imageManagerListSize']; //$pageSize = 2; // 调用封装的Oss文件列举办法 $res = $this->oss->listBuckets($this->config['bucket'],$dirname,$pageSize,$marker); $ret = []; if($res['code'] == 1) { foreach ($res['data']['obj']->getObjectList() as $key => $value) { //$ret[] = $proctol."//".$this->config['bucket'].".".$domain."/".$value->getKey(); array_unshift($ret,$proctol."//".$this->config['bucket'].".".$domain."/".$value->getKey()); } if(!$res['data']['next_marker']) { $marker = explode(".com/",$ret[0])[1]; } else { $marker = $res['data']['next_marker']; } // 标识存入缓存 Cache::store('redis')->set($this->config['bucket']."-".$dirname."maker",$marker); } return $ret; }Oss文件列举封装办法。/* * 列举文件 * @access public * @add: bqs * @praam: string $bucket 存储桶名 * @param: string $prefix 文件门路 * @param: int $page 开始页 * @param: int $maker 分页标识 * @return: array * */ public function listBuckets($bucket = "hhbusiness", $prefix = "", $pagesize = 20, $maker = "") { $config = $this->config; $res['code'] = 1; $res['message'] = ''; $res['data'] = ""; try { $ossClient = new OssClient($config['AccessKeyID'], $config['AccessKeySecret'], $config['EndPoint']); //$prefix = 'article/uploadimage/'; $delimiter = '/'; $nextMarker = ""; //$maxkeys = 1; $options = array( 'delimiter' => $delimiter, 'prefix' => $prefix, 'max-keys' => $pagesize, 'marker' => $maker, ); $result['obj'] = $ossClient->listObjects($bucket, $options); $result['next_marker'] = $result['obj']->getNextMarker(); // 没有值 if ($result['obj']->getIsTruncated() !== "true") { //break; } $res['code'] = 1; $res['data'] = $result; } catch (OssException $e) { $res['code'] = 0; $res['message'] = $e->getMessage(); } return $res; }  ...

May 26, 2023 · 2 min · jiezi

关于php:TP50使用助手函数model出现commonModel类不存在

在ThinkPHP5.0中有一个助手助手函数model(),能够实例化具体的模型,包含分层模型,只有传入类名(第一个参数),分层名(第二个参数)。这个函数其实是ThinkPHP框架Loader中的一个静态方法,能够关上"thinkphp\library\think\Loader"的380行左右查看,参数和实现办法能够自行查看。  问题在本地也就是windows环境下,我通过应用model实例自定义的模型类失常调用办法,放到服务器上(linux),发现模型类找不到,呈现相似“类不存在:app\common\Model\ArticleVote”。所以就去找Loader中的model办法,因为应用了model必定是先去调用外面的动态函数,而后依据传入的参数实例化返回,有点像工厂模式。 思路查看应用model函数中,传入的模型名和分层名是否都存在,也就是在application我的项目里有没有对应的模型分层名字的文件夹。thinphp5.0中我的项目文件夹须要是小写结尾,比方model,controller。留神定义的模型类的命名空间,是否存在大小写凌乱。呈现"common\ModelArticleVote",留神"common"就是Loader中model办法走了else代码块。 解决    发现了是为什么有一个"common"是因为定义的类实例化的时候未找到,那就是在model()应用时传入的参数有错,要么就是定义的模型类的命令空间有误。能够在Loader的静态方法中打断点查看在实例类时的命名空间别离是什么,而后应用class_exists函数查看是否存在,再认真看一些命名空间。最初发现果然是在model助手函数中传入第二个参数(分层名)和模型类的命名空间没有保持一致,一个大写,一个小写。  

May 26, 2023 · 1 min · jiezi

关于php:解决在TP5中无法使用快递鸟的即时查询API

快递鸟的接口对接其实很简略,先去官网注册账号,登陆把根本信息填好,而后在产品治理中订购一下“物流查问”,收费,不过其余产品是免费,收费的有对接口调用频率限度,联合本人的利用流量够用就能够。应用前复制一下账号下的用户ID和API key,并且快递鸟对各个API提供了各种语言的demo,其实下载下来,找一下平时寄快递的运单号,本地运行一下就能用了。(名称: KdApiSearchDemo) 其实拿到demo代码,能够放到我的项目中,因为demo是以面向过程写的,所以为了不便天然就想封装一下,然而运行起来就始终报错。 <?phpnamespace data\extend;use data\service\Config;/** * 快递鸟即时查问接口 * @author Administrator */class Kdniao{    private $ebusinessid;//商户ID    private $appkey;     //商户秘钥    private $request_type;//申请类型    private $request_url; //申请URL    /**     * 构造函数    */    public function __construct($shop_id){        $config=new Config();        $express_config=$config->getOrderExpressMessageConfig($shop_id);        $is_use=$express_config['is_use'];        if($is_use==0){            $this->ebusinessid = 'niushop';            $this->appkey = 'niushop';        }else{            $this->ebusinessid = $express_config["value"]["appid"];            $this->appkey = $express_config["value"]["appkey"];        }        $this->request_type = 1002;        $this->request_url = 'http://api.kdniao.cc/Ebusiness/EbusinessOrderHandle.aspx';    }    /**     * Json形式 查问订单物流轨迹     */    public function getOrderTracesByJson($requestData){        //$requestData= "{'OrderCode':'','ShipperCode':'YTO','LogisticCode':'12345678'}";        $datas = array(            'EBusinessID' => $this->ebusinessid,            'RequestType' => $this->request_type,            'RequestData' => urlencode($requestData) ,            'DataType' => '2',        );        $datas['DataSign'] = $this->encrypt($requestData, $this->appkey);        $result=$this->sendPost($this->request_url, $datas);        //依据公司业务解决返回的信息......        return $result;    }    /**     *  post提交数据     * @param  string $url 申请Url     * @param  array $datas 提交的数据     * @return url响应返回的html     */    public function sendPost($url, $datas) {        $temps = array();        foreach ($datas as $key => $value) {            $temps[] = sprintf('%s=%s', $key, $value);        }        $post_data = implode('&', $temps);        $url_info = parse_url($url);        if(empty($url_info['port']))        {            $url_info['port']=80;        }        $httpheader = "POST " . $url_info['path'] . " HTTP/1.0\r\n";        $httpheader.= "Host:" . $url_info['host'] . "\r\n";        $httpheader.= "Content-Type:application/x-www-form-urlencoded\r\n";        $httpheader.= "Content-Length:" . strlen($post_data) . "\r\n";        $httpheader.= "Connection:close\r\n\r\n";        $httpheader.= $post_data;        $fd = fsockopen($url_info['host'], $url_info['port']);        fwrite($fd, $httpheader);        $gets = "";        $headerFlag = true;        while (!feof($fd)) {            if (($header = @fgets($fd)) && ($header == "\r\n" || $header == "\n")) {                break;            }        }        while (!feof($fd)) {            $gets.= fread($fd, 128);        }        fclose($fd);        return $gets;    }    /**     * 电商Sign签名生成     * @param data 内容     * @param appkey Appkey     * @return DataSign签名     */    public function encrypt($data, $appkey) {        return urlencode(base64_encode(md5($data.$appkey)));    }}  demo中提供的申请快递鸟API是应用的fsockopen函数,该函数是PHP较高版本(5.0以上)中性能比拟弱小的,通过一个URL和PORT申请返回一个文件指针,前面的就能够通过文件操作函数获取返回后果。    ...

May 26, 2023 · 3 min · jiezi

关于php:ThinkPHP5中如何实现模板完全静态化

模板齐全动态化,也就是通过模板齐全生成纯动态的网页,相比动静页面和伪动态页面更平安更利于SEO拜访更快。相比前二者各有利弊吧,当初略微对这三种模式的优缺点比照一下,以及在ThinkPHP5我的项目中实现齐全动态化的根本过程。  比照1. 动静与真动态页面动态化与动静页的比照,动态没有了SQL和一些后端脚本运行,平安稳固,访问速度快,对SEO敌对(网上也有说当初的搜索引擎曾经对动静网页的抓取没什么压力了),然而搜索引擎再弱小,动态的URL也比动静的前面带问号冒号什么的要好看,不对SEO敌对对一般浏览用户者也是敌对(难看第一)。然而生成动态页面的弊病,也就是如果一个博客网站,随着文章内容的增多,那生成的页面也一直增多,就算一个html就30几Kb,数量多的状况下也挺耗存储空间,网上也有说频繁生成动态页面化,容易让硬盘呈现坏道。这个我的认识是不好测试能够疏忽,因为当初少数是应用云服务器或云虚拟主机,那些都不是物理硬件,就算太过碎片导致硬盘损坏,网站也能失常拜访的,因为那是云服务器。  2. 真动态与伪动态这二者的比照看起来像是正统之争,因为大家都晓得伪动态还是动静页,只是Apache通过URL重写规定让其变成了像动态网页的样子。次要也是让本人对SEO敌对,然而相比真动态多了Apache的步骤,所以也就比拟消耗一些服务器的资源。而真动态的毛病下面也说了,在我的项目中的抉择看需要,各有利弊,北桥苏的应用次要是本人网站有时要优化一下速度所以就做了模板动态化,以下是操作过程。 实现思路1. 依据模块控制器主动递归创立目录2. file_exists判断生成的动态页是否存在3. 或判断过期与否,存在重定向到动态网页4. file_put_contents($file,$content)函数生成页面 编码1. 目录的创立/* * 递归创立目录 * @param string $dir 文件目录门路 * @return boolean 创立后果 * **/function mkdirs($dir){ if(!is_dir($dir)) { if(!mkdirs(dirname($dir))){ return false; } if(!mkdir($dir,0777)){ return false; } } return true;} 2. 在基类中初始化需创立的目录protected $staticHtmlDir = ""; //动态模板生成目录protected $staticHtmlFile = ""; //动态文件protected function _initialize() { parent::_initialize(); $this->staticHtmlDir = "html".DS.$this->request->controller().DS;3. 基类中的生成前与生成后的办法//判断是否存在动态public function beforeBuild($param) { //生成动态 //$baseDir = "html".DS.$this->request->controller().DS; if(is_array($param)) { $param = implode("_",$param); } $this->staticHtmlFile = $this->staticHtmlDir.$this->request->action().($param?$param:'').'.html'; if(mkdirs($this->staticHtmlDir)) { if(file_exists($this->staticHtmlFile) && filectime($this->staticHtmlFile)>=time()-60*60*24*5) { //动态文件存在 $this->redirect('/'.$this->staticHtmlFile); } } }//开始生成动态文件public function afterBuild($html) { if(!empty($this->staticHtmlFile) && !empty($html)) { if(file_exists($this->staticHtmlFile)) { unlinnk($this->staticHtmlFile); } if(file_put_contents($this->staticHtmlFile,$html)) { $this->redirect('/'.$this->staticHtmlFile); } } }4. 视图控制器中的应用ThinkPHP5中fetch办法返回给file_put_contents函数作为content就能够生成一个残缺的动态页面了。 ...

May 25, 2023 · 1 min · jiezi

关于php:ThinkPHP51无法记录SQL日志解决方案

我的项目开发阶段,除了根本编码外,性能也须要实时关注与优化。之前我的大部分我的项目都是应用ThinkPHP5.0以及ThinkPHP3.2,对于框架提供的日志记录和日志配置都差不多,而后应用ThinkPHP5.1的时候就吃瘪,花了十几分钟才好,所以写一下避免前面遗记了再踩坑。 日志配置ThinkPHP5.1没有了config.php,日志配置独自提出来自成一块,叫Log.php,这个适应一下就能够。  日志记录在5.1以前的我的项目中记录集体调试的日志,都是应用use think\Log; 而后应用Log::write()。而后5.1的时候引入了facede,所以间接用以前的形式,不能应用动态调用write等办法,批改为use think\facede\Log,而后再应用。 理论日志配置当须要调试时,app配置中'app_debug' => true, 'app_trace'      => true, 调试和追踪开启,浏览器关上会右下角呈现TP的logo和运行工夫,点击图标会呈现调试明细。然而有时不会呈现,然而想调试SQL以及SQL的查问速度,就须要开启日志记录,尽量不要配置保留目录,默认在runtime下就能够,也不是因为自定义目录的写入权限问题,所以所有默认就好,先解决问题前面再钻研问题起因,Log.php配置如下。return [ // 日志记录形式,反对 file socket 或者自定义驱动类'type' => 'File','file_size' =>2097152,'apart_level' => ['sql','error'],//日志的工夫格局,默认是` c `'time_format' =>'c']; 当开发阶段完结,不须要除了error以外级别的日志,能够在"apart_level"配置只保留"error"。 

May 25, 2023 · 1 min · jiezi

关于php:php对接微信小程序支付

前言:这里我就伪装你曾经注册了微信小程序,并且根本的配置都曾经好了。注: 集体注册小程序不反对微信领取,所以我还是伪装你是企业或者个体工商户的微信小程序,其余的商户号注册,二者绑定,受权,领取开明,就浏览文档吧,这里我先负责实战。 根本流程:申请商户平台账号 微信小程序绑定已有商户号并开明微信领取登录商户平台对小程序受权,下载领取证书,记录商户号,领取密钥。浏览微信领取官网文档,实现接口的对接编码。开发领取流程:微信小程序的根本配置。(app_id[小程序惟一id],mch_id[商户号],md5_key[领取密钥],notify_url[异步回调告诉] )。按微信要求的程序将参数组成键值对数组,并对其进行签名(先将参数进行字段排序,参数能够解决中文字符,在申请参数字符串后拼上领取密钥,最初md5,签名实现)所有申请参数和签名一起组成新数组,再转为XML。以XML格局参数,POST申请形式发动对立下单申请。微信服务器接管下单申请,返回预领取ID(prepay_id)到本人服务端。本人服务端联结预领取ID,小程序APPID,32位随机串,工夫戳,签名形式一并返回到小程序。小程序依据微信提供的函数和返回的参数集调起微信领取。领取实现,微信通过异步告诉到本人服务指定的控制器。承受微信返回的告诉,将XML转为数组,须要先判断告诉过去的是不是同一个订单(依据订单号),因为有时微信异步告诉,本人服务器未接管解决,他会过一段时间反复发动告诉。依据告诉状态,更新本人业务的数据表,最初返回一个胜利标识的XML给微信服务器。 一、领取配置'wxxcx' =>[ 'app_id' => 'wx4c0e*******664b4', // 微信小程序appid 'mch_id' => '149***3342', // 微信商户id 'md5_key' => '3FN8WHO**********iPnNoJ576AxMmwQ', // 微信领取密钥 'app_cert_pem' => APP_PATH.'v1/wechat_cert/apiclient_cert.pem', // 领取证书,对立下单不需,退款等其余接口须要 'app_key_pem' => APP_PATH.'v1/wechat_cert/apiclient_key.pem', 'sign_type' => 'MD5',// MD5 HMAC-SHA256 'limit_pay' => [ ], 'fee_type' => 'CNY',// 货币类型 以后仅反对该字段 'notify_url' => 'https://***********.com/v1/Pay/notifyUrlApi', // 异步告诉地址 'redirect_url' => '', 'return_raw' => false, ]二、前端传来的参数或服务端生成$this->openid = $openid;      // 前端也可不传 $this->out_trade_no = $out_trade_no;   // 服务端生成$this->body = $body;$this->total_fee = $total_fee;    // 最好服务端数据库抓取,防止前端传$this->spbill_create_ip = $spbill_create_ip;  // 申请的ip地址三、封装对立下单类<?php/** * @createTime: 2018-04-30 18:02 * @description: 小程序微信领取 * 公众号:ZERO开发 */namespace app\v1\extend;class WeixinPay { protected $appid; protected $mch_id; protected $key; protected $openid; protected $out_trade_no; protected $body; protected $total_fee; protected $notify_url; protected $spbill_create_ip; function __construct($appid, $openid, $mch_id, $key,$out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip) { $this->appid = $appid; $this->openid = $openid; $this->mch_id = $mch_id; $this->key = $key; $this->out_trade_no = $out_trade_no; $this->body = $body; $this->total_fee = $total_fee; $this->notify_url = $notify_url; $this->spbill_create_ip = $spbill_create_ip; } /************测试方法可删除*****************/ public function test() { $ha = "hello world"; return $this->appid; } /************可删除*****************/ public function pay() { // var_dump($this->notify_url);// die; //对立下单接口 $return = $this->weixinapp(); return $return; } //对立下单接口 private function unifiedorder() { $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; // 这里的参数程序肯定要按上面的,不然可能就始终报商户号此性能未受权等谬误 $parameters = array( 'appid' => $this->appid, // 小程序ID //'body' => 'test', // 商品形容 'body' => $this->body, 'mch_id' => $this->mch_id, // 商户号 'nonce_str' => $this->createNoncestr(), // 随机字符串 'notify_url' => $this->notify_url, //'https://shop.gdpress.cn/syw_jingzhun/index.php/Api/xiaochengxu/notify_url_api', // 告诉地址 确保外网能失常拜访 'openid' => $this->openid, // 用户id // 'out_trade_no' => '2015450806125348', // 商户订单号 'out_trade_no'=> $this->out_trade_no, //'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], // 终端IP 'spbill_create_ip' => $this->spbill_create_ip, // 终端IP 'total_fee' => floatval(($this->total_fee) * 100), // 单位 分 //'total_fee' => $this->total_fee, // 单位 分 'trade_type' => 'JSAPI' // 交易类型 ); //对立下单签名 $parameters['sign'] = $this->getSign($parameters); $xmlData = $this->arrayToXml($parameters); $return = $this->xmlToArray($this->postXmlCurl($xmlData, $url, 60)); //$return = $this->postXmlCurl($xmlData, $url, 60); // print_r($return); // die; return $return; } // curl申请办法封装 private static function postXmlCurl($xml, $url, $second = 30) { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); //严格校验 //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求后果为字符串且输入到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); //post提交形式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); curl_setopt($ch, CURLOPT_TIMEOUT, 40); set_time_limit(0); //运行curl $data = curl_exec($ch); //返回后果 if ($data) { curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); throw new WxPayException("curl出错,错误码:$error"); } } //数组转换成xml private function arrayToXml($arr) { $xml = "<xml>"; foreach ($arr as $key => $val) { if (is_array($val)) { $xml .= "<" . $key . ">" . arrayToXml($val) . "</" . $key . ">"; } else { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } } $xml .= "</xml>"; return $xml; } //xml转换成数组 private function xmlToArray($xml) { //禁止援用内部xml实体 libxml_disable_entity_loader(true); $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA); $val = json_decode(json_encode($xmlstring), true); return $val; } //微信小程序接口 private function weixinapp() { //对立下单接口 $unifiedorder = $this->unifiedorder(); // 对立下单出错,参数出错等起因 if($unifiedorder['return_code'] == 'FAIL') { $retrunInfo['code'] = 0; $retrunInfo['msg'] = $unifiedorder['return_msg']; return $retrunInfo; } $parameters = array( 'appId' => $this->appid, // 小程序ID 'timeStamp' => '' . time() . '', // 工夫戳 'nonceStr' => $this->createNoncestr(), // 随机串 'package' => 'prepay_id=' . $unifiedorder['prepay_id'], // 数据包 'signType' => 'MD5' // 签名形式 ); // 小程序发动领取签名 $parameters['paySign'] = $this->getSign($parameters); // 胜利返回 $retrunInfo['code'] = 1; $retrunInfo['msg'] = $parameters; return $retrunInfo; } //作用:产生随机字符串,不长于32位 private function createNoncestr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } //作用:生成签名 private function getSign($Obj) { foreach ($Obj as $k => $v) { $Parameters[$k] = $v; } //签名步骤一:按字典序排序参数 ksort($Parameters); $String = $this->formatBizQueryParaMap($Parameters, false); //签名步骤二:在string后退出KEY $String = $String . "&key=" . $this->key; //签名步骤三:MD5加密 $String = md5($String); //签名步骤四:所有字符转为大写 $result_ = strtoupper($String); return $result_; } // 作用:格式化参数,签名过程须要应用 private function formatBizQueryParaMap($paraMap, $urlencode) { $buff = ""; ksort($paraMap); foreach ($paraMap as $k => $v) { if ($urlencode) { $v = urlencode($v); } $buff .= $k . "=" . $v . "&"; } $reqPar; if (strlen($buff) > 0) { $reqPar = substr($buff, 0, strlen($buff) - 1); } return $reqPar; }}四、发动申请接口的业务代码/** * ** * date: 2018-04-30 * desc: 这里开始对立下单领取 */ $wxxcx_config = config('pay.wxxcx'); // 微信小程序设置 $appid = $wxxcx_config['app_id']; // 小程序id $mch_id = $wxxcx_config['mch_id']; // 领取商户id $key = $wxxcx_config['md5_key']; // 商户的领取密钥 $notify_url = $wxxcx_config['notify_url']; // 微信服务器异步告诉 $spbill_create_ip = $_SERVER['REMOTE_ADDR']; // 客户端ip $openid = $Xcxopenid; // 用户openid $out_trade_no = $Orderno; // 订单编号 $body = $params['body']; // 订单形容 $total_fee = $Alltotal; // 领取金额 // 实例微信领取基类 $weixinPay = new WeixinPay($appid, $openid, $mch_id, $key,$out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip); // 发动微信领取 $result = $weixinPay->pay(); if($result['code'] == 0) { // 对立下单出错 return $this->sendError(1, $result['msg'], 200); } // 获取预领取返回参胜利 return $this->sendSuccess($result, 'success', 200); 五、异步告诉六、其余辅助办法/** * 将xml转为array * @param string $xml xml字符串 * @return array 转换失去的数组 */ public function toArray($xml) { //禁止援用内部xml实体 libxml_disable_entity_loader(true); $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $result; } ...

May 25, 2023 · 4 min · jiezi

关于php:PHP中对称加密DES3实战

对称加密:对称加密是一种数据加密算法,对一组数据的加密和解密都应用一样的密钥(key),能够无效爱护金融数据,常见的对称加密有DES,3DES,AES、RC2、RC4、RC5。DES3: 对DES算法的组合,指定3个KEY,运算3次DES,密钥KEY的总字符长度为24位。 阐明:接触这个次要是最近对接一个第三方的领取平台,调用他们的银行卡,证件,姓名的鉴权接口,须要对一些非凡数字(银行卡号,身份证号)进行DES加密,再进行签名申请。其实这个次要就是用来做银行卡绑定或者实名验证的。 流程:1.客户端输出银行卡卡号,实在姓名,手机号,身份证号,手机号验证码2.通过匹配以后用户提交的验证码和服务器上缓存的验证码是否统一再进行鉴权申请。3.在领取后台上拿到DES密钥保留,对银行卡号,证件号等进行DES加密。4.通常鉴权申请为四因素(实在姓名,身份证号,银行卡号,手机号)5.对申请参数进行签名,再发送申请,依据后果,认证胜利,则保留该用户身份证号,手机号,银行卡号。 其余:两头用户输完银行卡号时,须要依据卡号辨认出卡类型(什么银行)和银行编码(全大写英文,相似银行的惟一id),记录该银行的联行号(银行的详细信息编号,准确到开户银行的省市区,支行)更佳,因为有时对公账户须要联行号。这个辨认接口能够去网上找,有收费的,不过阿里云的接口更好用。 DES3类<?php namespace app\v1\extend;class DES3{ //数据加密 function encrypt($input, $key) { $size = mcrypt_get_block_size(MCRYPT_3DES,'ecb'); $input = $this->pkcs5_pad($input, $size); $key = str_pad($key,24,'0'); $td = mcrypt_module_open(MCRYPT_3DES, '', 'ecb', ''); $iv = @mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND); @mcrypt_generic_init($td, $key, $iv); $data = mcrypt_generic($td, $input); mcrypt_generic_deinit($td); mcrypt_module_close($td); $data = base64_encode($data); return $data; } //数据解密 function decrypt($encrypted, $key) { $encrypted = base64_decode($encrypted); $key = str_pad($key,24,'0'); $td = mcrypt_module_open(MCRYPT_3DES,'','ecb',''); $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_RAND); $ks = mcrypt_enc_get_key_size($td); @mcrypt_generic_init($td, $key, $iv); $decrypted = mdecrypt_generic($td, $encrypted); mcrypt_generic_deinit($td); mcrypt_module_close($td); $y=$this->pkcs5_unpad($decrypted); return $y; } function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); return $text . str_repeat(chr($pad), $pad); } function pkcs5_unpad($text) { $pad = ord($text{strlen($text)-1}); if ($pad > strlen($text)) { return false; } if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) { return false; } return substr($text, 0, -1 * $pad); }}?> 业务逻辑protected $desKey = 'uMPE00c86bPWWyjLhBUlkA82'; // des加密密钥/** * *鉴权申请 * @param [array] $[authParam] [绑卡根本信息输出] * @return [array] [接口信息反馈] */ public function verifyAuth($authParam) { if (empty($authParam)) { return false; } $Des3 = new DES3(); // 实例Des加密类 $paramReq = array( 'P1_bizType' => 'Authentication', // 交易类型 'P2_customerNumber' => $this->cusNum, // 商户编号 'P3_orderId' => $authParam['orderNum'], // 商户申请流水号 'P4_timestamp' => date('YmdHis'), // 工夫戳 'P5_verifyType' => $this->authType, // 认证类型 'P6_payerName' => $authParam['payerName'], // 姓名 'P7_idCardType' => 'IDCARD', // 证件类型 'P8_idCardNo' => $Des3->encrypt($authParam['idCardNo'],$this->desKey), // 证件号码 'P9_cardNo' => $Des3->encrypt($authParam['cardNo'],$this->desKey), // 银行卡号 'P10_year' => '', // 信用卡有效期年份 'P11_month' => '', // 信用卡有效期月份 'P12_cvv2' => '', // 信用卡平安码 'P13_phone' => $Des3->encrypt($authParam['phoneNo'],$this->desKey), // 手机号码 ); $preSignArr = array(); foreach($paramReq as $keys => $vals) { $preSignArr[] = $vals; } // 组装签名 $paramReq['sign'] = $this->buildAuthSign($preSignArr, $this->authSignKey); // 发动鉴权申请 $authReault = $this->curl_post($this->authHost, $paramReq); return json_decode($authReault, true); // 返回申请后果 }/*** *生成鉴权申请签名* @param [array] $[authParam] [鉴权参数集]* @return [string] [md5签名串]*/protected function buildAuthSign($authParam,$signKey) { $reqStr = ""; foreach($authParam as $keys=>$vals) { $reqStr .= '&'.$vals; } // if(!$key) { // return md5($reqStr); // die; // } // return $key; // die; //$newSign = $reqStr.'&'.$signKey; $newSign = md5($reqStr.'&'.$signKey); return $newSign;}/*** *curl发动post申请* @param [string] $[url] [申请地址]* @param [array] $[params] [申请参数集]* @return [返回后果集]*/protected function curl_post($url,$params) { $ch = curl_init(); // 初始化curl curl_setopt($ch,CURLOPT_URL,$url); // 抓取指定网页 curl_setopt($ch, CURLOPT_HEADER, 0); // 设置header curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // 要求后果为字符串且输入到屏幕上 // curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_POST, 1); // post提交形式 curl_setopt($ch, CURLOPT_POSTFIELDS, $params); $data = curl_exec($ch); // 运行curl curl_close($ch); return($data); // 输入后果 } ...

May 25, 2023 · 2 min · jiezi

关于php:PHP实现给用户发微信消息提醒功能

以前有一个我的项目我的项目,当有用户有资金到账或者成员变动时须要给他发一条微信音讯提醒。针对这个,开始想应用模板音讯,然而刚注册的公众号申请音讯模板须要几天工夫申请,在工夫有余下抉择了应用客服音讯接口。    这里跳过网页受权和用户信息获取,申请接口的步骤,次要看获取access_token,公布客服音讯,验证是否关注等等接口。 1. 获取access_token// 获取access_tokenpublic function getAccessToken($weid) { $appID = "wxfaddfdfdfd6cf6fc3569"; // 服务号appID $appSecret = "071bebfdfdofdfd23687bf53d63a"; // 服务号appSerect $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appID&secret=$appSecret"; $content = ihttp_get($url); // 自定义申请函数 if(is_error($content)) { return error('-1', '获取微信公众号受权失败, 请稍后重试!谬误详情: ' . $content['message']); } if (empty($content['content'])) { return error('-1', 'AccessToken获取失败,请查看appid和appsecret的值是否与微信公众平台统一!'); } $token = @json_decode($content['content'], true); if ($token['errcode'] == '40164') { return error(-1, $this->errorCode($token['errcode'], $token['errmsg'])); } if(empty($token) || !is_array($token) || empty($token['access_token']) || empty($token['expires_in'])) { $errorinfo = substr($content['meta'], strpos($content['meta'], '{')); $errorinfo = @json_decode($errorinfo, true); return error('-1', '获取微信公众号受权失败, 请稍后重试! 公众平台返回原始数据为: 错误代码-' . $errorinfo['errcode'] . ',错误信息-' . $errorinfo['errmsg']); } $record = array(); $record['token'] = $token['access_token']; $record['expire'] = TIMESTAMP + $token['expires_in'] - 200; $cachekey = cache_system_key('accesstoken', array('acid' => $weid)); cache_write($cachekey, $record); return $record['token']; }2. 判断是否关注// 判断以后用户是否关注公众号public public function isSubscribe($weid,$userid) { // 获取以后用户信息 $userinfo = pdo_get('hcface_users',array('uid'=>$userid)); //return $userinfo; if(empty($userinfo)) { return false; } // 获取access_token $accessToken = $this->getAccessToken($weid); // 是否关注接口 $url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=".$accessToken."&openid=".$userinfo['openid']."&lang=zh_CN"; $res = ihttp_request($url); if(is_error($res)) { return false; } if($res['code'] != '200') { return false; } $result = @json_decode($res['content'],true); if($result['subscribe'] == 1) { $updateData = []; // 判断以后用户头像和昵称是否更换 if($userinfo['avatar'] != $result['headimgurl']) { $updateData['avatar'] = $result['headimgurl']; } if($userinfo['nickname'] != $result['nickname']) { $updateData['avatar'] = $result['nickname']; } if(!empty($updateData)) { pdo_update('hcface_users',$updateData,array('uid'=>$userid)); } } $userInfoData = [ "subscribe" => $result['subscribe'], "user_openid" => $userinfo['openid'], "nickname" => $userinfo['nickname'], ]; return $userInfoData; }3. 发送客服音讯public function solPushMsg($openid, $content, $wid) { // 获取access_token $accessToken = $this->getAccessToken($wid); $data = array( 'touser' => $openid, // 用户openID 'msgtype' => 'text', 'text' => [ 'content' => $content, // 内容 ], ); $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$accessToken; $res = ihttp_request($url,json_encode($data,JSON_UNESCAPED_UNICODE)); // json_encode第二个参数必须带上,不然收回的音讯可能是unicode编码的 if(is_error($res)) { return false; } if($res['code'] != '200') { return false; } return @json_decode($res['content'],true); }4. 微信接口返回的是一个数组 ...

May 25, 2023 · 2 min · jiezi

关于php:uniapp结合PHP实现单用户登陆

单用户登陆,即在一个利用中,同一个用户只能在线登陆一个,一个用户登陆,在其余设施上会被即时挤下线,确认后清空登陆该设施上的登陆装填并退回到登陆界面。    uni-app是目前能通过应用vue.js框架只须要编写一套代码同时打包Android,IOS,微信小程序,头条支付宝小程序和H5,通过应用HBuilder工具不便调试与云打包,对于苹果证书,举荐CW.PUB。应用HBuilder打越狱包通过那个网站签名就能够在失常苹果手机装置,不过网上还有其余些办法这里就不列举了。    个别APP做单用户登陆会应用第三方音讯推送平台,尽管uni-app尽管也能够对接友盟,极光等推送平台。但还是因为工夫,对接平台审核等流程工夫不容许。之前应用gatewayworkman和websocket做了即时聊天,所以单用户登陆也应用websocket实现。  uni-app socket单用户登陆例uni-app前端在初始化socke时发送以后设施的惟一标识,而后实时接管一个“强制退出”类型的音讯,一下只是简略示例。//初始化socket.on('init', () => { //连贯初始化 socket.send({ type: 'login', token: uni.getStorageSync('access_token'), device_no: plus.device.uuid, //手机设施惟一编号 });}).on('quit_push',(res)=> { if(res) { uni.showModal({ title: '退出告诉', content: '你的账号在其余设施上登录!', showCancel: true, cancelText: '勾销', confirmText: '确定', success: res => { if(res.confirm) { uni.clearStorageSync() store.commit('chat/clear') uni.reLaunch({ url:"../../pages/login/index" }) }else if(res.cancel) { uni.clearStorageSync() store.commit('chat/clear') uni.reLaunch({ url:"../../pages/login/index" }) } } }); }});后端接管“设施惟一标识”参数,先查找缓存是否存在,不存在记录设施标识和socket的clientid。登陆接口接管设施标识,缓存或库里取出标识记录与以后接管的设施标识判断是否统一,不统一则依据缓存中的clientid发送音讯。$is_online = Db::name('UserLoginClient')->where('user_id',$user['id'])->order('id desc')->find();if(isset($device_no) && $device_no && $is_online['device_no'] != $device_no && !empty($is_online['device_no'])) { Tools::sendToClient($is_online['client_id'],json_encode([ 'type' => 'quit_push', 'data' => 'ip', 'message' => '强制下线' ])); }工具类sendToClient办法局部public static function sendToClient($client_id, $message) { Gateway::sendToClient($client_id, $message); }推送单用户登陆例首先对接了友盟,包含前端后端都加了SDK和应用上了他们的办法。音讯推送有一个惟一值"token",这里简称“pushtoken”,由客户端生成,能够标识一个惟一的设施。 后端登陆时,接管pushtoken,同样判断该pushtoken是否存在,不存在就以用户ID为键存储。 存在时再判断与缓存是否统一,统一则加长缓存工夫,不统一则给旧的pushtoken(缓存中的)推送一条音讯,并缓存新的pushtoken。if (self::$headToken && Cache::has(self::$prefix . self::$userId)) { if (self::$headToken == Cache::get(self::$prefix . self::$userId)) { Cache::set(self::$prefix . self::$userId, self::$headToken, self::$timeOut); } else { // 换了手机,客户端从新发送pushtoken到服务端,服务端与缓存中的pushtoken比拟,不同则给原来pushtoken手机推送一条并从新缓存新的token // modify by wensen on 20180816 // $addr = getCity(); $addr = getMobCity(); $ip = request()->ip(); if ($addr) { $addr['province'] = empty($addr['province']) ? '' : $addr['province']; $addr['city'] = empty($addr['city']) ? '' : $addr['city']; // $address = "\t" . $addr['country'] . "-" . $addr['region'] . "-" . $addr['city'] . " (IP:" . $ip . ")\t"; $address = "\t" . $addr['country'] . "-" . $addr['province'] . "-" . $addr['city'] . " (IP:" . $ip . ")\t"; } else { $address = "IP:" . $ip . ""; } $OldToken = Cache::get(self::$prefix . self::$userId); if (strlen($OldToken) == 64) { $content = array( 'title' => 'APP紧急通知', 'body' => '您的账号于:' . date('Y-m-d H:i:s') . '在' . $address . '处登录,若不为您自己登录,请您立刻批改明码!', 'pull_service' => 'login' ); \umeng\Push::send($OldToken, 'unicast', $content, 'message', true); } elseif (strlen($OldToken) == 44) { $content = array( 'pull_service' => 'login', 'msg' => '您的账号于:' . date('Y-m-d H:i:s') . '在' . $address . '处登录,若不为您自己登录,请您立刻批改明码!' ); \umeng\Push::send($OldToken, 'unicast', $content, 'message', true); } Cache::set(self::$prefix . self::$userId, self::$headToken, self::$timeOut); } } else { Cache::set(self::$prefix . self::$userId, self::$headToken, self::$timeOut); }APP客户端接管推送进行弹窗提醒和退出解决。 以上是依据友盟的SDK封装的推送办法,其中包含单播,播送,跳利用activity,跳网页连贯等等。 

May 25, 2023 · 2 min · jiezi

关于php:解决注册并发问题并提高QPS

前言后面在本地的windows通过apache的ab工具测试了600并发下“查问指定手机是否存在再提交数据”的注册性能会呈现反复提交的状况,并且在注册实现时还须要对邀请人进行处分,记录邀请记录,对该新用户主动公布动静信息,发短信或发邮件等其余业务性能。所以这里当并发时,注册性能就变得低效且容易呈现问题。 办法先对反复提交的问题通过redis解决,再把注册贮存用户根本信息当前的操作放到队列中进行异步执行,能够很好的优化注册性能,进步QPS。 一、环境要求PHP版本 >= 5.6.0PHP框架:Thinkphp5.1.*音讯队列:Think-queue2.0PHP扩大:Redis 二、下载框架和音讯队列中间件下载tp5.1。composer create-project topthink/think=5.1.* tp5 --prefer-dist装置think-queue。composer require topthink/think-queue php装置redis扩大和关上redis服务端和客户端。三、解决注册反复提交配置文件中cache设置为redis驱动,并新建控制器因为cache相干命名空间。 use think\Exception;use think\facade\Cache;use think\facade\Env;use think\Queue;应用无序汇合存手机号,通过判断以后手机号是否是在指定键里为成员(如果注册存入数据库失败,通过sRem删除该成员),而后再通过查询数据库判断是否存在。private $cache;private $handler;// 实例化redispublic function __construct() { $this->cache = Cache::init(); $this->handler = $this->cache->handler();}// 判断手机号是否在汇合中$is_existe = $this->handler->sIsMember("register:mobile",$mobile);if(!$is_existe) { $this->handler->sAdd("register:mobile",$mobile);}else { //Log::write('---压力测试'.date("Y-m-d h:i:s").'---手机号已存在'); var_dump('手机号已存在'); // 用户已存在 die;}// 查问手机号码是否已注册$user = db('user')->field('mobile')->where('mobile', $mobile)->find();if ($user) { //Log::write('---压力测试'.date("Y-m-d h:i:s").'---手机号注册了'); var_dump('手机号已注册'); // 用户已存在 die;} 四、音讯队列合成注册性能配置音讯队列,前面以redis驱动为例。<?phpreturn [ 'connector' => 'Redis', // Redis 驱动 'expire' => 60, // 工作的过期工夫,默认为60秒; 若要禁用,则设置为 null 'default' => 'default', // 默认的队列名称 'host' => '127.0.0.1', // redis 主机ip 'port' => 6379, // redis 端口 'password' => '', // redis 明码 'select' => 0, // 应用哪一个 db,默认为 db0 'timeout' => 0, // redis连贯的超时工夫 'persistent' => false, // 是否是长连贯 // 'connector' => 'Database', // 数据库驱动 // 'expire' => 60, // 工作的过期工夫,默认为60秒; 若要禁用,则设置为 null // 'default' => 'default', // 默认的队列名称 // 'table' => 'jobs', // 存储音讯的表名,不带前缀 // 'dsn' => [], // 'connector' => 'Topthink', // ThinkPHP外部的队列告诉服务平台 ,本文不作介绍 // 'token' => '', // 'project_id' => '', // 'protocol' => 'https', // 'host' => 'qns.topthink.com', // 'port' => 443, // 'api_version' => 1, // 'max_retries' => 3, // 'default' => 'default',// 'connector' => 'Sync', // Sync 驱动,该驱动的理论作用是勾销音讯队列,还原为同步执行];实现增加新用户后将指定数据退出音讯队列。<?phpnamespace app\index\controller;use think\Db;use think\Validate;use think\Exception;use think\facade\Cache;use think\facade\Env;use think\Queue;use think\Log;class Index{ private $cache; private $handler; public function __construct() { $this->cache = Cache::init(); $this->handler = $this->cache->handler(); } public function index() { $data = input('post.'); unset($data['balance']); unset($data['credit']); // $blacklist = [ // "18124198164","13401363108","17688552009","15089352898","13602940094","13346643336","13181351655","18301123028","13598020751","13014568187", // "13428733909","17337991130","13275342497" // ]; $rule = [ 'mobile' => 'require|number|length:11', 'password' => 'require|length:6,32', ]; $msg = [ 'mobile.require' => '手机号必须', 'mobile.length' => '手机号为11位数字', 'mobile.number' => '手机号为11位数字', 'password.require' => '明码必须', 'password.length' => '明码为6-12位之间', ]; //验证数据是否非法 $mobile = isset($data['mobile']) ? $data['mobile'] : ''; $validate = new Validate($rule, $msg); $result = $validate->check($data); if (!$result) { var_dump($validate->getError()); die; } // if(in_array($mobile,$blacklist)) { // var_dump('该手机号已注册了'); // 黑名单 // die; // } // 判断手机号是否在汇合中 $is_existe = $this->handler->sIsMember("register:mobile",$mobile); if(!$is_existe) { $this->handler->sAdd("register:mobile",$mobile); }else { //Log::write('---压力测试'.date("Y-m-d h:i:s").'---手机号已存在'); var_dump('手机号已存在'); // 用户已存在 die; } // 查问手机号码是否已注册 $user = db('user')->field('mobile')->where('mobile', $mobile)->find(); if ($user) { //Log::write('---压力测试'.date("Y-m-d h:i:s").'---手机号注册了'); var_dump('手机号已注册'); // 用户已存在 die; } // 用户不存在注册 // $data['id'] = getNewUserid(); $data['no'] = date("Ymdhis").rand(100, 999); $data['avatar'] = 'https://rumcdn-1255484416.cos.ap-chengdu.myqcloud.com/img/d_h.png'; $data['password'] = md5($data['password']); $randomNickname = date("Ymdhis").rand(100, 999); $data['nickname'] = 'rm_' . $randomNickname; $data['create_time'] = time(); $data['type'] = 1; /***是否存在邀请人的跑步钱进号***/ if(isset($data['pbqj_no']) && !empty($data['pbqj_no'])) { $inviter = db('user')->field('id')->where(["no"=>$data['pbqj_no']])->find(); if($inviter) { $data['inviter_id'] = $inviter['id']; } } /***是否存在邀请人的跑步钱进号***/ unset($data['pbqj_no']); $userid = db('user')->insertGetId($data); if ($userid) { /******************退出音讯队列异步解决后续操作*******************/ // 1.当前任务将由哪个类来负责解决。 // 当轮到该工作时,零碎将生成一个该类的实例,并调用其 fire 办法 $jobHandlerClassName = 'app\index\job\JobUser'; // 2.当前任务归属的队列名称,如果为新队列,会主动创立 $jobQueueName = "userJobQueue"; // 3.当前任务所需的业务数据 . 不能为 resource 类型,其余类型最终将转化为json模式的字符串 // ( jobData 为对象时,须要在先在此处手动序列化,否则只存储其public属性的键值对) //$jobData = ['ts' => time(), 'bizId' => uniqid() , 'a' => 1]; $jobData = ['userid'=>$userid,'time'=>time(),'mobile'=>$mobile,'inviterid'=>(isset($data['inviter_id']) ? $data['inviter_id'] : 0)]; // 4.将该工作推送到音讯队列,期待对应的消费者去执行 $isPushed = Queue::push($jobHandlerClassName , $jobData , $jobQueueName); // database 驱动时,返回值为 1|false ; redis 驱动时,返回值为 随机字符串|false if($isPushed !== false) { var_dump('退出队列胜利'); die; //Log::write('-----------退出音讯队列胜利-----------'); //echo date('Y-m-d H:i:s') . " a new Hello Job is Pushed to the MQ"."<br>"; }else{ var_dump('退出音讯队列'); die; //Log::write('-----------退出音讯队列失败-----------'); //echo 'Oops, something went wrong.'; } /******************退出音讯队列异步解决后续操作*******************/ $res['id'] = $userid; $res['no'] = $data['no']; // // token解决类 // $accessToken = new AccessToken(); // $accessToken = $accessToken->getToken($userid); // if (empty($accessToken)) { // //Log::write('---压力测试'.date("Y-m-d h:i:s").'---秘钥生成失败'); // var_dump('秘钥生成失败'); // } else { // $res['user_token'] = $accessToken; // } // if (method_exists(\chat\User::class, 'getToken')) { // $chat_token = \chat\User::getToken($res['id'], $data['nickname'], $data['avatar']); // if (!$chat_token) { // //Log::write('---压力测试'.date("Y-m-d h:i:s").'---聊天秘钥生成失败'); // var_dump('聊天秘钥生成失败'); // } else { // $res['chat_token'] = $chat_token; // } // } else { // $res['chat_token'] = ''; // } //Log::write('---压力测试'.date("Y-m-d h:i:s").'---注册胜利'); var_dump($res); die; } else { //Log::write('---压力测试'.date("Y-m-d h:i:s").'---数据库谬误'); $this->handler->sRem("register:mobile",$mobile); var_dump('数据库谬误'); die; } } public function hello($name = 'ThinkPHP5') { return 'hello,' . $name; }}创立消费者(job),对执行队列中的工作。(1). 在同一模块下新建job文件夹和一个执行类(JobUser), 须要对应生产者中jobHandlerClassName。(2). 后面执行完队列退出胜利后,能够本地应用redis客户端通过lrange queues:userJobQueue 0 -1 查看队列成员 (queues:userJobQueue中,userJobQueue是本人在退出队列前本人起的队列名称,与queues: 拼接就是redis的list的键名,所以能够间接查看 )。(3).队列中的data就是本人传递的数据,前面须要在消费者中通过该数据进行注册性能后的业务操作: 送处分,存储邀请记录,发动静,发短信,发邮件等等。<?phpnamespace app\index\job;use think\queue\Job;use think\Db;use think\Exception;use think\facade\Cache;use think\facade\Env;class JobUser { private $cache; private $handler; public function __construct() { $this->cache = Cache::init(); $this->handler = $this->cache->handler(); } /** * fire办法是音讯队列默认调用的办法 * @param Job $job 以后的工作对象 * @param array|mixed $data 公布工作时自定义的数据 */ public function fire(Job $job,$data) { $job->delete(); //print("hahah\n"); // print("<info>The user already exists "."</info>\n"); // exit(); if(empty($data) || empty($data['userid']) || empty($data['mobile'])) { $job->delete(); print("canshu buzu\n"); return; } // 如有必要,能够依据业务需要和数据库中的最新数据,判断该工作是否仍有必要执行. $isJobStillNeedToBeDone = $this->checkDatabaseToSeeIfJobNeedToBeDone($data); if(!$isJobStillNeedToBeDone) { print("hahah\n"); $job->delete(); return; } $isJobDone = $this->doHelloJob($data); if ($isJobDone) { //如果工作执行胜利, 记得删除工作 $job->delete(); print("<info>Hello Job has been done and deleted"."</info>\n"); }else{ if ($job->attempts() > 3) { //通过这个办法能够查看这个工作曾经重试了几次了 print("<warn>Hello Job has been retried more than 3 times!"."</warn>\n"); //$job->delete(); // 也能够从新公布这个工作 //print("<info>Hello Job will be availabe again after 2s."."</info>\n"); //$job->release(2); //$delay为延迟时间,示意该工作提早2秒后再执行 } } } /** * 有些音讯在达到消费者时,可能曾经不再须要执行了 * @param array|mixed $data 公布工作时自定义的数据 * @return boolean 工作执行的后果 */ private function checkDatabaseToSeeIfJobNeedToBeDone($data) { // 判断手机缓存汇合中是否存在 // $is_existe = $this->handler->sIsMember("register:mobile",$data['mobile']); // if($is_existe) { // return false; // } // // 查问以后用户是否在数据库中存在 // $userinfo = Db::name('user')->field('id')->where('id',$data['userid'])->find(); // if($userinfo) { // return false; // } return true; } /** * 依据音讯中的数据进行理论的业务解决 * @param array|mixed $data 公布工作时自定义的数据 * @return boolean 工作执行的后果 */ private function doHelloJob($data) { try{ if(isset($data['inviterid']) && !empty($data['inviterid'])) { // 增加邀请记录 $res_record = Db::name('user_inviter') ->insert([ 'inviterid' => $data['inviterid'], 'userid' => $data['userid'], 'code' => $data['inviterid'] . 'T' . $data['userid'], 'create_time' => $data['time'], ]); // 给邀请人赠送300步币 Db::name('user_credit') ->insert([ 'userid' => $data['inviterid'], 'type' => 1, 'credit' => 300, 'source' => $res_record, 'create_time' => $data['time'] ]); // 更新邀请人步币(用户表) Db::name('user')->where('id', $data['inviterid'])->setInc('credit', 300); } { // 注册胜利发表动静 $dynamic_data['userid'] = $data['userid']; $dynamic_data['dynamic'] = base64_encode('号外!号外!我退出跑步钱进了,大家一起走路领红包吧!'); $dynamic_data['images'][] = 'https://rumcdn-1255484416.cos.ap-chengdu.myqcloud.com/img/d_d.png'; $dynamic_data['images'] = serialize($dynamic_data['images']); $dynamic_data['create_time'] = $data['time']; $result = Db::name('dynamic')->insert($dynamic_data); } }catch(\Exception $e) { Log::write('---执行音讯队列出错---'.$e->getMessage()); return false; } return true; // 依据音讯中的数据进行理论的业务解决... //var_dump($data);// print("<info>Hello Job Started. job Data is: ".var_export($data,true)."</info> \n");// print("<info>Hello Job is Fired at " . date('Y-m-d H:i:s') ."</info> \n");// print("<info>Hello Job is Done!"."</info> \n"); //return true; } /** * 该办法用于接管工作执行失败的告诉,你能够发送邮件给相应的负责人员 * @param $jobData string|array|... //公布工作时传递的 jobData 数据 */ public function failed($jobData) { //send_mail_to_somebody() ; print("Warning: Job failed after max retries. job data is :".var_export($jobData,true)."\n"); }}(4). 设置工作执行失败后的解决,比方记录日志或发邮件给开发者。a. 在tags.php中配置失败后执行了类。 ...

May 24, 2023 · 6 min · jiezi

关于php:以PHP门面模式实现简单的邮件发送

前言:门面模式属于设计模式中三大分类之一的构造类型,也叫外观模式。其作用对客户端低耦合底层性能的封装,客户端不必晓得子系统间的调用。 举例:门面模式就相当于电脑主机,用户要关上某个应用程序,只须要晓得两步。关上开机按钮,电脑开机后再关上利用。开机按钮就相当于一个门面,外面的开机须要调用不同的模块,比方硬件自检,抉择启动盘,加载疏导,加载内核,OS初始化,启动指定级任务等,以下也通过发邮件的例子形容门面一模式。 波及:call_user_func函数的应用异样类的自定义解决类的分层封装发邮件性能的实现与配置 编码:必须先composer require phpmailer/phpmailer装置依赖库。创立扩大类目录,外面包含独立的配置文件,门面角色类,邮件性能类,校验类,异样类。3. 独立的配置类,包含smtp服务地址,端口,直达邮箱账号,受权码,邮件发送者昵称(惟一标识)。 <?php/** * @Notes: 邮箱SMTP服务配置 * @Interface getCondition * @Return mixed * @Author: bqs * @Time: 2020/8/31 10:15 */return [ 'smtp_server' => 'smtp.qq.com', // QQ邮箱开启的smtp 'smtp_port' => 465, // QQsmtp服务端口 'smtp_user' => '2652364582@qq.com', // 北桥苏邮箱 'smtp_pwd' => 'ynxdedefduuhecbj', // SMTP服务开启后受权码 'email_id' => '酷D' // 邮件发送者的惟一标识(自定义的昵称)];门面角色类,也就是客户间接调用的,只有一个发送办法,然而该办法须要调用校验和理论发送的办法实现。<?php/** * @Notes: 邮件门面 * @Interface getCondition * @Return mixed * @Author: bqs * @Time: 2020/8/31 13:10 */namespace mail;use think\Container;use mail\facade\MailException;use mail\facade\Mail;use mail\facade\Validate;class MailFacade{ protected $error; public static function __callStatic($method, $params) { //return (new static)->{$method}(...$params); return call_user_func([new MailFacade(),$method],$params); } /** * @Notes: 面向客户的邮件发送调用 * @Author: bqs * @Time: 2020/8/31 13:33 * @Interface send * @param $params * @Return boolean 胜利|失败 */ private function send($params) { // 校验参数 $validate = Validate::make(__FUNCTION__); $res = $validate->check($params); if (!$res) { // 抛出自定义异样 throw new MailException($validate->getError(),422); return false; } // 发送邮件 $mail = new Mail(); $res = $mail->send($params); return $res; }}自定义异样类,能够在门面角色中以该类抛出,而后在客户调用中以该类捕获,以下自定义了谬误音讯的输入。<?php/** * @Notes: 邮件发送校验器 * @Interface getCondition * @Return mixed * @Author: bqs * @Time: 2020/8/31 13:03 */namespace mail\facade;class MailException extends \Exception{ public function errorMessage() { return "mail error: ".$this->getMessage(); }}校验器,次要判断客户调用传入的参数。 ...

May 24, 2023 · 3 min · jiezi

关于php:PHP实现的7组经纬度与距离的计算函数

一. 依据以后地位计算周围的经纬度/**公众号:ZERO开发 * 依据以后地位计算周围的经纬度 * @param $lng * @param $lat * @param float $distance * @return array */function returnSquarePoint($lng, $lat, $distance = 0.5){ $earthRadius = 6378138; $dlng = 2 * asin(sin($distance / (2 * $earthRadius)) / cos(deg2rad($lat))); $dlng = rad2deg($dlng); $dlat = $distance / $earthRadius; $dlat = rad2deg($dlat); return array( 'left-top' => array('lat' => $lat + $dlat, 'lng' => $lng - $dlng), 'right-top' => array('lat' => $lat + $dlat, 'lng' => $lng + $dlng), 'left-bottom' => array('lat' => $lat - $dlat, 'lng' => $lng - $dlng), 'right-bottom' => array('lat' => $lat - $dlat, 'lng' => $lng + $dlng) );}二. 依据经纬度计算范畴/**公众号:ZERO开发 * 依据经纬度计算范畴 * @param $lat1 * @param $lng1 * @param $lat2 * @param $lng2 * @return float */function getDistance($lat1, $lng1, $lat2, $lng2){ $earthRadius = 6378138; // 近似地球半径米 // 转换为弧度 $lat1 = ($lat1 * pi()) / 180; $lng1 = ($lng1 * pi()) / 180; $lat2 = ($lat2 * pi()) / 180; $lng2 = ($lng2 * pi()) / 180; // 应用半正矢公式 用尺规来计算 $calcLongitude = $lng2 - $lng1; $calcLatitude = $lat2 - $lat1; $stepOne = pow(sin($calcLatitude / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($calcLongitude / 2), 2); $stepTwo = 2 * asin(min(1, sqrt($stepOne))); $calculatedDistance = $earthRadius * $stepTwo; return round($calculatedDistance);}三. 通过经纬度依据间隔从近到远排序/**公众号:ZERO开发 * 通过经纬度依据间隔从近到远排序 * @param $lat * @param $lng * @return mixed */function computePoint($lat, $lng){ $page = 1; $pageSize = 7; $EARTH = 6378.137; // 固定参数 地球半径 $PI = 3.1415926535898; // 固定参数 圆周率 $list = db('work')->alias('wk') ->field(" wk.id,wk.work_name,wk.age,wk.teach,wk.gwbqid,wk.start_money,wk.work_description,wk.xz_id, wk.category,cp.company_id,cp.company_name,cp.la,cp.lo, (2 * $EARTH* ASIN(SQRT(POW(SIN($PI*(" . $lat . "-cp.la)/360),2)+COS($PI*" . $lat . "/180)* COS(cp.la * $PI/180)*POW(SIN($PI*(" . $lng . "-cp.lo)/360),2)))) as juli ") ->order('create_time desc,juli asc') ->page($page, $pageSize) ->select()->toArray(); return $list;}四. 依据经纬度查问地理位置/**公众号:ZERO开发 * 依据经纬度查问地理位置 * @param $lat * @param $lng */function myLocation($lat, $lng){ $url = "http://api.map.baidu.com/geocoder/v2/?ak=YQH8OyfGcvOsPlHdnssSpkulaSNVgL0N&callback=renderReverse&location=$lat,$lng&output=json&pois=1"; $res = file_get_contents($url); $lres = ltrim($res, "renderReverse && renderReverse("); $rres = rtrim($lres, ")"); echo $rres;}五. 依据经纬度计算直线间隔/**公众号:ZERO开发 * 依据经纬度计算直线间隔 * @param $lat1 * @param $lng1 * @param $lat2 * @param $lng2 * @return float|int */function getDistances($lat1, $lng1, $lat2, $lng2){ define('PI', 3.1415926535898); define('EARTH_RADIUS', 6378.137); $radLat1 = $lat1 * (PI / 180); $radLat2 = $lat2 * (PI / 180); $a = $radLat1 - $radLat2; $b = ($lng1 * (PI / 180)) - ($lng2 * (PI / 180)); $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))); $s = $s * EARTH_RADIUS; $s = round($s * 10000) / 10000; return $s * 1000;}六. 依据经纬度和半径计算出范畴/**公众号:ZERO开发 * 依据经纬度和半径计算出范畴 * @param string $lat 纬度 * @param String $lng 经度 * @param float $radius 半径 * @return Array 范畴数组 */function calcScope($lat, $lng, $radius){ $degree = (24901 * 1609) / 360.0; $dpmLat = 1 / $degree; $radiusLat = $dpmLat * $radius; $minLat = $lat - $radiusLat; // 最小纬度 $maxLat = $lat + $radiusLat; // 最大纬度 $mpdLng = $degree * cos($lat * (3.141592 / 180)); $dpmLng = 1 / $mpdLng; $radiusLng = $dpmLng * $radius; $minLng = $lng - $radiusLng; // 最小经度 $maxLng = $lng + $radiusLng; // 最大经度 /** 返回范畴数组 */ $scope = array( 'minLat' => $minLat, 'maxLat' => $maxLat, 'minLng' => $minLng, 'maxLng' => $maxLng ); return $scope;}七. 获取两个经纬度之间的间隔/**公众号:ZERO开发 * 获取两个经纬度之间的间隔 * @param string $lat1 纬一 * @param String $lng1 经一 * @param String $lat2 纬二 * @param String $lng2 经二 * @return float 返回两点之间的间隔 */function calcDistance($lat1, $lng1, $lat2, $lng2){ if (empty($lat1) || empty($lng1) || empty($lat2) || empty($lng2)) { return false; } /** 转换数据类型为 double */ $lat1 = doubleval($lat1); $lng1 = doubleval($lng1); $lat2 = doubleval($lat2); $lng2 = doubleval($lng2); $theta = $lng1 - $lng2; $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta)); $dist = acos($dist); $dist = rad2deg($dist); $miles = $dist * 60 * 1.1515; return ($miles * 1.609344);} ...

May 24, 2023 · 3 min · jiezi

关于php:如何在业务代码中使用-ThinkPHP51-封装的容器内反射方法

invokeClass用法:能够不传命名空间实例化 (通过反射实例化) $obj = Container::getInstance()->invokeClass(InvokerTest::class);var_dump($obj->invokerNews());die;invokeMethod用法:传入带命名空间的类和对应办法,实例化后调用该办法 $methods = Container::getInstance()->invokeMethod(["app\common\service\InvokerTest", "invokerNews"]);var_dump($methods);die; invokeFunction用法:能够执行自定义的函数和闭包,参数以数组模式传递 $functions = Container::getInstance()->invokeFunction(function ($name) {    return "hello {$name}";}, ["beiqiaosu"]);var_dump($functions);die;$functions = Container::getInstance()->invokeFunction("procmsg", ["12312", "heillo"]);var_dump($functions);die;invoke用法:invokeFunction 和 invokeMethod 结合体 $invoke = Container::getInstance()->invoke(["app\common\service\InvokerTest", "invokerNews"]);var_dump($invoke);die;

May 24, 2023 · 1 min · jiezi

关于php:SocketLog-的基本使用

前言:最近在封装一个日志类时,想借鉴一下 TP 的实现形式,特地留神了一下 TP 日志的驱动。平时罕用文件模式记录日志却疏忽了还有一个 Socket 形式,只须要连贯一下近程服务,程序在单程运行中所打的日志就会呈现在浏览器上。然而前提浏览器装置了扩大,接下来就浅谈一下 SocketLog 的简略应用吧。         环境:1. SocketLog Chrome 扩大2. ThinkPHP5.13. SocketLog Server  (nodeJS)操作:一、NodeJs 和 npm 装置(略过)二、SocketLog 服务 /chrome 扩大下载能够在 github 上搜寻 SocketLog 我的项目,作者 luofei614 的就是了,下载后的目录大略如下。 三、socketLog 扩大装置谷歌浏览器关上 chrome://extensions/,勾选开发者模式。加载扩大程序 (抉择下载好的文件夹下的 chrome),最初就能够看到加载胜利了。        四、SocketLog 装置与启动1. 装置 (cd 进入根目录下 server 目录中)全局装置$ npm install -g socketlog-server 部分装置 (切换到指标我的项目所在目录)$ npm install socketlog-server 2. 启动 (仍然是 cd 进入 server 目录下执行上面其一命令)一般形式运行:$ socketlog-server 服务后盾运行:$ socketlog-server > /dev/null & 五、扩大工具监听服务1. 关上浏览器点击图标,链接胜利示意胜利监听。 六、代码测试1. 运行我的项目根目录下的 demo.php,设置链接地址 /client_ids 等,运行后果如下。 ...

May 24, 2023 · 1 min · jiezi

关于php:PHPGo-开发仿简书实战高并发高可用微服务架构

PHP和Go是两种罕用的编程语言,它们在不同的利用场景中都有着重要的作用。本文将探讨如何联合应用PHP和Go来进步Web应用程序的性能、可靠性和安全性。download:https://www.97yrbl.com/t-1512.html 首先,让咱们理解一下PHP和Go各自的特点。PHP是一种面向Web开发的脚本语言,它被广泛应用于创立动静网页。PHP的长处包含易学易用、稳定性好、扩展性强等。而Go则是一种现代化的零碎级编程语言,它被设计用于开发高并发、高性能的网络服务。Go的长处包含疾速编译、协程反对、垃圾回收机制等。 那么,为什么要将PHP和Go联合应用呢?因为PHP尽管易学易用,但对于解决大量并发申请时效率较低;而Go尽管具备高并发、高性能的特点,但开发难度较大。因而,将两者联合应用能够充分发挥它们各自的劣势。 具体来说,能够将PHP作为前端Web应用程序的实现语言用Go作为后端服务器的开发语言。这样做的益处是: 高性能:Go的并发机制能够无效地解决大量申请,使得Web应用程序的性能失去晋升。可靠性:Go是一种强类型语言,在编译时就能够查看出代码中的谬误,这有助于缩小代码中的破绽。安全性:Go提供了内存平安机制,能够无效地防止常见的安全漏洞,如缓冲区溢出、空指针援用等。扩展性:PHP和Go都具备良好的扩展性,能够轻松地集成各种第三方库和框架,从而满足不同的需要。总之,将PHP和Go联合应用能够为Web应用程序带来更高的性能、可靠性和安全性。当然,具体的实现形式还须要依据具体的利用场景来确定。

May 23, 2023 · 1 min · jiezi

关于php:ERP进销存源码php全开源

 ERP(Enterprise Resource Planning,企业资源打算)零碎是一种集成性的企业管理软件,以数据共享、业务协同为外围,使用计算机技术,对企业外部全副业务进行全面的布局、治理和协调。ERP进销存零碎对于古代企业来说曾经是不可或缺的,是企业信息化建设的重要一环,对于企业管理效率和经济效益的进步有着十分重要的作用。本文将从ERP进销存零碎的概述、倒退历程、劣势和利用及带来的挑战等方面进行论述。 ERP进销存源码:c.csymzs.top 局部源码:cuser.py __author__ = 'zhugl'# created at 15-4-21 #python importfrom threading import localfrom django.contrib import adminfrom django.apps import appsfrom django.conf import settingsfrom django.contrib.admin import ModelAdmin, actionsfrom django.contrib.auth import REDIRECT_FIELD_NAMEfrom django.core.exceptions import ImproperlyConfigured, PermissionDeniedfrom django.core.urlresolvers import NoReverseMatch, reversefrom django.db.models.base import ModelBasefrom django.http import Http404, HttpResponseRedirectfrom django.template.engine import Enginefrom django.template.response import TemplateResponsefrom django.utils import sixfrom django.utils.text import capfirstfrom django.utils.translation import ugettext as _, ugettext_lazyfrom django.views.decorators.cache import never_cachefrom django.views.decorators.csrf import csrf_protect_thread_local = local()def getuser(): return getattr(_thread_local,'user',None)class RequestUser(object): def process_request(self,request): django_user = getattr(request,'user',None) if django_user is not None: _thread_local.user = django_user def process_view(self, request, view_func, view_args, view_kwargs): app_weight = {'selfhelp':1,'purchase':3,'sale':2,'invent':4,'organ':5,'basedata':6,'syscfg':7,'workflow':8} if view_func.__name__ == 'index': app_dict = {} for model, model_admin in admin.site._registry.items(): app_label = model._meta.app_label has_module_perms = model_admin.has_module_permission(request) if has_module_perms: perms = model_admin.get_model_perms(request) if True in perms.values(): info = (app_label, model._meta.model_name) model_dict = { 'name': capfirst(model._meta.verbose_name_plural), 'object_name': model._meta.object_name, 'perms': perms, 'weight':getattr(model,'index_weight',99) } if perms.get('change', False): try: model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name) except NoReverseMatch: pass if perms.get('add', False): try: model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name) except NoReverseMatch: pass if app_label in app_dict: app_dict[app_label]['models'].append(model_dict) else: app_dict[app_label] = { 'name': apps.get_app_config(app_label).verbose_name, 'app_label': app_label, 'app_url': reverse( 'admin:app_list', kwargs={'app_label': app_label}, current_app=admin.site.name, ), 'has_module_perms': has_module_perms, 'models': [model_dict], 'weight':app_weight.get(app_label,99) } app_list = list(six.itervalues(app_dict)) app_list.sort(key=lambda x: x['weight']) for app in app_list: app['models'].sort(key=lambda x: x['weight']) context = dict( maxi_app_list=app_list, ) try: todolist = self.get_my_task(request) context.update(dict(todolist = todolist)) except Exception,e: pass # print context view_kwargs['extra_context'] = context if view_func.__name__ == 'app_index': app_label = view_kwargs['app_label'] app_name = apps.get_app_config(app_label).verbose_name app_dict = {} lib_dict = {} for model, model_admin in admin.site._registry.items(): if model_admin.has_module_permission(request): label = model._meta.app_label is_current = False if label == app_label: is_current = True lib_dict[label] = { 'name': apps.get_app_config(label).verbose_name, 'app_label': label, 'app_url': reverse( 'admin:app_list', kwargs={'app_label': label}, current_app=admin.site.name, ), 'weight':app_weight.get(label,99), 'is_current':is_current, } if app_label == model._meta.app_label: has_module_perms = model_admin.has_module_permission(request) if not has_module_perms: raise PermissionDenied perms = model_admin.get_model_perms(request) if True in perms.values(): info = (app_label, model._meta.model_name) model_dict = { 'name': capfirst(model._meta.verbose_name_plural), 'object_name': model._meta.object_name, 'perms': perms, 'weight':getattr(model,'index_weight',99) } if perms.get('change'): try: model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name) except NoReverseMatch: pass if perms.get('add'): try: model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name) except NoReverseMatch: pass if app_dict: app_dict['models'].append(model_dict), else: app_dict = { 'name': app_name, 'app_label': app_label, 'app_url': '', 'has_module_perms': has_module_perms, 'models': [model_dict], } if not app_dict: raise Http404('The requested admin page does not exist.') # Sort the models alphabetically within each app. app_dict['models'].sort(key=lambda x: x['weight']) app_lib = list(six.itervalues(lib_dict)) app_lib.sort(key=lambda x: x['weight']) context = dict( maxi_app_list=[app_dict], app_lib=app_lib, ) view_kwargs['extra_context'] = context def get_my_task(self,request): from workflow.models import TodoList if request and request.user: query = TodoList.objects.filter(user=request.user,status=0) if query.count() == 0: return None else: return query.all()[:10] ERP进销存零碎的概述 ERP进销存零碎是指一个企业外部,将产品营销、洽购、生产打算、库存管制、人事管理、财务等业务全副汇合在一起,造成一个疾速数据共享和业务决策的平台。ERP进销存零碎就是一个用于整合和治理企业全副业务流程的软件。它能够对企业外部的各种业务数据进行收集、整合、剖析,从而对业务进行可继续倒退。ERP进销存零碎的外围在于开发一套通用的、面向商业治理的软件,将各种商业治理功能模块集合起来,发明一个基于工作流程的商业治理解决方案,帮忙企业管理者疾速获取要害经营情报,便于优化企业的资源配置,进步企业的管理效率和经济效益。 ERP进销存零碎的倒退历程 ERP进销存零碎作为一种集成性企业管理软件,从20世纪80年代开始倒退,并迅速走向凋敝期间。接下来,让咱们看看ERP进销存零碎各个倒退阶段的具体内容: 1. 初期(20世纪80年代):由MRP零碎演进而来,次要用于对生产治理进行统筹规划。 2. 成长期(20世纪90年代):随着ERP进销存产业的倒退,ERP进销存零碎逐渐遍及,实用于生产治理、洽购治理、销售治理和通用治理等畛域。 3. 黄金期(2000年-2010年):许多大型企业开始将纯自主研发的ERP进销存零碎逐步转向SAP、ORACLE、PeopleSoft等企业信息化软件供应商的产品。 4. 连续期(2010年-至今):云计算、大数据、物联网等新兴技术的利用,使得ERP进销存零碎的性能不断丰富和欠缺。 ERP进销存零碎的劣势和利用 ERP进销存零碎具备很多劣势,让企业管理者实现高效流程治理,加强企业的竞争能力。接下来,就让咱们来看看ERP进销存零碎的利用和劣势。 1. 宏观视角:ERP进销存零碎能够在宏观层面上,管制企业各项流程,包含生产、销售、库存、洽购、财务等,让企业管理者及时取得各项经营数据。 2. 综合视角:ERP进销存零碎能够将企业外部各个性能部门的数据进行整合剖析,实现各个部门的合作,实现流程的优化。 3. 智能决策反对:ERP进销存零碎能够依据数据进行剖析,正当判断业务模式,优化生产流程,促成业务倒退。 4. 高效沟通:ERP进销存零碎能够在多部门之间建设无效的沟通和交换机制,促成部门间的密切合作与协调。 5.升高治理老本:ERP进销存零碎能够放慢各个流程的效率,促成信息共享,从而升高企业的治理老本,进步企业的盈利。 ...

May 23, 2023 · 3 min · jiezi

关于php:PHP弱类型变量实现原理zval垃圾回收refcountgc写时拷贝copyonwrite机制isrefgc

对于PHP的弱类型变量实现原理对于 PHP的弱类型变量实现原理 其实曾经有很多文章了,这里只是做个总结,不便当前疾速回顾;毕竟 工作尽管拧螺丝,考试还得背八股。简略来说,PHP是通过一个 zval 的构造体实现的,源代码如下:注:此源码是PHP5版本,7及以上版本,请参考官网 struct _zval_struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; zend_ast *ast; } value; zend_uint refcount__gc; zend_uchar type; zend_uchar is_ref__gc;};这个zval构造体中的type字段, 代表变量以后的类型值, 常见的可能选项是IS_NULL, IS_LONG, IS_STRING, IS_ARRAY, IS_OBJECT等;弱类型,就是通过type字段的值, 取对应的value值来实现的; 这个value是联合体(union): 如果type是 IS_STRING, 就取 value.str 的值;如果type是 IS_LONG, 就用 value.lval 的值;联合体的特点在于,容许在雷同的内存地位存储不同的数据类型,也就是共享内存地址,其长度等于最长的子成员长度;但同一时刻只能有一个成员带有有效值,比方设置 value.str='haha', 此时获取 value.lval 就不会失去 haha;这一特点可无效节俭内存占用。 而从源码中的_zval_struct定义,咱们也能够看出: 每次批改变量为不同类型的值,只需更改 type,并赋值给与其对应的 value 联合体即可;这就是 PHP的弱类型变量实现的基本原理,只管源码是老旧的PHP5版本,但原理基本一致。 对于PHP的垃圾回收PHP是通过援用计数来做根本的垃圾回收的, 就是下面zval构造体中的refcount__gc字段, 其含意是变量的援用数目; 这个内存回收算法是赫赫有名的 Reference Counting,个别译为 援用计数,其算法思维十分简洁: ...

May 22, 2023 · 2 min · jiezi

关于php:Elasticsearch的基础使用

前言:    在开发我的项目中个别都会有搜寻性能。如果是面向C端的搜寻性能,往往都特地考验性能。比方一般的商城零碎中的商品搜寻或者一些资源的站内搜索。     可能以前的做法就是对商品表做一个按名称或商品形容做含糊查问。更好一点的是对搜寻关键字进行分词,并且专门建一个搜寻词库表。不过后期须要对搜索词进行拆解而后幂集组合并于商品ID关联,搜寻字与词库表的字以齐全匹配的形式查问并找到商品ID。     尽管建词库表也是不错的解决办法,然而还要拆解存库建索引,绝对比拟麻烦。所以也是在网上查问理解到了elasticsearch,打算当前做站内搜索用ES,上面就简略介绍一下他的根本应用办法。  ES介绍:     ElasticSearch是一个基于Lucene的搜寻服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码公布,是以后风行的企业级搜索引擎。设计用于云计算中,可能达到实时搜寻,稳固,牢靠,疾速,装置使用方便。  下载安装:找到官网,按本人的零碎下载对应版本,目前200多M。     解压后关上目录中的bin里的"elasticsearch.bat"就能够启动。 启动工夫绝对慢一点,以下只演示单机单节点应用,分布式能够前期配置。   编码应用:以下是以ThinkPHP6.0.2进行演示应用,所以在ES服务开启后,先关上浏览器拜访127.0.0.1:9200,如果返回上面示意胜利开启。 ThinkPHP6.0中通过composer install elasticsearch/elasticsearch 下载ES依赖包,如果composer慢能够试一下下载办法后再装置。   并且每次要本地应用ES时都要先开启elasticsearch.bat文件,不然就会呈现上面的提醒。   在应用前须要了解一下ES的几个名词:索引,Type,文档,字段,映射。并且ES以restful格调进行查问,所以能够将后面的名词能够和MySQL的数据库,表,列,SQL关联一下。 比方索引相当于MySQL的数据库,文档相当于表中的一条记录restful形式申请就相似于SQL语句。比方GET 索引名/Type/ID 能够获取文档数据,POST,PUT,DELETE等等创立索引,创立/批改/删除文档等能够查问官网文档,而后应用POSTMAN用不同的申请127.0.0.1:9200。 当初回到ThinkPHP6中,在装置ES依赖后。能够封装一个ES根本办法工具类。 <?phpnamespace app\common\helper;use Elasticsearch\ClientBuilder as ESClientBuilder;class Elasticsearch{ protected $config = [ "127.0.0.1:9200", ]; protected $es; /* * @Title: 构造函数 * @Author: 北桥苏 * @Times: 2020/5/14 17:35 * */ public function __construct() { $this->es = $this->init(); } /* * @Title ElasticSearch服务初始化 * @Return object ES实例 * */ private function init() { // 从配置文件读取 Elasticsearch 服务器列表 $builder = ESClientBuilder::create()->setHosts($this->config); // 利用调试模式// if (env('APP_DEBUG')) {// // 配置日志,Elasticsearch 的申请和返回数据将打印到日志文件// $builder->setLogger(app('log')->error());// } return $builder->build(); } /* * @Title: 获取ES服务信息 * @Return: ES服务的根本信息 * */ public function info() { return $this->es->info(); } /* * @Titles: 创立索引 * @Param: string $name 索引名 * @Param: array $mappings 字段映射 * @Return: boolean 创立后果 * */ public function index($name, $mappings=[]) { $params = [ 'index' => $name, 'body' => [ 'settings' => [ 'number_of_shards' => 1, // 索引分片(一个索引能够分多个分片在不同的节点上) 'number_of_replicas' => 0, // 索引分片正本 ] ] ]; // 创立索引设置字段以及字段类型分词器等 if($mappings) { $params['body']['mappings']['properties'] = $mappings; } return $this->es->indices()->create($params); } /* * @Title: 获取索引配置,能够获取单个索引,不传参数是节点上的所有索引 * @Param: array $name 索引名数组 * @Return:array 索引的名称分片正本数等数据 * */ public function getIndexConfig($name=[]) { $params = []; if(!empty($name)) { $params = [ "index" => $name ]; } return $this->es->indices()->getSettings($params); } /* * @Title: 创立/更改映射(创立索引时也能够指定映射-其实就是存储数据的字段类型) * @Param: string $name 索引名 * @Param: array $data 字段设置 * @Return boolean 批改后果 * */ public function putMapping($name, $data) { $params = [ 'index' => $name, 'body' => [ '_source' => [ 'enabled' => true ], 'properties' => $data ] ]; return $this->es->indices()->putMapping($params); } /* * @Tile: 获取索引设置的字段映射 * @Param: string $name * @Return: array * */ public function getMapping($name) { $params = ['index' => $name]; return $this->es->indices()->getMapping($params); } /** * @Notes: 获取索引中的文档 * @param $name 索引名称 * @param $id 编号 * @Return: 文档数据 */ public function getIndex($name, $id) { return $this->es->get(['index' => $name, 'id' => $id]); } /** * @Notes: 删除索引 * @param $name 索引名称 * @return array */ public function deleteIndex($name) { return $this->es->indices()->delete(['index' => $name]); } /***********************文档局部********************************************************/ /* * @Tiele: 在某索引中创立一个文档(如果索引未设置映射,增加文档会主动创立字段类型等) * @Param: string $name 索引名 * @Param: array $data 文档数据 * @Return array * */ public function createDoc($name, $data) { $params = [ 'index' => $name, 'id' => $data['id'], 'body' => $data ]; $response = $this->es->index($params); return $response; } /** * @Notes: 更新文档 * @param $index 索引 * @param $id 文档 id * @param $data 字段 * @author: 北桥苏 * @Time: 2020/4/21 9:51 */ public function updateDoc($index, $id, array $data) { $params = [ 'index' => $index, 'id' => $id, //'type' => '_doc', 'body' => [ 'doc' => $data ] ]; $this->es->update($params); } // 删除一个文档 /* * @Tile: 删除一个文档 * @Param: array $param 删除索引名,ID * @Return array * */ public function delDoc($param) { $this->es->delete($param); } // 批量创立文档 public function bulk($params) { return $this->es->bulk($params); } // 搜寻 public function queryData($index,$kye, $from = 0, $size = 10) { $params = [ 'index' => $index, 'body' => [ 'from' => $from, 'size' => $size, 'query' => [ 'match' => [ 'class_name' => '版', ], ] ], ]; return $this->es->search($params); } /* * @Tile: 多功能搜寻 * @Param string $index 索引名称 * @Param array $where 条件数组 * @Param array $sort 排序数组 * @Param int $from 开始查问的地位 * @Param int $size 一共查问的条数 * @Return array 查问后果 * */ public function query($index,$where,$sort='',$from=0,$size=1500) { $params = [ "index" => $index, "body" => [ "from" => $from, "size" => $size, "query" => $where ] ]; if($sort) { $params['body']['sort'] = $sort; } return $this->es->search($params); }}4.业务应用代码块。 ...

May 20, 2023 · 4 min · jiezi

关于php:如何给phpoffice的excel导入功能解耦

前言:在业务中开发中,表格的导入导出性能很常见。然而这里次要是应用PhpOffice类库介绍实现导入表格数据的性能。  抵触:大部分的导入性能,就是通过点击按钮上传一张表格,而后后盾读取表格数据依据业务整顿后直接插入到数据库,最初再返回给前端。然而如果表格数据宏大,业务逻辑简单的时候,就会导致导入那一块很臃肿不好保护。  解决办法:解决形式是把导入与业务数据插入拆散,所以在二者之间增加一个队列就能够了。导入只负责将表格数据存入队列。业务局部能够是独自的零碎,最初就是生产队列中的数据了。这样一来,岂但晋升了导入速度,而且还让导入与零碎解耦,不会因为异样而影响到其余业务。  编码:1.下载PhpOffice。composer repuire phpoffice/phpspreadsheet2.导入导出代码。 <?phpnamespace app\common\helper;use PhpOffice\PhpSpreadsheet\Spreadsheet;use PhpOffice\PhpSpreadsheet\Writer\Xlsx;use PhpOffice\PhpSpreadsheet\IOFactory;use PhpOffice\PhpSpreadsheet\Cell\Coordinate;use think\Exception;class Excel{ // 导出 public function outPut($data, $columns, $table = '导出文件') { $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // 设置第一栏的题目 foreach ($columns as $k => $v) { $sheet->setCellValue($k . "1", $v['title']); } //第二行起 设置内容 $baseRow = 2; //数据从N-1行开始往下输入 这里是防止头信息被笼罩 foreach ($data as $key => $value) { foreach ($columns as $k1 => $v1) { $i = $key + $baseRow; $sheet->setCellValue($k1 . $i, $value[$v1['field']]); } } $writer = new Xlsx($spreadsheet); $filename = $table . date("Y-m-d", time()) . '_' . time() . '.xlsx'; $writer->save('./excel/' . $filename); return '/excel/' . $filename; } // 导入 public function importExcel($file = '', $sheet = 0, $columnCnt = 0, &$options = []) { try { $file = iconv("utf-8", "gb2312", $file); if (empty($file) OR !file_exists($file)) { throw new \Exception('文件不存在!'); } $objRead = IOFactory::createReader('Xlsx'); if (!$objRead->canRead($file)) { $objRead = IOFactory::createReader('Xls'); if (!$objRead->canRead($file)) { throw new \Exception('只反对导入Excel文件!'); } } /* 如果不须要获取非凡操作,则只读内容,能够大幅度晋升读取Excel效率 */ empty($options) && $objRead->setReadDataOnly(true); /* 建设excel对象 */ $obj = $objRead->load($file); /* 获取指定的sheet表 */ $currSheet = $obj->getSheet($sheet); //$currSheet = $obj->getSheetByName($sheet); // 依据名字 if (isset($options['mergeCells'])) { /* 读取合并行列 */ $options['mergeCells'] = $currSheet->getMergeCells(); } if (0 == $columnCnt) { /* 获得最大的列号 */ $columnH = $currSheet->getHighestColumn(); /* 兼容原逻辑,循环时应用的是小于等于 */ $columnCnt = Coordinate::columnIndexFromString($columnH); } /* 获取总行数 */ $rowCnt = $currSheet->getHighestRow(); $data = []; /* 读取内容 */ for ($_row = 1; $_row <= $rowCnt; $_row++) { $isNull = true; for ($_column = 1; $_column <= $columnCnt; $_column++) { $cellName = Coordinate::stringFromColumnIndex($_column); $cellId = $cellName . $_row; $cell = $currSheet->getCell($cellId); if (isset($options['format'])) { /* 获取格局 */ $format = $cell->getStyle()->getNumberFormat()->getFormatCode(); /* 记录格局 */ $options['format'][$_row][$cellName] = $format; } if (isset($options['formula'])) { /* 获取公式,公式均为=号结尾数据 */ $formula = $currSheet->getCell($cellId)->getValue(); if (0 === strpos($formula, '=')) { $options['formula'][$cellName . $_row] = $formula; } } if (isset($format) && 'm/d/yyyy' == $format) { /* 日期格局翻转解决 */ $cell->getStyle()->getNumberFormat()->setFormatCode('yyyy/mm/dd'); } $data[$_row][$cellName] = trim($currSheet->getCell($cellId)->getFormattedValue()); if (!empty($data[$_row][$cellName])) { $isNull = false; } } if ($isNull) { unset($data[$_row]); } } return $data; } catch (\Exception $e) { throw $e; } } }3.抽取指定的字段格式化Excel数据。 ...

May 19, 2023 · 3 min · jiezi

关于php:PHP接收不到json格式的数据用-filegetcontentsphpinput-试试

通常状况,PHP获取上游传来的参数,即内部变量,应用 $_POST、$_GET、$_REQUEST 就能够了。其中,$_REQUEST 是个组合体,默认状况蕴含了 $_GET,$_POST 和 $_COOKIE。 GET 形式咱们晓得,通过 URL 传递参数(又叫 query string),只有你的URL带有 query string 就没啥问题; POST 形式就有点简单了,$_POST 中获取变量的前提是,发动申请的 Content-Type 必须得是 application/x-www-form-urlencoded 或 multipart/form-data 格局; 因为个别用PHP解决的是网页申请,下面这两种格局都是默认的,所以没啥问题;而一旦用PHP给第三方提供接口时,就会遇到另一种状况:第三方应用的 Content-Type 不是下面提到的两种。 纯接口罕用的个别是 application/json 格局,当然还有 text/xml、text/plain、stream 等其余类型;这个状况用 $_GET(或者$_REQUEST) 只能获取第三方写在URL中的参数(该数组不仅仅对GET申请失效,所有带 query string 的申请都能够),而 json 格局的参数,就无能为力了。 这就要用到 file_get_contents('php://input') 了;php://input 容许读取【申请方】的原始数据, 实用于大多数类型的 Content-type,不止是下面提到的几种,但不能用于 multipart/form-data 类型。也就是说,当 Content-Type 为 application/x-www-data-urlencoded 时,php://input 获取的数据,和 $_POST 获取的数据统一。 言而总之: 如果用 $_POST、$_GET、$_REQUEST 获取不到参数时,能够用 php://input 打印一下原始数据瞅瞅;如果和 第三方 对接接口,尽量用 file_get_contents('php://input') 比拟稳当;

May 17, 2023 · 1 min · jiezi

关于php:浅谈一下ThinkPHP51实现事务嵌套的特性

前言:在咱们平时做的一个我的项目中,线上环境忽然发现数据库被锁住。导致很多无关数据插入和批改的接口全都瘫痪,我的项目基于ThinkPHP5.1。报错的时候,咱们发现了一条sql谬误日志,如下。      依据错误信息提醒,是说有一个事务回滚时没有找到savepoint 的暂存点。所以问题应该是事务嵌套导致的,目前ThinkPHP5封装的数据层办法是有对事务嵌套进行解决。而MYSQL到底支不反对事务嵌套呢?伪代码如下。 执行完后呈现了操作1的数据真正写入,只有操作2的数据回滚了。在第一个事务没有提交或回滚时,再开启第二个事务时,会主动提交第一个事务。 这显著不合乎心理预期,而且也无奈回滚一部分操作。首先,调用屡次begin的写法,在MySQL里必定是无奈首先事务嵌套的。    抱着疑难,我去网上也查了很多相似的问题。却意外的发现对于“解决事务嵌套的办法”都如此的雷同,清一色的 “开启事务时候应用单例,查看事务是否存在”。(蒙圈……)    持续回到MYSQL是否反对事务嵌套,最初我理解到MySQL中有一个叫savepoint和rollback to的语句。于是我棘手举了个例子。 下面有3张表,别离有不同的更新操作和最初的插入。然而最初只回滚到P1,执行完commit后,我发现只有p1地位的操作做了更新,前面的批改和插入全都没失效。可能savepoint和rollback to语句并不能称之为事务嵌套,也不能说MySQL是反对还是不反对事务嵌套。总之通过savepoint和rollback to,是能够用来达到一些事务嵌套个性的。     依据咱们我的项目的日志,我也试着复现一下,成心rollback to到一个不存在的点上,最初commit时发现,数据库的第一条sql批改了,前面插入语句也增加胜利了。 ThinkPHP5.1所以再回到ThinkPHP5框架实现的事务嵌套,于是我关上了框架的Connection抽象类。最次要的就是上面局部了。 1.开启事务。开启事务的局部加了次数累加,只有在代码块中应用了transaction就会被记录并叠加,当只有1时,才执行sql的begin,否则就savepoint记录一个保留点。 2.提交事务。判断了只有等于1才对事务进行提交,也就是在代码中最初面的commit才失效,应用一次减一次嵌套计数。initConnect办法次要是对分布式数据库和单机对读写连贯的判断。 3.回滚事务。回滚次要也是当计数等于1才让sql执行rollback,否则(也就是嵌套了)就让连贯资源回滚到执行的保留点。它外面的就是应用的rollback to p1 语句的。     

May 17, 2023 · 1 min · jiezi

关于php:无法记录ThinkPHP51的SQL日志解决方案

&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp我的项目开发阶段,除了根本编码外,性能也须要实时关注与优化。之前我的大部分我的项目都是应用ThinkPHP5.0以及ThinkPHP3.2,对于框架提供的日志记录和日志配置都差不多,而后应用ThinkPHP5.1的时候就吃瘪,花了十几分钟才好,所以写一下避免前面遗记了再踩坑。 日志配置&nbsp&nbsp&nbsp&nbsp&nbsp&nbspThinkPHP5.1没有了config.php,日志配置独自提出来自成一块,叫Log.php,这个适应一下就能够。  日志记录&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp在5.1以前的我的项目中记录集体调试的日志,都是应用use think\Log; 而后应用Log::write()。而后5.1的时候引入了facede,所以间接用以前的形式,不能应用动态调用write等办法,批改为use think\facede\Log,而后再应用。 理论日志配置&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp当须要调试时,app配置中'app_debug' => true, 'app_trace'      => true, 调试和追踪开启,浏览器关上会右下角呈现TP的logo和运行工夫,点击图标会呈现调试明细。然而有时不会呈现,然而想调试SQL以及SQL的查问速度,就须要开启日志记录,尽量不要配置保留目录,默认在runtime下就能够,也不是因为自定义目录的写入权限问题,所以所有默认就好,先解决问题前面再钻研问题起因,Log.php配置如下。return [ // 日志记录形式,反对 file socket 或者自定义驱动类'type' => 'File','file_size' =>2097152,'apart_level' => ['sql','error'],//日志的工夫格局,默认是` c `'time_format' =>'c']; 当开发阶段完结,不须要除了error以外级别的日志,能够在"apart_level"配置只保留"error"。

May 16, 2023 · 1 min · jiezi

关于php:玩转服务器之环境篇PHP和Python环境部署指南-京东云技术团队

前几篇文章中解说了如何搭建docker和Java Web环境的办法,本篇文章来教大家搭建一个好的PHP和Python环境,能够帮忙开发和运行PHP和Python应用程序,使其更加高效和稳固。 一、 PHP环境介绍好的开发环境无疑会大大晋升编码效率,近日钻研了一下Python环境装置的问题,稍加总结分享一下。本文以轻量云主机(CenTOS 7.6零碎)为例,介绍如何搭建PHP环境。 1.1、前置筹备正式开始部署前,您需实现如下的筹备工作: 开明京东云账户,若您还未注册京东云账号,可在京东云官网进行注册;账户开明后,须要进行实名认证;购买一台或多台云主机或者轻量云主机产品;登录轻量云主机,复制上面步骤中的命令进行执行操作;1.2、搭建PHP环境本次装置的是PHP7,有些linux版本的yum默认是装置的5,须要做如下更改 rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpmrpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm装置PHP yum -y install php72w装置PHP扩大 yum -y install php72w-common php72w-fpm php72w-opcache php72w-gd php72w-mysqlnd php72w-mbstring php72w-pecl-redis php72w-pecl-memcached php72w-devel1.3、装置Apache装置Apache yum install -y httpdsystemctl start httpd1.4、测试验证新建info.php文件 cd /var/www/htmlvi info.php在文件中输出 上面内容,并保留文件 <?php phpinfo(); ?> 在浏览器中输出 http://ip/info.php查看成果 二、 Python环境介绍Python是一种面向对象的解释型语言,可利用于各种畛域,包含但不限于网站、游戏开发、机器人,人工智能,大数据等。 CentOS零碎自身默认装置有python2.x,版本x依据不同零碎版本有所不同,可通过 python --V 或 python --version 查看零碎自带的python版本。 本文以轻量云主机(CenTOS 7.6零碎)为例,介绍如何部署python3.6环境。 2.1、前置筹备正式开始部署前,您需实现如下的筹备工作: 开明京东云账户,若您还未注册京东云账号,可在京东云官网进行注册;账户开明后,须要进行实名认证;购买一台或多台云主机或者轻量云主机产品;登录轻量云主机,复制上面步骤中的命令进行执行操作;2.2、搭建Python3.6环境查看能够装置的python3版本 yum -y list python3* 装置python3.6 yum -y install python36 验证phthon3.6 是否装置胜利 python3 -V ...

May 15, 2023 · 1 min · jiezi

关于php:快速排序学习记录

简介在数组中找一个标定点 p,把数组中小于 arr[p] 元素放到 arr[p] 的右边,把大于等于 arr[p] 的元素放到 arr[p] 的左边,即: 在区间 [0, p - 1] 内的元素小于 arr[p];在区间 [p + 1, count(arr) - 1] 内的元素大于 arr[p]。再别离对 arr[0,...,p - 1]、arr[p + 1,...,count(arr) - 1] 进行递归排序。 如果用以上思路去排序一个齐全无序的数组,工夫复杂度会是 O(nlgn),然而有两种特例会呈现性能进化的景象,工夫复杂度会变成 O(n^2): 数组齐全有序,例如 [1, 2, 3, 4];数组中的所有元素都雷同,例如 [0, 0, 0]。为了避免出现因为数组齐全有序而导致算法性能进化的景象,应用了随机化的办法,即在区间 [l, r] 内随机选一个元素,与 arr[l] 替换,把替换后的 arr[l] 作为标定点,而不是间接把 arr[l] 作为标定点。 而数组中所有元素都雷同导致性能进化的问题的解决办法则是应用双路快排: 三路快排则是对双路快排的进一步优化。 单路疾速排序<?phpclass OneWayQuickSort{ public function sort(&$arr) { $this->oneWaySort($arr, 0, count($arr) - 1); } protected function oneWaySort(&$arr, $l, $r) { if ($l >= $r) { return; } $p = $this->partition($arr, $l, $r); $this->oneWaySort($arr, $l, $p - 1); $this->oneWaySort($arr, $p + 1, $r); } protected function partition(&$arr, $l, $r) { // 原地宰割 // arr[l+1,...,j] < v; arr[j+1,...,i-1] >= v $j = $l; for ($i = $l + 1; $i <= $r; $i++) { if ($arr[$i] < $arr[$l]) { $j ++; $this->swap($arr, $i, $j); } } $this->swap($arr, $l, $j); return $j; } protected function swap(&$arr, $i, $j) { $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } public static function Main() { $arr = [1,3,5,7,2,4,6,8]; (new OneWayQuickSort())->sort($arr); $result = '['.implode(', ', $arr).']'; echo $result; }}OneWayQuickSort::Main();// [1, 2, 3, 4, 5, 6, 7, 8]双路疾速排序<?phpclass TwoWaysQuickSort{ public function sort(&$arr) { $this->twoWaysSort($arr, 0, count($arr) - 1); } protected function twoWaysSort(&$arr, $l, $r) { if ($l >= $r) { return; } $p = $this->partition($arr, $l, $r); $this->twoWaysSort($arr, $l, $p - 1); $this->twoWaysSort($arr, $p + 1, $r); } protected function partition(&$arr, $l, $r) { // 随机化,防止因有序数组呈现性能进化的问题 // 在区间[l, r]中取一个数k,替换arr[l]和arr[k] $k = mt_rand($l, $r); $this->swap($arr, $l, $k); // arr[l+1,...,i - 1] <= v; arr[j + 1,...,r] >= v $i = $l + 1; $j = $r; while (true) { while ($i <= $j && $arr[$i] < $arr[$l]) { $i ++; } while ($j >= $i && $arr[$j] > $arr[$l]) { $j --; } if ($i >= $j) { // 循环终止条件,但 i 和 j 相等时,循环终止。 break; } $this->swap($arr, $i, $j); $i ++; $j --; } $this->swap($arr, $l, $j); return $j; } protected function swap(&$arr, $i, $j) { $temp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $temp; } public static function Main() { $arr = [1,3,5,7,2,4,6,8]; (new TwoWaysQuickSort())->sort($arr); $result = '['.implode(', ', $arr).']'; echo $result; }}TwoWaysQuickSort::Main();// [1, 2, 3, 4, 5, 6, 7, 8]循环不变量是什么? ...

May 14, 2023 · 4 min · jiezi

关于php:Python技巧快速生成字典列表

在编程过程中,咱们常常须要解决数据结构。明天,咱们将为您介绍如何在Python中高效地生成字典列表。这将帮忙您简化代码,提高效率。 1. 应用列表推导式列表推导式是Python中的一种简洁、高效的生成列表的办法。以下是应用列表推导式生成字典列表的示例: keys = ['name', 'age', 'job']values = [['Tom', 28, 'teacher'], ['Jerry', 32, 'engineer'], ['Alice', 24, 'designer']]dict_list = [dict(zip(keys, value)) for value in values]print(dict_list)输入后果: [{'name': 'Tom', 'age': 28, 'job': 'teacher'}, {'name': 'Jerry', 'age': 32, 'job': 'engineer'}, {'name': 'Alice', 'age': 24, 'job': 'designer'}]2. 应用map()函数map()函数能够将一个函数利用于一个可迭代对象中的所有元素。以下是应用map()函数生成字典列表的示例: def create_dict(keys, values): return dict(zip(keys, values))keys = ['name', 'age', 'job']values = [['Tom', 28, 'teacher'], ['Jerry', 32, 'engineer'], ['Alice', 24, 'designer']]dict_list = list(map(lambda value: create_dict(keys, value), values))print(dict_list)输入后果: ...

May 14, 2023 · 1 min · jiezi

关于php:统一规范化项目的命名风格

最近在迁徙一个上古我的项目到 laravel 中。我这边的做法是先用 rector 做一个整体初步的语法降级与 laravel 写法的替换,而后次要就是手动重写数据操作的局部。到目前为止除了利用到 rector 自带的规定外,还写了一些自定义的规定,其中有一个规范化命名格调的规定(RenameToPsrNameRector)实用于所有的 PHP 我的项目,所以在此分享进去。该规定次要是针对常量、变量、函数、类、属性、办法等命名进行对立的标准。其中,常量名遵循大写蛇形命名格调,函数名遵循小写蛇形命名格调,类名遵循大驼峰命名格调,变量名、属性名、办法名遵循小驼峰命名格调。成果 <?php // lower snake-function functionName(){}-functionName();-call_user_func('functionName');-call_user_func_array('functionName');-function_exists('functionName');+function function_name(){}+\function_name();+call_user_func('function_name');+call_user_func_array('function_name');+function_exists('function_name'); // ucfirst camel-class class_name{}-enum enum_name{}-enum Enum{case case_name;}-interface interface_name{}-trait trait_name{}-class Foo extends class_name implements interface_name{}-class_name::$property;-class_name::CONST;-class_name::method();-enum Enum implements interface_name{}-use class_name;-use trait_name;-class_alias('class_name', 'alias_class_name');-class_exists('class_name');-class_implements('class_name');-class_parents('class_name');-class_uses('class_name');-enum_exists('enum_name');-get_class_methods('class_name');-get_class_vars('class_name');-get_parent_class('class_name');-interface_exists('interface_name');-is_subclass_of('class_name', 'parent_class_name');-trait_exists('trait_name', true);+class ClassName{}+enum EnumName{}+enum Enum{case CaseName;}+interface InterfaceName{}+trait TraitName{}+class Foo extends \ClassName implements \InterfaceName{}+\ClassName::$property;+\ClassName::CONST;+\ClassName::method();+enum Enum implements \InterfaceName{}+use ClassName;+use TraitName;+class_alias('ClassName', 'AliasClassName');+class_exists('ClassName');+class_implements('ClassName');+class_parents('ClassName');+class_uses('ClassName');+enum_exists('EnumName');+get_class_methods('ClassName');+get_class_vars('ClassName');+get_parent_class('ClassName');+interface_exists('InterfaceName');+is_subclass_of('ClassName', 'ParentClassName');+trait_exists('TraitName', true); // upper snake-class Foo{public const constName = 'const';}-Foo::constName;-define('constName', 'const');-defined('constName');-constant('constName');+class Foo{public const CONST_NAME = 'const';}+Foo::CONST_NAME;+define('CONST_NAME', 'const');+defined('CONST_NAME');+constant('CONST_NAME'); constant('Foo::constName');-constName;+\CONST_NAME; // lcfirst camel-$var_name;-$object->method_name();-$object->property_name;-call_user_method('method_name', $object);-call_user_method_array('method_name', $object);-class Foo{public $property_name;}-class Foo{public function method_name(){}}-class Foo{public int $property_name;}-Foo::$property_name;-Foo::method_name();-method_exists($object, 'method_name');-property_exists($object, 'property_name');+$varName;+$object->methodName();+$object->propertyName;+call_user_method('methodName', $object);+call_user_method_array('methodName', $object);+class Foo{public $propertyName;}+class Foo{public function methodName(){}}+class Foo{public int $propertyName;}+Foo::$propertyName;+Foo::methodName();+method_exists($object, 'methodName');+property_exists($object, 'propertyName');规定(RenameToPsrNameRector)<?phpnamespace App\Support\Rectors;use Illuminate\Support\Str;use PhpParser\Node;use PhpParser\Node\Expr\FuncCall;use PhpParser\Node\Name;use Rector\Core\Contract\Rector\ConfigurableRectorInterface;use Rector\Core\Rector\AbstractRector;use RectorPrefix202305\Webmozart\Assert\Assert;use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;class RenameToPsrNameRector extends AbstractRector implements ConfigurableRectorInterface{ /** * @var array<string> */ protected $except = [ '*::*', 'class', 'false', 'null', 'stdClass', 'true', ]; public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( 'Rename to psr name', [ new CodeSample( <<<'CODE_SAMPLE'// lower snakefunction functionName(){}functionName();call_user_func('functionName');call_user_func_array('functionName');function_exists('functionName');// ucfirst camelclass class_name{}enum enum_name{}enum Enum{case case_name;}interface interface_name{}trait trait_name{}class Foo extends class_name implements interface_name{}class_name::$property;class_name::CONST;class_name::method();enum Enum implements interface_name{}use class_name;use trait_name;class_alias('class_name', 'alias_class_name');class_exists('class_name');class_implements('class_name');class_parents('class_name');class_uses('class_name');enum_exists('enum_name');get_class_methods('class_name');get_class_vars('class_name');get_parent_class('class_name');interface_exists('interface_name');is_subclass_of('class_name', 'parent_class_name');trait_exists('trait_name', true);// upper snakeclass Foo{public const constName = 'const';}Foo::constName;define('constName', 'const');defined('constName');constant('constName');constant('Foo::constName');constName;// lcfirst camel$var_name;$object->method_name();$object->property_name;call_user_method('method_name', $object);call_user_method_array('method_name', $object);class Foo{public $property_name;}class Foo{public function method_name(){}}class Foo{public int $property_name;}Foo::$property_name;Foo::method_name();method_exists($object, 'method_name');property_exists($object, 'property_name');CODE_SAMPLE , <<<'CODE_SAMPLE'// lower snakefunction function_name(){}function_name();call_user_func('function_name');call_user_func_array('function_name');function_exists('function_name');// ucfirst camelclass ClassName{}enum EnumName{}enum Enum{case CaseName;}interface InterfaceName{}trait TraitName{}class Foo extends ClassName implements InterfaceName{}ClassName::$property;ClassName::CONST;ClassName::method();enum Enum implements InterfaceName{}use ClassName;use TraitName;class_alias('ClassName', 'AliasClassName');class_exists('ClassName');class_implements('ClassName');class_parents('ClassName');class_uses('ClassName');enum_exists('EnumName');get_class_methods('ClassName');get_class_vars('ClassName');get_parent_class('ClassName');interface_exists('InterfaceName');is_subclass_of('ClassName', 'ParentClassName');trait_exists('TraitName', true);// upper snakeclass Foo{public const CONST_NAME = 'const';}Foo::CONST_NAME;define('CONST_NAME', 'const');defined('CONST_NAME');constant('CONST_NAME');constant('Foo::CONST_NAME');CONST_NAME;// lcfirst camel$varName$object->methodName();$object->propertyName;class Foo{public $propertyName;}class Foo{public function methodName(){}}class Foo{public int $propertyName;}Foo::$propertyName;Foo::methodName();call_user_method('methodName', $object);call_user_method_array('methodName', $object);method_exists($object, 'methodName');property_exists($object, 'propertyName');CODE_SAMPLE ), ]); } /** * {@inheritDoc} */ public function getNodeTypes(): array { return [ \PhpParser\Node\Name::class, \PhpParser\Node\Expr\FuncCall::class, \PhpParser\Node\Expr\Variable::class, \PhpParser\Node\Identifier::class, ]; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ public function refactor(Node $node) { try { if ($this->shouldLowerSnakeName($node)) { return $this->rename($node, static fn (string $name): string => Str::lower(Str::snake($name))); } if ($this->shouldUcfirstCamelName($node)) { return $this->rename($node, static fn (string $name): string => Str::ucfirst(Str::camel($name))); } if ($this->shouldUpperSnakeName($node)) { return $this->rename($node, static fn (string $name): string => Str::upper(Str::snake($name))); } if ($this->shouldLcfirstCamelName($node)) { return $this->rename($node, static fn (string $name): string => Str::lcfirst(Str::camel($name))); } } catch (\RuntimeException $e) { // skip } return null; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function rename(Node $node, callable $renamer): Node { $preprocessor = function (string $value): string { if ($this->is($this->except, $value)) { throw new \RuntimeException("The name[$value] is skipped."); } if (ctype_upper(preg_replace('/[^a-zA-Z]/', '', $value))) { return mb_strtolower($value, 'UTF-8'); } return $value; }; if ($node instanceof Name) { $node->parts[count($node->parts) - 1] = $renamer($preprocessor($node->parts[count($node->parts) - 1])); return $node; } if ( $this->isSubclasses($node, [ Node\Expr\Variable::class, Node\Identifier::class, ]) ) { $node->name = $renamer($preprocessor($node->name)); return $node; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ 'call_user_func', 'call_user_func_array', 'call_user_method', 'call_user_method_array', 'class_alias', 'class_exists', 'class_implements', 'class_parents', 'class_uses', 'constant', 'define', 'defined', 'enum_exists', 'function_exists', 'get_class_methods', 'get_class_vars', 'get_parent_class', 'interface_exists', 'is_subclass_of', 'trait_exists', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { $node->args[0]->value->value = $renamer($preprocessor($node->args[0]->value->value)); } if ( $this->isNames($node, [ 'class_alias', 'is_subclass_of', 'method_exists', 'property_exists', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { $node->args[1]->value->value = $renamer($preprocessor($node->args[1]->value->value)); } } return $node; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldLowerSnakeName(Node $node): bool { $parent = $node->getAttribute('parent'); // function function_name(){} if ($node instanceof Node\Identifier && $parent instanceof Node\Stmt\Function_) { return true; } // function_name(); if ($node instanceof Node\Name && $parent instanceof FuncCall) { return true; } if ( $node instanceof FuncCall && $this->isNames($node, [ // function_exists('function_name'); 'function_exists', // call_user_func('function_name'); 'call_user_func', // call_user_func_array('function_name'); 'call_user_func_array', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldUcfirstCamelName(Node $node): bool { $parent = $node->getAttribute('parent'); if ( $node instanceof Node\Identifier && $this->isSubclasses($parent, [ // interface InterfaceName{} Node\Stmt\Interface_::class, // class ClassName{} Node\Stmt\Class_::class, // trait TraitName{} Node\Stmt\Trait_::class, // enum EnumName{} Node\Stmt\Enum_::class, // enum Enum{case CaseName;} Node\Stmt\EnumCase::class, ]) ) { return true; } if ( $node instanceof Node\Name && ! $this->isName($node, 'stdClass') && $this->isSubclasses($parent, [ // ClassName::CONST; Node\Expr\ClassConstFetch::class, // ClassName::$property; Node\Expr\StaticPropertyFetch::class, // ClassName::method(); Node\Expr\StaticCall::class, // class Foo extends ClassName implements InterfaceName{} Node\Stmt\Class_::class, // enum Enum implements InterfaceName{} Node\Stmt\Enum_::class, // use ClassName; Node\Stmt\UseUse::class, // use TraitName; Node\Stmt\TraitUse::class, ]) ) { return true; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ // class_alias('ClassName', 'AliasClassName'); 'class_alias', // class_exists('ClassName'); 'class_exists', // class_implements('ClassName'); 'class_implements', // class_parents('ClassName'); 'class_parents', // class_uses('ClassName'); 'class_uses', // enum_exists('EnumName'); 'enum_exists', // get_class_methods('ClassName'); 'get_class_methods', // get_class_vars('ClassName'); 'get_class_vars', // get_parent_class('ClassName'); 'get_parent_class', // interface_exists('InterfaceName'); 'interface_exists', // is_subclass_of('ClassName', 'ParentClassName'); 'is_subclass_of', // trait_exists('TraitName', true); 'trait_exists', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } if ( $this->isNames($node, [ // class_alias('ClassName', 'AliasClassName'); 'class_alias', // is_subclass_of('ClassName', 'ParentClassName'); 'is_subclass_of', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { return true; } } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldUpperSnakeName(Node $node): bool { $parent = $node->getAttribute('parent'); if ( $node instanceof Node\Identifier && ! $this->isName($node, 'class') && $this->isSubclasses($parent, [ // class Foo{public const CONST_NAME = 'const';} Node\Const_::class, // Foo::CONST_NAME; Node\Expr\ClassConstFetch::class, ]) ) { return true; } if ( $node instanceof FuncCall && $this->isNames($node, [ // define('CONST_NAME', 'const'); 'define', // defined('CONST_NAME'); 'defined', // constant('Foo::CONST_NAME'); 'constant', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } // CONST_NAME; if ( $node instanceof Name && ! $this->isNames($node, ['null', 'true', 'false']) && $parent instanceof Node\Expr\ConstFetch ) { return true; } return false; } /** * @param \PhpParser\Node\Name|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\Variable|\PhpParser\Node\Identifier $node */ protected function shouldLcfirstCamelName(Node $node): bool { // $varName; if ($node instanceof Node\Expr\Variable) { return true; } if ( $node instanceof Node\Identifier && $this->isSubclasses($node->getAttribute('parent'), [ // class Foo{public $propertyName;} Node\Stmt\Property::class, // class Foo{public int $propertyName;} Node\Stmt\PropertyProperty::class, // class Foo{public function methodName(){}} Node\Stmt\ClassMethod::class, // $object->propertyName; Node\Expr\PropertyFetch::class, // Foo::$propertyName; Node\Expr\StaticPropertyFetch::class, // $object->methodName(); Node\Expr\MethodCall::class, // Foo::methodName(); Node\Expr\StaticCall::class, ]) ) { return true; } if ($node instanceof FuncCall) { if ( $this->isNames($node, [ // call_user_method('methodName', $object); 'call_user_method', // call_user_method_array('methodName', $object); 'call_user_method_array', ]) && $this->hasFuncCallIndexStringArg($node, 0) ) { return true; } if ( $this->isNames($node, [ // method_exists($object, 'methodName'); 'method_exists', // property_exists($object, 'propertyName'); 'property_exists', ]) && $this->hasFuncCallIndexStringArg($node, 1) ) { return true; } } return false; } protected function isSubclasses($object, array $classes): bool { if (! is_object($object)) { return false; } foreach ($classes as $class) { if ($object instanceof $class) { return true; } } return false; } /** * @param string|iterable<string> $patterns * @param string $value */ public function is($patterns, $value): bool { $value = (string) $value; if (! is_iterable($patterns)) { $patterns = [$patterns]; } foreach ($patterns as $pattern) { $pattern = (string) $pattern; if ($pattern === $value) { return true; } $pattern = preg_quote($pattern, '#'); $pattern = str_replace('\*', '.*', $pattern); if (preg_match('#^'.$pattern.'\z#u', $value) === 1) { return true; } } return false; } protected function hasFuncCallIndexStringArg(FuncCall $funcCall, int $index): bool { return isset($funcCall->args[$index]) && $funcCall->args[$index]->name === null && $funcCall->args[$index]->value instanceof Node\Scalar\String_; } protected function hasFuncCallNameStringArg(FuncCall $funcCall, string $name): bool { foreach ($funcCall->args as $arg) { if ( $arg->name instanceof Node\Identifier && $arg->name->name === $name && $arg->value instanceof Node\Scalar\String_) { return true; } } return false; } public function configure(array $configuration): void { Assert::allStringNotEmpty($configuration); $this->except = [...$this->except, ...$configuration]; }}应用rector 配置文件中配置该规定即可 ...

May 9, 2023 · 6 min · jiezi

关于php:直播源码平台搭建直播源码技术推流的实现一

在当今这个时代,越来越多人喜爱看直播,这也使得直播行业成为现在最火爆的行业之一,直播源码推拉流技术成为了直播行业不可或缺的重要撑持局部。明天我就向大家介绍直播源码技术推流的实现。 一、直播源码推流技术是什么?直播源码搭建的直播平台是由推流端、源站、拉流端三局部组成,推流端,比方直播的人通过手机APP、PC、小程序,进行直播,再让收看的人观看,就称为推流端,通过这些音视频数据采集工具,应用TRMP协定将直播人的直播数据,推到源站。通俗易懂地讲直播源码技术推流就是在咱们直播的时候须要先将内容上传到服务器,而后服务器能力将内容输入给观众看。 二、直播源码技术推流配置实现(PHP代码)在这里我采纳的是PHP语言,要想使新直播的推流,首先要获取推流的地址,代码如下:取得推流地址如果不传给key或者过期的工夫,则会返回不含防盗链的ur1   而后应用以下代码推流到服务器实现推流,代码如下@param domain 这个是用来推流的域名,streamName用来辨别不同流地址的惟一流的名称Key是你的平安密钥,time是过期的工夫  这样,咱们就能够实现直播源码推流技术的实现。

May 8, 2023 · 1 min · jiezi

关于php:归并排序算法学习记录

简介归并排序(Merge sort),是创立在归并操作上的一种无效的排序算法,其工夫复杂度为O(N*logN)。该算法是采纳分治法(Divide and Conquer)的一个十分典型的利用,且各层分治递归能够同时进行。归并排序算法应用了分而治之的思维: 分:在数组的两头地位,将数组一分为二,将原数组排序的问题转换为排序两个子数组的问题;治:直到子数组的长度为 1 时,开始向上合并,一直地将两个曾经排序了的子数组归并成一个有序数组,直至归并至失去原数组的解。实现思路递归: 计算中点的地位 mid,将数组一分为二,并对这两个子数组递归解决,即 mergeSort(arr, l, mid);mergeSort(arr, mid+1, r);直到 l >= r 时进行递归。合并 暂存 arr 中区间 [l, r] 的元素到长期数组中;合并左右子数组: 循环遍历长期数组 temp;用 i 和 j 别离指向左右子数组的第一个元素,用 k 指向原数组中 l 的地位;当 i 大于左子数组的右边界时,阐明左子数组的元素曾经归并实现,因而增加 temp[j - l],并执行 j++;当 j 大于右子数组的右边界时,阐明右子数组的元素曾经归并实现,因而增加 temp[i - l],并执行 i++;当 temp[i - l] <= temp[j - l] 时,增加 temp[i - l],并执行 i++;否则增加 temp[j - l],并执行 j++;须要留神的是: 长期数组 temp 的索引是从 0 到 r - l 的;在循环遍历长期数组时,应该先判断 i 和 j 是否出界,再判断 i 和 j 所指向的元素哪个大。例子对数组 [5 , 3, 1, 2, 4, 7, 8, 2] 进行归并排序的过程: ...

April 30, 2023 · 3 min · jiezi

关于php:imi-助力-ChatGPT-应用开发支持优雅的-SSE-服务端推送功能

imi v2.1.45 公布,反对优雅的 SSE 服务端推送性能,文档:https://doc.imiphp.com/v2.1/components/httpserver/sse.html SSE 介绍SSE 是一种服务端被动向客户端(浏览器)推送数据的技术。 赫赫有名的 ChatGPT 的 API 接口就用了这项技术,实现逐字返回的打字机成果。 服务端向客户端发送一个响应头:Content-Type: text/event-stream 而后服务端按如下格局发送数据: : 正文data: 数据\nevent: 事件\nid: id值\nretry: 重试工夫距离,单位:秒\n\n其中每一行都是非必传项,每一行必须以 \n 结尾 \n\n 代表一次推送的完结 环境反对名称是否反对备注Swoole✔ Workerman✔ php-fpm✔php -S 临时有 BUG,php-fpm 可用。RoadRunner✖临时无奈实现应用示例use Imi\Server\Http\Message\Emitter\SseEmitter;use Imi\Server\Http\Message\Emitter\SseMessageEvent;/** * SSE. * * @Action */public function sse(): void{ $this->response->setResponseBodyEmitter(new class() extends SseEmitter { protected function task(): void { $handler = $this->getHandler(); // 模仿推送数据 foreach (range(1, 100) as $i) { // 推送数据 $handler->send((string) new SseMessageEvent((string) $i)); usleep(10000); } } });}SseMessageEventImi\Server\Http\Message\Emitter\SseMessageEvent 类是 SSE 推送事件类,构造方法参数如下: ...

April 29, 2023 · 1 min · jiezi

关于php:PHP验证码的语言包预警信息通知总结

结尾的话最近在开发PHP验证码我的项目,须要应用到不同的语言包和及时的预警短信告诉。上面我就好好的总结一下。 语言包默认应用浏览器语言,选项别离有主动(辨认浏览器语言)、简体中文、繁体英文、英文。 self.lang = self.get_lang() # 当前页语言包 def get_lang(self): """ 获取语言包 """ try: dLang = self.data.get("lang", "auto") # 获取利用配置 if dLang == "auto": # 主动 dLang = self.kg.get("HTTP_ACCEPT_LANGUAGE", "zh-cn").split(',')[0].lower() # 获取浏览器语言,取逗号前字符且转成小写 if dLang == "zh-hk": dLang = "zh-tw" if dLang in ("en-us", "en-gb"): dLang = "en" if not exists(url_absolute(f"config/language/{dLang}.py")): dLang = "zh-cn" r = import_module(f'config.language.{dLang}').lang.get(self.kg["PY_SELF"], {}) # 加载页面对象 except Exception: r = {} return r 预警信息预警信息启用 ...

April 20, 2023 · 1 min · jiezi

关于php:ModStartCMS-v620-VIP权益配置功能界面UI优化升级

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场领有丰盛的性能利用,反对后盾一键疾速装置,让开发者能快的实现业务性能开发。 零碎齐全开源,基于 Apache 2.0 开源协定,收费且不限度商业应用。 性能个性丰盛的模块市场,后盾一键疾速装置会员模块通用且残缺,反对残缺的API调用大文件分片上传,进度条显示,已上传文件治理弱小的模块扩大性能,所有模块能够无缝集成,反对在线装置、卸载模块欠缺的开发助手,实现模块、主题的的一键创立欠缺的后盾权限治理,反对基于RBAC的权限管理系统后盾治理反对应用手机、平板、PC,无论何时何地都可方便管理第三方登录(QQ、微信、微博、支付宝、微信小程序)第三方领取反对(微信、支付宝、支付宝当面付、微信扫码、微信小程序)第三方云存储反对,反对云贮存分片上传(阿里云、百度云、华为云、腾讯云、FTP、七牛云、UCloud、又拍云)第三方短信反对(阿里云、腾讯云、华为云、百度云、253云通信、聚合、七牛云、融云、赛邮、UCloud、云片、网易云)V6.2.0版本更新2023年04月18日ModStartCMS公布v6.2.0版本,减少了以下18个个性: [新性能] 图表显示新减少另存为图片、数据视图性能[新性能] Banner导航自动隐藏显示动画[新性能] Grid过滤组件Range-Datetime减少快捷工夫选取[新性能] 用户注册IP长度限度[新性能] Values组件减少Grid和Detail显示优化[新性能] 轮播图片疾速调用形式BannerView[新性能] CMS详情自定义字段主动反序列化[新性能] Response::abortMsg反对JSON格局自动识别[新性能] 装置疏导界面动态文件强制革除缓存[新性能] 用户VIP权利配置性能[系统优化] FileUtil文件下载失败时异样捕捉返回NULL[系统优化] 用户找回明码页面款式,减少明码找步骤[系统优化] 用户注册主动关联默认VIP和默认分组[系统优化] 后盾Grid图片预览采纳Contain模式[系统优化] UEditorPlus富文本编辑器图片主动抓取疏忽URL优化[系统优化] 代码目录构造重新整理,优化命名空间,弃用标记[Bug修复] Admin配置页面config获取数组逻辑异样问题[Bug修复] Checkbox组件默认值不失效问题修复模块市场一键装置零碎内置模块市场,有行业利用、插件、云存储、云短信等功能模块,后盾反对一键装置、启用、禁用、卸载,可疾速搭建属于本人的零碎利用。 零碎演示与文档码云仓库:https://gitee.com/modstart/ModStartCMSGithub仓库:https://github.com/modstart/ModStartCMS零碎演示:https://cms.demo.tecmz.com/下载试用:https://modstart.com/download开发者文档:https://modstart.com/doc模块市场:https://modstart.com/store

April 19, 2023 · 1 min · jiezi

关于php:Windows-下-PHP-7-中-getcsv-函数解析-CSV-错误的问题记录

封面图片源自 Pixabay前言前段时间在应用 str_getcsv 和 fgetcsv 解决 CSV 文件的时候遇到的一个问题: 测试中,文,foo,bar,123预期状况下,应该返回一个数组。["测试中", "文", "foo", "bar", "123"],而理论却失去了 ["测试中,文,foo", "bar", "123"],是的,测试中,文 竟然没有被离开,通过一番测试和查证,最初发现,这个问题默认状况下只会在 Windows 上的 PHP 7 版本(5 测试的时候没有问题,然而会乱码)中呈现(还跟字符长度无关),Linux 下默认没问题。 问题起源因为是间接从文件进行获取解决,共事一开始间接应用的 explode(',', $row) 进行解决,一开始是好的,然而当 CSV 列中呈现了 , 号的时候,就会被意外离开了,至于源数据,不便做批改。为了解决这个问题,我将其改为 str_getcsv 进行解决,却引发了这个问题。 简略说一下 CSV 格局,个别状况下,应用逗号(,)宰割列,用换行来示意新行,而共事一开始就是以 explode 的形式来解析单行的数据,而这种状况下,如果有一列的数据中呈现了 逗号(,) 就会导致被意外宰割,多处一列数据来,显然这是不合理的,为此就须要引入本义解决。 为了在单列数据中应用逗号(,),那就须要应用英文的双引号(")把这一列数据包起来(对于须要换行的数据也须要这样解决),而当咱们须要示意一个双引号时,就须要双写这一个双引号,就像这样子。 "php,composer",foo,bar"","say"下面的例子该当被解析为: array(4) { [0]=> string(12) "php,composer" [1]=> string(3) "foo" [2]=> string(5) "bar""" [3]=> string(4) "say"}解决问题通过多个环境验证,发现在 Linux 下没有问题,在 PHP 8 也没问题,就只有 PHP 7 上有这个问题。 当搜寻过一番时,发现遇到过最多的问题,都是乱码,偶有人提到过这个问题。 因为这里编码解析失常,天然不认为是编码的问题,所以持续找材料,顺带还问了问 ChatGPT,一开始他也文不对题的说,是分隔符的问题,最初再疏导下,他提到,能够增加 UTF-8 BOM(字节程序标记(英语:byte-order mark,BOM))来解决。 ...

April 17, 2023 · 2 min · jiezi

关于php:魔众AI付费创作系统-v100-提供可在线运营的C端AI付费创作系统

魔众AI付费创作零碎是一款基于ChatGPT的智能创作工具。它采纳最新的人工智能技术,可能主动生成高质量的文章、新闻、产品描述、广告语、甚至是小说等内容,帮忙用户疾速、高效地进行创作。 魔众AI付费创作零碎公布v1.0.0版本,新性能和Bug修复累计1项,提供可在线经营的C端AI付费创作零碎,文心一言、千义通问、ChatGPT。 2023年04月15日魔众AI付费创作零碎公布v1.0.0版本,减少了以下1个个性: ·[新性能] 提供可在线经营的C端AI付费创作零碎 魔众AI付费创作零碎,提供可在线经营的C端AI付费创作零碎,文心一言、千义通问、ChatGPT。

April 17, 2023 · 1 min · jiezi

关于php:ModStartBlog-v710-ChatGPT支持界面全新优化

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场领有丰盛的性能利用,反对后盾一键疾速装置,让开发者能快的实现业务性能开发。 零碎齐全开源,基于 Apache 2.0 开源协定。 性能个性丰盛的模块市场,后盾一键疾速装置会员模块通用且残缺,反对残缺的API调用大文件分片上传,进度条显示,已上传文件治理弱小的模块扩大性能,所有模块能够无缝集成,反对在线装置、卸载模块欠缺的开发助手,实现模块、主题的的一键创立欠缺的后盾权限治理,反对基于RBAC的权限管理系统后盾治理反对应用手机、平板、PC,无论何时何地都可方便管理第三方登录(QQ、微信、微博、支付宝、微信小程序)第三方领取反对(微信、支付宝、支付宝当面付、微信扫码、微信小程序)第三方云存储反对,反对云贮存分片上传(阿里云、百度云、华为云、腾讯云、FTP、七牛云、UCloud、又拍云)第三方短信反对(阿里云、腾讯云、华为云、百度云、253云通信、聚合、七牛云、融云、赛邮、UCloud、云片、网易云)V7.1.0版本更新2023年04月11日ModStartBlog公布v7.1.0版本,减少了以下17个个性: [新性能] Number、Decimal、Currency组件增加符号显示(signShow)和主动着色(autoColor)配置[新性能] Response::abortMsg反对JSON格局自动识别[新性能] 装置疏导界面动态文件强制革除缓存[新性能] 反对ChatGPT聊天(需装置ChatMobile模块,自行配置Key)[新性能] 申请谬误新增具体错误码和错误信息[新性能] LayUI降级到最新版,移除独立Layer[新性能] 为jQuery新增serializeJson办法[新性能] 轮播图减少背景色彩和container模式,适配大屏模式的显示[新性能] 博客URL被动推送[系统优化] 用户音讯模板查找门路[系统优化] UEditorPlus富文本编辑器图片主动抓取疏忽URL优化[系统优化] 后盾Grid图片预览采纳Contain模式[系统优化] 代码目录构造重新整理,优化命名空间,弃用标记[系统优化] 装置配置文件短少数据库端口配置问题修复[Bug修复] 博客评论审核状态文案谬误修复[Bug修复] Admin配置页面config获取数组逻辑异样问题[Bug修复] Checkbox组件默认值不失效问题修复模块市场一键装置零碎内置模块市场,有行业利用、插件、云存储、云短信等功能模块,后盾反对一键装置、启用、禁用、卸载,可疾速搭建属于本人的零碎利用。 零碎演示与文档码云仓库:https://gitee.com/modstart/ModStartBlogGithub仓库:https://github.com/modstart/ModStartBlog零碎演示:https://blog.demo.tecmz.com/下载试用:https://modstart.com/download开发者文档:https://modstart.com/doc模块市场:https://modstart.com/store

April 12, 2023 · 1 min · jiezi

关于php:闲来无事做个风扇玩玩

大家好,我是良许。 当初全国各地都差不多入冬了吧?冬天的寒风凛冽不? 不够凛冽?那良许就带大家做个风扇,把寒气传递给每一个人! 咱们也把视频录制好了,并且把代码开源进去了,大家能够文末收费支付。 话不多说,直入主题! 我的项目成绩【视频】 看到成果了没?你的手离它越近,风扇就转得越慢,手离它越远,就转得越快。 这是应用 Arduino 开发板实现的一个小我的项目,算是小白入门级的。有多小白呢?咱们公司连 C 语言都没听说过的经营小姐姐良许都能教会她做进去! 用到的模块从视频能够看出,这个小我的项目用到的模块其实并不多。看不清楚?良许再把它拆下来给你看看: ① WemosD1 -- 外围管制单片机② 超声波模块 -- 测量间隔③ L9110模块-- 电机控制器④ 电机风扇模块这个我的项目简略来说就是利用超声波传感器接管手到设施的间隔,并将其换算成电机转动工夫,从而实现依据手与设施间隔管制风扇转速的成果。 听起来有点拗口是吧? 看来我要放出终级大招了,手把手带你做出这样一个我的项目! 手把手带你做风扇如果你对嵌入式有趣味,良许强烈建议你一起入手来做一做这个小我的项目,保障让你播种满满! 视频教程曾经录制好了,良许曾经放在小鹅通了。 为了防止伸手党,请长按扫描上面公众号二维码,并在后盾回复关键字「风扇」收费支付。 【支付疏导】 另外,课程用到的材料及源码良许也给大家筹备好了,大家在答疑群里能够收费获取。 给大家看下咱们答疑群里的气氛: 有小伙伴问,硬件怎么买呢? 良许看起来像个大方的人吗?买什么买,间接送,还顺丰包邮! 然而。。有个小条件,你必须把课程看完,并且把这个风扇做进去,我才把硬件老本退给你(视频及材料收费,硬件30元先付后退)。 这还没完! 最开始的做进去的 10 个小伙伴,良许间接送你一本鸟哥的书,良许及上官老师亲笔签名哦! 良许曾经在直播间介绍过这个风扇,并且曾经胜利率领 300 多位小伙伴做进去了,你必定也会是下一个! 【支付疏导】

April 1, 2023 · 1 min · jiezi

关于php:ModStartBlog-v700-网站简单统计支持博客分享

零碎介绍ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场领有丰盛的性能利用,反对后盾一键疾速装置,让开发者能快的实现业务性能开发。 零碎齐全开源,基于 Apache 2.0 开源协定。 性能个性丰盛的模块市场,后盾一键疾速装置会员模块通用且残缺,反对残缺的API调用大文件分片上传,进度条显示,已上传文件治理弱小的模块扩大性能,所有模块能够无缝集成,反对在线装置、卸载模块欠缺的开发助手,实现模块、主题的的一键创立欠缺的后盾权限治理,反对基于RBAC的权限管理系统后盾治理反对应用手机、平板、PC,无论何时何地都可方便管理第三方登录(QQ、微信、微博、支付宝、微信小程序)第三方领取反对(微信、支付宝、支付宝当面付、微信扫码、微信小程序)第三方云存储反对,反对云贮存分片上传(阿里云、百度云、华为云、腾讯云、FTP、七牛云、UCloud、又拍云)第三方短信反对(阿里云、腾讯云、华为云、百度云、253云通信、聚合、七牛云、融云、赛邮、UCloud、云片、网易云)V7.0.0版本更新2023年03月28日ModStartBlog公布v7.0.0版本,减少了以下28个个性: [新性能] 组件验证规定反对数组增加,主动过滤空值[新性能] 折线图表数据库应用优化[新性能] ModStartRequestHandled事件[新性能] 网站默认宽度调整为60rem[新性能] AdminUser表单组件[新性能] 零碎款式随机色彩降级[新性能] 短信发送应用工作队列代替同步发送[新性能] 数据库字符集默认调整为utf8mb4[新性能] 后盾待审核链接在新标签页关上[新性能] GridFilter减少数字和文本范畴查问[新性能] 文件行将上传和文件上传实现事件[新性能] Select组件和Grid筛选新增selectSearch属性,选项可搜寻[新性能] 网站信息新增分割信息,反对邮箱电话等联系方式[新性能] 根本设置反对其余备案信息[新性能] Values组件新增viewMode,反对mini一行显示模式[新性能] 网页首页题目新减少副标题[新性能] data-header-sticky-disable个性可局部页面禁止头部滚动[新性能] UEditorPlus降级3.0.0[新性能] 装置向导减少zip和curl的PHP扩大检测[新性能] 集成ShareJS,反对博客一键分享[新性能] 数据库调整为严格模式(字段截取抛出异样)[系统优化] Decimal数据输出内容过滤和解决[系统优化] 搜索引擎爬虫疏导,减少局部链接ref=nofollow属性[系统优化] 富文本编辑器UEditor款式缓存问题[系统优化] 表单解决JS局部代码逻辑重构[Bug修复] 博客列表挪动端题目显示异样问题[Bug修复] 后盾治理角色减少异样问题[Bug修复] 博客留言回复显示异样问题模块市场一键装置零碎内置模块市场,有行业利用、插件、云存储、云短信等功能模块,后盾反对一键装置、启用、禁用、卸载,可疾速搭建属于本人的零碎利用。 零碎演示与文档码云仓库:https://gitee.com/modstart/ModStartBlogGithub仓库:https://github.com/modstart/ModStartBlog零碎演示:https://blog.demo.tecmz.com/下载试用:https://modstart.com/download开发者文档:https://modstart.com/doc模块市场:https://modstart.com/store

March 28, 2023 · 1 min · jiezi

关于php:我让chatGPT用PHP写一个MVC框架不仅写出来还能跑

本文摘要据说chatGPT很火,还会写代码,而且写进去的代码还真能跑起来!于是我尝试让chatGPT给我用PHP写一个简略的MVC框架进去。 没想到写进去的框架的确挺简略的,然而又没感觉哪里不对,于是我尝试把这个框架放到服务器试试能不能跑起来,最初还真的能够跑起来,为了让大家可能看到这个框架的演示,我间接爬一个热搜,而后便于展现数据。 当然了,这个框架只是通知你框架的根本构造,实际上一个PHP框架的设计是十分粗劣的,本文次要是学习框架的根本构造。框架目录框架是真的很简略,简略到一眼看完构造。 app/controllers/controller.php <?php // 加载模型和视图 require_once('app/models/model.php'); require_once('app/views/view.php'); class Controller { private $model; private $view; public function __construct() { // 实例化模型 $this->model = new Model(); // 实例化视图 $this->view = new View(); } public function handleRequest() { // 获取数据 $data = $this->model->getData(); // 将数据传递给视图 $this->view->render($data); } }?>app/models/model.php <?php class Model { public function getData() { // 获取百度热搜 $htmlcontent = file_get_contents('https://top.baidu.com/board?tab=realtime'); // 截取热搜列表 $hotList = substr($htmlcontent, strripos($htmlcontent, "hotList") + 19); // 返回列表 return substr($hotList, 0, strrpos($hotList, "moreAppUrl") - 11); } }?>app/views/view.php ...

March 23, 2023 · 3 min · jiezi

关于php:fastadmin和modstart对比

FastAdmin和ModStart都是基于PHP的开源疾速开发框架,它们都能够帮忙开发人员疾速构建高效、稳固的Web应用程序。以下是它们的一些不同点: 框架结构:FastAdmin采纳了简洁的MVC架构,而ModStart则是基于Laravel框架。这两种框架的构造和思路有所不同,开发者能够依据本人的需要和爱好进行抉择。 功能模块:FastAdmin提供了丰盛的插件和组件,包含菜单、权限、角色、用户、文件治理、数据备份等等,而ModStart则是反对模块化开发,能够依据需要灵便增加和删除模块。这两种框架的功能模块有所不同,开发者能够依据本人的需要进行抉择。 代码生成器:FastAdmin提供了弱小的代码生成器,能够依据数据库表构造主动生成增删改查的代码,而ModStart也提供了代码生成器,能够疾速生成控制器、模型、视图等代码。这两种框架的代码生成器都十分实用,能够极大地提高开发效率。 开源协定:FastAdmin采纳的是MIT开源协定,而ModStart采纳的是Apache2.0开源协定。这两种开源协定有所不同,开发者能够依据本人的需要进行抉择。 总的来说,FastAdmin和ModStart都是优良的PHP开源疾速开发框架,它们各自有着本人的劣势和特点,开发者能够依据本人的需要进行抉择。

March 22, 2023 · 1 min · jiezi

关于php:ModStartBlog-v690-博客赞赏功能置顶热门推荐

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场领有丰盛的性能利用,反对后盾一键疾速装置,让开发者能快的实现业务性能开发。 零碎齐全开源,基于 Apache 2.0 开源协定。 性能个性丰盛的模块市场,后盾一键疾速装置会员模块通用且残缺,反对残缺的API调用大文件分片上传,进度条显示,已上传文件治理弱小的模块扩大性能,所有模块能够无缝集成,反对在线装置、卸载模块欠缺的开发助手,实现模块、主题的的一键创立欠缺的后盾权限治理,反对基于RBAC的权限管理系统后盾治理反对应用手机、平板、PC,无论何时何地都可方便管理第三方登录(QQ、微信、微博、支付宝、微信小程序)第三方领取反对(微信、支付宝、支付宝当面付、微信扫码、微信小程序)第三方云存储反对,反对云贮存分片上传(阿里云、百度云、华为云、腾讯云、FTP、七牛云、UCloud、又拍云)第三方短信反对(阿里云、腾讯云、华为云、百度云、253云通信、聚合、七牛云、融云、赛邮、UCloud、云片、网易云)V6.9.0版本更新2023年03月15日ModStartBlog公布v6.9.0版本,减少了以下13个个性: [新性能] 后盾主题浅色模式,.env 配置 ADMIN_THEME=light 切换浅色[新性能] 置顶、热门、举荐博客获取函数[新性能] 字段Rules数据类型从字符串调整为数组[新性能] 友情链接图片链接懒加载[新性能] 动态资源反对自带参数并保留文件Hash[新性能] MS.util.getNextMaxZIndex() 办法,动静获取下一个最大的z-index[新性能] 后盾页面关上反对配置是否显示标签页,.env 配置 ADMIN\_TABS\_ENABLE=false 敞开[新性能] 适配通用打赏性能,需装置 Reward 和 PayCenter[系统优化] 后盾革除缓存操作菜单简化操作,间接点击操作[系统优化] 顶部导航二级菜单减少图标反对[系统优化] 图片懒加载组件加载中图片更新[系统优化] Checkbox 组件序列化保留时对数字字符串默认转数字[系统优化] modstart_config 函数依据默认值类型默认主动反序列化JSON模块市场一键装置零碎内置模块市场,有行业利用、插件、云存储、云短信等功能模块,后盾反对一键装置、启用、禁用、卸载,可疾速搭建属于本人的零碎利用。 零碎演示与文档码云仓库:https://gitee.com/modstart/ModStartBlogGithub仓库:https://github.com/modstart/ModStartBlog零碎演示:https://blog.demo.tecmz.com/下载试用:https://modstart.com/download开发者文档:https://modstart.com/doc模块市场:https://modstart.com/store

March 15, 2023 · 1 min · jiezi

关于php:Chapter-14PHPFPM模式下我为框架增加了伪异步defer功能

欢送来到「我是真的狗杂谈世界」,关注不迷路 略读CGI+同步阻塞计划异步工作计划有点重,但又不想放弃傻瓜计划的长处;思考后将问题转换成: 提前返回响应后持续同步执行非重要工作;程序编写逻辑,提早执行局部非重要工作;解决这两个问题: 应用fastcgi_finish_request;借鉴golang defer;实现、成果和注意事项。背景团队技术背景目前咱们团队小组的技术状况如下: 以PHP作为开发语言开发Web类接口服务;采纳传统的Nginx+FPM模式运行服务;我创立并保护了新的开发框架;框架基于Slim v3.7,是小组之前依赖的,因思考过渡老本临时没扭转。场景问题近期在一些我的项目中发现常常遇到业务接口有以下特点: 有主:一个接口中局部逻辑(比方下方栗子中的1/3/5,后续简称主逻辑)是须要保障解决胜利并将后果反馈给调用方的;有支:另一部分逻辑(比方下方栗子中的2/4,后续简称支逻辑)则可容忍(临时)失败,甚至调用方并不关怀后果或说感知不显著;简略:大部分支逻辑比较简单,简略判断加上打文件日志、写条MySQL日志记录、发个HTTP申请等类;混合:主支逻辑在代码编写程序上往往是穿插混同而非若明若暗的。对于代码编写程序当然也能够特意把两局部离开,但这样并不合乎惯例开发同学实现的思路脉络,也不利于代码浏览了解和变量管制举一个接口栗子(瞎编的): 【主】一个重要的扣除逻辑,胜利能力持续;【支】扣除失败则发送一条info级别的邮件音讯,胜利与否都行;【主】一个重要的发货逻辑,胜利能力持续;【支】发货失败则发送一条error级别的邮件音讯,发送失败则记录本地文件日志;【主】组装后果并返回。同步模型先闭上眼睛一把梭程序编写代码实现,同步执行流程如下(事实上一开始我真是这么一把梭实现的): 异步模型遗憾的是有一天QA同学说你这个接口响应耗时太高了,压测时的体现更显著;更遗憾的是线上竟然还产生了2/4步骤DNS解析超时问题(前面发现是整个libcurl问题,不仅DNS解析,当然这是另一个话题了)这种场景下传统常见的计划兴许咱们不会生疏——过程级别的异步工作计划! Laravel/Lumen也是采纳这个计划的咱们组内另一位同学也为咱们的开发框架反对了这种异步工作的计划,其实是能够间接采纳的,只是它并不是本文的配角~执行流程如下: 多线程、协程+异步IO调度模型当然还有很多其余计划,不过也都比较复杂,且不利于放弃CGI+同步阻塞这套模型给团队同学的门槛益处和对服务的稳固平安,所以也不是本文的配角~ 特地是团队往年的主基调是品质、与效率,更加不想这个时候搞事件啦~思路上述异步模型其实是比拟成熟的计划抉择,只是它也存在着一些问题/弊病: 队列服务依赖:须要额定依赖一个队列服务,这也肯定水平上依赖了其可用性、同时自身也是一条网络IO开销;生产过程治理:须要独立的生产过程生产队列中的异步工作,独立生产过程也须要额定思考其运行状态保护和管制;解决链路增长:这个就不多说了,尽管这对于异步工作而言倒不算什么。剖析转换那有没有(应用老本和运行效率上)轻量级又保留现有劣势的计划呢? 思考剖析: 既然依然是采纳同步阻塞计划,也就是说不去做串行改并行的优化,仍旧是原来的执行工夫开销;同时想要轻量级,那须要将队列服务、生产过程都干掉,只能仍旧由以后解决该申请的FPM解决过程来执行全副逻辑;那能不能搞一个伪"异步"呢?让调用方在感知上提前结束,但该FPM解决过程仍旧会实现剩下逻辑。问题转换: PHP在FPM运行模式下能不能让申请响应提前返回给调用方?能不能程序编写代码逻辑,但执行时将指定局部的代码逻辑块提早到某个指定逻辑之后?(这是因为我须要思考封装成不便大家应用的框架能力)问题1解法:fastcgi_finish_request很容易想到fastcgi_finish_request,FPM正好又是FastCGI模式,能够应用 当然应用它是要留神一些点的,具体我放在本文最初了~另外印象里Laravel/Lumen有个终结者中间件,如同也是实现了相似性能(先返回响应给调用方,再继续执行终结者中间件逻辑),于是打算去翻翻源码回顾验证一下其实现原理: 对这个有印象是因当年有共事应用Laravel时遇到通过框架提供的session批改和保留办法然而并没有失效的问题帮助排查时大略看到过框架对于session的操作都是在过程内存级别的,只在终结者中间件中才会将session残缺笼罩到存储驱动中问题出在共事一通逻辑解决后没有采纳框架提供的response办法返回响应,间接echo而后exit,以至于没法走到框架后续返回响应和执行终结者中间件。。。所以历历在目~~通过Laravel源码验证也是通过fastcgi_finish_request来实现此性能的: 浏览了解为:kernel->handle后失去response,response->send中执行了header和body的设置输入后,执行了fastcgi_finish_request当然它对其余运行模式也做了兼容,然而我临时用不到$app = require_once __DIR__.'/../bootstrap/app.php';$kernel = $app->make(Kernel::class);$response = $kernel->handle( $request = Request::capture())->send();$kernel->terminate($request, $response); /** * Sends HTTP headers. * * @return $this */ public function sendHeaders(): static { // headers have already been sent by the developer if (headers_sent()) { return $this; } // headers foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) { $replace = 0 === strcasecmp($name, 'Content-Type'); foreach ($values as $value) { header($name.': '.$value, $replace, $this->statusCode); } } // cookies foreach ($this->headers->getCookies() as $cookie) { header('Set-Cookie: '.$cookie, false, $this->statusCode); } // status header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); return $this; } /** * Sends content for the current web response. * * @return $this */ public function sendContent(): static { echo $this->content; return $this; } /** * Sends HTTP headers and content. * * @return $this */ public function send(): static { $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } elseif (\function_exists('litespeed_finish_request')) { litespeed_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); } return $this; }问题2解法:参考golang defer前文说到我要思考框架封装不便组内同学应用的,所以不能裸写而要思考大家应用时的便捷性 ...

March 13, 2023 · 2 min · jiezi

关于php:自动写代码

大家好,我是良许。 这几天,GitHub 上有个很火的插件在抖音刷屏了——Copilot。 这个神器有啥用呢?简略来讲,它就是一款由人工智能打造的编程辅助工具。 咱们来看看它有啥用。 首先就是代码补全性能,你只有给出函数名和参数,Copilot 就会主动帮你补全代码。 第二个性能,就是它能够依据正文来写代码。也就是说,你只有把正文写好,它就会主动帮你生成对应的代码,吓人吧~ 除此之外,它还能够主动生成重复性代码、主动生成测试代码,同时也能够生成多套代码计划供你抉择。 目前反对多种开发语言,包含 Python, JavaScript, TypeScript, Ruby, Java, Go等等,反对VS Code,Neovim,JetBrains 等 IDE。 目前还没有齐全凋谢,处于邀请制状态。前几天我看到了这个插件并申请了,然而目前还没通过,所以想看演示的话大家能够上 B 站,很多大佬做了测评。 看到这个插件这么弱小的性能,很多程序员曾经吓傻了,这么弱小,AI 都曾经能够帮你写代码了,程序员是不是要下岗了? 但在我看来,这个插件目前还只是个高级的玩具而已。 1. 只是帮你百度一下这个插件的实质是利用人工智能,通过开源社区里的大量代码进行训练,从而实现主动写代码的成果。 所以,对于通用性的代码,它的生成准确性还是比拟高的。 比方,咱们在写两个日期之间的天数,你写来写去,无非就是这样写(以 Python 为例): def days(str1,str2): date1=datetime.datetime.strptime(str1[0:10],"%Y-%m-%d") date2=datetime.datetime.strptime(str2[0:10],"%Y-%m-%d") num=(date1-date2).days return num这种代码说白了其实没有多少创造性,写纯熟了可能基本都不须要通过大脑,都造成肌肉记忆了,无非就是函数名、变量不太一样,其余的简直都一样。 相似的,咱们还有一些算法(比方冒泡排序)、工具(比方哈希校验),其实也都是重复性十分高的代码,它也能够帮你实现得很好。 重复性的工作,都有可能被代替的。 在这种状况下,Copilot 的作用就相当于帮你百度一下,而后再帮你 CV 一下。如果没有这个插件,咱们一样也能做,只是花点工夫而已。 2. 业务代码品质不高Copilot 本人也抵赖,他们会尝试理解程序员的用意,并「尽可能」生成最好的代码,但生成的代码并不总是无效,有时甚至还没有意义。 毕竟训练集来自公共代码,参差不齐,甚至齐全没有意义。 这点在 B 站大神的测试下也失去了印证,有时候的确也生成了一堆不知所云的代码。 毕竟,咱们的业务需要始终都是复杂多变的,有时咱们本人都不太能实现一些性能需要,还指望机器帮你写?别太空想了! 而且,它主动补全业务代码的前提是,你曾经须要有肯定的代码量供它参考,它才能够去猜想你接下来筹备写什么。 也就是说,你还是须要写一些代码,有这些代码了它才会写得更精确一些。而且你提供的代码量越少,它的举荐就越不精确。 也有人说了,它会依据正文写代码啊。然而,实际上,正文你都写好了,你本人其实也曾经差不多把代码都写好了。 再说了,程序员都晓得,读他人的代码是一件很苦楚的事件,Copilot 帮你写好了代码,你敢间接就用吗?你浏览并了解它的代码的工夫,兴许本人早就写完了。 所以,你说它会齐全帮你写代码吗?必定不行,至多目前不会。就算能帮你写局部业务代码,也不肯定写得好。 3. 有肯定的平安问题Copilot 它的原理就是利用大量的代码进行训练,样本越多天然就越精确。 那么问题来了,他们本人声称这些样本是来自开源的社区,但你应用了它们的插件,你敢保障你写的代码不会成为他们的样本? 而且,如果它们生成的代码不合你的要求,你手动批改了,它更加了解了你的用意,这对 Copilot 的训练几乎是神助啊,他们难道真的不会思考利用一下使用者的收费劳动力? 但凡应用第三方插件,而且还是不开源的,谁都无奈保障你的数据是否真的没有被透露进来。 本人练习的代码必定是无所谓了,然而如果波及到公司的商业秘密,那就可能会有法律问题了。 ...

March 11, 2023 · 1 min · jiezi

关于php:PHP验证码

什么是验证码❓验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动辨别计算机和人类的图灵测试)的缩写,是一种辨别用户是计算机还是人的公共全自动程序。能够避免:歹意破解明码、刷票、论坛灌水,无效避免某个黑客对某一个特定注册用户用特定程序暴力破解形式进行一直的登陆尝试,实际上用验证码是当初很多网站通行的形式,咱们利用比拟繁难的形式实现了这个性能。 前端代码<script src="https://cdn.kgcaptcha.com/captcha.js?appid=94dATYQa"></script><script>kg.captcha({ // 绑定元素,验证框显示区域 bind: "#captchaBox", // 验证胜利事务处理 success: function(e) { console.log(e); }, // 验证失败事务处理 failure: function(e) { console.log(e); }, // 点击刷新按钮时触发 refresh: function(e) { console.log(e); }});</script><div id="captchaBox">载入中 ...</div>PHP代码<?phpinclude "public/KgCaptchaSDK.php";// 填写你的 AppId,在利用治理中获取$appId = "94dATYQa";// 填写你的 AppSecret,在利用治理中获取$appSecret = "6u2BhEgORjQPkO1c69mpg2z5lLTLHf6a";$request = new kgCaptcha($appId, $appSecret);// 填写应用服务域名,在利用治理中获取$request->appCdn = "https://cdn.kgcaptcha.com";// 前端验证胜利后颁发的 token,有效期为两分钟$request->token = $_POST["kgCaptchaToken"];// 当安全策略中的防控等级为3时必须填写$request->userId = "kgCaptchaDemo";// 申请超时工夫,秒$request->connectTimeout = 10;$requestResult = $request->sendRequest();if ($requestResult->code === 0) { // 验签胜利逻辑解决 echo "验证通过";} else { // 验签失败逻辑解决 echo "验证失败,错误代码:{$requestResult->code}, 错误信息:{$requestResult->msg}";}运行后果 ...

March 10, 2023 · 1 min · jiezi

关于php:ThinkSwoole-全面协程化你的-ThinkPHP-应用

ThinkPHP 是一款经典的国产开源 PHP 开发框架。ThinkPHP 诞生于 2006 年,距今曾经有 17 年历史。ThinkPHP 在国内的利用十分宽泛,很多知名企业应用了 ThinkPHP 来构建 Web 我的项目。 晚期版本的 ThinkPHP 次要是运行在 PHP-FPM 模式下。为了解决长连贯、高并发、阻塞 IO 的问题,ThinkPHP 官网提供了 Think-Swoole 组件,底层全面适配了 Swoole 协程,使得 ThinkPHP 利用能够一键协程化。 GitHub 地址ThinkPHPThink-Swoole创立 ThinkPHP 我的项目composer create-project topthink/think tp应用 composer 命令能够疾速创立一个 ThinkPHP 新我的项目。已有我的项目可跳过此步骤。 引入 Think-Swoole 组件composer require topthink/think-swoole启动 HTTP 服务间接在命令行下启动 HTTP 服务端。 php think swoole启动实现后,默认会在 0.0.0.0:80 启动一个 HTTP Server,能够间接拜访以后的利用。相干配置参数能够在 config/swoole.php 外面配置(具体参考配置文件内容)。 若本机已装置了 Nginx,可能 80 已被占用,可批改 config/swoole.php 设置为其余的端口启动后通过 http://127.0.0.1:9580/ 拜访程序 热更新因为 Swoole 服务运行过程中 PHP 文件是常驻内存运行的,这样能够防止反复读取磁盘、反复解释编译,以便达到最高性能。所以更改业务代码后必须手动reload 或者 restart 能力失效。 ...

March 9, 2023 · 1 min · jiezi

关于php:️最好用的PHP时间日期类库

这是一个最不便的PHP工夫助手类所有办法都能够传入任意类型的工夫日期格局或者工夫戳作者将长期保护并不断完善使用率比拟高的助手函数通过Composer导入类库composer require zjkal/time-helper应用办法首先在类中援用TimeHelper助手类 use zjkal\TimeHelper;1. 获取须要的秒数个别用于设置缓存工夫,设置完结工夫等 //返回到明天早晨零点之前的秒数TimeHelper::secondEndToday();//返回N分钟的秒数(默认为1分钟)TimeHelper::secondMinute(5);//返回N小时的秒数(默认为1小时)TimeHelper::secondHour(2);//返回N天的秒数(默认为1天)TimeHelper::secondDay(10);//返回N周的秒数(默认为1周)TimeHelper::secondWeek(4);2. 返回敌对的日期格局,比方N秒前,N分钟前,N小时前等等个别用于社交类平台,评论,论坛等 //一共2个参数://第1个参数传入字符串类型的工夫或者工夫戳都能够,//第2个参数为语言(默认为中文,须要英文请传入en)TimeHelper::toFriendly('2022-3-2 10:15:33');//英文TimeHelper::toFriendly(1646186290, 'en');3. 判断工夫范畴//判断日期是否为明天TimeHelper::isToday('2020-4-10 23:01:11');//判断日期是否为本周TimeHelper::isThisWeek('2020-5-1');//判断日期是否为本月TimeHelper::isThisMonth(1586451741);//判断日期是否为往年TimeHelper::isThisYear('Apr 11, 2020');//判断指定工夫是星期几,不传默认为以后工夫. 返回值为1-7,1为星期一,7为星期日TimeHelper::getWeek('2022-11-27');//判断指定工夫是否为平时日(周一到周五)TimeHelper::isWeekday('2023-03-08');//判断指定工夫是否为周末(周六和周日)TimeHelper::isWeekend(1586451741);4. 计算两个工夫相差值如果只传入一个参数,则与以后工夫比拟 //计算两个日期相差天数TimeHelper::diffDays('2022-4-10 23:01:11','Apr 11, 2020');//计算两个日期相差周数TimeHelper::diffWeeks('2022-4-10 23:01:11');//计算两个日期相差月数TimeHelper::diffMonths(1586451741,'Apr 11, 2020');//计算两个日期相差年数TimeHelper::diffYears('2022-4-10 23:01:11','Apr 11, 2020');5. 返回N小时/天/星期/月/年前或者后的工夫戳只传入1个参数以以后工夫计算,传入第2个参数则以该工夫计算,传入第3个参数为true,则工夫取整 //返回指定工夫3分钟前0秒的工夫戳TimeHelper::beforeMinute(3,'2022-3-2 10:15:33',true);//返回以后工夫5分钟后的工夫戳TimeHelper::afterMinute(5);//返回指定工夫1小时前的工夫戳(请留神此用法为php8之后的用法)TimeHelper::beforeHour(datetime:'Apr 11, 2020');//返回2小时后的工夫戳TimeHelper::afterHour(2);//返回15天前0点的工夫戳TimeHelper::beforeDay(15,null,true);//返回15天后的工夫戳TimeHelper::afterDay(15);//返回指定工夫2星期前的工夫戳TimeHelper::beforeWeek(2,'2022-4-10 23:01:11');//返回指定工夫10星期后的工夫戳TimeHelper::afterWeek(10,1646360133);//返回指定工夫1个月前的工夫戳(请留神此用法为php8之后的用法)TimeHelper::beforeMonth(datetime:1646360133);//返回5个月后的工夫戳TimeHelper::afterMonth(5);//返回指定工夫3年前的工夫戳TimeHelper::beforeYear(3,'2022-7-11');//返回2年后的工夫戳TimeHelper::afterYear(2);6.获取以后秒级/毫秒级/微秒级/纳秒级的工夫戳,生成订单号或者与其余编程语言对接时可能会用到 //获取秒级的工夫戳,可用time()代替TimeHelper::getTimestamp();//获取毫秒级的工夫戳TimeHelper::getMilliTimestamp();//获取微秒级的工夫戳TimeHelper::getMicroTimestamp();//获取纳秒级的工夫戳TimeHelper::getNanoTimestamp();7.日期转换用于爬虫爬取网页或第三方程序对接时,工夫格局不对立的转换 //将任意格局的工夫转换为指定格局//第一个参数是工夫格局,与零碎函数date()的格局保持一致//第二个参数则是任意格局的工夫日期,不传则默认为以后工夫,可用零碎函数date()代替TimeHelper::format('Y-m-d H:i:s','May 3, 2022');//判断一个字符串是否为工夫戳,是返回true,否返回falseTimeHelper::isTimestamp(1646360133);//将任意工夫类型的字符串转为工夫戳TimeHelper::toTimestamp('Apr 11, 2020');8.平平年相干比原生办法应用起来更不便 //判断是否为平年,是返回true,否返回falseTimeHelper::isLeapYear('2020-3-2 10:15:33');//判断该日期的当年有多少天TimeHelper::daysInYear(1646360133);//判断该日期的当月有多少天TimeHelper::daysInMonth('Apr 11, 2020');9.国内节假日/工作日相干 专门针对国内的节假日进行判断,目前蕴含2020年-2023年的节假日数据,后续也会继续更新.为了便于保护,另起了一个类ChinaHoliday,同样能够传入任意类型的工夫格局或工夫戳 use zjkal\ChinaHoliday;//判断指定日期是否为国内的工作日ChinaHoliday::isWorkday('2023-01-23');//判断指定日期是否为国内的节假日ChinaHoliday::isHoliday(1646360133);特地阐明: 所有工夫的办法都能够传入任意格局的工夫或者工夫戳, 然而有一点请留神 m/d/y 或 d-m-y 格局的日期,如果分隔符是斜线(/),则应用美洲的 m/d/y 格局。如果分隔符是横杠(-)或者点(.),则应用欧洲的 d-m-y 格局。为了防止潜在的谬误,您应该尽可能应用 YYYY-MM-DD 格局或其余格局.

March 9, 2023 · 1 min · jiezi

关于php:三个步骤快速体验快速开发后台管理系统

SimplestAdmin是一个能够生成代码的通用后盾管理系统;零碎基于Thinkphp6和Vue2构建的,前后端拆散的开发框架。为了更加直观的理解SimplestAdmin的生成代码的个性,咱们一起来通过以下几步来体验疾速业务开发。第一步:创立菜单创立菜单能够手工创立,也能够通过数据库表一键创立。第二步:配置字段选中方才创立的菜单,增加或者批改字段;如果是通过数据库表生成菜单,只须要批改字段就能够了;如果是本人创立的菜单,就须要本人将每个字段进行配置好了;第三步:生成代码选中菜单,点击生成代码,或者在字段治理界面中,点击生成代码按钮,都能够生成代码;代码生成实现后,零碎会主动刷新,如果没有主动刷新,能够本人手工刷新页面;通过下面三个步骤,咱们就能够简略的将一个数据库表,导入到零碎,生成了针对这个表的,CRUD操作代码,同时,零碎为用户的性能扩大提供了不便的二次开发性能;

March 6, 2023 · 1 min · jiezi

关于php:Chapter-13一次性能测试和优化过程

欢送来到「我是真的狗杂谈世界」,关注不迷路 背景最近一个我的项目上线前QA对压测后果不是很称心(并且示意之前的我的项目压测后果也都不现实),于是我在上线后(上线前是必定来不及了)开始了一轮性能测试、排查和优化,记录一下整个过程。 思考过程根底信息QA同学提供的压测环境与论断: 施压侧: 并发度100~300;继续运行120~300秒;被压侧: 单正本资源1C2G;正本数1~4;后果体现: CPU:单正本100%(但有时呈现某正本100%,其余正本60~80%左右);内存:单正本15~20%(但有时呈现某正本高至50%)QPS:单正本100~120;响应耗时:Avg(487~596ms);50TH(19~27ms);90TH(1867~3099ms);95TH(2409~3902ms);99TH(4460~7002ms);MAX(8589~13298ms);外围问题对于性能、压测等相干概念可参考「Foundation 11.性能是什么」 QPS体现较低;CPU占用过高,很快被打满;响应耗时存在大量(90TH以上)过高;以后这三者是有关联的,以后看来单个申请对于CPU资源需要较高,导致在小并发下CPU资源已占满;随着并发度增高,零碎通过频繁调度来调配CPU资源,调度磨损减少(用于解决业务逻辑的比例升高);因而CPU占用过高也会肯定水平连累响应耗时、QPS体现。狐疑方向硬件性能:之前有发现压测环境后果较比生产环境低,且两侧环境为独立的集群,因而狐疑两侧集群节点自身硬件性能差别较大;执行过程:因为是PHP FPM模式下运行的服务,FastCGI处理过程、PHP代码解释执行过程等都可能造成CPU资源的占用、耗时;IO阻塞度:业务逻辑中简单(屡次)同步阻塞申请(三方HTTP接口、MySQL、Redis)较比简略(少次)会造成申请响应须要工夫变长,升高QPS;短连资源:整个服务对三方HTTP接口、MySQL、Redis都采纳的短连,连贯仅在申请周期内复用,申请完结后开释,对于连贯的频繁创立销毁也会占用CPU资源、耗时;框架磨损:新版开发框架投入使用后没有太关注性能磨损问题,实践上这块肯定会存在磨损,只是水平和要害磨损点问题。排查办法(尽量)管制其余变量放弃雷同的状况下,针对要排查的环节、对象进行多组压测比照,记录并剖析得出结论。 实际与论断定位基准因为QA提供的论断看起来存在很多不稳定性,因而我决定在压测环境上基于该我的项目先进行一波测试,作为后续比照、剖析、钻研等排查工作的基准。 第一波测试资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)2C2GPHP-FPM7.4+OPcache新框架+健康检查2980------802C2GPHP-FPM7.4+OPcache新框架+健康检查2490------90从第一波测试记录数据(因为QPS体现已产生过于不稳固景象了,其余数据就临时没有关注和记录)很容易发现,除了我管制住的变量外,肯定还存在一个我没发现的变量影响了压测体现;而这两次测试存在的变量只有两个: 测试工夫:因为压测服务做了串行管制(同一个工夫点只能最多一个压测工作执行),上述两波测试是在不同的工夫点进行的。被测正本:因为压测环境须要很多配置老本,没有别离配置两套压测环境,上述两波测试之间进行了利用正本重建(从新构建了服务)。为了进一步排查上述两个变量,我每次屡次重建正本,每次重建正本后进行相隔一段时间的多组压测,景象如下: 雷同正本(不重建正本)中的体现是稳固的(如果是500高低则始终是500高低,如果是1000高低则始终是1000高低),甚至到第二天仍旧是稳固的;不同正本(重建正本)时的体现会产生稳定(比方500变成1000,1000变500),但也不是每次重建都必然产生稳定切换。基于上述景象,根本能够排除工夫差别,而狐疑正本调度到的节点的硬件或其余基础设施的差别导致;我将此问题报告给服务器基础设施团队后帮助排查定位,最终发现压测环境4个节点其中1个节点的CPU基频在1.5~3.5GHz之间动静稳定(失常4个节点都应该是3.5GHz); 在他们解决问题的同时,我管他们要了一个独立节点来持续我的测试,前面所有资源将依照下述形容: 新节点:代表管他们要来的独立节点,性能自身很差,2C体现还不如原节点1C好;原节点:代表除CPU异样节点之外的压测环境节点(实践上与生产环境节点性能仍有差距);X号正本:代表正本重建,雷同的X代表正本未通过重建;原节点有意义(因为压测环境节点仍旧会有一些轻微差别),新节点无意义(就只有一个节点);健康检查:代表业务上一个接口间接返回,没有业务逻辑和三方申请;简略逻辑:代表业务上一个接口蕴含一个MySQL查问;简略逻辑*x:代表业务上一个接口蕴含x个雷同的MySQL查问;简单逻辑:代表该我的项目业务实在逻辑(一个接口蕴含2~8次MySQL、Redis、HTTP接口申请等);持续测试资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)2C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查11625.63666820311C2G;原节点;1号正本PHP-FPM7.4+OPcache新框架+健康检查14961.632222217801C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+健康检查14841.672223138732C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查55268.6667404716861001C2G;原节点;1号正本PHP-FPM7.4+OPcache新框架+健康检查56606.4322727723001001C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+健康检查56246.92373789001002C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查2046241.74793939518581001C2G;原节点;1号正本PHP-FPM7.4+OPcache新框架+健康检查2053036.32396969724021001C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+健康检查2050938.11396979817151002C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查50371129.510220020329820231001C2G;原节点;1号正本PHP-FPM7.4+OPcache新框架+健康检查50472104.1210019519720230561001C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+健康检查50446110.3100197199297182794新节点在并发1~5之间达到CPU100%,QPS最高体现也在这之间达到(实践上会略高于526),体现的确很拉垮。。。原节点在并发1~5之间达到CPU100%,QPS最高体现也在这之间达到(实践上会略高于660/624)。CPU未打满时,随着并发度提高,QPS、CPU占用率也随之增长,响应耗时保持稳定;CPU打满后,随着并发度提高,QPS先稳固后逐渐升高,响应耗时逐渐增大。PHP8&Jit资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)2C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查18311.1311171819158302C2G;新节点;0号正本PHP-FPM8.0+OPcache新框架+健康检查18411.041117181988302C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查18211.2111718191674382C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查528715.671342485716821002C2G;新节点;0号正本PHP-FPM8.0+OPcache新框架+健康检查529015.66124248581711002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查528815.871242485917301002C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查2027270.679410310719217401002C2G;新节点;0号正本PHP-FPM8.0+OPcache新框架+健康检查2027370.59410310719320811002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查2027271.349410310719218071002C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查50239200.0420030239849719981002C2G;新节点;0号正本PHP-FPM8.0+OPcache新框架+健康检查50251195.9119930239548921051002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查50249197.1820030239649221831002C2G;新节点;0号正本PHP-FPM7.4+OPcache新框架+健康检查100216454.79403797901120026641002C2G;新节点;0号正本PHP-FPM8.0+OPcache新框架+健康检查100225438.31404746896119325421002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查100222444.76407744.989210992443100PHP8.0较比7.4在业务代码不动的状况下(基于7.4及之前语法)没有间接的性能晋升;CPU耗费大头不在opcache转machine code环节(想想也是);PHP8.0+Jit目前无奈带来并发和性能体现的晋升。IO梗塞次数资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)2C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查11562.716668198302C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑1979.721010111327352C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑*517512.58121314161607272C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简单逻辑18211.2111718191674382C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查55089.096841481653992C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑529715.681044516020461002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑*5528216.51133443531809982C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简单逻辑528815.871242485917301002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查2045142.92893949618431002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑2027570.919410110419117401002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑*52026972.799210110518818091002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简单逻辑2027271.349410310719218071002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查50386126.5210319920229619891002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑50247197.519929930340420791002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑*550246200.2619929830140020671002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简单逻辑50249197.1820030239649221831002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+健康检查100357273.75292399473.6551020891002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑100233424.7240260269789723891002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简略逻辑*5100226438.53403604699852.9824171002C2G;新节点;0号正本PHP-FPM8.0+OPcache+JIt新框架+简单逻辑100222444.76407744.989210992443100逻辑中不同水平的IO会导致单次申请的响应耗时减少,但对CPU的占用率影响较小;申请响应耗时高阻塞IO较比低阻塞IO广泛减少;并发度未将CPU打满时,高阻塞IO较比低阻塞IO的QPS体现要低;当并发度晋升将CPU打满后,高阻塞IO较比低阻塞IO的QPS体现简直统一PDO长连贯资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)1C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连11396.56566152112401C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连12074.2334491337451C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连22417.335612331077831C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连23564.62346221060911C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连527416.37566748617221001C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连542710.3339677312931001C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连2026573.19931031963021778881C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连2039349.3112971001031742951C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连50253195.05198300398599.791914881C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连50345142.961022002023002136921C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+短连100241401.153976017051203.12644991C2G;原节点;2号正本PHP-FPM7.4+OPcache新框架+简略逻辑+长连100325303.062994965017012205100网络连接资源复用较比不复用在以后业务特点下能带来: 晋升35~55%QPS;升高25~37%响应耗时。框架截断资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)1C2G;原节点;3号正本PHP-FPM7.4+OPcache入口文件间接返回2065052.721126817741001C2G;原节点;3号正本PHP-FPM7.4+OPcache入口文件解析完申请2059562.721116918321001C2G;原节点;3号正本PHP-FPM7.4+OPcache引入主动加载之后2043413.611127721081001C2G;原节点;3号正本PHP-FPM7.4+OPcache加载我的项目配置之后2022788.1612868911011001C2G;原节点;3号正本PHP-FPM7.4+OPcache加载底层配置之后2084923.04294949518671001C2G;原节点;3号正本PHP-FPM7.4+OPcache注册全副申请接口之后2063030.97295969718991001C2G;原节点;3号正本PHP-FPM7.4+OPcache健康检查残缺解决2051637.623969798187877磨损次要产生环节: 引入主动加载(composer autoload);加载我的项目配置;加载底层配置。优化方向短连改长连将申请MySQL、Redis的连贯模式改成长连贯,将连贯周期由申请级别延长至FPM过程级别,以此缩小连贯创立销毁操作升高CPU占用、缩小响应耗时晋升性能整体体现。 但思考到: MySQL、Redis连接数将简直与FPM开启的Work过程数量统一;多我的项目、多组共用MySQL、Redis;短连模式下,QPS过高反而会带来本地端口用尽的问题(具体见本文最初)。决定临时不采纳此计划。 缓存我的项目与底层配置将原先每次申请都读取多个ini配置文件并解析构造革新成初始化时读取并解析而后回写为php数组文件: 防止config map的性能影响(原先这些ini配置都是通过config map挂载进我的项目);节俭ini配置解析老本(php文件间接借用php自身opcache、jit优化就足够)。我将这个优化做到了新框架的后续版本中,并且压测验证: 资源状况程序状况接口复杂度并发度QPSAVG50TH90TH95TH99THMAXCPU(峰值)1C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置14801.62222220771C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置17051.0511221775791C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置26192.552223525261001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置210831.541122622751001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置56596.822727724091001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置510604.361227223351001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置2053037.09396979823981001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置2097020.18293949526011001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置50469105.58100196198231.9927991001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置5094652.2290989910329011001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+不缓存配置100415239.4320239440059830001001C2G;原节点;4号正本PHP-FPM7.4+OPcache新框架+健康检查+缓存配置100920107.891001951992892609100缓存配置较比不缓存对没有连贯和阻塞IO的逻辑能带来: 晋升46~121%QPS;升高34~54%响应耗时;并发度越高,成果越显著(在可接受的并发度范畴内)。我也对带上不同水平业务逻辑的场景进行了测试; 简略逻辑下缓存配置较比不缓存能带来: 晋升9~14%QPS;升高9~12%响应耗时;简单逻辑下缓存配置较比不缓存能带来: 晋升4~17%QPS;升高5~14%响应耗时;换运行模式如果后面两者都不可用或者不能满足需要的时候,能够思考更换以后这种FPM的运行模式,改用Cli+异步模型: 天生能够应用MySQL、Redis、HTTP等连接池技术,又不须要当心连接数过多的问题;进一步缩小代码解释执行过程的耗费,Opcache和Jit技术能够进一步发挥作用;异步搭配yield/Fiber,或是swoole封装的异步库充分利用CPU,晋升单接口响应效率。但这样无疑是存在较高的老本和危险的: 现有的框架、库包都是基于同步阻塞封装的,异步可能带来并发争竞隐患;FPM同步阻塞模型就义肯定性能带来的是开发效率和门槛的劣势,如果换成异步模型,对开发人员和业务无疑会带来更高的门槛和危险;相似swoole+异步协程其实简直都相当于换了大半个语言了。整体论断目前1C2G在压测环境个别的逻辑都能够达到250甚至更高了,临时满足咱们的业务需要(更高的性能体现能够通过扩大副原本实现);还有很多的优化计划和空间,但思考到团队目前的状况和计划的老本与危险,临时不发展这类计划。短连单正本QPS过高的问题以后的零碎设置为: 本地可用客户端端口范畴:32768~60999time wait状态疾速回收和重用:没有权限,但依据景象察看应该是60s当我调大CPU资源为2C压测一个简略逻辑(每个申请会创立一个MySQL连贯,申请完结就被动close连贯)时,QPS达到500高低,但最初几秒会呈现本地端口用尽的问题。

March 6, 2023 · 1 min · jiezi

关于php:反射

什么是反射反射是操纵面向对象范型中元模型的 API,可用于构建简单,可扩大的利用。 反射的作用反射次要目标就是在运行时剖析类或者对象的状态,导出或提取出对于类、办法、属性、参数等的详细信息,包含正文。 反射的利用反射在日常的 Web 开发中其实用的不多,更多的是在偏差底层一些的代码中,比如说框架的底层中依赖注入、对象池、动静代理、主动获取插件列表、主动生成文档以及一些设计模式等等,都会大量使用到反射技术。 罕用的反射类因为在日常开发中用的不是很多,所以目前就只学一些比拟罕用的反射类,等当前再深刻学习其余的内容。 比拟罕用的反射类: ReflectionClass:报告了一个类的无关信息;ReflectionFunction:报告无关办法的信息;ReflectionMethod:报告无关函数的信息;ReflectionParameter:检索函数或办法参数的相干信息。例子<?phpclass Student{ public $name; public $year; public function __construct($name, $year) { $this->name = $name; $this->year = $year; } public function setBase(Printer $printer, $name, $year) { $this->name = $name; $this->year = $year; } public function getValue() { return $this->name; }}class Printer{}测试 ReflectionClass 类: $reflClass = new ReflectionClass('Student');echo get_calss($reflClass);$s = $reflClass->newInstanceArgs(['程心', 18]);echo $s->getValue();输入如下: [Running] php "d:\phpstudy_pro\WWW\Reflection\Foo.php"ReflectionClass程心[Done] exited with code=0 in 1.198 seconds这里须要留神的是:newInstanceArgs 办法模仿手动实例化对象,然而该办法的参数是一个数组。 ...

March 5, 2023 · 2 min · jiezi

关于php:PHP中出现Cannot-modify-header-information-headers-already-sent

"Cannot modify header information - headers already sent" 是一个常见的 PHP 谬误,它通常是因为在输入 HTTP 头信息之前输入了其余内容(例如空格、换行、HTML 代码等)导致的。HTTP 头信息包含了响应的状态码、响应的 MIME 类型、cookie 等等,它必须在任何响应内容之前输入。 解决这个谬误的办法是要确保在输入 HTTP 头信息之前没有输入任何内容。以下是几种可能导致这个谬误的状况以及相应的解决办法: (1) 在 PHP 文件中,确保没有在 <?php 和 ?> 标签之外输入任何内容。如果必须输入其余内容(例如 HTML 代码),能够将 PHP 代码和 HTML 代码拆散成两个文件。
(2)如果应用了 header 函数设置 HTTP 头信息,要确保在 header 函数之前没有输入任何内容。一种解决办法是将 header 函数放在 PHP 文件的结尾,或者在调用 header 函数之前应用 ob_start 函数开启输入缓冲区。 (3)如果应用了 session\_start 函数启动会话,要确保在调用 session\_start 函数之前没有输入任何内容。一种解决办法是将 session\_start 函数放在 PHP 文件的结尾,或者在调用 session\_start 函数之前应用 ob_start 函数开启输入缓冲区。
(4)查看是否在蕴含其余文件时产生了这个谬误。如果在被蕴含的文件中输入了内容,也会导致这个谬误。
如何检测 在 PHP 中,能够应用 headers\_sent 函数检测 HTTP 头信息是否曾经发送。headers\_sent 函数有两个参数,第一个参数是援用传递的变量,用于返回发送头信息的文件名和行号;第二个参数用于指定在哪个文件中开始检测。如果不指定第二个参数,则默认从以后脚本的起始地位开始检测。 ...

March 3, 2023 · 1 min · jiezi

关于php:用xdeubg追踪PHP代码执行逻辑的具体方法

1. 装置 xdebughttps://xdebug.org/docs/install不同PHP版本须要装置对应的xdebug。windows的装置形式很简略,间接将xdebug.dll文件拷贝到你的php扩大目录下,而后配置php.ini 2. 配置xdebug的配置批改php.ini文件,在文件底部增加上面代码,曾经有得批改即可。 [Xdebug];你的php ext门路及xdebug文件zend_extension=/php7.4.3nts/ext/php_xdebug.dllxdeubg.default_enable=0;%t:工夫戳 %R:$_SERVER['REQUEST_URI'], %p:pid,更多参数可见:;https://xdebug.org/docs/trace#trace_output_namexdebug.trace_output_name=trace.%t.%R.%p;以下都是trace手机的内容和格局配置,具体区别可见官网文档xdebug.show_mem_delta=1xdebug.collect_params=4xdebug.collect_return=1xdebug.trace_format=1;是否自动记录trace日志,我个别默认否xdebug.auto_trace=0xdebug.trace_output_dir=自定义本人的trace日志目录xdebug.profiler_enable=0xdebug.profiler_enable_trigger=13. 在须要追踪的中央搁置本人的追踪开始办法和追踪完结办法xdebug_start_trace();//本人的代码xdebug_stop_trace();4. 下载浏览软件浏览追踪记录git仓库里的好几个软件都试过了,只有这个是最好用,成果最好。Xdebug Trace View https://github.com/kuun/xdebug-trace-viewer

March 2, 2023 · 1 min · jiezi

关于php:如何快速成为点灯大师

大家好,我是良许。 随着疫情的安稳,最近全国各地都陆续放开了。但很可怜,我的几个号主敌人不小心中招了,成了「阳过」。 良许在此揭示各位朋友,放开不等于躺平,该做的防护一个都不能少。尽管当初病毒没有刚暴发时的可怕,但也绝不是大号的感冒。依据身边的阳大侠反馈,一旦中招那是相当不难受的,千万不要以身试毒。 好了,回归正题。最近在录制一套 STM32 的课程,导致公众号的原创变少了,跟各位朋友说声道歉哈。从当初开始,我也在公众号连更一些 STM32 相干的内容,毕竟俺是嵌入式开发工程师哦(不是你们认为的运维哈)~ 学习嵌入式,最经典的第一个试验就是点亮一颗 LED 灯。别小看这个试验,从我辅导的学员来看,不少初学者连这么简略的案例都做不进去。 高手都是从菜鸟过去的,成长须要过程嘛。前两天我在抖音上发了个视频——挑战 20 秒点亮一颗 LED 灯,后果被喷成了筛子,真不知道那些喷我的人是何存心。 想要成这一名优良的点灯巨匠,第一步就是要学会 STM32 最根底的外设——GPIO。 1. 什么是GPIO?1.1 定义GPIO是通用输入输出(general-purpose input/output)端口的简称,简略来说就是STM32可管制的引脚STM32芯片的GPIO引脚与外部设备连接起来,从而实现与内部通信、管制以及数据采集的性能。 这是比拟正式的说法,是不是听得一愣一愣的? 再简略一点,就是芯片里拉出一堆引脚,这些引脚在咱们的管制下能够输入高电平/低电平,或者能够通过这些引脚向芯片输出一些信号,从而实现你们的一些目标。 1.2 命名规定以 STM32F103C8T6 这颗芯片为例,它的 GPIO 口一共有 37 个,如何对它们进行命名呢?总不能叫翠花、二蛋、狗剩吧? 咱有正规的命名规定,那就是组编号+引脚编号。 什么是组编号?那就是 GPIOA, GPIOB, GPIOC, GPIOD .. GPIOG,个别最多到 GPIOG 。 什么是引引脚编号?那就是 0,1,2,3 ... 15。 所以组合起来,就是: PA0, PA1, PA2 .. PA15 PB0, PB1, PB2 .. PB15 PC0, PC1, PC2 .. PC15 ... 但并不是所有的芯片都有 A 到 G 个组,就比方咱这颗芯片就只有 A~D 四组。而且也不是每组都有 15 个引脚,就比方 D 组就只有 0 和 1 两个引脚。 ...

March 1, 2023 · 2 min · jiezi

关于php:ModStartBlog-v680-博客置顶功能界面样式优化

零碎介绍ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场领有丰盛的性能利用,反对后盾一键疾速装置,让开发者能快的实现业务性能开发。 零碎齐全开源,基于 Apache 2.0 开源协定。 性能个性丰盛的模块市场,后盾一键疾速装置会员模块通用且残缺,反对残缺的API调用大文件分片上传,进度条显示,已上传文件治理弱小的模块扩大性能,所有模块能够无缝集成,反对在线装置、卸载模块欠缺的开发助手,实现模块、主题的的一键创立欠缺的后盾权限治理,反对基于RBAC的权限管理系统后盾治理反对应用手机、平板、PC,无论何时何地都可方便管理第三方登录(QQ、微信、微博、支付宝、微信小程序)第三方领取反对(微信、支付宝、支付宝当面付、微信扫码、微信小程序)第三方云存储反对,反对云贮存分片上传(阿里云、百度云、华为云、腾讯云、FTP、七牛云、UCloud、又拍云)第三方短信反对(阿里云、腾讯云、华为云、百度云、253云通信、聚合、七牛云、融云、赛邮、UCloud、云片、网易云)V6.8.0版本更新2023年02月28日ModStartBlog公布v6.8.0版本,减少了以下11个个性: [新性能] 后盾登录界面显示ICP备案编号[新性能] 博客内容置顶性能,后盾快捷编辑[新性能] UEditorPlus降级2.9.0[新性能] 内容审核Job新增疾速创立办法[新性能] 后盾二次平安验证形式工具类[新性能] 博客标签、分类、最近内容、信息等款式优化[新性能] 根底导入导出性能新增CSV、Excel多表格反对[新性能] 模块控制器中反对全门路视图门路解析[新性能] 全局异样解决疏忽局部非法申请日志记录[系统优化] 内容审核形象提供者应用形式简化[系统优化] 侧边栏菜单款式显示优化模块市场一键装置零碎内置模块市场,有行业利用、插件、云存储、云短信等功能模块,后盾反对一键装置、启用、禁用、卸载,可疾速搭建属于本人的零碎利用。 零碎演示与文档码云仓库:https://gitee.com/modstart/ModStartBlogGithub仓库:https://github.com/modstart/ModStartBlog零碎演示:https://blog.demo.tecmz.com/下载试用:https://modstart.com/download开发者文档:https://modstart.com/doc模块市场:https://modstart.com/store

February 28, 2023 · 1 min · jiezi

关于php:SwooleCli-v502-增加-opcachereadline-扩展强化-CliServer

最近咱们公布了 Swoole-Cli v5.0.2,这个版本减少了 opcache 扩大,使得 swoole-cli 内置的 php-fpm、cli-http-server 也能够用于生产环境了,而不仅仅是作为测试应用。 目前 swoole-cli 反对了 5 种环境的二进制包可供大家抉择: Linux x86-64Linux arm64 (aarch64)Windows CygWin x86-64macOS arm64 (aarch64、Apple M1/M2)macOS x86-64下载地址:https://www.swoole.com/downloadSwoole-Cli 是齐全动态编译的,不依赖任何动态链接库,可间接下载到任意操作系统上应用,而不须要额定其余第三方包。 在 Docker/K8s 环境下也尤为便当,可应用最简化的 alpine linux 根底镜像,而后装置 swoole-cli 构建镜像,最终的镜像只有 100M-200M 。 减少 Opcache 扩大swoole-cli --ri opcacheopcacheVersion => 8.1.12在 5.0.2 版本中 swoole-cli 集成了 php-fpm、cli-http-server ,但因为未反对 opcache 扩大,所以仅可用于测试环境。新的 5.0.2 版本中将 opcache 也动态编译到了 swoole-cli 二进制程序中。 这样内置的 php-fpm、cli-http-server 服务就能够应用 opcache 和 opcache.jit 进行减速,性能达到生产可用。 强化 Cli-Http-Server在 5.0.2 版本中咱们还强化了 PHP 的内置 Web 服务器,更新内容包含: ...

February 27, 2023 · 2 min · jiezi

关于php:计算机专业要考什么证书

大家好,我是良许。 从去年 12 月开始,我曾经在视频号、抖音等支流视频平台上间断更新视频到当初,并失去了不错的评估。 视频 100% 原创录制,绝非垃圾搬运号,每个视频都花了很多工夫精力用心制作,欢送大家关注哦~ 思考到有些小伙伴没有看过我的视频,在此我将视频脚本分享进去,心愿可能给小伙伴们提供一些帮忙。 计算机专业要考什么证书?计算机专业的同学,千万不要再去加入全国计算机等级考试了。 以下几个证书,含金量最高,拿下就是高薪。 第一,ACM,被称为程序员的奥林匹克,含金量高,难度大(这个其实是较量,不是证书,相似的还有蓝桥杯)。第二,计算机技术与软件业余资格考试证书,俗称,软考,国内针对计算机专业最高程度的考试。第三,华为证书,由华为公司推出的网络工程师认证。第四,思科证书,互联网畛域的国内权威认证。第五,微软认证,全世界90多个国家认可无效,领有该证书的人大多都能失去丰富的薪水。评论区通知我,你拿了几个证书? 公司几号发工资才靠谱?面试的时候,如何判断一家企业是否靠谱,你只须要问 HR 一个问题,你们公司几号发工资? 10号之前的多为高大上的好企业,它们个别规模较大,治理较标准,流程制度福利待遇都比拟好。10号到15号之间的个别是制度较健全的公司,尽管比不上第一类公司那么正规,但也差不到哪里去。15号之后的个别是小企业,要么是这家企业的现金流有问题,要么是这家企业的员工到职率较高,企业心愿通过扣押员工一个月工资的形式,来圈流员工。评论区通知我,你们公司几号发工资? 程序员会不会越老越升值?会的! 当初很多公司的工作流程十分标准,员工的工作内容和工作形式都以一种标准化的形式进行发展。越是大公司,这方面的标准和要求越多越严格。 为什么会这样呢? 其实,公司早就为本人想好进路了,万一某一天你到职了,公司很快就能够找到一个新人来代替你,而不至于使我的项目陷入停摆。 说白了,你只是公司里的一颗螺丝钉而已! 你的能力没有失去晋升,怎么跟年轻人比?年轻人听话,精力充沛,能加班,爱学习,不会老油条,而且要的工资还比你少!你怎么能跟人家比吗? 所以,如果你在公司里没有尽快成长,成为架构师、管理层,或者我的项目负责人,你的集体价值是随着年龄的增长始终在升高的。 大专生能进大厂吗?很难! 你认为你很优良,但大厂收到的简历中,优良的人多如牛毛!所以,学历成了很多大厂的硬性门槛。 那么大专生就肯定进不了大厂吗?也不齐全是。 首先前提是你本身能力的确足够强,满足大厂的招聘需要,而后你再找大厂里的人脉进行内推,应有肯定的概率进入大厂。 再者,你能够去晋升本人的学历,先专升本,再考研,先满足大厂的硬性条件,同时晋升本人的综合实力,那么进大厂的概率就会更高了。 最初,如果你进不了当初的大厂,千万不要放弃,如果运气好的话,说不定你能够进将来的大厂! 跳槽选在年前还是年后?个别问这个问题的人,都是在纠结年终奖。 如果你的公司年终奖特地丰富,动不动就是四个月五个月甚至七八个月年终奖,那么想都不必想,当然是打死也要等拿到年终奖再跳槽。 因为你的工资涨幅要超过33%才有可能补救年终奖造成的损失,但当初的环境,要找到一个涨幅超过33%的机会谈何容易? 如果你的工资也就一两个月,这点钱其实也算不上多,那么就没必要为了这点小钱而错过潜在的好机会。年前竞争更小,岗位更多,甚至很多公司还会额定给一笔签字费来补救局部候选人损失的年终奖,这样的机会何乐而不为呢? 评论区通知我,你的年终奖有几个月? 程序员如何成为一名优良的工程师?只会写代码的是码农;学好数据库,根本能混口饭吃; 在此基础上再学好操作系统和计算机网络,就能当一个不错的程序员。 如果能再把离散数学、数字电路、体系结构、数据结构/算法、编译原理学通透,再加上丰盛的实践经验与畛域特定常识,就能算是一个优良的工程师了。 评论区通知我,你当初都把握了哪些技能呢? 程序员如何疾速薪资翻倍?跳槽,是实现薪资翻倍的最快形式,没有之一!! 在业内,咱们称之为面向招聘编程。 如果你的月薪是10000,那么你能够关注15000或者20000薪资的相干工作机会,针对他们的岗位需要、岗位职责,做相应的筹备。 跳槽不要太频繁,但每年你都要更新一次简历,对本人的技能进行一次全面的回顾。而后在本人找工作的时候,针对不同的招聘需要,分档次写几份简历,针对性地投放。 记住,简历千万不要造假,但肯定要学会吹牛! 程序员眼中的浏览器是什么样的?第一,谷歌浏览器,是最最受欢迎的浏览器,因为它齐全恪守了W3C的规范,是咱们程序员的标配。第二,火狐浏览器,安全性高,而且还反对很多插件,新版的火狐,还是不错滴。第三,360浏览器,广告多,安全性还行,如果你喜爱360全家桶的话,能够抉择它。第四,IE浏览器,嗯,它是用来重装系统之后,下载别的浏览器的!IE:有本事你卸了我! 为什么感觉计算机专业大学四年没学到货色?你的感觉是对的。 计算机专业的学生在学校里学到的常识的确比拟古老,无奈满足企业的需要。 一方面,技术更新换代十分快,企业里用到的最新技术基本上在学校里都没有教过,存在很大的偏差与脱节。 另一方面,在学校里很难有企业级别的我的项目学习。 所以我倡议: 第一,尽早去企业里做实习,哪怕不给工资也要去;第二,老师手里往往有不少企业我的项目,能够跟老师做一些这类型的实战我的项目;第三,GitHub上有很多优良的开源我的项目,能够深度参加一两个我的项目的开发。只有这样,咱们才可能在毕业之后,找到一个绝对让本人称心的工作。

February 23, 2023 · 1 min · jiezi

关于php:技术文档-OpenSCA技术原理之composer依赖解析

OpenSCA常识小课堂开课了! 明天次要介绍基于composer包管理器的组件成分解析原理。 composer介绍composer是PHP的依赖管理工具。 开发者受到Node.js的npm及Ruby的bundler启发,composer设计上与两者有诸多类似。 composer的依赖管理文件是composer.json。开发者能够在composer.json中指定每个依赖项的版本范畴或应用composer require/update/remove ${name}命令治理依赖项。 如果一个我的项目中存在composer.json文件,便能够执行composer install命令主动装置以后我的项目所需的依赖项并生成composer.lock文件 composer.json残缺文件构造如下: { "name": "cakephp/app", "type": "project", "license": "MIT", "require": { "php": ">=7.2", "cakephp/cakephp": "^4.3", "cakephp/migrations": "^3.2", "cakephp/plugin-installer": "^1.3", "mobiledetect/mobiledetectlib": "^2.8" }, "require-dev": { "cakephp/bake": "^2.6", "cakephp/cakephp-codesniffer": "^4.5", "cakephp/debug_kit": "^4.5", "josegonzalez/dotenv": "^3.2", "phpunit/phpunit": "~8.5.0 || ^9.3" },}其中name为项目名称;type为包的类型,有library、project、metapackage和composer-plugin四种类型,默认状况下为library;license为我的项目申明的许可证,能够是一个字符串或是一个字符串数组。 require-dev为开发环境或测试应用的依赖,require为生产环境应用的依赖,依赖写法为"name":"version",版本能够指定精确版本或一个范畴。 解析算法composer.lock composer.lock文件为主动生成的文件,能够精确定位到PHP我的项目应用的依赖及版本,所以优先解析composer.lock文件。 composer.lock文件构造如下: { "packages": [ { "name": "a", "version": "1.1.0", "require": { "c": "1.1.*" } }, { "name": "b", "version": "1.2.2", "require": { "c": "^1.0.2" } }, { "name": "c", "version": "1.1.2" } ], "packages-dev": []}其中packages和packages-dev字段蕴含我的项目应用的所有间接和间接依赖,而且记录了组件间的依赖关系,packages为生产环境的依赖,packages-dev为开发环境的依赖。 ...

February 20, 2023 · 1 min · jiezi

关于php:GWA2Python吉娃兔改进兼答为何需要软件开发框架

在2022年9月份的更新中,咱们公布了 GWA2 in Python的初始预览版本。目前该版本正在一直功能完善与改良中,行将公布生产就绪版本。在之前的Blog中,咱们绘制了 GWA2 Python中的面向对象的实现,参考下图。GWA2 in Python的类与对象的继承关系( https://ufqi.com/blog/gwa2-in... )。 GWA2 in Python在上图中,实体对象类 FinanceFund 在拜访路由控制器的 ctrl/financefund 中被调用。实体类FinanceFund继承基类 WebApp, 基类WebApp实现了根底接口 WebInterfance, 而基类接口WebInterface则继承了 Python起因的形象根底类 ABC(Abstract Base Class)。如此以来,从 Python 语言视角残缺地实现了类与对象的继承关系, ABC –> WebInterface –> WebApp –> FinanceFund –> FinanceFundBase …这次咱们以一个从数据库中读取数据记录的实例来回顾下面这些类与对象的关系,并尝试答复为何要在软件开发过程中应用软件开发框架。下图中,GWA2 in Python 数据库读取及数据流转, GWA2 Python数据库读取及数据流在路由控制器 ctrl/financefund 中,实例化的 FinanceFund 的对象 ffund 调用放 ffund.getInfo 试图拜访数据库中的一条数据记录;ffund.getInfo 进一步地调用父类 WebApp.getBy;WebApp.getBy 依据调用参数,辨认出是默认从数据库中读取;WebApp.getBy 将申请分发给 Dba, Dba被激活并初始化——应用哪个连贯服务,应用哪种数据库驱动接口;Dba.select随之被调用;依据所选连贯及数据库驱动接口程序进行下一步;MySql数据服务及接口程序被抉择并调用,MySql.readSingle 被调用;MySql.readSingle 触发内置的 mysql.connector 相干的办法读取数据并返回:数据按之前的流程逐渐向上程序返回,直至最终到达 路由控制器 ctrl/financefund 那里。这里令人不能直观而形象地了解的是,为何要通过这么繁琐的过程进行数据记录的读取?实际上,在路由控制器 ctrl.financefund 中间接引入 mysql.connector 并依据相干指令语句实现,填入服务主机信息,创立连贯,组装SQL,递交查问,解析数据,返回数据….这样也是能够的,间接写更容易了解,也可能速度上因为少了类与对象的封装、数据流转步骤而略微快一些。 这实际上要答复为何在软件开发过程中要应用开发框架。简而言之,软件开发框架是一种代码开发、组织与治理的办法。软件开发框架是以极大地提高软件开发效率、运行效率,同时升高软件维护老本、总持有老本为指标的。在开发层面,代码须要复用,同样性能的代码,只需写一次即可在整个软件中调用;在组织层面,应用面向对象的形式,尽可能地映射残缺的物理世界的分类与层级:在运行层面,提供便捷的配置,可承受的安全性、强壮与鲁棒性:在保护扩大层面,可不便纵向降级优化,可便当地横向扩大;….. 为了更好地开发软件、组织和管理软件代码,咱们须要遵循肯定的开发标准和规定,这些标准和规定,以一种成套系的模式出现进去的,就是软件开发框架。比方,当咱们能够在 ctrl/financefund路由控制器中写数据库连贯的账户信息时,咱们不那么做,是因为这些敏感信息不应该散落和散布在各个路由控制器中,既有安全隐患,也不利于集中统一治理,如果将来某一天须要更换/切换某个数据库的账号信息时,不能一一文件的一一进行修改。 ...

February 17, 2023 · 2 min · jiezi

关于php:aicommit-让-AI-为你自动生-conventional-格式的提交信息

ai-commit - 让 AI 为你主动生 conventional 格局的提交信息源码https://github.com/guanguans/ai-commit 性能默认生成 conventional 格局的提交信息可自定义生成信息的 AI 驱动(目前仅反对 Open AI)可自定义生成待选提交信息的数量可自定义生成信息的提醒模板装置间接下载 ai-commit 文件curl 'https://raw.githubusercontent.com/guanguans/ai-commit/main/builds/ai-commit' -o ai-commit --progresschmod +x ai-commit通过 Composer 装置# 全局composer global require guanguans/ai-commit --dev -v# 本地composer require guanguans/ai-commit --dev -v应用配置 OpenAI API key./ai-commit config set generators.openai.api_key sk-... --global生成且提交信息./ai-commit commit╰─ ./ai-commit commit ─╯1. Checking run environment: ✔2. Generating commit messages: generating...[ { "id": 1, "subject": "Docs(README): Configure OpenAI API key", "body": "- Update README-zh_CN.md\n- Update README.md\n- Explain how to configure OpenAI API key" }, { "id": 2, "subject": "Install(Composer): Add global and local install instructions", "body": "- Update README.md\n- Add instructions for global and local install via Composer" }, { "id": 3, "subject": "Usage(Commit Messages): Add best practices", "body": "- Update README.md\n- Add best practices for writing commit messages" }]2. Generating commit messages: ✔3. Choosing commit message: choosing... Please choice a commit message: [1] Docs(README): Configure OpenAI API key [2] Install(Composer): Add global and local install instructions [3] Usage(Commit Messages): Add best practices >

February 15, 2023 · 1 min · jiezi

关于php:三十分钟入门基础GoJava小子版

作者:京东科技 韩国凯 前言Go语言定义Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种动态、强类型、编译型语言。Go 语言语法与 C 相近,但性能上有:内存平安,GC,构造状态及 CSP-style 并发计算。 适用范围本篇文章实用于学习过其余面向对象语言(Java、Php),但没有学过Go语言的初学者。文章次要从Go与Java性能上的比照来论述Go语言的根底语法、面向对象编程、并发与谬误四个方面。 一、根底语法Go语言的根底语法与惯例的编程语言根本相似,所不同的有申明变量的形式,数组、切片、字典的概念及性能与Java不太雷同,不过Java中这些数据结构都能够通过类比性能的形式在Go中应用。 1.1 变量、常量、nil与零值、办法、包、可见性、指针1.1.1 变量申明Go语言中有两种形式 1.应用var关键字申明,且须要留神的是,与大多数强类型语言不同,Go语言的申明变量类型位于变量名称的前面。Go语句完结不须要分号。 var num int var result string = "this is result" 2.应用:=赋值。 num := 3 等同于 var num int = 3 其中变量的类型会依据右侧的值进行匹配,例如"3"会匹配为int,"3.0"会匹配为float64,"result"会匹配为string。 1.1.2 常量申明应用const来申明一个常量,一个常量在申明后不可扭转。 const laugh string = "go" 1.1.3 nil与零值只申明未赋值的变量,其值为nil。相似于java中的“null” 。 没有明确初始值的变量申明会被赋予它们的 零值。 零值是: 数值类型为 0,布尔类型为 false,字符串为 ""(空字符串)。1.1.4 办法、包Go中办法的定义应用func关键字来定义一个办法,前面跟办法名,而后是参数,返回值(如果有的话,没有返回值则不写)。 func MethodName(p1 Parm, p2 Parm) int{} //学习一个语言应该从Hello World开始!package mainimport "fmt"func main() { fmt.Println("Hello World!")// Hello World! fmt.Println(add(3, 5)) //8 var sum = add(3, 5)}func add(a int, b int) int{ return a+b;}多个返回值Go 函数与其余编程语言一大不同之处在于反对多返回值,这在处理程序出错的时候十分有用。例如,如果上述 add 函数只反对非负整数相加,传入正数则会报错。 ...

February 14, 2023 · 6 min · jiezi

关于php:使用kartikvyii2mpdf中文相关配置

一:解决中文乱码配置'autoLangToFont' => true, //这几个配置加上能够显示中文'autoScriptToLang' => true, //这几个配置加上能够显示中文'autoVietnamese' => true, //这几个配置加上能够显示中文'autoArabic' => true, //这几个配置加上能够显示中文二:中文的标点符号凑近英文数字的时候,中文符号就又会变回小方块问题配置'useSubstitutions' => true,配置前: 配置后:

February 14, 2023 · 1 min · jiezi

关于php:error14090086SSL-routinesssl3getservercertificate

SSL handshake error: stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages:error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failedphp curl 执行 https 申请时报错如上 下载 cacert.pemwget https://curl.haxx.se/ca/cacert.pem --no-check-certificatmv cacert.pem /etc/pki/tls/certs/cacert.pem批改 PHP OpenSSL 证书php --ini 查看 php.ini 的门路 将原有的证书文件替换为下载的 cacert.pem [openssl]; The location of a Certificate Authority (CA) file on the local filesystem; to use when verifying the identity of SSL/TLS peers. Most users should; not specify a value for this directive as PHP will attempt to use the; OS-managed cert stores in its absence. If specified, this value may still; be overridden on a per-stream basis via the "cafile" SSL stream context; option.; openssl.cafile=/etc/pki/tls/certs/ca-bundle.crtopenssl.cafile=/etc/pki/tls/certs/cacert.pem从新执行即可~ ...

February 14, 2023 · 1 min · jiezi

关于php:在-Swoole-服务器程序中如何实现压力反馈

为什么要反馈压力服务端程序在遇到高并发申请时,一旦超过程序所能解决的极限,可能会导致解体,引发线上服务的大规模雪崩。压力反馈(Back Pressure)是一种由服务端被动告知客户端本身资源有余,无奈提供服务的一种伎俩。在 Web 服务中能够返回 HTTP 503 (Service Unavailable)告知客户端以后服务器处于比拟高的负载状态。这时客户端能够抉择: 期待肯定工夫后进行重试切换其余负载较低的节点或者服务器上临时停止使用此服务,对相干的业务进行降级解决PHP-FPM 如何反馈压力在传统 Nginx+PHP-FPM 服务端程序中,底层有 3 个要害的配置影响压力反馈: php-fpm.conf 中 pm.max_chindren 配置的最大过程数,如配置为 200 示意最大启动 200 个过程,一旦超过最大过程数,新的申请将不会被 Accept,而是进入到 Listen Backlog 队列中进行排队,直到有闲暇过程才会从队列弹出一个新的申请进行解决php-fpm.conf 中 listen.backlog 配置 Listen Backlog 队列长度,如配置为 512 ,则示意若没有闲暇过程时,最大容许有 512 个申请排队内核参数 net.core.somaxconn ,listen.backlog 设置的数值不肯定是无效的,这取决于内核参数 net.core.somaxconn 的设置,理论队列长度为 min(listen.backlog, net.core.somaxconn),例如 net.core.somaxconn=128、listen.backlog=512,理论的队列长度为 128 而不是 512因而在 Nginx+PHP-FPM 程序中,最大并发为 pm.max_chindren + min(listen.backlog, net.core.somaxconn),一旦超过之后,就会回绝新的申请,返回 502 谬误,向客户端反馈压力。理论我的项目中,该当设置为一个适合的值,不宜过大或过小。否则就出产生客户端等工夫过长,低负载拒绝请求两种问题。 Nginx 无奈辨别 502 和 503 谬误,这是一个毛病Swoole 程序如何反馈压力因为 Swoole 是齐全异步的架构,并发能力更强,在机器资源未耗尽的前提下,是能够有限承受、解决申请的。相比 Nginx+PHP-FPM 不好实现压力反馈,个别须要框架层面或者应用层代码中自行抛出 503 谬误。Swoole 底层提供了多项配置能够解决一部分问题。 ...

February 10, 2023 · 1 min · jiezi

关于php:laravel-数据库操作

一:数据库配置数据库配置文件搁置在config/database.php 文件中 1:根底配置'connections' => [ 'mysql' => [ 'driver' => 'mysql', #数据库类型 'host' => env('DB_HOST', '127.0.0.1'),#数据库地址 'port' => env('DB_PORT', '3306'),#端口号 'database' => env('DB_DATABASE', 'forge'),#数据库名称 'username' => env('DB_USERNAME', 'forge'),#用户名 'password' => env('DB_PASSWORD', ''),#明码 'unix_socket' => env('DB_SOCKET', ''),#应用 socket 链接 'charset' => 'utf8mb4',#编码 'collation' => 'utf8mb4_unicode_ci',#字符集 'prefix' => '',#表前缀 'strict' => true, 'engine' => null, ],],上述的host,port,database.....应用了env函数,他应用的是.env文件的配置项,你也能够不必.env文件配置项,间接填写相干信息,如果你应用了.env文件配置项,你须要批改.env文件内容 2:多表配置'connections' => [ 'mysql' => [ 'driver' => 'mysql', #数据库类型 'host' => env('DB_HOST', '127.0.0.1'),#数据库地址 'port' => env('DB_PORT', '3306'),#端口号 'database' => env('DB_DATABASE', 'forge'),#数据库名称 'username' => env('DB_USERNAME', 'forge'),#用户名 'password' => env('DB_PASSWORD', ''),#明码 'unix_socket' => env('DB_SOCKET', ''),#应用 socket 链接 'charset' => 'utf8mb4',#编码 'collation' => 'utf8mb4_unicode_ci',#字符集 'prefix' => '',#表前缀 'strict' => true, 'engine' => null, ], 'mysql001' => [ 'driver' => 'mysql', 'host' => 'localhost', 'port' => '3306', 'database' => 'blog', 'username' => 'root', 'password' => 'root', 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'engine' => null, ],],3:主从数据库配置'connections' => [ 'mysql' => [ //读库地址 'read' => [ 'host' => [ '192.168.1.1', '196.168.1.2', ], ], //写库地址 'write' => [ 'host' => [ '196.168.1.3', ], ], 'driver' => 'mysql', #数据库类型 'database' => env('DB_DATABASE', 'forge'),#数据库名称 'username' => env('DB_USERNAME', 'forge'),#用户名 'password' => env('DB_PASSWORD', ''),#明码 'unix_socket' => env('DB_SOCKET', ''),#应用 socket 链接 'charset' => 'utf8mb4',#编码 'collation' => 'utf8mb4_unicode_ci',#字符集 'prefix' => '',#表前缀 'strict' => true, 'engine' => null, ],],二:数据库根底操作1:数据查问$users = DB::select('select * from user'); #查问默认mysql的user表所有数据$users = DB::select('select * from user where id = ?',[1]); #查问默认mysql的user表id等于1的数据$users = DB::select('select * from user where id = ? and user_no = ?',[1,'001']);#查问默认mysql的user表id等于1且user_no等于001的数据$users = DB::select('select * from user where id = :id and user_no = :user_no',['id'=>1,'user_no'=>'001']);#查问默认mysql的user表id等于1且user_no等于001的数据$article = DB::connection('mysql001')->select('select * from article'); #查问mysql001库的article表数据$article = DB::connection('mysql001')->select('select * from article where id = ?',[1]); #查问mysql001库的article表id等于1的数据$article = DB::connection('mysql001')->select('select * from article where id = ? and column_no = ?',[1,'COL00005']);#查问mysql001库的article表id等于1且column_no等于COL00005的数据$article = DB::connection('mysql001')->select('select * from article where id = :id and column_no = :column_no',['id'=>1,'column_no'=>'COL00005']);#查问mysql001库的article表id等于1且column_no等于COL00005的数据2:数据插入DB::insert('insert into user (user_no,user_name) values (?, ?)', ['002','test']);#向默认mysql的user表插入一条数据DB::connection('mysql001')->insert('insert into article (title,column_no,content) values (?, ?, ?)',['test','COL00005','testcontent']);#向mysql001库的article表插入一条数据3:数据更新DB::update('update user set user_name = ? where id = ?', ['test',1]); #更新默认mysql的user表id等于1的user_name字段为testDB::connection('mysql001')->update('update article set title = ? where id = ?', ['test',1]);#更新mysql001库的article表的id等于1的title字段为test4:数据删除DB::delete('delete from user where id = ?',[1]);删除默认mysql的user表id等于1的数据DB::connection('mysql001')->delete('delete from article where id = ?',[1]);删除mysql001库的article表id等于1的数据5:数据库事务DB::transaction(function () { //sql操作});DB::transaction(function () { //sql操作}, 5);传递第二个可选参数给 transaction 办法,该参数定义在产生死锁时应该从新尝试事务的次数。一旦尝试次数都用尽了,就会抛出一个异样6:手动操作事务如果你想要手动开始一个事务,并且可能齐全管制回滚和提交,那么你能够应用beginTransaction 办法实现 ...

February 7, 2023 · 6 min · jiezi

关于php:laravel在视图中使用php代码

在laravel框架中,在视图文件中应用PHP代码应用到了Blade 模板引擎,Blade 是 laravel 提供的一个简略弱小的模板引擎。它不像其余风行的 PHP 模板引擎那样限度你在视图中应用原生的 PHP 代码,事实上它就是把 Blade 视图编译成原生的 PHP 代码并缓存起来。缓存会在 Blade 视图扭转时而扭转,这意味着 Blade 并没有给你的利用增加编译的累赘。Blade 视图文件应用 .blade.php 后缀,个别状况下都被存储在 resources/views 目录 留神:应用blade模板引擎时视图文件的后缀名必须是.blade.php,如果你的视图文件后缀名是.php的话,这样就无奈应用blade模板引擎 一:输入PHP变量{{ $name }}{{ date('Y-m-d H:i:s',time()) }}{{ in_array($name,$arr)?'true':'false' }}{ isset($name)?$name:'default' }}{{ $name or 'default' }}二:显示未本义数据默认的,Blade {{}} 申明会主动的应用 PHP 的 htmlentities 办法来防止 XSS 攻打。如果你不想你的数据被本义,你能够应用上面的语法 {!! $name !!}留神:当你在利用中输入用户输出的数据时应该十分的审慎,你应该总是应用 {{}} 来本义内容中任意的 HTML 实体三:if语句@if ($name == 'hello') hello@elseif($name == 'yes') yes@else who@endif为了不便,Blade 也提供了 @unless 指令: @unless($name) {{$name}}@endunless@unless()就能够了解为 if( ! ),就是if not ...

February 7, 2023 · 1 min · jiezi

关于php:PHP英文博客专栏PHP快速入门个人笔记

英文博客专栏PHP疾速入门引言 本文是对于英文原始博客的一个PHP入门专栏的集体笔记摘录,因为十分入门并且本身有JAVA语言根底,看的比拟快并且会疏忽很多共同点,倡议读者有能力能够看看博客的原文顺带晋升英文能力,作者文字表达能力十分强,写的十分棒。 这篇专栏介绍了PHP8入门,专栏写于2022年中旬,不论是单词还是语法句式都非常通俗易懂,学技术的同时晋升英语水平并且有助于晋升自信心。 目录介绍过往历史php是一个怎么样的语言设置PHP第一个PHP程序根本类型操作符 字符串操作编写正文和数字无关的内置函数Array数组 arrays罕用函数关联数组条件语句循环函数 匿名函数值传递和地址传递箭头函数应用map,reduce,filter函数循环数组解决面向对象 面向对象探讨如何构建对象属性和办法继承重写动态对象比拟对象遍历对象克隆魔术办法文件蕴含文档零碎的有用常量、函数和变量谬误异样日期常量和枚举PHP web平台部署 解决HTTP申请&dollar;\_SEVER 对象应用cookiesSessionsIOdatabaseJSONemailComposer部署PHP利用原始博客地址https://thevalleyofcode.com/php/ 介绍PHP是一个两级分化的语言,感觉它好的人称誉它简略,PHP的语法比拟自在上手非常简单。而不好的人则会像我一样认为是个四不像语言,既有前端脚本的影子,然而同时反对面向对象的形式组合代码,总是会有种奇怪的感觉。 然而不得不抵赖,世界上绝大多数WEB网站都是PHP构建的,PHP是web畛域当之无愧的佼佼者。尽管这语言当初在国内是一潭死水,然而在国外它是能排进前十的热门编程语言。 PHP在短短的几年内疾速倒退,从最开始几年的PHP4和PHP5的收缩,到当初PHP8的版本公布,更新迭代的速度还是很快的。 过往历史PHP起源于1994年的集体博客网站,作者是rasmus lerdorf,PHP在1997到2000随着互联网的疾速崛起并且爆炸式增长。 用处: 和HTML存在一点点交互动静的HTML语言,以及web应用程序当中对外提供拜访。Facebook就是构建在PHP网站之上的,晚期微微博也同样用的PHP语言wiki百科同样应用PHP构建PHP是一个怎么样的语言尽管PHP被戏称脚本语言,然而实际上它是解释型语言,和宽广编译运行的服务端语言没什么区别。只不过和其余大部分解释型语言不同点是PHP不须要编译就能够运行,或者能够认为编译的动作自身就是主动的。这和Java,GO以及c语言等等都有很大不同。在JAVA畛域PHP十分像JSP,然而理论比照会发现要比JSP更灵便和不便,也更好用。 这个语言外部可主动通过编译器把代码翻译成机器能够意识以及能够运行的语言。从集体角度看PHP被称作脚本语言是比拟适合的词,因为它在web畛域蛟龙得水。此外因为PHP是动静类型语言,开发者不须要关注变量类型,然而有时候又因为类型转化的问题呈现一些难以觉察的谬误。 动静类型语言是高级编程语言的趋势这一点毋庸置疑。就连JDK11也实现了 var 关键词的动静类型语法糖定义就能够看出端倪。 最初用作者的原文总结:PHP是一门很像JavaScript的语言,不同的是它有动静类型,灵便类型的解释型后端语言。 设置PHP本局部作者介绍了mamp的装置应用,集体没有应用教训就不具体记录了,对于PHP作者举荐应用VScode 编辑器开发,集体应用下来发现的确好用,当然Jerbrian的PHP IDE也不错,对于长年应用IDEA的开发人员根本能够无缝连接。 PHP 开发个别依赖套件,PHP自身就是起源于集体博客专职于WEB Application畛域,所以他须要最为基本的软件比方Apach,Mysql,Redis等等。 PHP开源套件软件很多,这里就不过多开展了。当然套件开发不是强制的,当然开发者开发过程中也能够独自部署中间件和数据库。 对于php的web利用,必备组件无外乎上面几个: PHP语言环境变量,举荐最新版的PHP8。数据库,通常以MySQL为主。apache或者nignix作为web服务器。PHP是面向http web利用程序开发语言,很多时候都须要和HTML页面配合,这和古老的JSP语言有点相似,然而理论应用的时候更多是和模板引擎以及框架配合。 第一个PHP程序PHP的Helloworld非常简单,只须要在mamp或者其余PHP程序的开发软件根目录创立index.html的文件即可。很多web server服务器根本都应用index.html作为默认的拜访页面,所以如果间接拜访localhost端口的webserver根门路,那么就会展现对应index.html页面。 PHP代码通常以及<?php结尾以及?>结尾,两头编写无关PHP语言代码即可,咱们能够在index.html文件全文替换成上面的代码。 <?phpecho 'World';?>尽管拜访的是html页面,然而外面的PHP代码却会被辨认翻译并且执行。根本类型PHP是动静类型语言,定义变量形式如下: <?php$a = 5;$b = '444';?>PHP反对上面的根底类型: bool boolean values (true/false)int integer numbers (no decimals)float floating-point numbers (decimals)string stringsarray arraysobject objectsnull a value that means “no value assigned”如果要晓得变量的数据类型,能够应用var_dump()的办法查看: $age = 20;var_dump($age);操作符PHP的根底操作符: 算数操作: +, -, *, / (division), % (remainder) and ** (exponential). ...

January 24, 2023 · 9 min · jiezi

关于php:适配-Laravel-多版本的开源项目版本号规划

在公布一个开源我的项目时,版本号是很重要的一个细节,它能够帮忙用户理解我的项目的更新状况。 版本号个别由数字和字母组成,常见的版本号格局为 主版本号。次版本号。订正版本号,如 1.0.0。 主版本号(major)示意大版本更新,通常是在毁坏向后兼容性的状况下才会降级。次版本号(minor)示意小版本更新,通常是在增加新性能或修复小谬误的状况下降级。订正版本号(patch)示意订正版本,通常是修补某个版本。如果咱们的我的项目是依赖于多个不同的 Laravel 版本,同时在不同版本须要做一些兼容解决,这时候就须要获取 Laravel 的版本号。 在 Laravel 框架中,能够应用 App::version () 函数来获取以后框架版本号。 $version = App::version();在开源我的项目中解决时,能够通过如下判断来进行版本的判断,比方要判断大于 6.0.0 的 Laravel 框架 if( version_compare( $version, '6.0.0' ) ) { // ...}如果须要获取框架的主版本号(例如 5),能够应用 explode () 函数将版本号拆分为数组,而后获取数组的第一个元素即可。 例如,在 Laravel 框架中,能够应用 App::version () 函数获取以后框架版本号,而后应用 explode () 函数拆分版本号: $version = App::version(); $parts = explode('.', $version); $major_version = $parts[0]; 在下面的代码中,变量 $major_version 就是框架的主版本号。

January 18, 2023 · 1 min · jiezi

关于php:mac-系统使用-homebrew-安装php环境

更不便的在微信公众号阅读文章能够关注公众号:海生的go花园 一、本地php环境装置咱们这里以apple m2 零碎为例子。咱们以https://github.com/shivammath...第三方tap源为例子,官网的brew速度太慢了。这里文章次要分为两局部 装置php装置composer这里咱们装置php会分两种 没有php状况已有php状况,比方php7.3,咱们要更新成php7.4。咱们这里应用 https://github.com/hisheng/first 测试php代码其中composer.json外面php的版本要求7.3.0以上 "require": { "php": ">=7.3.0", "phpunit/phpunit": "^4.8" },二、装置php步骤之--(没有php环境的状况)1、查看 php 版本php -v此时发现没有php环境的话,就全新装置 2、brew tap减速,应用github库homebrew默认应用官网的库,但这个源很慢,个别咱们会应用第三方tap brew tap shivammathur/php3、装置php7.3brew install shivammathur/php/php@7.34、创立link,这样能够在全局环境里拜访brew link --overwrite --force php@7.35、查看是否装置 php -v咱们会发现曾经装置好了 PHP 7.3.33 (cli) (built: Dec 8 2022 08:29:04) ( NTS )Copyright (c) 1997-2018 The PHP GroupZend Engine v3.3.33, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.3.33, Copyright (c) 1999-2018, by Zend Technologies

January 16, 2023 · 1 min · jiezi

关于php:php-实现-Promiseall-和-Promiserace

测试 $promise1 = function () { msleep(500); return 'one';};$promise2 = function () { msleep(100); return 'two';};$promise3 = function () { msleep(50); throw new \Exception('Reject');};var_dump(promise_all([$promise1, $promise2]));var_dump(promise_race([$promise1, $promise2]));var_dump(promise_race([$promise1, $promise2, $promise3]));后果 # php promise.phparray(2) {[1]=>string(3) "two"[0]=>string(3) "one"}string(3) "two"object(Exception)#15 (7) {}实现 <?phpdeclare(strict_types=1);use Swow\Coroutine;use Swow\Channel;use Swow\Selector;use Swow\Sync\WaitGroup;use function Swow\defer;/** * @param array $callbacks * @param int $parallel 并发数量 * @return array */function promise_all(array $callbacks, int $parallel = -1){ $wg = new WaitGroup(); $channel = new Channel($parallel); $results = []; foreach ($callbacks as $key => $callback) { $wg->add(); $channel->push(true); Coroutine::run(static function () use ($wg, $channel, $callback, $key, &$results) { try { $results[$key] = $callback(); } catch (\Throwable) { } finally { $channel->pop(); $wg->done(); } }); } $wg->wait(); return $results;}/** * @param array $callbacks * @param int $timeout 超时 * @param bool $throw 是否抛出异样 * @return mixed */function promise_race(array $callbacks, int $timeout = -1, bool $throw = true){ $coroutines = []; defer(static function () use (&$coroutines) { Coroutine::run(static function () use (&$coroutines) { foreach ($coroutines as $coroutine) { if ($coroutine && $coroutine->isAlive()) { $coroutine->kill(); } } }); }); $selector = new Selector(); foreach ($callbacks as $callback) { $channel = new Channel(); $coroutines[] = Coroutine::run(static function () use ($channel, $callback) { try { $channel->push($callback()); } catch (\Throwable $e) { $channel->push($e); } }); $selector->pop($channel); } try { $selector->commit($timeout); return $selector->fetch(); } catch (\Throwable $e) { if ($throw) { throw $e; } } return false;}

January 14, 2023 · 2 min · jiezi

关于php:初识PHP2语法和变量创建

PHP 脚本文件在服务器上执行,而后将后果以HTML 的模式发送回浏览器。 PHP 语法PHP 文件通常会蕴含HTML 代码和PHP 代码;而PHP作为脚本代码,能够存在于文件任意的地位,只须要注明一下即可;以 <?php 开始,以?>完结。 <?php// PHP 代码?>PHP 代码中每个代码行都必须以分号完结,示意一种分隔,用于把指令集辨别开来;而在PHP 代码中,有两种在浏览器输入文本的根底指令:echo 和 print。咱们以上面这个PHP 文件作为例子,它能够向浏览器输入文本"Hello World!": <!DOCTYPE html><html><body><h1>My first PHP page</h1><?phpecho "Hello World!";?></body></html>运行的后果如下:PHP 代码中的正文则是通过//和 / 多行的正文 / 来示意的: <?php// 这是 PHP 单行正文/*这是PHP 多行正文*/?>PHP 变量创立变量是用于存储信息的"容器",能够是很短的名称(比方X或则Y)或则有形容象征的名称(比方camera, age,carname),咱们能够给PHP 变量赋予某个值(x=5)或者表达式(z=x+y)。变量规定能够有:变量须要以$符号开始,前面跟着变量的名称变量名结尾必须以字母或下划线字符开始变量名只能蕴含字母、数字以及下划线(A-Z、a-z、0-9 和 _ )变量名不能蕴含空格变量名是辨别大小写的($x 和 $X是两个不同的变量);注:PHP 语句也是辨别大小写的。PHP 没有申明变量的命令,变量在咱们第一次赋值给它的时候被创立;比方上面这个例子里,变量txt 将保留值 Hello world!,变量x 将保留值 5,变量y 将保留值10.5: <?php$txt="Hello world!";$x=5;$y=10.5;?>参考资料www.php.nethttps://www.runoob.com/php/ph...

January 13, 2023 · 1 min · jiezi

关于php:LatticePHP使用PHP生成点阵图

<!-- more --> 入门这是什么LatticePHP是一个点阵图生成软件包,用于PHP生成点阵图。因为点阵图生成个别是嵌入式语言所须要的,而PHP简直不应用,于是我开发了这一款冷门的软件包,心愿能帮忙你。 我的项目曾经开源:Github地址 环境PHP >= 7.1装置应用composer装置 composer require zmxy/lattice或者返回GitHub仓库下载源文件,自行手动导入 应用先创立一个画布use Lattice\LatticePck\Lattice;$width = 296;$height = 128;$lattice = new Lattice();$lattice->createBlankImage($width, $height);这样子咱们就创立了一个296X128的一个画布 接下来往上写字$lattice->text('LatticePHP-应用PHP生成点阵图', [0, 0]);成果如下 画一个矩形use Lattice\LatticePck\LatticeImg;LatticeImg::Rectangle($lattice, 50, 50, [50, 50], 0, 1);成果如下 画二维码use Lattice\LatticePck\LatticeImg;$order = "http://weixin.qq.com/r/BRy0rI7EoNPfrcrP90kX";LatticeImg::QrCode($lattice, $order, [0, 12, 'center'], "public/", 0);输入HTML来看看成果use Lattice\LatticePck\LatticeOutput;$latticeOutput = (new LatticeOutput($lattice));echo $latticeOutput->getHTML(); 残缺Demorequire 'vendor/autoload.php';use Lattice\LatticePck\Lattice;use Lattice\LatticePck\LatticeImg;use Lattice\LatticePck\LatticeOutput;$width = 296;$height = 128;$lattice = new Lattice();$latticeOutput = (new LatticeOutput($lattice));$lattice->createBlankImage($width, $height);// 坐标$x = 6;$y = 6;$lattice->text('LatticePHP-应用PHP生成点阵图', [$x, $y, 'top-center']);// LatticeImg::Rectangle($lattice, 50, 50, [50, 50], 0, 1);$order = "http://weixin.qq.com/r/BRy0rI7EoNPfrcrP90kX";LatticeImg::QrCode($lattice, $order, [0, 12, 'center'], "public/", 0);echo $latticeOutput->getHTML();代码文档画布您在做任何操作之前起码得先创立一个画布。 ...

January 13, 2023 · 3 min · jiezi

关于php:发布自己的composer包

筹备Github账号packagist账号装置好git装置好composer步骤创立并拉取一个仓库Github右上角+号,点 New repository。创立实现后应用git拉取到本地。 composer初始化主动创立应用composer init命令进行初始化 composer init composer init Welcome to the Composer config generator 欢送来到Composer配置生成器This command will guide you through creating your composer.json config. 这个命令将领导您创立编写器。json配置。Package name (<vendor>/<name>) [z/lattice-php]: // 你的包名,不能够反复Description []: // 你的形容Author [Chenilove <2665468087@qq.com>, n to skip]: // 作者Minimum Stability []: // 版本稳定性 例如 dev stable 等Package Type (e.g. library, project, metapackage, composer-plugin) []: // 包类型License []: MIT // 协定Define your dependencies.Would you like to define your dependencies (require) interactively [yes]? 你想交互式地定义你的依赖关系吗Search for a package:Would you like to define your dev dependencies (require-dev) interactively [yes]? 您想交互式地定义您的开发依赖项(require-dev)吗Search for a package:Add PSR-4 autoload mapping? Maps namespace "Z\LatticePhp" to the entered relative path. [src/, n to skip]: 增加PSR-4主动加载映射?将命名空间“Z\LatticePhp”映射到输出的相对路径。{ "name": "z/lattice-php", "license": "MIT", "autoload": { "psr-4": { "Z\\LatticePhp\\": "src/" } }, "authors": [ { "name": "Chenilove", "email": "2665468087@qq.com" } ], "require": {}}Do you confirm generation [yes]? 确定生成吗?Generating autoload filesGenerated autoload filesPSR-4 autoloading configured. Use "namespace Z\LatticePhp;" in src/Include the Composer autoloader with: require 'vendor/autoload.php';手动创立实质上composer init命令是在根目录创立一个composer.json,咱们齐全能够本人手动创立 ...

January 13, 2023 · 1 min · jiezi

关于php:PHP-中命令行调用-escapeshellarg-函数中文问题

escapeshellarg 是 PHP 中的一个函数,它能够将字符串本义为平安的 shell 参数。它的中文名称可能是 "本义 shell 参数"。 在 PHP 中,你能够应用 escapeshellarg 函数来保障传递给 shell 命令的参数是平安的。这样能够防止命令注入攻打。 例如,如果你想在 PHP 中执行一个命令,你能够这样做: $output = shell_exec('mycommand ' . escapeshellarg($input)); 在这个例子中,$input 是一个用户输出的字符串,你能够应用 escapeshellarg 将它本义为平安的 shell 参数。这样,你就能够确保用户输出不会导致命令注入攻打。 在应用 escapeshellarg 函数时,你可能会发现蕴含中文字符的字符串被本义后变成空字符串。这是因为 escapeshellarg 函数默认应用的是 ASCII 字符集,对于非 ASCII 字符,它会将其视为有效字符,并将其过滤掉。

January 11, 2023 · 1 min · jiezi

关于php:win10-docker-laradock-搭建PHP简易开发环境

装置windows版本docker,并且装置 【官网】:https://www.docker.com/下载laradock 【github】:https://github.com/laradock/l...git clone https://github.com/laradock/laradock.git进入laradock文件夹复制.env.example到.env关上.env,按需进行一些调整,比方: WORKSPACE_INSTALL_NODE=true 改为 false,不须要装置nodeWORKSPACE_INSTALL_YARN=true 改为 false,不须要装置yarnWORKSPACE_INSTALL_NPM_GULP=true 改为 false,不须要装置gulpWORKSPACE_INSTALL_NPM_VUE_CLI=true 改为 false,不须要装置vueWORKSPACE_SSH_PORT=2222 改为 60022 ,windows 2222 会报错占用按需启动:docker-compose up -d nginx php-fpm mysql redis workspace进入workspace控制台,装置laravel代码 composer create-project --prefer-dist laravel/laravel:^7.0 my_php_project拜访 localhost 验证

January 9, 2023 · 1 min · jiezi

关于php:Hyperf-30-发布PHP-新时代

Hyperf 3.0,新时代降临回顾在过来的一年半工夫里,Hyperf 2.2 共公布了 35 个小版本,使 Hyperf 达到了一个前所未有的高度,这里也取得了一些不错的数据反馈。 Hyperf 在 GitHub 和 Gitee 上的关注度也失去了显著晋升,别离取得了 4.9k 和 791 个 star,整体关注度增长也很稳固。 Hyperf 框架的装置量也达到了 90万次,每天都有约 1300次的装置,这也表明了 Hyperf 曾经广泛应用于相干行业中并撑持了大量的零碎运行。 Hyperf 组织下的无效 repo 更是达到了约 140个(去除掉 Archive 我的项目后),保护工作量空前微小,但迭代依然高频。 感激 Hyperf 团队全体成员的辛勤以及奉献,同时也感激所有的 PR 贡献者,没有你们的参加也就没有明天的 Hyperf。 Thanks ALL Contributors Hyperf 3.0 新时代Hyperf 3.0 带来了很多十分有意思的新能力,其中一些新能力不乏是 PHP 畛域外面前所未有的,当然这些新能力也脱离不了其余开源社区的踊跃倒退,包含但不限于 PHP、Swoole、Swow、PHPMicro、DTM、Seata 等开源社区,也衷心希望大家在空闲工夫能够为这些开源社区也奉献出本人的一份力,搭上一砖一瓦,共建更加美妙的将来。 原生注解(Attribute)随着 PHP 8.1、8.2 的公布,给 PHP 带来了很多新的个性,其中与 Hyperf 最为相干的就是 PHP 的原生注解(Attribute)了,Hyperf 3.0 也放弃了过往采纳的基于正文解析的注解性能实现,转而采纳 PHP 的原生注解,当然对应依赖的 PHP 版本,也将调整为最低要求 PHP 8.0。 ...

January 6, 2023 · 4 min · jiezi