将MySQL的tinyint布尔值(0/1)转换为PHP布尔值(true/false)

4
我需要反复将MySQL tinyint(1) 'boolean'数据类型转换为PHP布尔值,并且一直在尝试测试最快的方法。我的数据映射如下:
  • NULL = FALSE
  • 0 = FALSE
  • 1 = TRUE
在寻找了几个小时后,我似乎找不到任何有关此的性能解释/评论,因此我尝试自己制作了一些可能的解决方案。我的代码如下:
echo 'Current PHP Version: ' . phpversion() . '<br /><br />';
$start1 = 1;
$start0 = 0;

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = !empty($start1);
    $answer = !empty($start2);  
}
$time_end = microtime(TRUE);
echo 'Did NOT EMPTY in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = (bool)$start1;
    $answer = (bool)$start2;    
}
$time_end = microtime(TRUE);
echo 'Did TYPECAST BOOL in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = $start1 == TRUE;
    $answer = $start2 == TRUE;  
}
$time_end = microtime(TRUE);
echo 'Did EQUALS TRUE in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = !!$start1;
    $answer = !!$start2;    
}
$time_end = microtime(TRUE);
echo 'Did NOT NOT in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = is_null($start1);
    $answer = is_null($start2);     
}
$time_end = microtime(TRUE);
echo 'Did IS NULL in ' . ($time_end - $time_start) . " seconds<br>";

我的结果如下:

Current PHP Version: 5.4.16

Did NOT EMPTY in 1.00608086586 seconds
Did TYPECAST BOOL in 2.5599420070648 seconds
Did EQUALS TRUE in 2.7039749622345 seconds
Did NOT NOT in 2.7622299194336 seconds
Did IS NULL in 4.1728219985962 seconds

我选择了850万次迭代,因为我通常喜欢将最快的测试尽可能接近1秒,以便一目了然地看到扩展情况。从这个测试中可以看出,最佳选择是!empty($value),而且差距非常大。
我发布这篇文章是为了分享我的发现,并看看是否有其他方法可以获得相同的结果,可能更快,或者在未来的PHP版本中会发生根本性的变化。我们目前处于5.4环境中,希望在明年将其移植到5.6,并希望最终看到php7。
在新版本的PHP中,这些方法中是否有一个不同的胜出者,或者是否有完全不同的方法可以解决这个问题?感谢您的见解!
编辑:正如所指出的那样,上面的测试是不正确的。下面是一个正确的测试,还有一个额外的选项:
echo 'Current PHP Version: ' . phpversion() . '<br /><br />';

$start1 = 1;
$start0 = 0;

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = $start1 == 1;
    $answer = $start0 == 1;     
}
$time_end = microtime(TRUE);
echo 'Did EQUALS 1 in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = (bool)$start1;
    $answer = (bool)$start0;    
}
$time_end = microtime(TRUE);
echo 'Did TYPECAST BOOL in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = $start1 == TRUE;
    $answer = $start0 == TRUE;  
}
$time_end = microtime(TRUE);
echo 'Did EQUALS TRUE in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = !!$start1;
    $answer = !!$start0;    
}
$time_end = microtime(TRUE);
echo 'Did NOT NOT in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = !empty($start1);
    $answer = !empty($start0);  
}
$time_end = microtime(TRUE);
echo 'Did NOT EMPTY in ' . ($time_end - $time_start) . " seconds<br>";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 14000000; $i++ )
{
    $answer = !is_null($start1);
    $answer = !is_null($start0);    
}
$time_end = microtime(TRUE);
echo 'Did NOT IS NULL in ' . ($time_end - $time_start) . " seconds<br>";

这个测试给了我:

Current PHP Version: 5.4.16

Did EQUALS 1 in 1.0449228286743 seconds
Did TYPECAST BOOL in 1.0921199321747 seconds
Did EQUALS TRUE in 1.3697588443756 seconds
Did NOT NOT in 1.3729720115662 seconds
Did NOT EMPTY in 1.4694240093231 seconds
Did NOT IS NULL in 3.2058408260345 seconds

看到PHP新版本中可能会有显著的性能变化,我对它未来的表现很感兴趣,因为目前竞争非常激烈!


NULL强制转换为false可能会让你某天陷入严重的麻烦。"未设置"与"设置为非真"完全不同。 - tadman
我同意,但客户和团队都同意这个映射。这是一个十年以上的系统,用户输入数据时很懒惰,所以他们希望看到偶尔不正确的数据(<0.01%)会促使他们去修复它,而不是留空。虽然这不是我的决定 耸肩 - Ripptor
1
$start2未定义。 - Paolo
@Paolo,谢谢你指出了这个问题,这可能改变了事情的情况。让我重新运行并编辑... - Ripptor
3个回答

3

