This function works fine outside of a class. Ie simply define the function and call it. Yet when I add it to a class it no longer works - any help is greatly appreciated:
public function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value || (is_array($value) && $this->recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
obviously the $this is removed when not in a class.
Edit:
The error I an getting when using it in a class is:
Invalid argument supplied for foreach()
app\components\GenFun::recursive_array_search('9377907', 9378390)
My sole expectation from the function is that it returns any key (ie identifies that the needle exists in the haystack) - I actually dont care about the actual index.
To be perfectly honest, "it no longer works" isn't a helpful metric by which to assist you in debugging your problem. Nor is "it works fine", since that doesn't tell us your definition of what works means to you. More precisely, these statements don't tell us what you expected the code to do that it's not doing, or what the code is doing that you did not expect.
To me this code is doing exactly what you've told it to do and the result of both a function as well as a class method (using the same code) are identical... See the working 3v4l pastebin here.
However, my guess is that your expectations may be different from what this code actually does. Specifically, this function will return at the very first match of the $needle in the $haystack. Such that the following array, returns 0 (_that is with a needle of 'foo').
$haystack = ['foo', ['foo', 'bar']];
It will also return only the key of the outer-most array in the $haystack. Meaning, the following array returns 0 as the key. Even though the actual match is in $haystack[0][1][2]
$haystack = [['bar',['quix','baz','foo'],'baz'],'quix'];
So depending on what you expected (the inner-most key, or the outer-most key), you may believe this function doesn't work.
So you'll need to clarify exactly what you want the code to do and provide some reproducible example of what didn't work (and that includes the data used or arguments provided to the function).
EDIT:
Hey, I'm glad you figured it out. Here are just a few suggestions to maybe help you refactor this code slightly as well...
So since you're looking for the existence of the needle in any part of the array and don't actually care about the key, you may want to make your intent more obvious in the logic.
So for example, always return a boolean (true on success and false on failure) rather than return false on failure and the key on success. This makes checking the function's result easier and clearer from the caller's perspective. Also, consider naming the function to describe it's intent more clearly (for example: in_array_recursive rather than recursive_array_search since we're not actually intent on searching the array for something, but proving that something is actually in the array). Finally, consider avoiding multiple return points in the same function as this makes debugging harder.
So a cleaner way to write the same code might be something like this:
public function in_array_recursive($needle, $haystack, $strict = false) {
$result = false;
foreach($haystack as $value) {
if(!is_array($value)) {
$result = $strict ? $needle === $value : $needle == $value;
} else {
$result = $this->in_array_recursive($needle, $value, $strict);
}
if ($result) {
break;
}
}
return $result;
}
Now the caller simply does...
$arr = ['bar',['foo']];
if (in_array_recursive('foo', $arr)) {
/* 'foo' is in $arr! */
} else {
/* 'foo' is not in $arr... */
}
Making the code more readable and easier to debug. Notice you also don't have to use exact match if you wanted to add an optional argument for $strict at the end of the function there and also be more inline with in_array.
So the reason this was not working was due to the method by which I was defining $needle.
In my old code it would be input as an integer and in my new code it was a string. The === operator then obviously denied it as being the same. This is why you don't work at 2am :)
Related
I have an array that I want to test for empty elements, I have tried using array_walk to walk an array (a single dimension array) and return true if the array of elements (values) has got any empty elements.
I naturally went to the PHP website, looked at the examples on offer and they don't make any sense because it does not give a clear example of how you would use array_walk for this. I tried array_filter and that didn't seem to do much either.
Example of what I was trying to do.
$test = array("Tree"=>"Ash","TreeID"=>"Q23-123","count"=>14,"User"=>"P.Williams");
$result = array_walk( $test, "empty", true );
All I get as a result is "Array".
and PHP parser is having a hissy fit about it, they say clearly, bool array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] ) in their site, this I deduced to be something $result array_walk ( $theTargetArray, "StringNameOfFunction", theResultIfTure); but nothing is that simple.
So far I have found lots of examples on many sites and it seems that people have just copied and pasted the PHP examples, some have changed their names to hide that they have copied and pasted the example...
Can someone let me know what it is that I am doing wrong here please?
(Also FAO stackoverflow site maintainers, What is the point in suggesting a tag, I click to use it and then I get told I can not create a new tag unless I have 1500 points??? Seriously Why? Good idea if you go and think about that one.)
$my_arr = array(....); // your array here
$has_empty_value = sizeof($my_arr) != sizeof(array_filter($my_arr));
array_walk is not going to help you on that. It's a mutator function, and is intended to change array elements, not to retrieve information about them. What you're looking for is a boolean aggregator function known as any or some in other languages. PHP doesn't provide it out of the box, so you have to write it by hand:
function any($iter, $pred) {
foreach($iter as $item)
if($pred($item))
return true;
return false;
}
However, an attempt to use it with empty, as in
print any($test, 'empty')
will fail, because empty is not a real function and can't be used indirectly. A workaround is to wrap it in yet another function and pass that one to any:
any($test, function($x) { return empty($x); })
Another option is to filter an array through boolval, thus removing "falsy" values, and compare lengths:
$hasEmptyElements = count(array_filter($test, 'boolval')) < count($test);
Note that, unlike any, which is "lazy", filter always processes the whole array.
The docs for array_walk say "Applies the user-defined callback function to each element of the array array." Therefore, you can use it with your own callbacks or with with a closure, like so:
$test = array("Tree"=>"Ash","TreeID"=>"Q23-123", "count"=> 14, "User"=>"P.Williams");
$result = array_walk( $test, function($value) {
return empty($value);
});
Of course, this depends on what you are trying to achieve as this will loop through all the values and $result will be true if all the values are empty but the last one is not.
If you are looking to find out if any of the values are empty, a function that stops after it finds an empty item would be better:
function hasEmptyValues(array $array)
{
foreach ($array as $key => $value) {
if (empty($value)) {
//Empty value found
return true;
}
}
//None of the values are empty
return false;
}
Ok... right of the batt, let me clear what the question is not about.
it is not about in_array.
Because as the PHP manual clearly explains, the function 'in_array' checks if a value exists in an array. But it does this check based on equality. It does not do as based on partial existence.
For example, if the value I'm looking is 'overflow' and I happened have an array like this
array('cnn','stackoverflow'),
the in_array would come back with a FALSE, telling me that overflow does not exist in the in values of this array, which in a way is TRUE. but also in a way, is FALSE.
To me, the string "overflow" do exists in the string stackoverflow". Therefore, it should have returned TRUE. Of course I cannot argue this point much.
Is there another function ( or an efficient one-liner) in PHP to get me what I want?
i'm looking for a solution something like this
array_filter($ary,'strlen');
which removes the empty lines from the $ary in a very efficient way.
I do not want to go thru the traditional way that is to go thru a foreach and do a comparison between the needle and the haystack using strpos. That solution I already know.
I'm looking for a one liner, like in the (strlen) example
Thx.
No function available in php which satisfy exact requirement of author. Developer has to write some code so you can try below code:
function array_check($arr, $keyword) {
foreach($arr as $index => $string) {
if (strpos($string, $keyword) !== FALSE)
return $index;
}
}
var_dump(array_check(array('cnn','stackoverflow'),'overflow'));
exit;
Lame option: false !== strpos(implode($ary, '|'),'overflow') As long as the separator character (| here) isn't in your search string, this works.
More sophisticated option: count(array_filter( $ary, function($x) { return strpos($x, 'overflow'); } ) );
Edit: Second option full code looks like this:
$ary = array('cnn', 'stackoverflow'); // or whatever your data is
(bool) count(array_filter( $ary, function($x) { return strpos($x, 'overflow'); } ) );
The count() value will be 0 if not found, or positive if a match was found. So, you could use it in an if() statement, return it from a function, or whatever.
I wanted to allow only specific email domain. Actually I did it. What i wanted to ask why my first code did not work at all.
I am just trying to learn PHP so that the question may seem silly, sorry for that.
Here is my code:
function check_email_address($email) {
$checkmail = print_r (explode("#",$email));
$container = $checkmail[1];
if(strcmp($container, "gmail.com")) {
return true;
}else {
return false;
}
}
Check out the documentation for strcmp() , it will return 0 of the two strings are the same, so that's the check you want to be doing. Also, you're using print_r() when you shouldn't be, as mentioned by the other answerers.
Anyway, here's how I would have done the function - it's much simpler and uses only one line of code:
function check_email_address($email) {
return (strtolower(strstr($email, '#')) == 'gmail.com');
}
It uses the strstr() function and the strtolower() function to get the domain name and change it to lower case, and then it checks if it is gmail.com or not. It then returns the result of that comparison.
It's because you're using print_r. It doesn't do what you seem to expect from it at all. Remove it:
$checkmail = explode("#", $email);
You can find the docs about print_r here:
http://php.net/print_r
Besides that, you can just use the following (it's much shorter):
$parts = explode("#", $email);
return (strcmp($parts[1], "gmail.com") == 0);
The following row doesn't work as you think it does:
$checkmail = print_r (explode("#",$email));
This means that you're trying to assign the return value from print_r() into $checkmail, but it doesn't actually return anything (if you don't supply the second, optional parameter with the value true).
Even then, it would've gotten a string containing the array structure, and your $container would have taken the value r, as it's the second letter in Array.
Bottom line: if your row would've been without the call to print_r(), it would've been working as planned (as long as you made sure to compare the strcmp() versus 0, as it means that the strings are identical).
Edit:
Interesting enough, I just realized that this could be achieved with the use of substr() too:
<?php
//Did we find #gmail.com at the end?
if( strtolower(substr($email, -10)) == '#gmail.com' ) {
//Do something since it's an gmail.com-address
} else {
//Error handling here
}
?>
You want:
if(strcmp($container, "gmail.com")==0)
instead of
if(strcmp($container, "gmail.com"))
Oh! And no inlined print_r() of course.
Even better:
return strcmp($container, "gmail.com")==0;
No need for the print_r; explode returns a list. And in terms of style (at least, my style) no need to assign the Nth element of that list to another variable unless you intend to use it a lot elsewhere. Thus,
$c = explode('#',$mail);
if(strcmp($c[1],'gmail.com') == 0) return true;
return false;
Do you consider this a code smell?
foreach((array)$foo as $bar)
{
$bar->doStuff();
}
Should i use that instead?
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
Any other good practices to cover not set variables and assert an array?
They're both code smells. The second one is just evading all the error messages, kind of like turning off the fire alarm before you set your kitchen on fire. Both of those tell you that you have no idea what's in the variable $foo or if it's even been defined in the code above. You need to go back up through the code and find out exactly what's going on with $foo.
If it was my code, $foo would probably be always defined either as an array, or else false to indicate the array isn't needed:
if(do_we_need_an_array())
$foo = function_returning_an_array();
else
$foo = false;
[...snip...]
if($foo)
foreach($foo as $f) { ... }
If you are testing if variables are set, you can initialize them:
if (! $foo or !is_array($foo))
$foo = array();
foreach($foo as $bar)
{
$bar->doStuff();
}
Personally, I would never do the first method and always opt for the second.
If $foo should always be an array, then the second form would be much better if you did some kind of handling for the error case, e.g.:
if (isset($foo) && is_array($foo))
{
foreach($foo as $bar)
{
$bar->doStuff();
}
}
else
{
// This should not happen, exit angrily.
exit("Oh crap, foo isn't an array!");
}
Of course you don't have to just exit the application, but do whatever is appropriate in that case, maybe logging or some alternate logic.
(array)$foo != if (isset($foo) && is_array($foo))
The (array) cast can be useful for casting objects to arrays or scalars to arrays so you can create consistent interfaces to variables that may contain single values or arrays.
(array)$foo == array($foo)
As defined in the PHP Manual for Array Types.
So if you need to always use an array then the first code snippet you presented would be the answer. However type casting rules still apply so you may not get what you want, so look to the manual for more info. Otherwise the second option would prevent accessing unset variables that are not arrays.
As far as a code smell, I would say that checking for unset variables can certainly be avoided, however always knowing that a variable is going to have an array is more often than not, going to creep up. So I would aim to keep code wrapped in is_array($foo) if-then statements to a minimum.
I usually do this to make sure a foreach can handle both scalars and collections:
<?php
foreach (makeSureTraversable($scalarOrCollection) as $val)
{
// Do something.
}
function
makeSureTraversable($anything)
{
if (is_array($anything) || ($anything instanceof Traversable))
{
return $anything;
}
else
{
return array($anything);
}
}
This way I also handle classes that implement Traversable (from the SPL), which means allowing them to be used in foreaches.
if (!isset($foo) && !is_array($foo)) {
throw new InvalidArgumentException('Wrong array passed');
// Or do something to recover lost array
}
foreach($foo as $bar) {
$bar->doStuff();
}
There's quite a few times that you'd like to write a function to take one or more values for a parameter:
function getNamesById($id) { }
In this case, it would make sense that if this function was called with an array of ids, it should probably return an array of names. Similarly, to save the calling code from having to wrap the input in an array and then unwrap the output, if you just pass a scalar, then a scalar should be returned. Consider the likely contents of the function designed to handle both scalar and array parameters:
function getNamesById($id) {
$returnAnArray = is_array($id);
$output = array();
foreach ((array)$id as $theId) {
// perform some logic
$output[] = someFunction($theId);
}
return $returnAnArray ? $output : $output[0];
}
You can see that in this case, casting to an array definitely makes things a lot easier for everyone. As they say, be liberal in what you accept... As long as it is documented that it is expected that a variable could be either, then I see no problem. PHP is a duck-typed language, which has both benefits and drawbacks, but this is one of the benefits, so enjoy it!
Since PHP is a dynamic language what's the best way of checking to see if a provided field is empty?
I want to ensure that:
null is considered an empty string
a white space only string is considered empty
that "0" is not considered empty
This is what I've got so far:
$question = trim($_POST['question']);
if ("" === "$question") {
// Handle error here
}
There must be a simpler way of doing this?
// Function for basic field validation (present and neither empty nor only white space
function IsNullOrEmptyString($str){
return ($str === null || trim($str) === '');
}
Old post but someone might need it as I did ;)
if (strlen($str) == 0){
do what ever
}
replace $str with your variable.
NULL and "" both return 0 when using strlen.
Use PHP's empty() function. The following things are considered to be empty
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
NULL
FALSE
array() (an empty array)
$var; (a variable declared, but without a value)
For more details check empty function
I'll humbly accept if I'm wrong, but I tested on my own end and found that the following works for testing both string(0) "" and NULL valued variables:
if ( $question ) {
// Handle success here
}
Which could also be reversed to test for success as such:
if ( !$question ) {
// Handle error here
}
Beware false negatives from the trim() function — it performs a cast-to-string before trimming, and thus will return e.g. "Array" if you pass it an empty array. That may not be an issue, depending on how you process your data, but with the code you supply, a field named question[] could be supplied in the POST data and appear to be a non-empty string. Instead, I would suggest:
$question = $_POST['question'];
if (!is_string || ($question = trim($question))) {
// Handle error here
}
// If $question was a string, it will have been trimmed by this point
There is no better way but since it's an operation you usually do quite often, you'd better automatize the process.
Most frameworks offer a way to make arguments parsing an easy task. You can build you own object for that. Quick and dirty example :
class Request
{
// This is the spirit but you may want to make that cleaner :-)
function get($key, $default=null, $from=null)
{
if ($from) :
if (isset(${'_'.$from}[$key]));
return sanitize(${'_'.strtoupper($from)}[$key]); // didn't test that but it should work
else
if isset($_REQUEST[$key])
return sanitize($_REQUEST[$key]);
return $default;
}
// basics. Enforce it with filters according to your needs
function sanitize($data)
{
return addslashes(trim($data));
}
// your rules here
function isEmptyString($data)
{
return (trim($data) === "" or $data === null);
}
function exists($key) {}
function setFlash($name, $value) {}
[...]
}
$request = new Request();
$question= $request->get('question', '', 'post');
print $request->isEmptyString($question);
Symfony use that kind of sugar massively.
But you are talking about more than that, with your "// Handle error here
". You are mixing 2 jobs : getting the data and processing it. This is not the same at all.
There are other mechanisms you can use to validate data. Again, frameworks can show you best pratices.
Create objects that represent the data of your form, then attach processses and fall back to it. It sounds far more work that hacking a quick PHP script (and it is the first time), but it's reusable, flexible, and much less error prone since form validation with usual PHP tends to quickly become spaguetti code.
This one checks arrays and strings:
function is_set($val) {
if(is_array($val)) return !empty($val);
return strlen(trim($val)) ? true : false;
}
to be more robust (tabulation, return…), I define:
function is_not_empty_string($str) {
if (is_string($str) && trim($str, " \t\n\r\0") !== '')
return true;
else
return false;
}
// code to test
$values = array(false, true, null, 'abc', '23', 23, '23.5', 23.5, '', ' ', '0', 0);
foreach ($values as $value) {
var_export($value);
if (is_not_empty_string($value))
print(" is a none empty string!\n");
else
print(" is not a string or is an empty string\n");
}
sources:
https://www.php.net/manual/en/function.is-string.php
https://www.php.net/manual/en/function.trim.php
When you want to check if a value is provided for a field, that field may be a string , an array, or undifined. So, the following is enough
function isSet($param)
{
return (is_array($param) && count($param)) || trim($param) !== '';
}
use this :
// check for null or empty
if (empty($var)) {
...
}
else {
...
}
empty() used to work for this, but the behavior of empty() has changed several times. As always, the php docs are always the best source for exact behavior and the comments on those pages usually provide a good history of the changes over time. If you want to check for a lack of object properties, a very defensive method at the moment is:
if (is_object($theObject) && (count(get_object_vars($theObject)) > 0)) {