批量获取某目录及子孙目录下的所有图片image、视频video、目录dir、目录树dir_tree
/**
* 批量获取某目录及子孙目录下的所有图片image、视频video、目录dir、目录树dir_tree
* @param array $filter_array 要过滤的目录名称列表
*/
function get_file_list($type, $path, $filter_array=array()){
if(!in_array($type, explode(',','image,video,dir,dir_tree')))return return_data(1,'类型错误:'.$type);
/**
* SELF_FIRST 目录从浅到深返回
* CHILD_FIRST 先处理子节点,目录从深到浅返回
*/
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
$photos = [];
$videos = [];
$dirs = [];
foreach ($iterator as $SplFileInfo){
$skip=false;
foreach($filter_array as $filter){
if(strpos($SplFileInfo->getRealPath(), $filter)!==false){
$skip=true;
break;
}
}
if($skip)continue;
/*
$SplFileInfo 实际是 RecursiveDirectoryIterator 类
参考:https://www.php.net/manual/zh/class.recursivedirectoryiterator.php
常用方法:
getExtension 获取扩展名,如:jpg
getFilename 获取文件名,如:abc.jpg
getPathname 返回创建 SplFileInfo 对象时传入的原始路径字符串(不做任何解析或验证),如:D:\web/pictures\2001\a.jpg
getRealPath 返回文件的规范化的绝对路径,如:D:\web\pictures\2001\a.jpg
getSize
getType
isDir
isFile
isLink 快捷方式,会自动跟随与链接文件
*/
if($SplFileInfo->isDir()){
$dirs[$iterator->getDepth()][] = $SplFileInfo->getRealPath();
}else if($SplFileInfo->isFile()){
$cur_path = dirname($SplFileInfo->getRealPath());
$cur_file = $SplFileInfo->getRealPath();
$cur_size = $SplFileInfo->getSize();
switch(strtolower($SplFileInfo->getExtension())){
case 'jpe':
case 'jpg':
case 'jpeg':
case 'png':
//因生成缩略图时可能出错,所以不展示大图片
if($cur_size < 20*1024*1024){
$photos[$cur_path][] = $cur_file;
}
break;
case 'mp4':
$videos[$cur_path][] = $cur_file;
break;
}
/*
$photos[] = [
'getRealPath' => $SplFileInfo->getRealPath(),
'getPathname' => $SplFileInfo->getPathname(),
'getExtension' => $SplFileInfo->getExtension(),
'getFilename' => $SplFileInfo->getFilename(),
'getSize' => $SplFileInfo->getSize(),
'getType' => $SplFileInfo->getType(),
];
*/
}
}
ksort($dirs);
// 构建树形结构
$dir_tree = [];
foreach ($dirs as $depth => $paths) {
foreach ($paths as $path) {
insertPathIntoTree($dir_tree, $path, $path);
}
}
switch($type){
case 'image':
return return_data(0,'',$photos);
case 'video':
return return_data(0,'',videos);
case 'dir':
return return_data(0,'',dir);
case 'dir_tree':
return return_data(0,'',dir_tree);
}
}
//生成目录树
function insertPathIntoTree(&$tree, $currentPath, $rootPath) {
// 计算相对路径
$relativePath = substr($currentPath, strlen($rootPath));
if (empty($relativePath)) {
$tree[$currentPath] = [];
return;
}
// 分割路径
$parts = explode(DIRECTORY_SEPARATOR, trim($relativePath, DIRECTORY_SEPARATOR));
// 找到父路径
$parentPath = $rootPath;
for ($i = 0; $i < count($parts) - 1; $i++) {
$parentPath .= DIRECTORY_SEPARATOR . $parts[$i];
}
// 如果是根目录下的直接子目录
if ($parentPath === $rootPath) {
$tree[$currentPath] = [];
return;
}
// 在树中找到父路径对应的节点
$current = &$tree;
$pathSoFar = $rootPath;
for ($i = 0; $i < count($parts) - 1; $i++) {
$pathSoFar .= DIRECTORY_SEPARATOR . $parts[$i];
// 查找当前路径在树中的位置
$found = false;
foreach ($current as $key => $value) {
if (strpos($key, $pathSoFar) === 0) {
$current = &$current[$key];
$found = true;
break;
}
}
if (!$found) break;
}
// 在父节点中添加当前路径
$current[$currentPath] = [];
}php获取图片方向、纠正图片方向
/**
* 自动纠正图片方向
* 方向经过旋转的图片,在电脑、手机、浏览器中查看时一般会自动纠正,
* 但是在生成缩略图、使用某些打印控件打印时,可能会出现方向不正确的情况。
*/
function rotateImage($imagePath) {
if (!function_exists('exif_read_data')) {
return false; // EXIF扩展未启用
}
if (!file_exists($imagePath)) {
return false; // 文件不存在
}
$exif = exif_read_data($imagePath);
$orientation=1;
if ($exif && isset($exif['Orientation'])) {
$orientation= (int)$exif['Orientation'];
}
// 读取图片
$size = getimagesize($imagePath);
$width = $size[0];
$height= $size[1];
$mime = $size['mime'];
switch ($mime) {
case 'image/jpeg':
$image = imagecreatefromjpeg($imagePath);
break;
case 'image/png':
$image = imagecreatefrompng($imagePath);
break;
case 'image/gif':
$image = imagecreatefromgif($imagePath);
break;
default:
header('Content-Type: '.$mime);
echo file_get_contents($imagePath);
return false;
}
// 根据方向值进行旋转
switch ($orientation) {
case 2:
// 水平翻转
imageflip($image, IMG_FLIP_HORIZONTAL);
break;
case 3:
// 旋转180°
$image = imagerotate($image, 180, 0);
break;
case 4:
// 垂直翻转
imageflip($image, IMG_FLIP_VERTICAL);
break;
case 5:
// 顺时针90° + 水平翻转
$image = imagerotate($image, -90, 0);
imageflip($image, IMG_FLIP_HORIZONTAL);
break;
case 6:
// 顺时针90°
$image = imagerotate($image, -90, 0);
break;
case 7:
// 逆时针90° + 水平翻转
$image = imagerotate($image, 90, 0);
imageflip($image, IMG_FLIP_HORIZONTAL);
break;
case 8:
// 逆时针90°
$image = imagerotate($image, 90, 0);
break;
}
// 输出图片
switch ($mime) {
case 'image/jpeg':
header('Content-Type: image/jpeg');
imagejpeg($image, null, 90); // 90% 质量
break;
case 'image/png':
header('Content-Type: image/png');
imagepng($image, null, 9); // 9级压缩
break;
case 'image/gif':
header('Content-Type: image/gif');
imagegif($image);
break;
}
imagedestroy($image);
}
/*获取图片方向*/
function getImageOrientation($imagePath) {
if (!function_exists('exif_read_data')) {
return return_data(1, 'EXIF扩展未启用');
}
if (!file_exists($imagePath)) {
return return_data(2, '文件不存在');
}
$exif = @exif_read_data($imagePath);
if($exif===null)return return_data(2,'方向无法读取:'.$imagePath);
$orientation=0;
if ($exif && isset($exif['Orientation'])) {
$orientation= (int)$exif['Orientation'];
}
// 方向值说明:
// 1 = 正常
// 2 = 水平翻转
// 3 = 旋转180°
// 4 = 垂直翻转
// 5 = 顺时针90°+水平翻转
// 6 = 顺时针90°
// 7 = 逆时针90°+水平翻转
// 8 = 逆时针90°
$txt='';
switch ($orientation) {
case 1:
$txt="正常方向 (0°)";
break;
case 2:
$txt="水平翻转";
break;
case 3:
$txt="旋转 180°";
break;
case 4:
$txt="垂直翻转";
break;
case 5:
$txt="顺时针 90° + 水平翻转";
break;
case 6:
$txt="顺时针 90°";
break;
case 7:
$txt="逆时针 90° + 水平翻转";
break;
case 8:
$txt="逆时针 90°";
break;
default:
$txt="未知方向";
break;
}
return return_data(0, $orientation.chr(32).$txt, array('orientation'=>$orientation, 'description'=>$txt));
}
function return_data($code=0,$msg='',$data=array()){
return array('code'=>$code, 'msg'=>$msg, 'data'=>$data);
}
PHP try catch示例
try {
throw new Exception("错误消息", 404);
} catch (Exception $e) {
echo "消息: " . $e->getMessage() . "\n"; // 错误消息
echo "代码: " . $e->getCode() . "\n"; // 404
echo "文件: " . $e->getFile() . "\n"; // 文件路径
echo "行号: " . $e->getLine() . "\n"; // 行号
echo "回溯: " . $e->getTraceAsString(); // 调用栈
}
php判断png/gif/webp是否背景透明
function isPngTransparent($filePath) {
$image = imagecreatefrompng($filePath);
if (!$image) return false;
// 获取图像尺寸
$width = imagesx($image);
$height = imagesy($image);
// 检查alpha通道
if (imagecolortransparent($image) >= 0) {
return true;
}
// 逐像素检查透明度
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$color = imagecolorat($image, $x, $y);
$alpha = ($color >> 24) & 0xFF;
if ($alpha > 0) {
//imagedestroy($image);
return true;
}
}
}
imagedestroy($image);
return false;
}
function isGifTransparent($filePath) {
$image = imagecreatefromgif($filePath);
if (!$image) return false;
// 获取透明色索引
$transparentIndex = imagecolortransparent($image);
// 如果有透明色索引
if ($transparentIndex >= 0) {
// 获取调色板中的透明色
$transparentColor = imagecolorsforindex($image, $transparentIndex);
if ($transparentColor['alpha'] == 127) {
//imagedestroy($image);
return true;
}
}
imagedestroy($image);
return false;
}
function isWebpTransparent($filePath) {
if (!function_exists('imagecreatefromwebp')) {
throw new Exception('WebP支持未启用');
}
$image = imagecreatefromwebp($filePath);
if (!$image) return false;
$width = imagesx($image);
$height = imagesy($image);
// WebP支持alpha通道,检查方式类似PNG
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$color = imagecolorat($image, $x, $y);
$alpha = ($color >> 24) & 0xFF;
if ($alpha > 0) {
//imagedestroy($image);
return true;
}
}
}
imagedestroy($image);
return false;
}
php遍历获取当前目录及子目录下所有文件
/**
* SELF_FIRST 目录从浅到深返回
* CHILD_FIRST 先处理子节点,目录从深到浅返回
*/
function getAllFilesWithSize() {
$files = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($this->cacheBaseDir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $SplFileInfo) {
if ($SplFileInfo->isFile()) {
$files[] = [
'getRealPath' => $SplFileInfo->getRealPath(),
'getPathname' => $SplFileInfo->getPathname(),
'getExtension' => $SplFileInfo->getExtension(),
'getFilename' => $SplFileInfo->getFilename(),
'getSize' => $SplFileInfo->getSize(),
'getType' => $SplFileInfo->getType(),
];
}
}
return $files;
}参考:
PHP标准库 https://www.php.net/manual/zh/book.spl.php
递归目录迭代器 https://www.php.net/manual/zh/class.recursivedirectoryiterator.php
JSON 网络令牌JWT(JSON Web Token)
参考:https://zhuanlan.zhihu.com/p/12876076909
1、什么是JWT?
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间以 JSON 格式安全地传输信息。它通常用于身份验证和授权流程中,特别是在无状态的 RESTful API 和现代 Web 应用中。
2、什么时候应该使用 JSON Web Token?
以下是 JSON Web Token (JWT) 的一些适用场景:
授权(Authorization): 这是 JWT 最常见的用途。用户登录后,后续的每个请求都会携带 JWT,从而允许用户访问该令牌所授权的路由、服务和资源。单点登录(Single Sign On)是当前广泛使用 JWT 的一个功能,因为它具有较小的开销,并且可以轻松跨不同域名使用。
信息交换(Information Exchange): JWT 是在各方之间安全传输信息的一种好方式。由于 JWT 可以签名(例如,使用公钥/私钥对),你可以确认发送者的身份。此外,由于签名是基于头部(header)和负载(payload)计算得出的,你还可以验证内容是否被篡改。
3、JSON Web Token 的结构是什么?
在紧凑形式中,JSON Web Token(JWT)由三部分组成,各部分之间通过点号(.)分隔:
头部(Header)
负载(Payload)
签名(Signature)
JWT有效期可以在 $payload 中设置(建议),也可以通过在 $secretKey 原有秘钥后加日期,动态改变秘钥来实现
PHP生成和验证jwt
//生成JWT令牌
function jwt_create($payload, $secretKey){
function base64url_encode($data){return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');}
// 定义 Header、Payload 和密钥
$header = array('alg' => 'HS256');
// 编码 Header 和 Payload
$headerEncoded = base64url_encode(json_encode($header));
$payloadEncoded = base64url_encode(json_encode($payload));
// 生成签名(HMAC-SHA256)
$signatureInput = "$headerEncoded.$payloadEncoded";
$signature = hash_hmac('sha256', $signatureInput, $secretKey, true);
$signatureEncoded = base64url_encode($signature);
// 组合完整 JWT
$jwt = "$headerEncoded.$payloadEncoded.$signatureEncoded";
return $jwt;
}
//验证JWT令牌并获取数据
function jwt_verify($jwt,$secretKey){
function base64url_encode($data){return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');}
function base64url_decode($data) {$padding = strlen($data) % 4; if($padding > 0){$data .= str_repeat('=', 4 - $padding);} return base64_decode(strtr($data, '-_', '+/'));}
// 提取已生成的 JWT 各部分
list($header, $payload, $signature) = explode('.', $jwt);
// 重新计算签名
$recomputedSignature = base64url_encode(
hash_hmac('sha256', "$header.$payload", $secretKey, true)
);
// 比较签名
if($signature === $recomputedSignature){
$res=json_decode(base64url_decode($payload),true);
if(is_array($res) && isset($res['userid']))return $res['userid'];
}
return false;
}php获取指定目录的所有子孙目录
方法一:scandir + 递归
function getAllSubdirectories($path) {
$subdirs = [];
$items = scandir($path);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$fullPath = $path . DIRECTORY_SEPARATOR . $item;
if (is_dir($fullPath)) {
// 添加当前目录
$subdirs[] = $fullPath;
// 递归获取子目录的子目录
$subdirs = array_merge($subdirs, getAllSubdirectories($fullPath));
}
}
return $subdirs;
}
// 使用示例
$allSubdirs = getAllSubdirectories('/path/to/directory');
print_r($allSubdirs);
方法二:使用 RecursiveDirectoryIterator(推荐)
function getAllSubdirectoriesIterator($path) {
$subdirs = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDir()) {
$subdirs[] = $fileinfo->getPathname();
}
}
return $subdirs;
}
// 使用示例
$allSubdirs = getAllSubdirectoriesIterator('/path/to/directory');
print_r($allSubdirs);
分别用js、php实现GS84与GCJ02两种坐标系的互转
高德地图: 使用 GCJ-02 坐标系,也称为火星坐标系。它是基于国际标准 WGS-84 坐标系进行加密处理得到的。
百度地图: 采用 BD-09 坐标系,这是在 GCJ-02 基础上进行二次加密而来的,具有更高的安全性和隐私保护。
天地图: 使用 CGCS2000 坐标系,该坐标系与 WGS-84 存在微小偏差,在要求不高的情况下可以直接与 WGS-84 互换使用。
js
/**
* WGS84转GCJ02(火星坐标系)
* @param {number} wgsLon WGS84坐标系的经度
* @param {number} wgsLat WGS84坐标系的纬度
* @returns {Array} GCJ02坐标 [经度, 纬度]
*/
function wgs84ToGcj02(wgsLon, wgsLat) {
const PI = 3.14159265358979324;
const a = 6378245.0;
const ee = 0.00669342162296594323;
if (outOfChina(wgsLat, wgsLon)) {
return [wgsLon, wgsLat];
}
let dLat = transformLat(wgsLon - 105.0, wgsLat - 35.0);
let dLon = transformLon(wgsLon - 105.0, wgsLat - 35.0);
const radLat = wgsLat / 180.0 * PI;
let magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
const sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
const gcjLat = wgsLat + dLat;
const gcjLon = wgsLon + dLon;
return [gcjLon, gcjLat];
}
/**
* GCJ02(火星坐标系)转WGS84
* @param {number} gcjLon GCJ02坐标系的经度
* @param {number} gcjLat GCJ02坐标系的纬度
* @returns {Array} WGS84坐标 [经度, 纬度]
*/
function gcj02ToWgs84(gcjLon, gcjLat) {
const PI = 3.14159265358979324;
const a = 6378245.0;
const ee = 0.00669342162296594323;
if (outOfChina(gcjLat, gcjLon)) {
return [gcjLon, gcjLat];
}
let dLat = transformLat(gcjLon - 105.0, gcjLat - 35.0);
let dLon = transformLon(gcjLon - 105.0, gcjLat - 35.0);
const radLat = gcjLat / 180.0 * PI;
let magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
const sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
const wgsLat = gcjLat - dLat;
const wgsLon = gcjLon - dLon;
return [wgsLon, wgsLat];
}
function transformLat(x, y) {
let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformLon(x, y) {
let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
return ret;
}
function outOfChina(lat, lon) {
return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
}
// 使用示例
// let gcjCoord = wgs84ToGcj02(116.404, 39.915); // WGS84转GCJ02
// let wgsCoord = gcj02ToWgs84(116.404, 39.915); // GCJ02转WGS84
PHP
/**
* WGS84转GCJ02(火星坐标系)
* @param float $wgsLon WGS84坐标系的经度
* @param float $wgsLat WGS84坐标系的纬度
* @return array GCJ02坐标 [经度, 纬度]
*/
function wgs84ToGcj02($wgsLon, $wgsLat) {
$PI = 3.14159265358979324;
$a = 6378245.0;
$ee = 0.00669342162296594323;
if (outOfChina($wgsLat, $wgsLon)) {
return array($wgsLon, $wgsLat);
}
$dLat = transformLat($wgsLon - 105.0, $wgsLat - 35.0);
$dLon = transformLon($wgsLon - 105.0, $wgsLat - 35.0);
$radLat = $wgsLat / 180.0 * $PI;
$magic = sin($radLat);
$magic = 1 - $ee * $magic * $magic;
$sqrtMagic = sqrt($magic);
$dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * $PI);
$dLon = ($dLon * 180.0) / ($a / $sqrtMagic * cos($radLat) * $PI);
$gcjLat = $wgsLat + $dLat;
$gcjLon = $wgsLon + $dLon;
return array($gcjLon, $gcjLat);
}
/**
* GCJ02(火星坐标系)转WGS84
* @param float $gcjLon GCJ02坐标系的经度
* @param float $gcjLat GCJ02坐标系的纬度
* @return array WGS84坐标 [经度, 纬度]
*/
function gcj02ToWgs84($gcjLon, $gcjLat) {
$PI = 3.14159265358979324;
$a = 6378245.0;
$ee = 0.00669342162296594323;
if (outOfChina($gcjLat, $gcjLon)) {
return array($gcjLon, $gcjLat);
}
$dLat = transformLat($gcjLon - 105.0, $gcjLat - 35.0);
$dLon = transformLon($gcjLon - 105.0, $gcjLat - 35.0);
$radLat = $gcjLat / 180.0 * $PI;
$magic = sin($radLat);
$magic = 1 - $ee * $magic * $magic;
$sqrtMagic = sqrt($magic);
$dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * $PI);
$dLon = ($dLon * 180.0) / ($a / $sqrtMagic * cos($radLat) * $PI);
$wgsLat = $gcjLat - $dLat;
$wgsLon = $gcjLon - $dLon;
return array($wgsLon, $wgsLat);
}
function transformLat($x, $y) {
$ret = -100.0 + 2.0 * $x + 3.0 * $y + 0.2 * $y * $y + 0.1 * $x * $y + 0.2 * sqrt(abs($x));
$ret += (20.0 * sin(6.0 * $x * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
$ret += (20.0 * sin($y * M_PI) + 40.0 * sin($y / 3.0 * M_PI)) * 2.0 / 3.0;
$ret += (160.0 * sin($y / 12.0 * M_PI) + 320 * sin($y * M_PI / 30.0)) * 2.0 / 3.0;
return $ret;
}
function transformLon($x, $y) {
$ret = 300.0 + $x + 2.0 * $y + 0.1 * $x * $x + 0.1 * $x * $y + 0.1 * sqrt(abs($x));
$ret += (20.0 * sin(6.0 * $x * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
$ret += (20.0 * sin($x * M_PI) + 40.0 * sin($x / 3.0 * M_PI)) * 2.0 / 3.0;
$ret += (150.0 * sin($x / 12.0 * M_PI) + 300.0 * sin($x / 30.0 * M_PI)) * 2.0 / 3.0;
return $ret;
}
function outOfChina($lat, $lon) {
return $lon < 72.004 || $lon > 137.8347 || $lat < 0.8293 || $lat > 55.8271;
}
// 使用示例
// $gcjCoord = wgs84ToGcj02(116.404, 39.915); // WGS84转GCJ02
// $wgsCoord = gcj02ToWgs84(116.404, 39.915); // GCJ02转WGS84
百度地图API在CodeIgniter中的实现:
地理编码、全球逆地理编码、坐标系转换
/**
* 百度地图开放平台接口
*/
class Baidumap extends MY_Controller{
public function __construct(){
parent::__construct();
//百度地图配置
define('BAIDUMAP_CONFIG', array(
'ak' => '百度地图AK',
'sk' => '百度地图SK',
'host' => 'https://api.map.baidu.com',
));
}
//计算请求验证的SN值
private function caculateAKSN($sk, $uri, $param, $method = 'GET'){
if($method === 'POST'){
ksort($param);
}
$querystring = http_build_query($param);
return md5(urlencode($uri.'?'.$querystring.$sk));
}
/**
* 地理编码
* 为保证高德、腾讯、百度地图通用,所以获取的是国测局坐标gcj02ll
* https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-base
*/
public function geocoder(){
$address=trim(G('address'));
if(empty($address))return json(101,'参数address不能为空');
$city=trim(G('city'));
$city OR $city='济南市';
$coord_type=G('coordtype', 'gcj02ll'); //或百度坐标bd09ll
$uri = '/geocoding/v3/';
//构造请求串数组
$param = array(
'address' => $address,
'city' => $city,
'ret_coordtype' => $coord_type,
'output' => 'json',
'ak' => BAIDUMAP_CONFIG['ak'],
);
//调用sn计算函数,默认get请求
$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);
$res=my_curl(BAIDUMAP_CONFIG['host'].$uri,'GET',$param);
$res=json_decode($res,true);
$res['result']['location']['lng']=(string)$res['result']['location']['lng'];
if(!is_array($res))return json(102,'请求失败,请重试');
if($res['status']==0){
$data=$res['result'];
$data['lon_lat']=$data['location']['lng'].','.$data['location']['lat'];
$data['lat_lon']=$data['location']['lat'].','.$data['location']['lng'];
unset($data['location']);
return json(0,'',$data);
}else{
return json($res['status'],$res['message']);
}
}
/**
* 坐标转换
* 把国测局坐标gcj02ll转为百度坐标
* https://lbsyun.baidu.com/faq/api?title=webapi/guide/changeposition-base
* coord坐标经度在前
$model 转换方式可选值:
1:amap/tencent to bd09ll
2:gps to bd09ll
3:bd09ll to bd09mc
4:bd09mc to bd09ll
5:bd09ll to amap/tencent
6:bd09mc to amap/tencent
*/
public function getconv(){
$coord=str_replace(',',',',trim(G('coord')));
if(!preg_match('/^\d+(\.\d+)?,\d+(\.\d+)?$/',$coord))return json(101,'坐标格式错误');
$model=G('model','1');
$model=is_id($model) ? (int)$model : 1;
$uri = '/geoconv/v2/';
//需转换的源坐标,多组坐标以";"分隔
$param=array(
'coords' => $coord,
'model' => $model,
'output' => 'json',
'ak' => BAIDUMAP_CONFIG['ak'],
);
$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);
$res = my_curl(BAIDUMAP_CONFIG['host'].$uri, 'GET', $param);
//echo $res;
$res = json_decode($res,true);
if(!is_array($res))return json(102,'请求失败,请重试');
if($res['status']!=0){
return json($res['status'], $res['message']);
}
$data=array(
'lon_lat' => $res['result'][0]['x'].','.$res['result'][0]['y'],
'lat_lon' => $res['result'][0]['y'].','.$res['result'][0]['x'],
);
return json(0,'',$data);
}
/**
* 全球逆地理编码
* https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-abroad-base
* coord坐标纬度在前,格式:纬度,经度
*/
public function reverse_geocoder(){
$coord=str_replace(',',',',trim(G('coord')));
if(empty($coord))return json(101,'参数coord不能为空');
if(!preg_match('/^\d+(\.\d+)?,\d+(\.\d+)?$/',$coord))return json(102,'坐标格式错误');
$coordtype=G('coordtype', 'gcj02ll'); //或百度坐标bd09ll
$uri = '/reverse_geocoding/v3';
$param=array(
'location' => $coord,
'coordtype' => $coordtype,
'extensions_poi' => '1',
'radius' => 50, //poi半径:0-3000米
'region_data_source' => 1, //行政区划数据的来源:1统计局(把开发区作为行政区划返回),2民政部
'entire_poi' => 1,
'sort_strategy' => 'distance',
//'poi_types' => '交通设施|公交线路|铁路',
'output' => 'json',
'ak' => BAIDUMAP_CONFIG['ak'],
);
//printr($param);
$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);
$res = my_curl(BAIDUMAP_CONFIG['host'].$uri, 'GET', $param);
$res = json_decode($res,true);
if($res['status']!=0){
return json($res['status'], $res['message']);
}
$result=$res['result'];
$data=array(
'formatted_address' => $result['formatted_address'],
'formatted_address_poi' => $result['formatted_address_poi'],
'addressComponent' => $result['addressComponent'],
);
json(0,'',$data);
}
}
PHP 中将字符串转换为字符数组
方法1:使用 str_split() 函数
$string = "Hello"; $array = str_split($string); print_r($array); // 输出:Array ( [0] => H [1] => e [2] => l [3] => l [4] => o )
方法2:使用 preg_split() 正则表达式分割
preg_split() 函数配合 UTF-8 模式可以正确处理多字节字符(如中文、日文、表情符号等),将它们分割为单个字符的数组。
$string = "你好,World";
$array = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
print_r($array);
// 输出:Array ( [0] => 你 [1] => 好 [2] => , [3] => W [4] => o [5] => r [6] => l [7] => d )
方法3:使用 mb_str_split() 处理多字节字符(PHP 7.4+)
$string = "你好"; $array = mb_str_split($string); print_r($array); // 输出:Array ( [0] => 你 [1] => 好 )
preg_split()参数说明:
'//u' - 正则表达式模式,u 修饰符表示 UTF-8 模式
$string - 要分割的字符串
-1 - 表示不限制分割次数
PREG_SPLIT_NO_EMPTY - 过滤掉空元素
PHP按目标比例和尺寸缩小并裁切图片
重点是关于图片翻转、旋转部分的处理
/**
* 按目标比例和尺寸缩小并裁切图片,如果原图尺寸小于目标尺寸,则只按比例裁切
* @param $img_name 原图片名称(不是路径)
* @param $width 目标图片宽度,如果原图宽度不等于目标宽度,则原图按目标比例缩放后再裁切
* @param $width 目标图片高度
*/
private function img_convert_do($img_name, $width, $height){
//$new_name=date('YmdHis').my_str_shuffle(5,'number,lower').substr($img_name,strrpos($img_name,'.'));
$new_name=date('YmdHis').my_str_shuffle(5,'number,lower').'.jpg';
$old_img=SRC_PATH.$img_name;
$new_img=DST_PATH.$new_name;
/*
exif_read_data($old_img)结果如下:
Array
(
[FileName] => a.jpg
[FileDateTime] => 1750318340
[FileSize] => 17175
[FileType] => 2
[MimeType] => image/jpeg
[SectionsFound] => ANY_TAG, IFD0, EXIF
[COMPUTED] => Array
(
[html] => width="536" height="404"
[Height] => 404
[Width] => 536
[IsColor] => 1
[ByteOrderMotorola] => 1
)
[Orientation] => 8
[Exif_IFD_Pointer] => 318
[UndefinedTag:0xEA1C] =>
)
*/
$exif_res=@exif_read_data($old_img);
if($exif_res===false){
$size_arr = @getimagesize($old_img);
if(!is_array($size_arr)){
exit('图片尺寸获取失败:'.$img_name);
}
$w = $size_arr[0];
$h= $size_arr[1];
//printr($size_arr);
}else{
$w=$exif_res['COMPUTED']['Width'];
$h=$exif_res['COMPUTED']['Height'];
//printr($exif_res);
}
$source = imagecreatefromstring(file_get_contents($old_img));
switch(array_item($exif_res,'Orientation')){
case 1: //无翻转
break;
case 2: //水平翻转
imageflip($source,IMG_FLIP_HORIZONTAL);
break;
case 3: //旋转180度
$source = imagerotate($source,180,0);
break;
case 4: //垂直翻转
imageflip($source,IMG_FLIP_VERTICAL);
break;
case 5: //顺时针旋转90度+水平翻转
$source = imagerotate($source,-90,0);
imageflip($source,IMG_FLIP_HORIZONTAL);
break;
case 6: //顺时针旋转90度
$source = imagerotate($source,-90,0);
break;
case 7: //逆时针旋转90度+水平翻转
$source = imagerotate($source,90,0);
imageflip($source,IMG_FLIP_HORIZONTAL);
break;
case 8: //逆时针旋转90度
$source = imagerotate($source,90,0);
break;
default: //png等无方向属性
}
switch(array_item($exif_res,'Orientation')){
case 5:
case 6:
case 7:
case 8:
//旋转90度的图片扶正后要宽高值要互换
$tmp=$w;
$w=$h;
$h=$tmp;
unset($tmp);
break;
}
$src_w=$w;
$src_h=$h;
if($w/$h < $width/$height){
$h=ceil($w*$height/$width);
}else if($w/$h > $width/$height){
$w=ceil($width*$h/$height);
}else{
//比例一致时,如果原图小于100K则直接复制
if(filesize($old_img)<100*1024){
copy($old_img,$new_img);
return $new_name;
}
}
if($w<$width){
$width=$w;
$height=$h;
}
//echo $w,'x',$h,'<br>',$width,'x',$height,'<br>';return;
//新建真彩色图像
$im = imagecreatetruecolor($width, $height);
//为图像分配颜色
//$color = imagecolorallocate($im, 255,255,255);
//从指定坐标开始填充颜色
//imagefill($im, 0,0, $color);
//从字符串的图像流中新建图像,可以自动识别文件类型,但是比 imagecreatefromjpeg 等多消耗内存
//totxt($old_img, FCPATH.'photo.txt', true);
//$source = imagecreatefromstring(file_get_contents($old_img));
/*
从 x、y 坐标 src_x、src_y 开始,将 src_image 的一部分复制到 dst_image 上,
宽度为 src_width,高度为 src_height。定义的部分将被复制到 x,y 坐标 dst_x 和 dst_y 上。
imagecopy(
GdImage $dst_image,
GdImage $src_image,
int $dst_x,
int $dst_y,
int $src_x,
int $src_y,
int $src_width,
int $src_height
): bool
从 src_image 位置(src_x、src_y)开始取出一个宽度为 src_width 高度为 src_height 的矩形区域,
并将其放置在 dst_image 中位置从(dst_x、dst_y)开始宽度为 dst_width 高度为 dst_height 的矩形区域中。
imagecopyresampled(
GdImage $dst_image,
GdImage $src_image,
int $dst_x,
int $dst_y,
int $src_x,
int $src_y,
int $dst_width,
int $dst_height,
int $src_width,
int $src_height
): bool
输出图象到浏览器或文件
image 由图象创建函数(例如imagecreatetruecolor())返回的 GdImage 对象。
file 文件保存的路径或者已打开的流资源(此方法返回后自动关闭该流资源),如果未设置或为 null,将会直接输出原始图象流。
quality 为可选项,范围从 0(最差质量,文件最小)到 100(最佳质量,文件最大)。默认值(-1)使用 IJG 默认的质量值(大约 75)。
imagejpeg(GdImage $image, resource|string|null $file = null, int $quality = -1): bool
*/
//执行剪裁(高质量重采样)
//($src_h-$h)/4 此处是4而不是2的原因:尽量从上边开始剪裁,避免上边剪裁过大,导致头到照片顶部留白太小甚至剪到头
imagecopyresampled($im, $source, 0,0, $src_w>$w?($src_w-$w)/2:0,$src_h>$h?($src_h-$h)/3:0, $width,$height, $w,$h);
imagedestroy($source);
//保存到文件
imagejpeg($im, $new_img, 90);
imagedestroy($im);
//exit('ok');
return $new_name;
}