PHP: Faster to check if variable is integer or starts with # - php

I'm developing a module with some arrays in it.
Now my array contains:
$omearray = array (
'#title' = 'title',
0 = array ( 'another array', '2ndvalue', ),
);
foreach($omearray as $key => $value)
When I load the array to display I want to check whether the $key value is a 0, 1, 2, 3, etc. or a $key value that starts with a #.
Which would be better to use: checking if the value is_int() or a regex that checks whether the first character is a #?
EDIT: checking stringpos 1 is # vs is_int() since regex seems to be slow.

Since if ($key[0]=='#') is faster and is_int() is exhaustive, and || is a short circuit operator with left associativity (evaluates from left to right, see example 1 on http://php.net/manual/en/language.operators.logical.php) I would say:
if ($key[0]=='#' || is_int($val)) /* etc */
Because this way you only need to bother about using the # key naming self made convention with the keys you'll compare so much that you'd benefit from an optimization.
But unless you're making a huge number of evaluations I would stick to just if(is_int($val)), because it's clearer and less messy.

I would check it using if($key[0]=="#")
You can also check if the $value is an array (is_array($value)), in this case you dont need either regex,is_int, and # char.
PS: # character means (somewhere) "I'm a number/ID"

if (is_int($val) || $val{0} == '#') ...

I would go for the is_int(), because you are not dependent on the string. It can be anything and your code will still just take the integer indeices. Looking for # as the first character will prevent that flexibility. But if you are absolutely sure that the string's first character will always be a #, than the $Key[0] == '#' will be the fastest option.

You haven't given much insight into your actual data or reason for choosing this structure, but depending on that info, altering your structure so that you aren't inter-mixing integer indexes with hash keys may be an option:
$omearray = array(
'#title' => 'title',
'foo' => array(
'anotherarray',
'2ndvalue'
)
);
foreach ($omearray as $key => $value) {
if ($key == 'foo') {
// do something with $value, which is the 2nd array, numerically indexed
}
}
Apologies if this solution doesn't suit your needs.

You can check if string is integer
or if it starts with #
or both
or whatever.
It's all makes not a slightest difference.
It is not a part of your code that may affect any performance issue ever.

Related

Using Eval to perform if comparison of strings in PHP

I have some code that looks like this.
eval('$ruleTrue = '."{$value} {$operator} {$value2};");
I am pulling mostly numeric values from a database and comparing them with other numeric values. The operator comes from a database as well. Possible operators are <,>,==.
Well when comparing ints and floats this works perfectly. BUT when comparing strings it breaks. For instance..
WORKS:
5 > 4
$ruleTrue = true
Doesn't Work Right:
John-Adams == Alice
$ruleTrue = true <--- WHY? Because John is not == to Alice.
For some reason my $ruleTrue variable is being returned as true when comparing strings.
You're trying to evaluate this code:
$ruleTrue = John == Alice;
John and Alice aren't strings, they're undefined constants. You want to put quotes around them. But be careful, because if your users are able to edit those fields in your database, they could find a way of unquoting the strings and executing their own php code, which could be disastrous. Eval is very unsecure that way, and you probably shouldn't be using it.
The expression John-Adams == Alice parses somewhat like (John - Adams) == Alice. The left side is trying to subtract two strings (undefined constants are considered 'barewords', and are equal to a stringification of their names; John === 'John', for example), and in order to make sense of such an odd operation, PHP turns both strings into numbers. As integers, both strings have a value of 0, so the left side equates to 0.
An int.
Now, when PHP wants to compare with ==, it wants to coerce both sides into the same type. In this case, it's converting to int. Alice also converts to 0. Both sides being 0, they're "obviously" equal.
In order to prevent this from happening, you should probably put quotes around your values. You might also consider using the strict equals operator (===), unless you really want that type coercion magic.
Alternatively, if you have a known set of operators, you can eliminate eval and make this safer and more robust all around, by making a comparison function that has sub-functions for the operators. Like so:
function compare($value1, $op, $value2) {
static $known_ops = array(
'==' => function($a, $b) { return $a == $b; },
'!=' => function($a, $b) { return $a != $b; },
...
# you can even make up your own operators. For example, Perl's 'eq':
'eq' => function($a, $b) { return "$a" === "$b"; }
...
);
$func = $known_ops[$op];
return $func($value1, $value2);
}
...
$ruleTrue = compare($value, $operator, $value2);
Now you don't have to worry about your values. You do have to worry about $operator, but that's only an issue if you let a user input it without you validating it. In which case, you may want to throw an exception or something if $op wasn't in $known_ops, cause if you leave PHP to handle it, you'll likely get a fatal error when it tries to call null.
Make sure, if the value are strings, they are surrounded with "quotes"

What is this bitwise OR doing in this weird array key construct?

Can someone explain to me what this means?? I have never seen this construct - taken from the Prestashop doc
foreach ( $languages as $language )
{
echo '<div id="test_' . $language['id_lang'|'id_lang'] .... // <-- What the??
// ...
}
$language contains the following keys:
Array
(
[id_lang] => 1
[name] => English (English)
// and others...
)
The result is that it takes the value of $language["id_lang"] - 1. But I don't understand the syntax and can't find any documentation about it.
This php -a session shows that it's totally meaningless:
php > $value = 'something'|'something';
php > echo $value;
something
php > $arr = array('abc' => 1, 'def' => 2);
php > echo $arr['abc'|'abc'];
1
php > echo $arr['def'|'def'];
2
Basically, if you "bitwise or" anything by itself, you get the original value. This property is called idempotence in mathematics. For further info, read:
http://en.wikipedia.org/wiki/Idempotence
http://en.wikipedia.org/wiki/Bitwise_operation#OR
Honestly, the original author of that code had no idea what they were doing.
What that does is use the bitwise operator on the ASCII values of the characters in the string "id_lang", although why they are doing this is beyond me, since the result is always going to be the same.
To elaborate a little bit, let's say (for convenience) that we're using ASCII, where each character is encoded as a single byte. Let's look at what happens when it does the comparison for the binary representation of the first character (i is 105, which in binary is 01101001):
"i": 01101001
OR "i": 01101001
___________
= 01101001
= "i"
0|0 is 0, 1|1 is 1, so inevitably all bits remain unchanged.
It's not doing anything, strangely enough.
var_dump('id_lang'|'id_lang');
#=> string(7) "id_lang"
http://ideone.com/zXdRMO
Even if it was doing something, using a bitwise operator on a string-based array key certainly feels like code smell to me.

Mongodb - logical operators e.g. "$lt" "$lte" - but is there one for '=' to simplify programming

Jimmy Sawczuk gave a great help going forward, but also inspired me to look into "$in" for using it as an equal '=' operator, but it might not be possible...
I have this code which I would like to make more nice (know it looks complicated (so skip it) but I will get to the point soon and then it might not be that complicated:
// First html drop down selection / filter selection
if ($key[0] <> "") {
if ($op[0] == "=") $query = array($key[0] => $val[0]);
else $query = array( $key[0] => array( $op[0] => $val[0] ) );
}
else return($query);
// Second html drop down selection / filter selection
if ($key[1] <> "") {
if ($op[1] == "=") $query = array( $log[0] => array( array($key[1] => $val[1]), $query));
else $query = array( $log[0] => array( array( $key[1] => array( $op[1] => $val[1] ) ), $query) );
}
else return($query);
What I would like to do is to simplify the code (for simplicity I have merged key0 and key1 lines):
if ($key[1] <> "") $query = array( $log[0] => array( array( $key[1] => array( $op[1] => $val[1] ) ), array( $key[0] => array( $op[0] => $val[0] ) )) );
$log: logical operater: "$and", "$or"
$key: key in database
$val: value to be matched against
$op: operater e.g. "$lt", "$gt", "$lte", "$gte", "$ne" all supported by Mongo.
Now my problem is that I would like to have a $op which was '$=' or a bit like $in. However $in seems to match the same key for several values, but in my case the keys could be different. Having a '$=' like operator in mongo would highly simplify my programming. I have read several tutorials but can not find such a operator. I find it is strange as when you have $ne (not equal) why then not having equal. Have I missed something, is there a workaround (well I do workaround now but it looks ugly) or does $= exist ?
There is no equal operator. Instead, MongoDB simple uses the javascript colon as an equal operator.
So {x : 1} means x equals 1. In PHP this would translate array('x' => 1).
I find it is strange as when you have $ne (not equal) why then not having equal.
Yes, it is definitely weird. In fact, it's a long outstanding bug.
A similar problem existed with $and. In versions prior to 2.0, there was no $and operator. Instead you used the comma {x:1, y:2} meant x equals 1 and y equals 2. This actually made certain queries with $and/$or combinations very difficult.
Long term, I don't expect that bug to ever be fixed. It will likely all be supplanted by the new aggregation framework which actually supports an $eq operator.
As a small note, if you really need to emulate an $eq, you can either use:
{x: {$in: [1] } }
Or, if you want to avoid $in for whatever reason, this also works:
{x: {$gte: 1, $lte: 1} }
Any value that is both greater than or equal and less than or equal to another value must equal the other value. This works fine with strings:
{x: {$gte: 'James Bond', $lte: 'James Bond' } }
It will not work with arrays or objects.
Equality operator $eq was introduced in MongoDB 3.0.
http://docs.mongodb.org/manual/reference/operator/query/eq/

Simplifing PHP or statements

Okay, I believe that I can simplify this line of code except I can't find anything online. Can anyone help me?
if(empty($action) || $action == "a" || $action == "b" || $action == "c") {
}
Thanks!
You can use in_array() to search an array of possible values for $action:
if (empty($action) || in_array($action, array('a', 'b', 'c'))) {
}
I keep the empty() condition separate because empty() tests true for a number of things besides the empty string ''.
If all you care is to test for the empty string, you can include it in in_array(), and get rid of that empty():
if (in_array($action, array('', 'a', 'b', 'c'))) {
}
Performantly, I think explicitly including empty() is a teeny weeny little bit better, due to short-circuiting.
$options = array("a", "b", "c");
if (empty($action) || in_array($action, $options) ) {
}
I suggest you don't simplify this code because you may gain some kind of readability but you would also increase computation time. And that's a thing I would prefer to improve more than readability (code is executed a lot more often than read in plain text).
Why is that?
An expression like yours consisting only of or conjunctions will end right the moment the first 'true' value is found. So in best case PHP will just evaluate empty($action) with true and leave the if-header without evaluating all the other parts. In worst case it will check every part till the end. So in average - without further knowlege about the data you will check - you are running and computing half of the if-header.
But if you use something like in_array($action, array('a', 'b', 'c')) than you will construct the whole array with all elements and search in it. So your PHP interpreter has to create all the elements and the array just to match one of the first ones. Which is anyway more computation than your current line.
So think twice about readabilty vs. code complexity.
if (empty($action) || preg_match('/^[abc]$/', $action)) { ... }
Edit: empty($action) is needed to allow false, array(), 0, and null to pass which you may not want.

What's better, isset or not?

Is there any speed difference between
if (isset($_POST['var']))
or
if ($_POST['var'])
And which is better or are they the same?
It is a good practice to use isset for the following reasons:
If $_POST['var'] is an empty string or "0", isset will still detect that the variable exists.
Not using isset will generate a notice.
They aren't the same. Consider a notional array:
$arr = array(
'a' => false,
'b' => 0,
'c' => '',
'd' => array(),
'e' => null,
'f' => 0.0,
);
Assuming $x is one of those keys ('a' to 'f') and the key 'g' which isn't there it works like this:
$arr[$x] is false for all keys a to g;
isset($arr[$x]) is true for keys a, b, c, d and f but false for e and g; and
array_key_exists($x, $arr) is true for all keys a to f, false for g.
I suggest you look at PHP's type juggling, specifically conversion to booleans.
Lastly, what you're doing is called micro-optimization. Never choose which one of those by whichever is perceived to be faster. Whichever is faster is so negligible in difference that it should never be a factor even if you could reliably determine which is faster (which I'm not sure you could to any statistically significant level).
isset tests that the variable has any value, while the if tests the value of the variable.
For example:
// $_POST['var'] == 'false' (the string false)
if (isset($_POST['var'])) {
// Will enter this if
}
if ($_POST['var']) {
// Won't enter this one
}
The big problem is that the equivalency of the two expressions depends on the value of the variable you are checking, so you can't make assumptions.
In strict PHP, you need to check if a variable is set before using it.
error_reporting(E_ALL | E_STRICT);
What you are doing here
if($var)
Isn't checking if the value is set. So Strict PHP will generate a notice for unset variables. (this happens a lot with arrays)
Also in strict PHP (just an FYI for you or others), using an unset var as an argument in a function will throw a notice and you can't check isset() within the function to avoid that.
Just repeating what others said, if you execute:
if($variable)
and $variable is not set, you'll get a notice error. Plus..
$var = 0;
if($variable) {
//This code will never run, because $var is false
}
but using isset would return true in this case.

Categories