I am using phpBB as a forum platform. I'm using a Mutualised Linux server with PHP Version 5.6.31 and MySQL(i) 5.7.19-log.
And I've found a bug that doesn't happen on others servers, since some phpbb team members as some friend of mine couldnt reproduce the problem. But there is more people with the same problem.
For people that want to check by themselves:
Basically with a fresh install of phpBB 3.2.1, we can go to ACP - Forums - Your first category - Your first forum.
There we confirmed that the option "Enable quick reply" is marked as "No"
Then we go to ACP - Posting - Messages - Post Settings and click on "Submit and enable quick reply in all forums".
After that we go again to the "your first forum" to check the "Enable quick reply" and its still as "No".
And it should be "Yes".
I've try to DEBUG and on the .php file that have the function that will create the SQL query that will be sent to database, I've put:
print(1 << 6); and it gives me 64.
So I've put too: print($bit); and it gives me 6.
So the code (1 << $bit) that is on the php should be correct, and should give me 64 BUT if I put: print(1 << $bit); it gives me 32!
I've put the 3 prints:
print(1 << 6);
print($bit);
print(1 << $bit);
And the result was: 64 6 32
Wtf?! Why the hell when we've an variable with 6 as value, it assumes as 5?!, or it assumes as the 6th position the representation of a byte?
Anyone have any idea why this is happening? Maybe PHP versin bug? Or can any type of configuration mess with this?
Let me explain better.
In /includes/constants.php we can find:
define('FORUM_FLAG_QUICK_REPLY', 64);
That value will be use to create the $bit value.
And on /includes/acp/acp_board.php we've a function that will create the $bit variable:
$config->set($config_name, $config_value);
if ($config_name == 'allow_quick_reply' && isset($_POST['allow_quick_reply_enable']))
{
enable_bitfield_column_flag(FORUMS_TABLE, 'forum_flags', log(FORUM_FLAG_QUICK_REPLY, 2));
}
This _enable_bitfield_column_flag_ function is what will create the sql code.
And the log(FORUM_FLAG_QUICK_REPLY, 2) = Log2(64) = 6.
So that's why the $bit is 6.
And in includes/functions_admin.php we've:
function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '')
{
global $db;
$sql = 'UPDATE ' . $table_name . '
SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . '
' . $sql_more;
$db->sql_query($sql);
}
We can see here the sql code being created by php code.
And finally on /phpbb/db/driver/driver.php we've:
return $column_name . ' | ' . (1 << $bit) . (($compare) ? ' ' . $compare : '');
And before that line I've put the 3 prints, and the values were 64 6 32... and it doesnt make sense, why print $bit gives 6 and 1 << $bit gives 32...
Thanks in advance!
Based on our exchanges in comment, and assuming $bit is indeed log2(64), I ran some tests and proved my initial idea :
$bit = log(64,2);
echo gettype(6)."\n"; // integer
echo gettype($bit)."\n"; // double
echo (int)$bit."\n"; // prints 6, but might as well have been 5
echo round($bit)."\n"; // prints 6
demo here : https://3v4l.org/Zogme
In this demo, all php versions tested appear to cast the result to 6 when converting to integer type (as it is the case with bitwise shifting operators such as <<, which works with integer arguments), but that's not guaranteed.
Float/double values are not really safe to work with, better round() it explicitly to a proper integer just to be sure and avoid the bug you are seeing.
$bit = round($bit);
Kudos goes to #axiac for fact-checking and pushing me to write this as an answer. Let me know if I can improve it further.
I have the following error;
Note: Array to string conversion in [file_path] on line 919
which relates to this line of code where I'm trying to assign this string as a value in an array
$contents[11] = "$hours:$minutes:$seconds\n$ca_1[remaining_time]\n$h:$m:$s";
Why am I getting this error, and how do I resolve it?
It's a bad practice to interpolate string this way because it makes the code very difficult to read, so you should rather use "{$h}" instead of "$h".
As Terminus mentioned in comments, depending on the PHP version,
echo "$ca_1[remaining_time]"
Does not necessarily give a
PHP Notice: Use of undefined constant
Like echo $ca_1[remaining_time] would. But since that didn't work for you, you'd better quote that like ['remaining_time'].
You might also find some interesting things on this topic here.
Second, use curvy braces to explicitly tell what you want to insert:
$contents[11] = "$hours:$minutes:$seconds\n{$ca_1['remaining_time']}\n$h:$m:$s";
This really improves readability.
Try:
$contents[11] = $hours . ':' . $minutes . ':' . $seconds + "\n" . $ca_1['remaining_time'] . "\n " . $h . ':' . $m . ':' . $s";
If this still fails, check your variables. Maybe one of them is an array!?
I thought I've known the String Operator . well enough until I was asked a question about it today. The question looks quite simple:
echo 100...100;
At the first glance I thought it would make a syntax error. But when I ran the code and saw the result I was totally confused. The result is
1000.1
So I wonder how could this happen?
Thanks.
Read it like this:
(100.) . (.100)
Thus it concats 100 and 0.1.
Assuming you meant
echo 100...100;
The reason for this is the beauty of PHP. :)
This statement is understood as
100. . .100
which is equivalent to
100.0 . 0.1
<=>
'100' . '0.1'
<=>
'1000.1'
You can read it as echo 100 . 0.1.
Actually, that only works without the quotes:
echo "100...100"; 100...100 << with quotes the . is just a char
echo 100 . 100; 100100 << two concatenated strings "100"
echo 100.100; 100.1 << 100.100 is just a number
echo 100...100; 1000.1 << what you asked
echo 100. . .100; 1000.1 << what PHP actually interprets
I was wondering if there is a simple method in PHP to format currency correctly for the following tasks:
If a value is: 4.37 then the output will be $4.37
If a value is: 4.00 then the output will be $4
If a value is: 4.3 or 4.30 then the output will be $4.30
If a value is 0.37 then the output will be 37¢
I'm sure this is quite complicated to do (I'm a beginner in PHP), but if anyone has any suggestions it would be greatly appreciated.
function format_currency($val) {
if ($val < 1) return intval(round($val * 100)) . '¢';
if (fmod($val, 1.0) == 0) return '$' . intval($val);
return '$' . intval($val) . '.' . intval(round((fmod($val,1))*100));
}
// Call it like this
$val = 1.2;
echo 'Your total: ' . format_currency($val);
Although this function will work, it's generally a bad idea to encode dollar amounts in a float.
I know this might be a bit of an overkill but take a look at Zend_Currency, it will take care of many different types of currency for this, it's also simple to use. Do note that you don't have to use the whole framework, just the currency class and the file it requires
http://framework.zend.com/manual/en/zend.currency.html
I read somewehere (I thought on codinghorror) that it is bad practice to add strings together as if they are numbers, since like numbers, strings cannot be changed. Thus, adding them together creates a new string. So, I was wondering, what is the best way to add two strings together, when focusing on performance?
Which of these four is better, or is there another way which is better?
//Note that normally at least one of these two strings is variable
$str1 = 'Hello ';
$str2 = 'World!';
$output1 = $str1.$str2; //This is said to be bad
$str1 = 'Hello ';
$output2 = $str1.'World!'; //Also bad
$str1 = 'Hello';
$str2 = 'World!';
$output3 = sprintf('%s %s', $str1, $str2); //Good?
//This last one is probaply more common as:
//$output = sprintf('%s %s', 'Hello', 'World!');
$str1 = 'Hello ';
$str2 = '{a}World!';
$output4 = str_replace('{a}', $str1, $str2);
Does it even matter?
String Concatenation with a dot is definitely the fastest one of the three methods. You will always create a new string, whether you like it or not.
Most likely the fastest way would be:
$str1 = "Hello";
$str1 .= " World";
Do not put them into double-quotes like $result = "$str1$str2"; as this will generate additional overhead for parsing symbols inside the string.
If you are going to use this just for output with echo, then use the feature of echo that you can pass it multiple parameters, as this will not generate a new string:
$str1 = "Hello";
$str2 = " World";
echo $str1, $str2;
For more information on how PHP treats interpolated strings and string concatenation check out Sarah Goleman's blog.
You are always going to create a new string whe concatenating two or more strings together. This is not necessarily 'bad', but it can have performance implications in certain scenarios (like thousands/millions of concatenations in a tight loop). I am not a PHP guy, so I can't give you any advice on the semantics of the different ways of concatenating strings, but for a single string concatenation (or just a few), just make it readable. You are not going to see a performance hit from a low number of them.
Here's the quick and dirty test code, to understand the performance bottlenecks.
Single concat:
$iterations = 1000000;
$table = 'FOO';
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = sprintf('DELETE FROM `%s` WHERE `ID` = ?', $table);
}
echo 'single sprintf,',(microtime(true) - $time)."\n";
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = 'DELETE FROM `' . $table . '` WHERE `ID` = ?';
}
echo 'single concat,',(microtime(true) - $time)."\n";
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = "DELETE FROM `$table` WHERE `ID` = ?";
}
echo 'single "$str",',(microtime(true) - $time)."\n";
I get these results:
single sprintf,0.66322994232178
single concat,0.18625092506409 <-- winner
single "$str",0.19963216781616
Many concats (10):
$iterations = 1000000;
$table = 'FOO';
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = sprintf('DELETE FROM `%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s`,`%s` WHERE `ID` = ?', $table, $table, $table, $table, $table, $table, $table, $table, $table, $table);
}
echo 'many sprintf,',(microtime(true) - $time)."\n";
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = 'DELETE FROM `' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '`,`' . $table . '` WHERE `ID` = ?';
}
echo 'many concat,',(microtime(true) - $time)."\n";
$time = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$sql = "DELETE FROM `$table`,`$table`,`$table`,`$table`,`$table`,`$table`,`$table`,`$table`,`$table`,`$table` WHERE `ID` = ?";
}
echo 'many "$str",',(microtime(true) - $time)."\n";
Results:
many sprintf,2.0778489112854
many concats,1.535336971283
many "$str",1.0247709751129 <-- winner
As conclusion, it becomes obvious that single concat via dot (.) char is the fastest. And for cases, when you've got many concats, the best performing method is using direct string injection via "injection: $inject" syntax.
Unless its really large amount of text it really doesnt matter.
As the others said, $str1 . $str2 is perfectly OK in most cases, except in (big) loops.
Note that you overlook some solutions:
$output = "$str1$str2";
and for large number of strings, you can put them in an array, and use implode() to get a string out of them.
Oh, and "adding strings" sounds bad, or at least ambiguous. In most languages, we prefer to speak of string concatenation.
It doesn't matter unless used in a looong loop. In usual cases focus on code readability, even if you lost several processor cycles.
Example 1 and 2 are similar, I don't think there should be much difference, this would be the fastes of all. No. 1 might be slightly faster.
Example 3 will be slower, as sprintf format ('%s %s') needs to be parsed.
Example 4 does the replace, which involves searching within a string - additional thing to do, takes more time.
But firstly, is concatenating strings a performance problem? It's very unlikely, you should profile code to measure how much time does it take to run it. Then, replace the concatenating method with a different one and time again.
If you identify it as a problem, try googling for php string builder class (there are some to be found) or write your own.
Found this post from Google, and thought I'd run some benchmarks, as I was curious what the result would be. (Benchmarked over 10,000 iterations using a benchmarker that subtracts its own overhead.)
Which 2 strings 10 strings 50 strings
----------------------------------------------------------------
$a[] then implode() 2728.20 ps 6.02 μs 22.73 μs
$a . $a . $a 496.44 ps 1.48 μs 7.00 μs
$b .= $a 421.40 ps ★ 1.26 μs 5.56 μs
ob_start() and echo $a 2278.16 ps 3.08 μs 8.07 μs
"$a$a$a" 482.87 ps 1.21 μs ★ 4.94 μs ★
sprintf() 1543.26 ps 3.21 μs 12.08 μs
So there's not much in it. Probably good to avoid sprintf() and implode() if you need something to be screaming fast, but there's not much difference between all the usual methods.
there are 3 types of string joining operations.
Concatenate, take 2 string, allocate memory size length1+length2 and copy each into the new memory. quickest for 2 strings. However, concatenating 10 strings then requires 9 concat operations. The memory used is the 1st string 10 times, 2nd string 10 times, 3rd string 9 times, 4th string 8 times, etc. Runs X+1 +(X-1)*2 operations using more memory each cycle.
sprintf (array_merge, join, etc), take all the strings together, sum their length, allocate a new string of size sum, then copy each string into its respective place. memory used is 2*length of all initial strings, and operations is 2*X (each length, each copy)
ob (output buffer) allocate a generic 4k chunk and copies each string to it. memory 4k + each initial string, operations = 2 + X. (start, end, each copy)
Pick your poison.
OB is like using a memory atom bomb to join 2 small strings, but is very effective when there are many joins, loops, conditions or the additions are too dynamic for a clean sprintf.
concat is the most efficient to join a few fixed strings,
sprintf which works better for building a string out of fixed values at one time.
I don't know which routine php uses in this situation: "$x $y $z", might just be reduced to an inline $x . " " . $y . " " . $z
The advice you have read may have been related to the echo function, for which it's quicker to use commas, eg:
echo $str1, $str2;
Another approach is to build up a string in a variable (eg using the . operator) then echo the whole string at the end.
You could test this yourself using the microtime function (you'll need to make a loop that repeats eg 1,000 or 100,000 times to make the numbers significant). But of the four you posted, the first one is likely to be the fastest. It's also the most readable - the others don't really make sense programmatically.
This is not a solution for 2 strings, but when you thinking of joining more strings best way like that:
$tmp=srray();
for(;;) $tmp[]='some string';
$str=implode('',$tmp);
It's faster to create array element and join them all at once, than join them hundred times.
I'm not a PHP guru, however, in many other languages (e.g. Python), the fastest way to build a long string out of many smaller strings is to append the strings you want to concatenate to a list, and then to join them using a built-in join method. For example:
$result = array();
array_push("Hello,");
array_push("my");
array_push("name");
array_push("is");
array_push("John");
array_push("Doe.");
$my_string = join(" ", $result);
If you are building a huge string in a tight loop, the fastest way to do it is by appending to the array and then joining the array at the end.
Note: This entire discussion hinges on the performance of an array_push. You need to be appending your strings to a list in order for this to be effective on very large strings. Because of my limited exposure to php, I'm not sure if such a structure is available or whether php's array is fast at appending new elements.
For nearly 2 years after last post in this thread, I think that below solution may be the fastest for huge number of tight loops:
ob_start();
echo $str1;
echo $str2;
.
.
.
echo $str_n;
$finalstr = ob_get_clean();
This method ensures a flat storage of all strings and no processing or concatenation overhead. With the final code line, you get entire buffer as well. You can safely run loops instead of independent echos.