6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHPでversion_compare()を使うときは桁を合わせよう

Last updated at Posted at 2016-12-19

TL;DR

version_compare() 関数を使うときは、桁区切りのフォーマットは同一にしたほうが安心です。

もし、比較時に version_compare("2.0.0", "2.0", ">") と2つのフォーマットが違うと、直感的には同じバージョンだったとしても、小数点で区切られた数が多い方が大きいと判定され、想定しない動きになる可能性があります。

前提条件

$ php -v
PHP 5.6.27 (cli) (built: Oct 15 2016 09:29:55)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

バージョン判定がうまくいかない?

比較する2つの値が同じフォーマットの時

version_compare() を用い、比較する値がどちらも同じフォーマット(x.y.z)となっている場合です。以下コードを書き、実行してみます。

version_compare_3_3.php
<?php

print "2.0.0 > 2.0.0 = False? : " . (version_compare("2.0.0", "2.0.0", ">") ? "True" : "False") . "\n";
print "2.0.0 < 2.0.0 = False? : " . (version_compare("2.0.0", "2.0.0", "<") ? "True" : "False") . "\n";
print "2.0.0 >= 2.0.0 = True? : " . (version_compare("2.0.0", "2.0.0", ">=") ? "True" : "False") . "\n";
print "2.0.0 <= 2.0.0 = True? : " . (version_compare("2.0.0", "2.0.0", "<=") ? "True" : "False") . "\n";
print "2.0.0 == 2.0.0 = True? : " . (version_compare("2.0.0", "2.0.0", "==") ? "True" : "False") . "\n";
print "2.0.0 != 2.0.0 = False? : " . (version_compare("2.0.0", "2.0.0", "!=") ? "True" : "False") . "\n";

想定した動きになっています。

$ php version_compare_3_3.php
2.0.0 > 2.0.0 = False? : False
2.0.0 < 2.0.0 = False? : False
2.0.0 >= 2.0.0 = True? : True
2.0.0 <= 2.0.0 = True? : True
2.0.0 == 2.0.0 = True? : True
2.0.0 != 2.0.0 = False? : False

左辺の桁が1桁長いが右辺のほうが大きい値の時

左辺は2.0.0、右辺は2.1となったとき、どんな動きになるでしょうか。

version_compare_3_2_1.php
<?php

print "2.0.0 > 2.1 = False? : " . (version_compare("2.0.0", "2.1", ">") ? "True" : "False") . "\n";
print "2.0.0 < 2.1 = True? : " . (version_compare("2.0.0", "2.1", "<") ? "True" : "False") . "\n";
print "2.0.0 >= 2.1 = False? : " . (version_compare("2.0.0", "2.1", ">=") ? "True" : "False") . "\n";
print "2.0.0 <= 2.1 = True? : " . (version_compare("2.0.0", "2.1", "<=") ? "True" : "False") . "\n";
print "2.0.0 == 2.1 = False? : " . (version_compare("2.0.0", "2.1", "==") ? "True" : "False") . "\n";
print "2.0.0 != 2.1 = True? : " . (version_compare("2.0.0", "2.1", "!=") ? "True" : "False") . "\n";

これは、おそらく多くの方が想定した動きになります。

$ php version_compare_3_2_1.php
2.0.0 > 2.1 = False? : False
2.0.0 < 2.1 = True? : True
2.0.0 >= 2.1 = False? : False
2.0.0 <= 2.1 = True? : True
2.0.0 == 2.1 = False? : False
2.0.0 != 2.1 = True? : True

左辺の桁が1桁長い時

では、左辺は2.0.0、右辺は2.0となったとき、どんな動きになるでしょうか。人が見たら、これはイコールと思えるかもしれませんが、実際は違います。

version_compare_3_2.php
<?php

print "2.0.0 > 2.0 = False? : " . (version_compare("2.0.0", "2.0", ">") ? "True" : "False") . "\n";
print "2.0.0 < 2.0  = False? : " . (version_compare("2.0.0", "2.0", "<") ? "True" : "False") . "\n";
print "2.0.0 >= 2.0 = True? : " . (version_compare("2.0.0", "2.0", ">=") ? "True" : "False") . "\n";
print "2.0.0 <= 2.0 = True? : " . (version_compare("2.0.0", "2.0", "<=") ? "True" : "False") . "\n";
print "2.0.0 == 2.0 = True? : " . (version_compare("2.0.0", "2.0", "==") ? "True" : "False") . "\n";
print "2.0.0 != 2.0 = False? : " . (version_compare("2.0.0", "2.0 ", "!=") ? "True" : "False") . "\n";

桁が長い左辺が大きいと評価されます。

$ php version_compare_3_2.php
2.0.0 > 2.0 = False? : True
2.0.0 < 2.0  = False? : False
2.0.0 >= 2.0 = True? : True
2.0.0 <= 2.0 = True? : False
2.0.0 == 2.0 = True? : False
2.0.0 != 2.0 = False? : True

PHPの ext/standard/versioning.cphp_version_compare() をみてください。

メジャーバージョンの桁から順に評価されています。そのうえで、左辺か右辺、どちらかの評価が尽きると、桁が大きい方が小さいという処理に移っています。

2.1.02.01.0 はどうか

では、フォーマットがにているようで、minorバージョンが1桁の場合と2桁の場合ではどうでしょうか。これも変わった動きをします。

version_compare_3_a.php
<?php

print "2.1.0 > 2.01.0 = False? : " . (version_compare("2.0.0", "2.01.0", ">") ? "True" : "False") . "\n";
print "2.1.0 < 2.01.0 = False? : " . (version_compare("2.0.0", "2.01.0", "<") ? "True" : "False") . "\n";
print "2.1.0 >= 2.01.0 = True? : " . (version_compare("2.0.0", "2.01.0", ">=") ? "True" : "False") . "\n";
print "2.1.0 <= 2.01.0 = True? : " . (version_compare("2.0.0", "2.01.0", "<=") ? "True" : "False") . "\n";
print "2.1.0 == 2.01.0 = True? : " . (version_compare("2.0.0", "2.01.0", "==") ? "True" : "False") . "\n";
print "2.1.0 != 2.01.0 = False? : " . (version_compare("2.0.0", "2.01.0", "!=") ? "True" : "False") . "\n";

桁が長いほうが、大きいと評価されました。

$ php php:version_compare_3_a.php
2.1.0 > 2.01.0 = False? : False
2.1.0 < 2.01.0 = False? : True
2.1.0 >= 2.01.0 = True? : False
2.1.0 <= 2.01.0 = True? : True
2.1.0 == 2.01.0 = True? : False
2.1.0 != 2.01.0 = False? : True

PHPの ext/standard/versioning.cphp_version_compare() をみてください。

…と思ったのですが、あれ、なんでこうなるんだろう。調査中です。

参考資料

6
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?