php笔记:引用定位

  最近,打算通读一遍php官方文档。在看到引用的时候,文档中提出:“许多 PHP 的语法结构是通过引用机制实现的”。还举例了“global引用” 和 “$this” ,都是通过引用传递的。文档中,有一句话,吸引了我的注意力,“unset $var 不会 unset 全局变量”。我也做了相应的demo测试,还真是如此。详见下代码。

[code lang=”php”]
$a = 345;
function test()
{
global $a;
$a = 123;
unset($a);
}

test();
var_dump($a); // int(123)
var_dump($GLOBALS[‘a’]); // int(123)
[/code]

如果在函数里删除变量$a,只是把函数作用域里的$a删除,并不会对全局变量$a产生影响。如下图

附上官网的这一章节:https://www.php.net/manual/zh/language.references.spot.php

终于弄懂cgi、fast-cgi、php-cgi、php-fpm的关系了

  今天,公司的电脑加了块固态。上午花了点时间重装系统,下午快下班的时候,才想起来本地的php环境还没装。以前,在windows上装过apache压缩包,也装过phpStudy,这次又想折腾一下nginx。
  在nginx关联php时,发现php-cgi的作用跟php-fpm类似,这才让我恍然大悟。结合网上查阅的资料( https://blog.csdn.net/illusion_you/article/details/79424670 )。
  我觉得,cgi和fast-cgi就好像一个说明书,说明了某个东西的使用方法,但又不仅仅是说明,它还规范了这个东西该怎么做,能做什么。cgi和fast-cgi的差别,就像是1.0跟2.0的差别。前者每次调用php,都需要初始化配置和扩展。后者会守护一个进程去管理,性能上肯定是有提高的。
  而php-cgi,是一个实实在在的可执行文件( 在php包里 ),它实现了fast-cgi定义的东西,可以拿来用。但是它又存在问题,比如说,修改配置了需要重启服务进程。而当时,php-fpm的出现,取代了它的地位,它就变成了1.0。

再遇指针,不递归转树结构

  时隔几年,我又遇到关于传引用的问题,这次终于可以运用引用了,先说说起因,以前在做数组转树结构时,我通常采用递归的方式,代码如下所示。
[code lang=”php”]
<pre><?php
function recursion_to_tree($data = [], $pid = 0)
{
$tree = [];
foreach ($data as $item) {
if ($item[‘pid’] == $pid) {
$son = recursion_to_tree($data, $item[‘id’]);
if (!empty($son)) {
$item[‘son’] = $son;
}
$tree[] = $item;
}
}
return $tree;
}
$array = [
[‘id’ => 1, ‘pid’ => 0, ‘name’ => ‘广东省’],
[‘id’ => 2, ‘pid’ => 1, ‘name’ => ‘广州市’],
[‘id’ => 3, ‘pid’ => 2, ‘name’ => ‘白云区’],
[‘id’ => 4, ‘pid’ => 2, ‘name’ => ‘天河区’],
[‘id’ => 5, ‘pid’ => 1, ‘name’ => ‘深圳市’],
[‘id’ => 6, ‘pid’ => 0, ‘name’ => ‘香港’],
];
$result = recursion_to_tree($array);
echo var_export($result);
[/code]
效果如下所示

array (
  0 => 
  array (
    'id' => 1,
    'pid' => 0,
    'name' => '广东省',
    'son' => 
    array (
      0 => 
      array (
        'id' => 2,
        'pid' => 1,
        'name' => '广州市',
        'son' => 
        array (
          0 => 
          array (
            'id' => 3,
            'pid' => 2,
            'name' => '白云区',
          ),
          1 => 
          array (
            'id' => 4,
            'pid' => 2,
            'name' => '天河区',
          ),
        ),
      ),
      1 => 
      array (
        'id' => 5,
        'pid' => 1,
        'name' => '深圳市',
      ),
    ),
  ),
  1 => 
  array (
    'id' => 6,
    'pid' => 0,
    'name' => '香港',
  ),
)

  那时还沾沾自喜,以为自己掌握了多么炫酷的奇技淫巧。就在几个月前,阅读数据结构的书籍时,才发现递归其实是没效率的,也被我曾经的组长聪哥提醒过,但一直也没在意。
  直到昨天,听说华哥遇到一个奇葩的面试题目,要求:不使用递归,不使用函数,只遍历一次数组,将数据转为树结构。这就尴尬了,不过,我以前查资料时,貌似有了解到,可以不用递归转树结构,只是一直不怎么关注。
  正好趁着这个机会,我再次百度,很容易就找到了方法,他使用的是引用( & ),抱着试一试的心态,我试了下,代码如下所示
[code lang=”php”]
<pre><?php
function iteration_to_tree($data = [])
{
//重组键名
foreach ($data as $item) {
$temp[$item[‘id’]] = $item;
}

//转为树
$tree = [];
foreach ($temp as $item) {
$pid = $item[‘pid’];
$id = $item[‘id’];

if (isset($temp[$pid])) {
$temp[$pid][‘son’][] = &$temp[$id];
} else {
$tree[] = &$temp[$id];
}
}
return $tree;
}
$array = [
[‘id’ => 1, ‘pid’ => 0, ‘name’ => ‘广东省’],
[‘id’ => 2, ‘pid’ => 1, ‘name’ => ‘广州市’],
[‘id’ => 3, ‘pid’ => 2, ‘name’ => ‘白云区’],
[‘id’ => 4, ‘pid’ => 2, ‘name’ => ‘天河区’],
[‘id’ => 5, ‘pid’ => 1, ‘name’ => ‘深圳市’],
[‘id’ => 6, ‘pid’ => 0, ‘name’ => ‘香港’],
];
$result = iteration_to_tree($array);
echo var_export($result);
[/code]
最终的结果,和上面递归的结果是一样的。

乍一眼,确实不好理解,怎么一个foreach,一个if else 就搞定了呢?

  重点是要先理解,引用传递的作用( http://www.php.net/manual/zh/language.references.pass.php ),它和C的指针类似。理解了引用传递,再仔细看 15、16、18 这三行,就很好理解了。

代码流程:
  1、先把数组重组:将键名改为id,然后再遍历重组后的数据;
  2、第18行代码:遍历时,如果,当前记录的pid,不存在于数组中,则把当前记录存入“树结构”。注意:这时候的$temp,已经带了引用的状态,对$temp进行改变值,会影响到已经存入“树结构”的数据;
  3、第16行代码:遍历时,如果,当前记录的pid,存在于数组中,则把当前记录存入$item。注意:这时候又有一个引用,对这第二个$item,进行值的改变,就会影响$item里面的内容;

就这么简单的实现了递归效果,但实际的效率,我也不知道是不是比递归强。

关于strtotime,使用-1 month可能出现的bug

先上例子:
[code lang=”php”]
<?php

//根据某日期,获取上个月,结果: 2018-03
echo date(‘Y-m’, strtotime(‘2018-03-31 -1 month’));
[/code]
早在二三年前,我就有做过一个需求:根据时间区间的筛选,获取工作排班的内容。

当时,我用php写了一个简单的日历排班。涉及到跨月动态计算“排班结果”时,我用strtotime去获取上个月和下个月。

一开始上线时,运行还挺好,结果到了下个月31号,再回去看历史结果,就发现数据错乱了。最后,纠其原因,发现是月份获取出错了。

当时,不太理解为什么会出现这种错乱,只是知道,如果跨月时,涉及到上个月或下个月,没有31号或30号时,就会出现这种问题。

最近,无意间看到一个php大神写的文章( http://www.laruence.com/2018/07/31/3207.html ),才明白问题所在。

如上页面的例子,当2018-03-31时,获取上个月,理论上的结果是2018-02-31,但实际结果是2018-03-03。

但2月一般只有28,闰年也才29。31比28多了3天,所以结果应该是往后推了3天。如此类推,2018-10-31获取上个月,结果也是2018-10-01

红包分配方法:二倍均值法 和 线段切割法

[code lang=”php”]
<?php
/**
* 二倍均值法
* @param int $money 红包金额
* @param int $people 领取人数
* @return array 结果
*/
function double_average($money = 10, $people = 5)
{
$remain_people = $people; //剩余人数
$red_packet = []; //所有红包金额
//循环分配
for ($i = 0; $i < $people – 1; $i++) {
$get = mt_rand(1, $money / $remain_people * 2); //单个红包金额

$money -= $get; //剩余金额
$remain_people–; //剩余人数

$red_packet[] = $get; //存入金额
}
//最后一个包
$red_packet[] = $money;
//返回结果
return $red_packet;
}

/**
* 线段切割法
* @param int $money 红包金额
* @param int $people 领取人数
* @return array 结果
*/
function line_cut($money = 10, $people = 5)
{
//获取切割处
$temp = [];
while (count($temp) < $people – 1) {
$number = mt_rand(1, $money – 1);
$temp[$number] = 1;
}

//补头补尾
$temp[0] = 1;
$temp[$money] = 1;
$temp = array_keys($temp);
sort($temp);

//循环分配
$red_packet = [];
for ($i = 0; $i < $people; $i++) {
$red_packet[] = $temp[$i + 1] – $temp[$i];
}
//返回结果
return $red_packet;
}

var_dump(double_average());
var_dump(line_cut());
[/code]

参考来源:https://mp.weixin.qq.com/s/AIE33sdT2QI6UL8cs1kJCQ

php常用命令行参数理解

在命令行模式下,进入php根目录,输入 php -help 就可以看到所有的参数,如下。
这里我就不一一列举了,只列举我觉得常用且简单的。

示例 说明
php -a 进入交互式 PHP 命令行模式
php -v 输出版本信息
php -m 输出内置以及已加载的 PHP 及 Zend 模块
php -i 输出 phpinfo() 的内容。内容太多,不推荐使用
php -r “echo 123;” 运行单行 PHP 代码
php -f 文件路径 运行指定的 PHP 文件
php -s 文件路径 输出有语法高亮色彩的源代码
php -w 文件路径 输出除去了注释和多余空白的源代码

更多详见官方文档:http://php.net/manual/zh/features.commandline.php

关于传引用的问题

以前,在某网友的博客里,看到一个关于传引用的问题。(http://www.dengwz.com/?p=532)在我看过之后,也是百思不得其解。

虽然,我知道在变量A给变量B赋值时,如果加上引用(&),就会导致:改变其中任意一个变量时,另一个变量也会随之改变。就好像是同一个变量,只是名称不一样而已。这种方法,可以在调用函数时,不返回值的同时,改变传递过来的变量的值。如下,就会输出2
[code lang=”php”]$a=1;
function demo(&$b)
{
$b++;
}
demo($a);
echo $a;[/code]
在一场面试中,我就遇到关于“传值和传地址是什么意思”的问题。因为Php自学的关系(而且现在都还没有买书,不过已经准备了),我压根不知道他说的什么意思。脑海里的第一个念头是:url传值?传url地址?好像完全没关系啊?就只好说不知道。

回来的时候,我百度了下(百度是最好的老师,我就是这样学习的)传值和传地址。才明白,这个传值原来是赋值(说明该买书看看了)。传地址就是前面说到的传引用(好像网上说,只有C语言才区分传引用和传地址,在Php里面是一个意思。之前我还不知道加&原来叫传引用,再次说明该买书看看了)

就在这个时候,也浏览到关于文章开头提到的类似问题(http://www.jb51.net/article/22140.htm)。在好奇心的的驱使下,我仔细的阅读了他的内容,自己也做了测试,最后终于明白了问题。

在使用两个foreach语句时,如果在第一个foreach循环的值(如&$value)前面加上引用(&)。就会倒置后面的foreach使用到相同变量($value)时,改变前者最后一次引用到的变量的值,也就是改变$arr[2]的值。如下
[code lang=”php”]$arr  = array(‘a’, ‘b’, ‘c’);
$arr2 = array(‘d’, ‘e’, ‘f’);
foreach($arr as &$value){}
foreach($arr2 as $value)
{
print_r($arr);
}[/code]
我们会看到出来的结果是
[code lang=”php”]Array
(
[0] => a
[1] => b
[2] => d
)
Array
(
[0] => a
[1] => b
[2] => e
)
Array
(
[0] => a
[1] => b
[2] => f
)[/code]
这样很容易看出来,$arr[2]的值一直在改变。这时候我们想想,如果没有$arr2,全部都用$arr会怎样?
[code lang=”php”]$arr  = array(‘a’, ‘b’, ‘c’);
foreach($arr as &$value){}
foreach($arr as $value)
{
print_r($arr);
}[/code]
结果如下
[code lang=”php”]Array
(
[0] => a
[1] => b
[2] => a
)
Array
(
[0] => a
[1] => b
[2] => b
)
Array
(
[0] => a
[1] => b
[2] => b
)[/code]
这样我们可以很直观的看到,$arr[2]在第一次变成a,第二次变成b,第三次还是b。这是为什么呢?原因很简单,在第二次执行的时候,$arr[2]的值已经变成的b。第三次取值时,取出来的就是b。

ps: 其中一个网址里提到断开引用关系,那么,我们的问题又来了。$arr[0]和$arr[1]也没有断开,为什么他们不改变?
我个人的理解是,使用引用(&)同时,就会断开这个变量之前的引用关系。