PHP strtotime函数计算下个月时候的一个坑

我们用php获取下个月的月份的时候通常会想当然

1
echo date('Ym', strtotime('next month'));

平时看来并没有什么问题,但是当今天是1月31号的时候会发生什么呢?

1
2
3
4
5
6
7
8
9
10
11
<?php 
$date = "2018-01-31";

$ym1 = date('Y-m', strtotime('+1 month ' . $date));
$ym2 = date('Y-m', strtotime('+2 month ' . $date));
$ym3 = date('Y-m', strtotime('+3 month ' . $date));

var_dump($date);
var_dump($ym1);
var_dump($ym2);
var_dump($ym3);

我们期待的输出结果可能是

1
2
3
4
string(10) “2018-01-31”
string(7) “2018-02”
string(7) “2018-03”
string(7) “2018-04”

但是输出的结果确是

1
2
3
4
string(10) “2018-01-31”
string(7) “2018-03”
string(7) “2018-03”
string(7) “2018-05”

这到底是怎么会是呢?

原来,strtotime函数将1月31日的1个月以后计算成2月31日,显然,2月没有31日,于是,这个1个月后的时间就变成了相应的3月3日。去掉月份,于是得到了2018-03的结果。

那么要正确获取下个月的日期可以怎么做呢?

其实很简单。先取得这个月的第一天,然后再加上一个月就好了。

1
2
$date = date("Y-m-01");
$ym1 = date('Y-m', strtotime('+1 month ' . $date));
PHP

Cannot Change Session when sesion is active

背景

最近把准备把项目直接从PHP5.6 升级到7.3。
升级过程中遇到个比较棘手的问题,那就是出现了大量Cannot change session name when session is active的Warnning。

剖析

我们通常使用session_name($name)来改变session的名称(默认PHPSESSIONID)。而这个warning出现在我们在session_start()之后调用session_name

1
2
3
4
5
6
7
8
9
10
11
12
<?php

session_start();
session_name("hoge");

//echo session_name()."<br/>";
//echo session_id()."<br/>";

session_regenerate_id(true);

echo session_name()."<br/>";
echo session_id()."<br/>";

上面这段,如果使用PHP5.6,则会产生两个cookie。一个是PHPSESSIONID,另一个则是我们变更之后的hoge。echo输出的内容为hoge

1
2
d78ca52754275ef448ea8bff2ef4c051
hoge

环境切换到PHP7.3,我们发现出现了Warnning,然后查看Cookie,我们只看到了PHPSESSIONID

1
2
3
Warning: session_name(): Cannot change session name when session is active in /Users/ning.a.li/workplace/operations/tmp/test.php on line 4
PHPSESSID
tqa2u87v89ul1a7k7srrn8fhf5

原因我知道了,那么为什么会出现这种问题呢?

原来,现有的code在我们开始load framework之前有对session和cookie进行操作,而进入framework之后,由于framework并不知道我们已经建立了session,framework尝试根据config建立session的时候就会产生Warning。

那么如何解决这个warnning呢?

首先我们得考虑是否真正有必要变更session_name

通常,当我们有两个web app运行在同一个domain下面的时候,我们可以通过设置不同的session_name来区分两个不同app的用户。

但是这种情况往往会造成不必要的麻烦。

就目前项目的code来说,修正的方法很简单,我们在framework中对session_name相关的调用添加@即可。即类似@session_name(my_session)

后记

虽然网上多数人抱怨这个Warning设计的莫名其妙,多数情况下,相比还是现有的Code的设计更加不合理吧。

PHP
息来生成一个GenericUser的对象就行了。

想这样。 当然由于GenericUser需要有些特殊的attribute,所以当没有的时候我们可以找适当的项目不上。比如下面的例子,我们需要提供一个name的attribute。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function validateCredentials(\Illuminate\Contracts\Auth\Authenticatable $user, array $credentials)
{
// we'll assume if a user was retrieved, it's good
$attributes = $this->client->Auth(['username' => $credentials["username"], 'passwd'=>$credentials["password"]]);
if($attributes["status"] != 1)
{
return null;
}
else
{
$attributes["data"]["name"] = $attributes["data"]["firstname"].$attributes["data"]["lastname"];
return new GenericUser($attributes["data"]);
}
}

具体实现就不贴了,由于API本身不是很优雅,上层实现也不是那么优雅就是了。

php Laravel