说起PHP里的数数,大多数人首先想到的就是count()
这东西。这东西在我们日常编程里简直就是计数神器,但家伙,你真懂它吗?今天咱们就来聊聊这count()
的坑和怎么玩得更溜。
count()的基本用法
先来个简单的例子:
php
$array = [1, 2, 3, 4, 5];
echo count($array); // 输出5
这事简单得跟玩儿似的,小P孩儿都能上手。可你有没有琢磨过,要是塞进去的不是个数组,那会是啥下场?
count()的坑
来,看看这段代码:
$string = "hello";
echo count($string); // 输出1
惊不惊?意不意?PHP里那货count()
对字符串一算,愣是给整出个1来!这事你信不?就因为那东西把字符串当成了单元素数组,你一不小心传给它个字符串,结果能让你怀疑人生!
再来看个更坑的:
$null = null;
这事倒也说得过去,可你要是以为count()
遇到空值就非得跳脚报错,那可真是想得太美了。
多维数组的计数
咱有时候得跟多维数组打交道,这时候count()
那东西第二个参数就成救星了:
$array = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
这货数了数那数组,结果整出来3个,不多不少。
这货叫COUNT_NORMAL
,就是那默认模式,专数第一层货;要是来个COUNT_RECURSIVE
,那就来个全面开花,从里到外,从上到下,数个遍。这参数,在多维数组那块儿,那可是个宝贝。
对象计数
PHP里头,对象也能玩儿数数游戏,但这得看它是不是搞定了Countable
这个门道儿。
我写了个类,叫MyCountable,它实现了Countable接口。
这货里头装的是一串数字,1、2、3、4、5,就这么简单,别想太多。
public function count() {
return count($this->data);
}
}
$obj = new MyCountable();
echo count($obj); // 输出5
这功能在玩儿自己造的集合东西时特别给劲,好比说你弄了个自己的列表东西,只要把它弄成能跟Countable
这东西接轨,那count()
这功能就能顺畅地嗨起来。
更高效的计数
咱得数数数组里某个特定数字露脸的次数,这时候“array_count_values”这货就能大展神威了:
这货里头,1和2各来俩,3来了仨,4更狠,直接来了仨!
这货儿数了数数组里各元素的个数,弄了个计数大法。
print_r($counts);
输出结果:
Array
(
[1] => 1
)
这货儿弄了个函数,弄出来的东西是个关联数组,数组的键儿是数组的值,值儿是那值儿在数组里冒泡的次数。这东西在整那些统计数据的时候,那可真是好使得很。
计数与性能
在大数组里,老调用count()
这招,性能就得受影响了。瞧瞧这代码,真是够呛。
$array = range(1, 1000000);
循环这东西,从零开始,数到数组里个数不够,一个一个过。
// do something
每次都得跑一遍count($array)
,这操作挺耗性能的。咱得聪明点,先算出数组的长度存起来,省得每次都算。
$length = count($array);
循环这破东西,从零开始数,数到长度一个一个来。
这样count()
只调用一次,性能会好很多。
计数与空值
咱得数数数组里头那些不空的东西,这时候array_filter()
就能派上用场了。
这货里头有数字1,还有个空值,接着是数字2,然后是个空字符串,再来是数字3,再后面是个假值,最后还有个数字4。
这东西非空计数,就是数数这堆东西里有几个不是空的。
echo $nonEmptyCount; // 输出4
array_filter()
这货把那些值是假的东西全给筛出去了,比如null
啊、空字符串啊、零啊啥的。这样一来,咱们数个数就方便多了,非空值一个都不会落下。
计数与迭代器
搞大项目数据,咱得省着点内存,这时候迭代器就能派上用场。但说到count()
这东西,它对迭代器的态度可不一样。
这货儿弄了个数组迭代器,弄了个[1, 2, 3, 4, 5]的货色。
echo count($iterator); // 输出5
count()
这货对遵循Countable
规矩的迭代器玩得溜,可要是你使的生成器(Generator)那东西,它就跟你急眼,给你来个错误提示。
function generator() {
yield 1;
$gen = generator();
这货echo了count($gen),结果闪现错误提示:Countable接口没给面子,没实现。
这时候我们需要手动遍历生成器来计数:
$count = 0;
foreach ($gen as $value) {
$count++;
echo $count; // 输出3
计数与数据库
咱在玩数据库这东西的时候,数数这事那是家常便饭。比如说,你得数数某个表格里有多少条记录。
这货儿搞了个PDO,连个MySQL数据库都搞定了,本地主机的,还弄了个test库,用户名和密码都整得挺专业似的。
这货儿往数据库里一戳,直接就数了数咱们这帮用户到底有多少。
这货成功抓取了一列数据。
echo $count; // 输出用户表中的记录数
咱们这儿用COUNT(*)
这招数来数数记录,然后fetchColumn()
这东西就能抓到结果里头第一排的数据。
计数与缓存
在高并发环境下,老往数据库里翻来覆去地查,那东西能不卡吗?咱们得想个法子,比如用缓存来存着那些数,这样就不至于每次都去数据库那破地方翻个底朝天了。
$cache = new Memcached();
这货往本地的缓存服务器上扔了个地址,地址是localhost,端口是11211,搞定了。
这货从缓存里捞了个“user_count”的数。
if ($count === false) {
家伙,这事简单,我让数据库里头数一数,到底有多少用户在里头晃悠。 $pdo
这货儿,它一出手,直接就给我搞了个计数。
这东西从数据库里捞出来个数字,就那么一捞,直接抓了个结果。
这东西把用户数存进缓存,保质期一小时,过了这村儿可就真没这店儿了。
echo $count;
缓存这东西能存个数,咱们就能少跑数据库,系统跑得快,就像开挂一样。
计数与分页
搞分页这事,计数那是关键。你得弄清楚总共多少条记录,这样才能算出总共得有几页。
$perPage = 10;
$page = $_GET['page'] ?? 1;
算出来这页的起始位置,先得把页数减一,再乘以每页的数量。
这东西直接抓出来那一列数据,$total 就搞定了。
这货总共就这么多页,算下来每页得装这么多内容,反正就是四舍五入,多出来的都得往上加一页。
这货儿用PDO那东西整了个东西,想从users那破表里捞东西,限定了个数量,还跳过了前面的一大堆。
这东西执行了一下,参数里头把每页数量和起始位置都给搞定了。
$users = $stmt->fetchAll();
咱们先数一数总共有多少条记录,接着算出能分成几页,最后看咱们现在在哪一页,再按每页给多少条来挑出相应的数据。
计数与优化
有时候咱们得给计数查询来个美容:好比说,在那些个数据量大的表格里头,得数一数符合要求的条目数。
这货儿一查,就一个活人儿在那儿晃悠。
要是有个索引挂active
这字段上,那这查询分分钟搞定。没索引的话,搞不好得慢慢等。这时候咱们得想想给那字段加个索引,要不就找找其他快法子。
计数与并发
在高并发环境下,计数动作容易引发数据不一致的bug。好比好几个小伙伴儿一齐给篇文章点赞,咱得保证那个zan数得准着点儿。
$pdo->beginTransaction();
try {
这货儿把PDO那东西搞了个准备,想给文章表里那叫id的东西加个赞,直接就搞了个SQL语句,看得我直想笑。
这货执行了个操作,把那文章ID给传了进去。
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
搞个事务,咱们就能保证计数动作一气呵成,不搞出数据乱七八糟的情况。
计数与统计
有时候我们需要统计某些数据的分布情况,比如用户的年龄分布:
这货儿把数据库里头的人按岁数给分了类,还数了数每个年龄段有多少人。
这东西把数据库里那堆东西全捞出来了,还弄了个键值对,挺逗的。
print_r($ageDistribution);
这查询能给你整出来个年龄跟人数挂钩的列表,年龄那栏是关键,数数看多少人多少岁。有了这数据,咱们就能把用户年龄这事给捋明白了。
计数与大数据
搞大数据这事,数数儿能让人头大。这时候,咱们得想点辙,比如来个HyperLogLog这种近似计数法,能快那么一小点。
$redis = new Redis();
连接本地 Redis 服务器,端口 6379,操作完成。这回咱们就是本地 Redis 的老大哥了!
这东西往“users”那堆里硬塞了几个名字,叫user1、user2、user3,就差没喊它们来吃饭了。
这货儿统计了下,咱们这儿的用户数是啥来着?数了数,看看有多少人在这瞎晃悠。
这HyperLogLog算法,就是那种能估个数儿的,专治大数据的。它占内存少,算得快,简直就是大数据处理的小能手。
计数与日志
日志解析里,数数那可是个关键活儿。好比算算在一定时间内,访问了多少回。
$logFile = 'access.log';
2023年10月1日零点整,时间机器启动,计时器开始滴答作响。
2023年10月1日晚上11点59分59秒,这时间点,咱们给它算出来。
我打开了那日志文件的读取模式,搞了个句柄。
这东西就像是个不停吸水的海绵,一边儿读一边儿往里装,直到把那东西填满。
时间戳$logTime等于把那行字符串的前19个字符截取后,用strtotime函数转换成时间戳。