Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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() をみてください。

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

参考資料

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away