I am recursively searching a tree node for its parents, and then trying to return its parent categories in an array.
The function receives and passes itself an array of each parent which is finally returned.
Even though this array has elements, the statement before the return when viewed outside the function is nul.
To make it work, I just made the parameter by reference. But why is it always nul?
Here is my code:
function getParent($id,$parents){ // to work changed this to getParent($id,&$parents)
if($id < 2) { // 1 is the Top of the Tree , so job is done
return $string;
}
$child = DB::fetchExecute((object)array( // pdo query for category to get parents
'sql' => "category",
'values'=> array($id),
'single'=> 1,
'debug' => 0)
);
$parent = DB::fetchExecute((object)array( // pdo query for parents info
'sql' => "category",
'values'=> array($child->native_parent_category_id),
'single'=> 1,
'debug' => 0)
);
$string[]= "<li>$parent->name ($parent->native_category_id)</li>
";
getParent($parent->native_category_id , $parents);
}
// call function
$array = array();
$returnString = getParent($id,$string);
var_dump($returnString,$array); // will both be NULL, or if called by Reference $array has the goods
?>
Change:
function getParent($id,$parents){
To:
function getParent($id,$parents, $string){
And change:
getParent($parent->native_category_id , $parents);
To:
getParent($parent->native_category_id , $parents, $string);
The scope of $string only exists in the function as it runs - so, if you rerun the function, it will reset all the variables within it. You need to send the variable each time you rerun it.
I would declare $string before function, and inside function, use global $string; at top.
Related
This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I have written the function below to return parent id's of all categories in Worpress by category id. Everything works fine except it does not return the array... Any suggestions would be great! :-)
$current_category = (int) $_GET['current_category'];
$cat_ids = array($current_category);
function getParentCatIds($current_category,$cat_ids){
$child = get_category($current_category);
$parent_id = $child->parent;
if($parent_id !== 0){
array_push($cat_ids, $parent_id);
getParentCatIds($parent_id,$cat_ids);
}else{
var_dump($cat_ids); // <--- this returns the right array
return $cat_ids; // <--- this returns NULL
}
}
if($current_category){
$cat_ids = getParentCatIds($current_category,$cat_ids);
var_dump($cat_ids); // <--- this returns NULL
}
When you call getParentCatIds() (line 9), you didn't do anything with the return of the function. You should assign it or return it.
You should always sanitize input. A good starting point is filter_var(). Using the right flags for validation and sanitization and makes your input save(r). Keep in mind that FILTER_VALIDATE_* only tells you if something is valid, while FILTER_SANITIZE_* actually cleans your data from unwanted and potentially malicious data.
$currentCat = intval( $_GET['current_category'] );
$currentCat = filter_var( $currentCat, FILTER_SANITIZE_NUMBER_INT );
if ( empty( $currentCat ) ) {
// Abort – no valid data
return;
}
Then you can build an array, containing your original Category parent ID. This you can pass to array_walk(), with a callback as second parameter. The callback itself has assigned a collecting/ final array that is passed as reference and serves as target for your results. Now you can loop recursively over an indefineatly nested WordPress category hierarchy.
// Base array
$parents = [];
$callback = function( $index, $id ) use ( &$parent ) {
0 !== get_category( $id )->parent and $parents[] = $id;
};
array_walk( [ get_category( $currentCat )->parent ], $callback );
1) You should have asked at wordpress.stackexchange.com
2) I think solution is: on line 9 , instead of
getParentCatIds($parent_id,$cat_ids);
you should have
return getParentCatIds($parent_id,$cat_ids);
I needed a function that recursively parses a multi-dimensional array for a (part) of a certain string value and, if found, returns the entire value the string is contained in.
I came up with the following solution:
function & ransackArray(array & $haystack, $needle) {
foreach($haystack as &$element) {
if(is_array($element)){
if($v=ransackArray($element, $needle))
return $v;
} else {
if(strstr($element, $needle))
return $element;
}
}
return false;
}
This works fine. For instance, providing:
$data=array(
'key' => 'something',
'children' => array(
'someValue' => 'myTest',
'someMore' => 'yes'
)
);
And running:
$r=ransackArray($data, 'myTes');
This will result in $r containing 'myTest'.
The problem is that now that i found this value, i want to change it in $data, right on the fly, by writing:
$r='new value';
Which should then effectively result in data looking like this:
$data=array(
'key' => 'something',
'children' => array(
'someValue' => 'new value',
'someMore' => 'yes'
)
);
This however, doesn't seem to work. Perhaps i misunderstand something about references. In any case, the reason i needed this to work is why i pass $haystack as a reference and also return the function's result as one.
Can this be done? If yes, how? And if not - why not?
You're missing two ampersands...one on this line:
if($v = self::ransackArray($element, $needle))
which should be:
if($v = &self::ransackArray($element, $needle))
and one on this line:
$r = ransackArray($data, 'myTes');
which should be:
$r = &ransackArray($data, 'myTes');
(Note: it looks like your ransackArray function is actually a method in a class, so if you're calling that method from within the class it would be $r = &$this->ransackArray($data, 'myTes');)
When passing variables to a function, you don't need to use & - just put the & in front of the parameter in the function signature - but in cases like yours where you are getting a return value from a function, there needs to be a & both in the function call and in the function signature. For more info see http://www.php.net/manual/en/language.references.pass.php
Note: While this is probably a simple fix, I'm new to using arrays and am completely stumped.
I'm trying to save the data from a shortcode array via the Options API in WordPress, then call that array and use the data to create another array to hook into a plugin's function. It's a responsive slider plugin and I'm basically trying to attach a shortcode to it so I can create the slider on the backend and display it on the front end with a shortcode that looks like: [responsive_slider slider_name="imageslider"].
The implementation documentation can be found here, and here's my code:
function responsive_gallery_shortcode($atts, $content=null) {
extract(shortcode_atts( array('slider_name' => 'product_page') , $atts));
foreach ($slider_name as $value) {
update_option('_unique_slider_name', $value );
}
if(function_exists('show_flexslider_rotator'))
echo show_flexslider_rotator( $slider_name );
add_image_size( $slider_name , '550', '250', true );
}
add_shortcode('responsive_gallery', 'responsive_gallery_shortcode');
if (!function_exists('custom_set_flexslider_hg_rotators')) {
function custom_set_flexslider_hg_rotators() {
$slider_name = get_option('_unique_slider_name');
foreach ($slider_name as $value) {
$rotators = array();
$rotators[ $value ] = array( 'size' => $value );
return $rotators;
}
}
}
add_filter('flexslider_hg_rotators', 'custom_set_flexslider_hg_rotators', 9999);
I'm getting an "Invalid argument supplied for foreach()" error on both foreach functions. On the page where I have two shortcodes both errors show up twice. It seems as though $slider_name is a string instead of an array, but there's got to be a way to save it in the update_option() function so that it returns an array. I'm quite new to arrays, and I'm definitely struggling here. I've spent hours on this and have already received a little help on the WordPress side, but I'm not quite getting it.
As the shortcode attribute will arrive as a string, you need to convert it to an array first.
At the same time, as it has to be passed as a string, you'll need to use a separator so you can manage this.
And for all that, you'll need the PHP function explode.
$string = "one,two";
$array = explode( ',', $string );
var_dump( $array );
Results in:
array (size=2)
0 => string 'one' (length=3)
1 => string 'two' (length=3)
And
$string = "one";
$array = explode( ',', $string );
var_dump( $array );
Results in:
array (size=1)
0 => string 'one' (length=3)
PS: It's always worth to consult the PHP Manual and also the comments in each of its pages : http://www.php.net/manual/en/language.types.array.php
[update]
There are many issues with your original code, check the comments of this revised version:
function responsive_gallery_shortcode($atts, $content=null) {
extract(shortcode_atts( array('slider_name' => 'product_page') , $atts));
// Convert string into array
// Using comma as separator when writing the shortcode in the post
$array_slider = explode( ',', $slider_name );
// YOU DON'T NEED A `foreach` HERE
//foreach ($array_slider as $value) {
update_option('_unique_slider_name', $array_slider );
//}
// I DON'T KNOW WHAT THIS FUNCTIONS DOES
// But in any case, being $array_slider an array, maybe it should be inside a `foreach`
if(function_exists('show_flexslider_rotator'))
echo show_flexslider_rotator( $array_slider );
// THIS DOESN'T MAKE SENSE
// You're not supposed to be adding images sizes at each Shortcode call
// And you are dealing with an array
add_image_size( $slider_name , '550', '250', true );
}
add_shortcode('responsive_gallery', 'responsive_gallery_shortcode');
if (!function_exists('custom_set_flexslider_hg_rotators')) {
function custom_set_flexslider_hg_rotators() {
// The option was already saved as array, so we can work directly with it
$slider_name = get_option('_unique_slider_name');
// YOU DON'T WANT TO DECLARE THE VARIABLE AT EACH STEP OF THE LOOP
$rotators = array();
foreach ($slider_name as $value) {
$rotators[ $value ] = array( 'size' => $value );
}
// RETURN THE VALUE ONLY AFTER COMPLETING THE LOOP
return $rotators;
}
// PUT THE FILTER HOOK INSIDE if(!function_exists())
add_filter('flexslider_hg_rotators', 'custom_set_flexslider_hg_rotators', 9999);
}
In Python it is possible to have a function with several variables all having a default value. And then just passing the value of one of the values. So if I have
function foo(a=10,b=50, c=70)
pass
pass
return
Then I can call
foo(b=29)
and it would call
foo(10,29,70)
(using the default for all the values, and the exact value for that one variable).
Is something similar possible in PHP?
No there is no equivalent to that in PHP. You can have default values for function arguments, but they are evaluated from left to right and are not named:
function test($var1 = 'default1', $var2 = 'default2')
{
}
In that example the two variables are optional, but you must specify the first argument if you want to specify the second.
test(); // works
test('arg1'); // works
test('arg1', 'arg2'); // works
test('arg2'); // this will set the first argument, not the second.
A common workaround if you need flexibility on your optional arguments is to pass an array as the argument:
function test($options)
{
}
This can have a variable number of arguments in the form of a single associative array:
$options = array('var1' => 'arg1', 'var2' => 'arg2');
test($options);
Use array as an argument. For example:
function a(array $params) {
$defaults = array(
'a' => 10,
'b' => 50,
'c' => 70,
);
$params += $defaults;
// use $params
}
a(array('b' => 29));
Can pass a value to specific argument in function ?
function fun1($a,$b)
{
echo $b;
}
#fun1(123);
Functions can be defined so that they do not require all parameters. For example:
function foo($a, $b = 2) {
echo $a + $b;
}
foo(1); //gives 3
Read about default function values here
However, you cannot pass in later parameters without specifying earlier ones. Some simple programming-function-parameters-basics... when you do foo($b) the function has no idea that the variable was named b... It just gets the data; usually a primitive type (in this case an int) or a reference.
You haven't specified how you're using these variables, so you may want to give a dummy value like "-1" to $a (and handle it in your function) (foo(-1, 123)), or rewrite your function so that $a is the second parameter with the default value. (function foo($b, $a = NULL))
That's why you must pass the variables in order; the function assumes you're using it right, and it lines up the values passed with the function definition. function foo($a, $b) means "I'm assuming I should associate your first value with a and your second value with b)".
With your original example function foo($a, $b):
No context, so I would just say do this function foo($b, $a = some_default_value). However, I'm assuming you're using $a and $b equally so you could check to see if it was some default-invalid-value and act on it. However, if your function performs different tasks depending on the (number of) parameters passed, you probably want to separate your function.
If you insist on not switching the order, you could call foo(-1, 123) with a dummy value and check it. Again though, same problem as above
Edit: You've given another example foo($a, $b, $c) and you said you want to do foo($b) to update the middle value. See the explanation in the first paragraph about how a function knows what parameter is what.
If you mean you want to pass an arbitrary set of variables to a function and it knows which ones it got? Again I don't think this is the best practice (you'll need to give us more detail about how you're using this) but you could pass an associative array:
function foo($arr) {
if (isset($arr['a'])) {
echo $a;
}
if (isset($arr['b'])) {
echo $b;
}
if (isset($arr['c'])) {
echo $c;
}
}
foo(array('b' => 123));
I feel horrible after writing this function :P
<?php
function FUN1($a, $b)
{
echo "HI";
echo $b;
} //$_a= 123; //error_reporting (E_ALL ^ E_NOTICE ^ E_WARNING); //$b=23; echo #FUN1(123);//it gives HI123
?>
I formatted your function. Firstly, when I tried that call it doesn't give me "HI123". Secondly, # is bad practice and really slows down the code. Thirdly, you don't echo FUN1 since it doesn't return anything; your function prints the stuff itself.
You (your student) are/is going in the wrong direction. As I said in my comment, functions already have a beautiful way of sorting out the parameters. Instead of trying to do something funky and work around that, just change your approach.
The example above has no real use and I'm sure in actual code you should just write different functions when you're setting different variables. like setA($a) setB($b) setC($c) setAll($a, $b, $c) and use them accordingly. Arrays are useful for easy variable length functions, but if you're checking each tag to do something, then something's wrong.
If you only want to pass one argument, you could make a wrapper function like this:
function PassOne($arg)
{
fun1(NULL,$arg);
}
function fun1($a,$b)
{
echo $b;
}
Forgive any inaccuracies. It's been a while since I coded in PHP.
If you want to ensure the order of arguments, you can pass a single array as an argument.
$args = array(
'name' => 'Robert',
'ID' => 12345,
'isAdmin' => true
);
example($args);
function example($args)
{
echo $args['name']; // prints Robert
echo $args['ID']; // prints 12345
echo $args['isAdmin']; // prints true
}
Using this approach, you can also hard-code default values into the function, replacing them only when they're provided in the argument array. Example:
$args = array(
'name' => 'Robert',
'ID' => 12345
// Note that I didn't specify whether Robert was admin or not
);
example($args);
function example($args)
{
$defaultArgs = array(
'name' => '',
'ID' => -1,
'isAdmin' => false // provides a default value to incomplete requests
);
// Create a new, mutable array that's a copy of the default arguments
$mixArgs = $defaultArgs;
// replace the default arguments with what was provided
foreach($args as $k => $v) {
$mixArgs[$k] = $v;
}
/*
Now that we have put all the arguments we received into $mixArgs,
$mixArgs is mix of supplied values and default values. We can use
this fact to our advantage:
*/
echo $mixArgs['name']; // prints Robert
// if ID is still set to the default value, the user never passed an ID
if ($mixArgs['ID'] == -1) {
die('Critical error! No ID supplied!'); // use your imagination
} else {
echo mixArgs['ID']; // prints 12345
}
echo mixArgs['isAdmin']; // prints false
// ... etc. etc.
}
2018's PHP syntax and defaults
function example($args=[], $dftArgs=['name'=>'', 'ID' => -1, 'isAdmin'=>false])
{
if (is_string($args))
$args = json_decode($args,true); // for microservice interoperability
$args = array_merge($dftArgs,$args);
// ... use $args
}
// PS: $dftArgs as argument is not usual, is only a generalization
No.
But by convention you can skip arguments to built in functions by passing NULL in that position:
fun1(NULL, 123);
Obviously this is doesn't make sense for everything - for example this makes no sense:
$result = strpos(NULL, 'a string');
For user defined functions, it's up to you to handle the arguments in whatever way you see fit - but you might find func_get_arg()/func_get_args() useful for functions that use an indeterminate number of arguments.
Also, don't forget you can make arguments optional by defining default values:
function fun ($arg = 1) {
echo $arg;
}
fun(2); // 2
fun(); // 1
Note that default values can only be defined on the right-most arguments. You cannot give an argument a default value if an argument to its right does not have one. So this is illegal:
function fun ($arg1 = 1, $arg2) {
// Do stuff heere
}