PHP 扩展包 – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Thu, 21 May 2026 11:34:04 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 mime_content_type — 检测文件的 MIME 类型 实现,替换为基于 Mime-type Detection league/mime-type-detection 的实现 https://www.shuijingwanwq.com/2023/06/16/7739/ https://www.shuijingwanwq.com/2023/06/16/7739/#respond Fri, 16 Jun 2023 01:35:38 +0000 https://www.shuijingwanwq.com/?p=7739 浏览量: 81 1、参考:同样的 CSS 文件(其 Etag 相等),由于 响应的 Content-Type 的差异,进而导致网页界面的差异。发现 响应的 Content-Type 是基于 mime_content_type — 检测文件的 MIME 类型 实现,最终基于 Mime-type Detection league/mime-type-detection league/mime-type-detection 实现。按内容检测,回退到按扩展检测。如图1
基于 Mime-type Detection league/mime-type-detection league/mime-type-detection 实现

图1



use League\MimeTypeDetection\ExtensionMimeTypeDetector;

$this->detector = new ExtensionMimeTypeDetector();

Log::info(
	$return['asset_key'],
	[
		$return['asset_key'],
		$this->detector->detectMimeTypeFromPath($fullPath),
	]
);




[2023-05-18 14:00:04] local.INFO: apps/internal/code-display/blocks/code-display.blade.php [
    "apps/internal/code-display/blocks/code-display.blade.php",
    "application/x-httpd-php"
] 
[2023-05-18 14:00:04] local.INFO: apps/internal/comment/.env.local [
    "apps/internal/comment/.env.local",
    null
] 
[2023-05-18 14:00:04] local.INFO: apps/internal/comment/app.json [
    "apps/internal/comment/app.json",
    "application/json"
] 
[2023-05-18 14:00:04] local.INFO: apps/internal/comment/assets/app.6e10b7.css [
    "apps/internal/comment/assets/app.6e10b7.css",
    "text/css"
] 
[2023-05-18 14:00:04] local.INFO: apps/internal/comment/assets/app.f8ed5d.js [
    "apps/internal/comment/assets/app.f8ed5d.js",
    "application/javascript"
] 
[2023-05-18 14:00:05] local.INFO: apps/internal/custom-button/assets/images/ali-express-large.8fdc24.png [
    "apps/internal/custom-button/assets/images/ali-express-large.8fdc24.png",
    "image/png"
] 
[2023-05-18 14:00:07] local.INFO: apps/internal/sharing-incentives/assets/images/sparkles.a0d182.jpg [
    "apps/internal/sharing-incentives/assets/images/sparkles.a0d182.jpg",
    "image/jpeg"
] 


2、查看生成的 Content-Type ,符合预期。当对应的扩展名不存在时,会返回 null 。此时需要回退处理一下。


Log::info(
	$return['asset_key'],
	[
		$return['asset_key'],
		$this->detector->detectMimeTypeFromPath($fullPath) ?? mime_content_type($fullPath),
	]
);


]]>
https://www.shuijingwanwq.com/2023/06/16/7739/feed/ 0
在 Laravel 6 中,基于后置中间件统一处理 302 跳转后的请求查询参数丢失问题 https://www.shuijingwanwq.com/2023/06/09/7709/ https://www.shuijingwanwq.com/2023/06/09/7709/#respond Fri, 09 Jun 2023 01:07:44 +0000 setTargetUrl]]> query]]> https://www.shuijingwanwq.com/?p=7709 浏览量: 89

1、在 Laravel 6 中,带查询参数的请求,在 302 跳转后,查询参数丢失。如图1

在 Laravel 6 中,带查询参数的请求,在 302 跳转后,查询参数丢失

图1

2、原计划在具体的请求时,判断此查询参数是否存在,如果存在,则在 302 跳转后,自动带上。但是此方案,无法避免后续可能还存在着其他的请求也有类似的问题。最终决定,基于后置中间件统一处理 302 跳转后的请求查询参数丢失问题。

3、将新建的中间件放入 web 中间件组,因为其仅需要应用于 Web UI 。



    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            // ...
            \App\Http\Middleware\ThemeEditorQueryParam::class,
        ]
    ];



4、在一个控制器方法中实现 重定向到命名路由


return redirect()->route('account_login');


5、基于 league/uri 添加查询参数,执行 composer require league/uri-components、composer require league/uri。可参考:在 PHP 7.4 中,针对 URI ,基于 league/uri 添加查询参数

6、中间件中的代码实现如下

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\RedirectResponse;
use League\Uri\Uri;
use League\Uri\UriModifier;
 