这似乎在PHP的后续版本中进行了优化:

Current PHP Version: 5.6.16-2+deb.sury.org~wily+1

Did NOT EMPTY in 0.43913006782532 seconds
Did TYPECAST BOOL in 0.40566301345825 seconds
Did EQUALS TRUE in 0.42750406265259 seconds
Did NOT NOT in 0.43936395645142 seconds
Did IS NULL in 1.3173689842224 seconds

PHP 7 更加令人印象深刻:

Current PHP Version: 7.0.1-1+deb.sury.org~wily+2

Did NOT EMPTY in 0.21985292434692 seconds
Did TYPECAST BOOL in 0.18928909301758 seconds
Did EQUALS TRUE in 0.17465591430664 seconds
Did NOT NOT in 0.20792722702026 seconds
Did IS NULL in 0.15517687797546 seconds

在这里,类型转换似乎是最佳的解决方案,既考虑到性能又考虑到可读性(像 (bool) $someVar 这样的结构可以更好地传达您的意图给其他开发人员,而不是使用 !!$someVar 或其他模糊的结构)。


哇,这些是相当显著的改进和差异!非空从队列前面变成了最后一个,而对于IS NULL则相反(尽管我意识到现在应该是!(is_null()))。 - Ripptor

2

PHP默认的活动不够用?

来自手册的内容

转换为布尔值

要显式地将一个值转换为布尔值,请使用(bool)或(boolean)。然而,在大多数情况下,这种转换是不必要的,因为如果运算符、函数或控制结构需要布尔值参数,则值会自动转换。

请参阅类型转换

在转换为布尔值时,以下值被视为FALSE:

the boolean FALSE itself
the integer 0 (zero)
the float 0.0 (zero)
the empty string, and the string "0"
an array with zero elements
an object with zero member variables (PHP 4 only)
the special type NULL (including unset variables)
SimpleXML objects created from empty tags

每个其他值都被视为TRUE(包括任何资源)。

完全同意。PHP对真实性采取了“足够接近”的方法,因此没有必要显式地进行转换。 - tadman
不够好,不能满足客户的需求。我在一个面向对象的环境中与其他人组成团队工作,我的特定对象应该从数据库中提取数据,然后将数据存储并传递为真和假。因此,是的,我的要求确实需要我上面列出的内容。此外,将其强制转换为bool需要250%的时间,而!empty()则需要更少的时间,因此我不确定为什么除了稍微考虑可读性之外,我会选择这样做。在PHP的新版本中速度更快吗? - Ripptor
如果你想这么正式,这并不一定是件坏事,为什么不使用一个ORM来处理这个问题呢?低级数据库调用容易暴露你的程序于所有这些丑陋的东西。 - tadman

1
首先,您应该修复测试,因为您分配值给$start0$start1,但随后检查$start1$start2,而后者未定义。

一旦修复,最快的测试是简单比较(您没有尝试):

$answer = $start0 == 1

这是我的代码(我从终端运行它)。

<?php
echo 'Current PHP Version: ' . phpversion() . "\n\n";
$start1 = 1;
$start0 = 0;

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = $start0 == 1;
    $answer = $start1 == 1;  
}
$time_end = microtime(TRUE);
echo 'Did COMPARISON in ' . ($time_end - $time_start) . " seconds\n";

$time_start = microtime(TRUE);
for( $i = 0 ; $i < 8500000; $i++ )
{
    $answer = !empty($start0);
    $answer = !empty($start1);  
}
$time_end = microtime(TRUE);
echo 'Did NOT EMPTY in ' . ($time_end - $time_start) . " seconds\n";

/* 
   .
   .
   .
*/

这是结果。
Current PHP Version: 5.4.45

Did COMPARISON in 0.60736107826233 seconds
Did NOT EMPTY in 0.75234413146973 seconds
Did TYPECAST BOOL in 0.67925190925598 seconds
Did EQUALS TRUE in 0.80053496360779 seconds
Did NOT NOT in 0.95479583740234 seconds
Did IS NULL in 2.1385459899902 seconds

您可以在此处运行不同版本的PHP测试

https://3v4l.org/t4K5j


谢谢你提供的额外选项,我之前没有考虑到!我已经修复了我的测试并添加了你的选项,结果几乎相同。我们都在运行5.4版本,所以我仍然想知道PHP的新版本是否有不同的结果? - Ripptor
只是提醒一下,我在PHP5.6.15中运行了Paolo的代码,并得到了一个相当奇怪的结果:Did IS NULL in 56.041206121445 seconds,而我从5.4.45中得到了非常相似的数字。所以如果你正在考虑升级,也许你应该检查一下这个问题。 - RiggsFolly
哇,那个网站真是个好发现!但是它在几乎所有服务器上都在测试过程中退出了... - Ripptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接