PHP百万数据导出Excel

背景 公司业务部门经常会需要导出各种数据进行分析,因为需要的数据模板多变,产品一直没有将其规划成系统功能,每次需要的数据都是手写 SQL 语句进行导出。在写了上万行 SQL 语句以后,我对这种体力劳动感觉到了厌烦,于是和领导申请,自己利用空闲时间规划开发了一个灵活配置、适用性强的导表系统。 使用技术 API 框架 Hyperf Web 框架 Ant Design Pro Excel 扩展 xlsWriter 优化 为了尽快实现功能,导出 Excel 功能是做的同步导出,但在我的设计中应该是异步导出,因此在后续的优化中引入了 RabbitMQ 消息队列实现异步导出功能。 因为不能将 Db 对象投递到消息队列中,所以投递过去的是 SQL 的预处理语句以及对应的变量,使用Db::select($sql, $bindings);执行 SQL 。然后尝试查询百万条数据,直接就内存溢出了。 vendor/hyperf/database/src/Connection.php 271 行 /** * Run a select statement against the database. */ public function select(string $query, array $bindings = [], bool $useReadPdo = true): array { return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) { if ($this->pretending()) { return []; } // For select statements, we'll simply execute the query and return an array // of the database result set....

2022-05-19 · 4 分钟

如何记录可读性的操作日志?

前言 公司生产环境发生了一个事故,导致公司造成了损失。先说结果是因为业务人员修改参数时设置错了参数,导致事故发生。一开始业务人员认为是我们代码程序出了错误,好在有操作日志记录,于是前去查看发现,操作日志记录不全,没有记录到那个修改的参数。然后在排查代码发现没有问题之后,只能通过查找 MySQL 的 binlog 执行日志与 Nginx 的请求日志作为佐证,才将扣在身上的锅甩了出去。因此我意识到操作日志的重要性。 为什么要记录操作日志? 上文也已经说了,操作日志几乎存在于所有的系统中,尤其是后台管理系统,需要详细的记录下管理员所做出的操作,以便于更好的排查问题与溯源分析。 系统日志与操作日志的区别 系统日志 系统日志主要为了开发更好的排查问题,一般记录在日志文件中。系统日志一般是开发人员会进行查看,所以可读性不需要很高,甚至会包含一些代码信息。 操作日志 操作日志主要是为了记录使用人员的添加、修改、删除操作。一般是给用户看的,所以需要很高的可读性,能让人一眼就看明白操作了什么东西,而不需要再去结合代码进行分析。 常见的日志记录方式 自定义函数 在很多系统中都是定义了一个类似的admin_log()函数,然后在需要记录日志的地方调用这个函数传入记录的信息即可。 优点: 比较灵活,可以随意拼接自己想要记录的日志信息 在想要记录日志的地方只需调用函数就可以了 缺点: 日志的记录穿插在代码中,与代码耦合度很高,增加了代码复杂度 大块的日志信息在代码中看起来很丑 随着代码的修改可能还会需要对日志信息进行修改维护 有可能会忘记日志的记录,导致出了问题背锅 注解+ AOP 在 Java 中使用注解+ AOP 是一种很常见的日志记录方式,网上有很多这样的例子,但是在 PHP 中好像没有见过这种方式。 思考 我们现在的系统中就是使用第一种自定义函数的方式来记录操作日志的,有着很多的缺点,所以就想要寻求一种优雅的记录操作日志的方式。 在美团技术团队的《如何优雅的记录操作日志?》一文中很详细的描写了如何利用注解+AOP 实现操作日志的记录。而 PHP 也是有着注解和 AOP 的,看了这篇文章之后就一直思考该如何实现。想了很久,看了很多遍这篇文章还是感觉不是很完美,虽然把日志的记录从代码迁移到注解中与代码分离,但还是有很强的耦合,每次业务代码的修改还是需要对日志进行更新的。 想法 操作日志一般是需要记录下用户的增删改操作,对数据库增删改的操作我们一般使用 ORM 完成,所以我们可以使用 ORM 的模型事件created、updated、deleted来记录用户的增删改操作。 在其中我们可以很方便的获取到用户的操作内容,然后怎么实现可读性呢? 我的想法是通过获取表注释来明确这次操作的含义,然后通过字段注释知道操作字段的具体含义内容。如此我们就可以记录到可读性的日志内容了嘛? 实现 假如有张用户表,SQL 如下: create table tb_user ( id int auto_increment primary key, name varchar(20) null comment '姓名', sex tinyint default 0 null comment '性别' ) comment '用户'; 在其中添加一条数据:...

2022-01-04 · 1 分钟

在PhpStorm中使用PHPUnit进行单元测试

PHPUnit 是什么 PHPUnit 是一个面向 PHP 程序员的测试框架,这是一个 xUnit 的体系结构的单元测试框架。 PHPUnit 的官网地址为:https://phpunit.de/,中文镜像网站:http://www.phpunit.cn/。 安装 PHPUnit PHPUnit 有两种安装方式,一种是下载 PHAR 发行包进行全局安装,一种是使用 composer 来为某一个项目安装。 推荐使用 composer 安装,本文也是使用这种安装方式。 首先以上一篇文章创建的空的 composer 包为基础,执行以下命令即可。 root@d63b4f236f0c:/home# composer require --dev phpunit/phpunit 编写 PHPUnit 测试 首先在项目下面新建一个tests文件夹,用来存放单元测试文件。 然后编辑 composer.json 文件为 tests 文件夹增加一个命名空间"Chance\\Log\\Test\\": "tests/"并执行composer dump-autoload更新 composer 的命名空间与文件夹映射关系。 在 tests 目录创建 StackTest.php 文件,使用官网的一个例子来测试。 StackTest.php <?php namespace Chance\Log\Test; use PHPUnit\Framework\TestCase; class StackTest extends TestCase { public function testPushAndPop() { $stack = []; // 断言方法 assertEquals 判断两值是否相等 $this->assertEquals(0, count($stack)); array_push($stack, 'foo'); $this->assertEquals('foo', $stack[count($stack)-1]); $this->assertEquals(1, count($stack)); $this->assertEquals('foo', array_pop($stack)); $this->assertEquals(0, count($stack)); } } 命令行执行单元测试 运行....

2021-12-19 · 1 分钟

创建一个composer包

# 1. 执行composer init root@7608e16a4e0a:/home# composer init Welcome to the Composer config generator This command will guide you through creating your composer.json config. # 2. 填写包名 Package name (<vendor>/<name>) [root/home]: chance/log # 3. 输入描述 Description []: Elegant logging of operations # 4. 输入作者 Author [, n to skip]: chance <ctx_ya@qq.com> # 5. 最低稳定版本 可选值:stable, RC, beta, alpha, dev 一般填dev Minimum Stability []: dev # 6. 输入包类型 Package Type (e.g. library, project, metapackage, composer-plugin) []: library # 7....

2021-12-13 · 1 分钟

使用docker-compose构建PHP8.0 + Swoole + Redis + MongoDB环境

最近公司考虑使用 PHP8 + swoole 进行项目的重构,所以要搭建一个环境进行开发学习。swoole 只能运行在 Linux 系统下,考虑到统一团队的开发环境,避免别的小伙伴在环境问题上浪费太多时间,所以选择使用 docker 来构建一个开发环境。 首先创建我们的工作目录Docker,然后在DockerHub上查找 PHP8.0 最新版本的镜像目前为PHP8.0.12,在Docker目录下创建php目录,因为以后可能会使用别的版本的 PHP,所以在php目录下在创建一个php8.0.12的目录,并在目录中创建Dockerfile文件。 目录结构为 Docker ├─ php │ ├─ php8.0.12 │ │ └─ Dockerfile │ ├─ ... 构建自己的 PHP 镜像 Dockerfile # 因为要使用swoole直接使用cli版本FROMphp:8.0.12-cli# 更新依赖RUN apt-get update \ && apt-get install -y \ unixodbc-dev \ zlib1g-dev \ libzip-dev# 创建`/usr/src/php/ext`目录RUN docker-php-source extract# 下载redis扩展# printf "" | 是为了跳过扩展安装过程中弹出让我们选择的yes noRUN printf "" | pecl install redis-5.3.4# 下载mongodb扩展RUN printf "" | pecl install mongodb-1....

2021-11-24 · 3 分钟