class ThemeEditorQueryParam
{
    /**
     * Handle an incoming request.
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);
 
        // 当响应为重定向时,判断请求中是否存在请求参数:oseid
        if ($response instanceof RedirectResponse) {
            $oseid = $request->query('oseid'); // 主题编辑器的主题的预览(2.0)
            if (isset($oseid)) {
                $uri = Uri::createFromString($response->getTargetUrl());
                $editorUri = UriModifier::mergeQuery($uri, 'oseid=' . $oseid);
                $response->setTargetUrl($editorUri->jsonSerialize());
            }
        }
 
        return $response;
    }
}

7、打开网址:https://xxx.local/products/ri-yuan-ce-shi-bian-zhong?variant=656 ,不带 oseid 参数,302 跳转至:https://xxx.local/account/login 。符合预期。如图2

打开网址:https://xxx.local/products/ri-yuan-ce-shi-bian-zhong?variant=656 ,不带 oseid 参数,302 跳转至:https://xxx.local/account/login 。符合预期

图2

8、打开网址:https://xxx.local/products/ri-yuan-ce-shi-bian-zhong?variant=656&oseid=6462e3db4cd20 ,有带 oseid 参数,302 跳转至:https://xxx.local/account/login?oseid=6462e3db4cd20 。符合预期。如图3

打开网址:https://xxx.local/products/ri-yuan-ce-shi-bian-zhong?variant=656&oseid=6462e3db4cd20 ,有带 oseid 参数,302 跳转至:https://xxx.local/account/login?oseid=6462e3db4cd20 。符合预期

图3

]]>
https://www.shuijingwanwq.com/2023/06/09/7709/feed/ 0
在 PHP 7.4 中,针对 URI ,基于 league/uri 添加查询参数 https://www.shuijingwanwq.com/2023/06/02/7684/ https://www.shuijingwanwq.com/2023/06/02/7684/#respond Fri, 02 Jun 2023 01:14:34 +0000 https://www.shuijingwanwq.com/?p=7684 浏览量: 83 1、在 Laravel 6 中,基于判断字符串中是否包含 ?,有一个简单的实现


use Illuminate\Support\Str;

if (Str::contains($themeAwareUrlPath, '?')) {
	$suffix = '&';
} else {
	$suffix = '?';
}

return new HtmlString($prefix.$themeAwareUrlPath.$suffix.'d='.config('app.url'));


2、不过此实现,一些复杂的 URI 未考虑到。例:?d=3&e=5#6,会被替换为:?d=3&e=5#6&d=https://xxx.com 。 3、最终决定基于 league/uri 添加查询参数,执行 composer require league/uri-components、composer require league/uri 4、代码实现如下


use League\Uri\Uri;
use League\Uri\UriModifier;

$uri = Uri::createFromString($themeAwareUrlPath);
$cdnUri = UriModifier::mergeQuery($uri, 'd=' . config('app.url'));

return new HtmlString($prefix . $cdnUri->jsonSerialize());


5、基于一些常见的 URI ,替换后的结果如下,符合预期


$themeAwareUrlPath = '9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg';

$themeAwareUrlPath .= '?a=3';
string(102) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?a=3&d=https://xxx.local"

$themeAwareUrlPath .= '?d=3';
string(98) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?d=https://xxx.local"

$themeAwareUrlPath .= '?d=3&e=5#6';
string(104) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?d=https://xxx.local&e=5#6"

$themeAwareUrlPath .= '?a=1&d=3&e=5#6';
string(108) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?a=1&d=https://xxx.local&e=5#6"

$themeAwareUrlPath .= '?#';
string(100) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?&d=https://xxx.local#"

$themeAwareUrlPath .= '?';
string(99) "9919b10c-9217-44eb-8488-198a321067cc/assets/images/default-banner.64bbdd.jpg?&d=https://xxx.local"


]]>
https://www.shuijingwanwq.com/2023/06/02/7684/feed/ 0
在 Windows 10 中,一个 ZIP 压缩文件(基于 ZipStream PHP),基于 360 压缩解压成功,基于 WinRAR 解压缩失败 https://www.shuijingwanwq.com/2023/04/27/7591/ https://www.shuijingwanwq.com/2023/04/27/7591/#respond Thu, 27 Apr 2023 01:38:48 +0000 https://www.shuijingwanwq.com/?p=7591 浏览量: 85 1、参考:在 Laravel 6 中基于 ZipStream PHP ,将 zip 文件流式传输到 S3 存储桶。参考:在 PHP 7.4 中基于 fopen 生成一个流资源,操作了磁盘,调整为完全基于内存的实现。基于 360 压缩解压成功,基于 WinRAR 解压缩失败。如图1
参考:在 Laravel 6 中基于 ZipStream PHP ,将 zip 文件流式传输到 S3 存储桶。参考:在 PHP 7.4 中基于 fopen 生成一个流资源,操作了磁盘,调整为完全基于内存的实现。基于 360 压缩解压成功,基于 WinRAR 解压缩失败。

图1

2、在 Linux 上用 7z 进行解压,虽然能解压出来,但是其实还是有报错信息。如图2
在 Linux 上用 7z 进行解压,虽然能解压出来,但是其实还是有报错信息

图2



ERRORS:
Unexpected end of archive

--
Path = assets_new.zip
Type = zip
ERRORS:
Unexpected end of archive
Physical Size = 781844



Archives with Errors: 1

Open Errors: 1


3、参考:https://maennchen.dev/ZipStream-PHP/guide/FlySystem.html 。在上传流之前,需要先执行:$zip->finish(); 调整前:


        $name = $this->themeInstallationTask->themeInstallation->theme_id . '/assets.zip';

        $stream = fopen('php://memory', 'w+');
        $options = new Archive();
        $options->setOutputStream($stream);
        $zip = new ZipStream($name, $options);

		$zip->addFile(str_replace('\\', '/', str_replace(self::$destination . '/', '', $file->getPathname())), $file->getContents());

        Storage::disk(config('theme_asset.filesystem.disk'))->putStream(
            $name,
            $stream,
            config('theme_asset.filesystem.options')
        );
		
		$zip->finish();
        fclose($stream);


调整后:


        $name = $this->themeInstallationTask->themeInstallation->theme_id . '/assets.zip';

        $stream = fopen('php://memory', 'w+');
        $options = new Archive();
        $options->setOutputStream($stream);
        $zip = new ZipStream($name, $options);

		$zip->addFile(str_replace('\\', '/', str_replace(self::$destination . '/', '', $file->getPathname())), $file->getContents());

        $zip->finish();

        Storage::disk(config('theme_asset.filesystem.disk'))->putStream(
            $name,
            $stream,
            config('theme_asset.filesystem.options')
        );
		
        fclose($stream);


4、基于 360 压缩解压成功,基于 WinRAR 解压缩成功。]]>
https://www.shuijingwanwq.com/2023/04/27/7591/feed/ 0
在 Laravel 6 中基于 ZipStream PHP ,将 zip 文件流式传输到 S3 存储桶 https://www.shuijingwanwq.com/2023/04/18/7554/ https://www.shuijingwanwq.com/2023/04/18/7554/#respond Tue, 18 Apr 2023 01:36:57 +0000 https://www.shuijingwanwq.com/?p=7554 浏览量: 83 1、当在请求 S3 存储桶时,遇到了限流问题:Please reduce your request rate,进而导致请求失败。如图1
当在请求 S3 存储桶时,遇到了限流问题:Please reduce your request rate,进而导致请求失败

图1

2、最终实现代码如下


$name = $themeId . '/assets.zip';

$zip = new ZipStream($name);

$zip->addFile('apps/internal/back-top/assets/app.9ce8af2.js', '0000000000000');

$res = Storage::disk(config('theme_asset.filesystem.disk'))->put(
	$name,
	$zip->finish(),
	config('theme_asset.filesystem.options')
);


3、在浏览器中打开 S3 对应的 CDN 地址:https://xxx.cloudfastin.com/static/xxx/98cb73f9-e61a-40b1-a27a-3beb99015e5e/assets.zip 。文件可下载,确认文件上传至 S3 成功。如图2
在浏览器中打开 S3 对应的 CDN 地址:https://themes-statics-test.cloudfastin.com/static/xxx/98cb73f9-e61a-40b1-a27a-3beb99015e5e/assets.zip 。文件可下载,确认文件上传至 S3 成功

图2

3、将下载后的文件解压缩,失败,提示:该文件为非压缩文件,无法打开。如图3
将下载后的文件解压缩,失败,提示:该文件为非压缩文件,无法打开

图3

4、使用 putStream 方法,且设置 ZipStream 对象的选项中,输出流。下载后,解压缩成功。如图4
使用 putStream 方法,且设置 ZipStream 对象的选项中,输出流。下载后,解压缩成功

图4



$name = $themeId . '/assets.zip';

$tmp = tempnam(sys_get_temp_dir(), 'zip_stream');
$stream = fopen($tmp, 'w+');

$options = new Archive();
//$options->setContentType('application/x-zip-compressed');
$options->setOutputStream($stream);
$zip = new ZipStream($name, $options);

$zip = new ZipStream($name);

$zip->addFile('apps/internal/back-top/assets/app.9ce8af2.js', '0000000000000');

$zip->finish();

$res = Storage::disk(config('theme_asset.filesystem.disk'))->putStream(
	$name,
	$stream,
	config('theme_asset.filesystem.options')
);


fclose($stream);


5、但是,如果路径中包含反斜杠,例,路径为:css\app.6156ec.css 的文件,其路径已经变化为:css_app.6156ec.css。如图5
但是,如果路径中包含反斜杠,例,路径为:css\app.6156ec.css 的文件,其路径已经变化为:css_app.6156ec.css

图5

6、需要将 路径为:css\app.6156ec.css 的文件,替换为:css/app.6156ec.css。上传至 S3,下载后解压缩,目录结构正常。如图6
需要将 路径为:css\app.6156ec.css 的文件,替换为:css/app.6156ec.css。上传至 S3,下载后解压缩,目录结构正常

图6



$zip->addFile(str_replace('\\', '/', 'css\\app.6156ec.css'), $file->getContents());


]]>
https://www.shuijingwanwq.com/2023/04/18/7554/feed/ 0