Unexpected Result from User Defined Function - PHP - php

I'm trying to write a simple function which takes two arguments, adds them together and returns the result of the calculation.
Before performing the calculation the function checks whether either of the two arguments are undefined and if so, sets the argument to 0.
Here's my function:
Function - PHP
function returnZeroAdd ($arg, $arg2)
{
if(!isset($arg))
{
$arg = 0;
}
if(!isset($arg2))
{
$arg2 = 0;
}
echo $arg + $arg2;
}
I've tried to execute it like so :
returnZeroAdd($bawtryReturnCount, $bawtryFReturnCount);
But this throws up an undefined variable $bawtryFReturnCount error.
I do not know why the function isn't setting $bawtryFReturnCount) to 0 before performing the calculation thereby negating the 'undefined variable' error.
Can anybody provide a solution?

You cannot do this the way you want. As soon as you use an undefined variable, you will get this error. So the error doesn't occur inside your function, but already occurs in the call to your function.
1. Optional parameters
You might make a parameter optional, like so:
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
return $arg + $arg2;
}
This way, the parameter is optional, and you can call the function like this:
echo returnZeroAdd(); // 0
echo returnZeroAdd(1); // 1
echo returnZeroAdd(1, 1); // 2
2. By reference
But I'm not sure if that is what you want. This call will still fail:
echo returnZeroAdd($undefinedVariable);
That can be solved by passing the variables by reference. You can then check if the values are set and if so, use them in the addition.
<?php
function returnZeroAdd (&$arg, &$arg2)
{
$result = 0;
if(isset($arg))
{
$result += $arg;
}
if(isset($arg2))
{
$result += $arg2;
}
return $result;
}
echo returnZeroAdd($x, $y);
Note that you will actually change the original value of a by reference parameter, if you change it in the function. That's why I changed the code in such a way that the parameters themselves are not modified. Look at this simplified example to see what I mean:
<?php
function example(&$arg)
{
if(!isset($arg))
{
$arg = 0;
}
return $arg;
}
echo example($x); // 0
echo $x // also 0
Of course that might be your intention. If so, you can safely set $arg and $arg2 to 0 inside the function.

The error is not thrown by the function itself, as the function is not aware of the global scope. The error is thrown before even the function is executed, while the PHP interperter is trying to pass $bawtryFReturnCount to the function, one does not find it, and throws error, however, it's not a fatal one and the execution is not stopped. THerefore, the function is executed with a non-set variable with default value of null, where I guess, isset will not work, as the arguments are mandatory, but not optional. A better check here will be empty($arg), however the error will still be present.
Because the functions are not and SHOULD NOT be aware of the global state of your application, you should do these checks from outside the functions and then call it.
if (!isset($bawtryReturnCount)) {
$bawtryReturnCount = 0
}
returnZeroAdd($bawtryReturnCount);
Or assign default values to the arguments in the function, making them optional instead of mandatory.

Your function could be rewritten as:
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
echo $arg + $arg2;
}
You missunderstand how variables work. Since $bawtryFReturnCount isn't defined when you call the function; you get a warning. Your isset-checks performs the checks too late. Example:
$bawtryReturnCount = 4;
$bawtryFReturnCount = 0;
returnZeroAdd($bawtryReturnCount, $bawtryFReturnCount);
Will not result in an error.

If you really want to make the check inside the function you could pass the arguments by reference:
function returnZeroAdd (&$arg, &$arg2)
{
if(!isset($arg))
{
$arg = 0;
}
if(!isset($arg2))
{
$arg2 = 0;
}
echo $arg + $arg2;
}
However this will potentially modify your arguments outside the function, if it is not what you intend to do then you need this:
function returnZeroAdd (&$arg, &$arg2)
{
if(!isset($arg))
{
$localArg = 0;
}
else
{
$localArg = $arg;
}
if(!isset($arg2))
{
$localArg2 = 0;
}
else
{
$localArg2 = $arg2;
}
echo $localArg + $localArg2;
}
You can now pass undefined variables, it won't throw any error.
Alternatively you might want to give a default value to your arguments (in your case 0 seems appropriate):
function returnZeroAdd ($arg = 0, $arg2 = 0)
{
echo $arg + $arg2;
}

You have to define the variable before pass it to an function. for example
$bawtryReturnCount=10;
$bawtryFReturnCount=5;
define the two variable with some value and pass it to that function.
function returnZeroAdd ($arg=0, $arg2=0)
{
echo $arg + $arg2;
}
if you define a function like this means the function takes default value as 0 if the argument is not passed.
for example you can call the functio like this
returnZeroadd();// print 0
returnZeroadd(4);// print 4
returnZeroadd(4,5);// print 9
or you can define two variables and pass it as an argument and call like this.
$bawtryReturnCount=10;
$bawtryFReturnCount=5;
returnZeroadd($bawtryReturnCount, $bawtryFReturnCount);

Related

Optional parameters in PHP function without considering order

Is there a way in PHP to use a function which has optional parameters in its declaration where I do not have to pass an optional arguments which already have values declared and just pass the next argument(s) which have different values that are further down the parameter list.
Assuming I have a function that has 4 arguments, 2 mandatory, 2 optional. I don't want to use null values for the optional arguments. In usage, there are cases where I want to use the function and the value of the 3rd argument is the same as the default value but the value of the 4th argument is different.
I am looking for a not so verbose solution that allows me to just pass the argument that differs from the default value without considering the order in the function declaration.
createUrl($host, $path, $protocol='http', $port = 80) {
//doSomething
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
I find myself repeating declaring variables so that I could use a function i.e to use $port, I redeclare $protocol with the default value outside the function scope i.e
$protocol = "http";
$port = 8080;
Is there any way to pass the 2nd optional parameter($port) without passing $protocol and it would "automatically" fill in the default value of $protocol i.e
getHttpUrl($server, $path, $port);
This is possible in some languages like Dart in the form of Named Optional parameters.See usage in this SO thread. Is their a similar solution in PHP
You could potentially use a variadic function for this.
Example:
<?php
function myFunc(...$args){
$sum = 0;
foreach ($args as $arg) {
$sum += $arg;
}
return $sum;
}
Documentation:
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
PHP doesn't allow at this state to call functions parameters in the order we want.Maybe in the future it will.However you can easily achieve your purpose by using an associative array as the only argument, and then define, the default parameter in the function.For the call you will need to pass an array with only the values which interest you.This array will be merged with the default array.You can even implement required parameters and call them in any order you want.
example:
function mysweetcode($argument){
$required=['first'];//specify required parameters here
$default=['first'=>0,'second'=>1,'third'=>2];//define all parameters with their default values here
$missing=[];
if(!is_array($argument)) return false;
$argument=array_intersect_key($argument,$default);
foreach($required as $k=>$v){//check for missing required parameters
if(!isset($argument[$v]))
$missing[]=$v;
}
if(!empty($missing)){// if required are missing trigger or throw error according to the PHP version
$cm=count($missing);
if (version_compare(PHP_VERSION, '7.0.0') < 0) {
trigger_error(call_user_func_array('sprintf',
array_merge(array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s,',$cm).(($cm>1)?' are':' is').' missing'),$missing)),
E_USER_ERROR);
}else{
throw new Error(call_user_func_array('sprintf',array_merge(
array('Required '.(($cm>1)?'parameters:':'parameter:').
str_repeat('%s',$cm).(($cm>1)?' are':' is').' missing'),$missing)));
}
}
$default=array_merge($default,$argument);//assign given values to parameters
extract($default);/*extract the parameters to allow further checking
and other operations in the function or method*/
unset($required,$missing,$argument,$default,$k,$v);//gain some space
//then you can use $first,$second,$third in your code
return $first+$second+$third;
}
var_dump(mysweetcode(['first'=>9,'third'=>8]));//the output is 18
var_dump(mysweetcode(['third'=>8]));//this throws Error on PHP7 and trigger fatal error on PHP5
You can check a live working code here
Well, this should work:
function myFunc($arg1, $arg2, $arg3=null, $arg4= null){
if ( is_null( $arg3 ) && is_null( $arg4 ) {
$arg3 = 3;
$arg4 = 4;
} else if ( is_null( $arg4 ) ) {
$arg4 = $arg3;
$arg3 = 3;
}
echo $arg1 + $arg2 + $arg3 + $arg4;
}
However I suggest you to rethink your problem (as a whole) because this is not a very good idea.
You could refactor this to use a parameter object; this way, you could include the default parameters in this object and set them in any order (with a trade-off of more verbose code). As an example using your above code,
<?php
class AdditionParameters
{
private $arg1 = 0;
private $arg2 = 0;
private $arg3 = 3;
private $arg4 = 4;
public function getArg1() { return $this->arg1; }
public function getArg2() { return $this->arg2; }
public function getArg3() { return $this->arg3; }
public function getArg4() { return $this->arg4; }
public function setArg1($value) { $this->arg1 = $value; return $this; }
public function setArg2($value) { $this->arg2 = $value; return $this; }
public function setArg3($value) { $this->arg3 = $value; return $this; }
public function setArg4($value) { $this->arg4 = $value; return $this; }
}
From there, you could simply call the function while passing in this new object.
function myFunc(AdditionParameters $request) {
return $request->getArg1()
+ $request->getArg2()
+ $request->getArg3()
+ $request->getArg4();
}
echo myFunc((new AdditionParameters)->setArg1(1)->setArg2(2)->setArg4(6));
// or echo myFunc((new AdditionParameters)->setArg1(1)->setArg4(6)->setArg2(2));
Otherwise, PHP doesn't allow you to have named optional parameters. (e.g. myFunc(1, 2, DEFAULT, 4);)
You have the response in your question, you can declare your function like
function myFunc($arg1, $arg2, $arg3 = null, $arg4 = null){
//here you check if the $arg3 and $arg4 are null
}
then you call your function using
myFunc($arg1, $arg2);
There is no such way in PHP(like in python for example).
You have to use some tricks in order to do that but will not always work.
For example:
// creates instances of a class with $properties.
// if $times is bigger than 1 an array of instances will be returned instead.(this is just an example function)
function getInstance($class, $properties = [], $times = 1){
//my code
}
$user = getInstance("User", ["name" => "John"]); // get one instance
$users = getInstance("User", ["name" => "John"],2); // get two instances.
If you want to use the function without passing the $parameters argument, like this:
$users = getInstance("User",2);
you can change the function to:
// creates instances of a class with $properties.
// if times is bigger than 1 an array of instances will be returned instead.
function getInstance($class, $properties = [], $times = 1){
if(is_numberic($properties)){
$times = $properties;
$properties = [];
}
//my code
}
Of course, this strategy will work only if you parameters have different types.
PS. This method is use in the Laravel Framework a lot. From there I got the inspiration.
This is modified from one of the answers and allows arguments to be added in any order using associative arrays for the optional arguments
function createUrl($host, $path, $argument = []){
$optionalArgs = [
'protocol'=>'http',
'port'=>80];
if( !is_array ($argument) ) return false;
$argument = array_intersect_key($argument,$optionalArgs);
$optionalArgs = array_merge($optionalArgs,$argument);
extract($optionalArgs);
return $protocol.'://'.$host.':'.$port.'/'.$path;
}
//No arguments with function call
echo createUrl ("www.example.com",'no-arguments');
// returns http://www.example.com:80/no-arguments
$argList=['port'=>9000];
//using port argument only
echo createUrl ("www.example.com",'one-args', $argList);
//returns http://www.example.com:9000/one-args
//Use of both parameters as arguments. Order does not matter
$argList2 = ['port'=>8080,'protocol'=>'ftp'];
echo createUrl ("www.example.com",'two-args-no-order', $argList2);
//returns ftp://www.example.com:8080/two-args-no-order
As of version 8.0, PHP now has named arguments. If you name the arguments when calling the function, you can pass them in any order and you can skip earlier default values without having to explicitly pass a value for them.
For example:
function createUrl($host, $path, $protocol = 'http', $port = 80)
{
return "$protocol://$host:$port/$path";
}
createUrl(host: 'example.com', path: 'foo/bar', port: 8080);
// returns: "http://example.com:8080/foo/bar"

How to make complete eval result as a return value of function

I know, eval is called 'evil' and it's seems to be the worst way for everything, but...
How to return complete result of eval outside of function? It is just hypotetic question, I've found easy better solution for me.
In database (want execute):
$var1 = "yes";
$var2 = "no";
include('file.php');
function:
function executeScriptData($id) {
$data = anydbhandler("SELECT `data` FROM `data` WHERE ID = $id");
if(trim($data['data']) != "") {
echo $data['data']; // echo data from database
return eval($data['data']);
}
}
calling function:
executeScriptData(someID);
echo $var1; // nothing happened :(, no result
Make sure the evalling happens in the scope you want it to (now $var1 is only available within the method executeScriptData()).
Possible solution:
function executeScriptData($id) {
$data = dbhandler("SELECT `data` FROM `data` WHERE ID = $id");
if(trim($data['data']) != "") {
return $data['data'];
}
}
$data = executeScriptData(SOMEID);
eval($data);
echo $var1;
This is impossible via return value as the docs are telling you:
http://php.net/manual/en/function.eval.php
Returnvalues:
eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned. If there is a parse error in the evaluated code, eval() returns FALSE and execution of the following code continues normally. It is not possible to catch a parse error in eval() using set_error_handler().
The return you are using there is in a subscope of eval, in an extra function. This scope cannot make your evaluation end. Otherwise eval() would not be able to define functions within its contents because every occurence of "return" would screw the code execution passed to eval().
All you can do is make a var global within your eval'ed code and overwrite it in context.
$a = true;
$myVar = "abc";
eval('global $a; global $myVar; if($a == true) { $myVar = "def"; } ');
echo $myVar;

PHP functions that have optional arguments but allow null (and similar) values?

I'm aware that you can have PHP functions with optional arguments like so:
function do_something($argument = null)
{
// argument not provided
if ($argument === null)
........
}
Consider the case that null/false and other values are all valid arguments to my function. How can I determine whether an argument was provided or not?
do_something(null); // argument provided
do_something(false); // argument provided
do_something(0); // argument provided
do_something("test"); // argument provided
do_something(new stdClass()); // argument provided
do_something(); // nothing provided
How can I detect the last case? I have thought about using func_num_args which would work in most cases but it doesn't work if I have several optional arguments.
Is there anything that solves this problem?
func_num_args() should work exactly as you want it to, because you might be assuming something that's actually not the case: You can't have optional arguments left out if they are in the middle of your arguments list.
So let's look at this function:
function test1 ($param1 = null, $param2 = null) {
return func_num_args();
}
If I call that with different parameter combinations I get the following results:
test1() => 0
test1(true) => 1
test1(true, true) => 2
There is just no way to call the function in a way where $param2 would be set while $param1 isn't. So you can map every possible output of func_num_args() to exactly one parameter configuration.
In the example above you can rely on the fact that
if the return value is 1, $param2 definitely hasn't been set, while $param1 has been.
For 0 it's 100% sure that neither one has been given.
And, of course, if it's 2 both are there.
What you actually would need are named parameters, as many other languages have them. PHP doesn't at the moment. NikiC actually wrote an RFC that suggests the addition of named parameters to PHP, but I think that's still way off in the future. You can check that out here: https://wiki.php.net/rfc/named_params
As these are not yet available, here are a few workarounds you can try:
Workaround 1
If you really need to be able to have all the parameters optional, try a parameter array:
function test1 (array $opts) {
if (!isset($opts['opt1'])) { $opts['opt1'] = 'default1'; }
if (!isset($opts['opt2'])) { $opts['opt2'] = 'default2'; }
}
Then you can call it like this:
test1(array('opt2' => true))
It would set the first parameter to "default1" while keeping the second. And there are definitely better and more elegant ways to do this (e.g. using an object instead), but the general idea is the same.
Workaround 2
You could also go with alias functions:
function test ($param1, $patam2) { ... ]
function testNoParam1 ($param2) {
test("default1", $param2);
}
That at least makes it very easy to read, but of course you need to pick the right function depending on the parameters you have.
Workaround 3
By adding a lot of additional code you could get really fancy and use a FactoryObject:
class FunctionExecutor {
private $param1 = "default1";
private $param2 = "default2";
public function param1($val) {
$this->param1 = $val;
return $this;
}
public function param2($val) {
$this->param2 = $val;
return $this;
}
public function execute() {
return yourFunction($this->param1, $this->param2);
}
}
This could be used like this:
$doSomething = new FunctionExecutor();
$returnValue = $doSomething->param2(42)->execute();
In this approach it would probably be a better idea to actually put your function into the object instead of defining it globally. Anyway...this is definitely a possibility, but not the most practical one. Just wanted to add it, because it has some benefits.
perhaps this will help: http://www.php.net//manual/en/function.func-get-args.php
$args = func_get_args();
if(!isset($arg[0])) {
echo 'no argument';
}
or
isset(func_get_arg(0));
Passing "null", "0", or "false" means that you allocate memory to store a variable, regardless it's scope, type, or size. Then it is used as a parameter to a function.
In PHP you cannot override functions by arguments, but you can access them by calling the "func_get_args()", and this is the only way to handle different numbers / types of arguments passed to a function:
function do_something() {
$args = func_get_args();
//do_something(stdClass, 1)
if($args[0] instanceof stdClass && is_numeric($args[1])) {
//handle
//return
//do_something(1, "string")
} else if(is_numeric($args[0]) && is_string($args[1])) {
//handle
//return
}
throw new Exception('invalid arguments');
}
do_something(new StdClass(), 100); //ok
do_something(100, "hell world") // ok
do_someting(); //throws Exception('invalid arguments');
In PHP 7, you can do:
function action(...$args) {
if (count($args) === 0) {
return action_default();
}
$var1 = array_shift($args);
$var2 = array_shift($args);
$var3 = array_shift($args);
// etc.
}

How to tell if a param was passed assuming it was a constant?

I am using this code (note: HELLO_WORLD was NEVER defined!):
function my_function($Foo) {
//...
}
my_function(HELLO_WORLD);
HELLO_WORLD might be defined, it might not. I want to know if it was passed and if HELLO_WORLD was passed assuming it was as constant. I don't care about the value of HELLO_WORLD.
Something like this:
function my_function($Foo) {
if (was_passed_as_constant($Foo)) {
//Do something...
}
}
How can I tell if a parameter was passed assuming it was a constant or just variable?
I know it's not great programming, but it's what I'd like to do.
if a constant isn't defined, PHP will treat it as String ("HELLO_WORLD" in this case) (and throw a Notice into your Log-files).
You could do a check as follows:
function my_function($foo) {
if ($foo != 'HELLO_WORLD') {
//Do something...
}
}
but sadly, this code has two big problems:
you need to know the name of the constant that gets passed
the constand musn't contain it's own name
A better solution would be to pass the constant-name instead of the constant itself:
function my_function($const) {
if (defined($const)) {
$foo = constant($const);
//Do something...
}
}
for this, the only thing you have to change is to pass the name of a constant instead of the constant itself. the good thing: this will also prevent the notice thrown in your original code.
You could do it like this:
function my_function($Foo) {
if (defined($Foo)) {
// Was passed as a constant
// Do this to get the value:
$value = constant($Foo);
}
else {
// Was passed as a variable
$value = $Foo;
}
}
However you would need to quote the string to call the function:
my_function("CONSTANT_NAME");
Also, this will only work if there is no variable whose value is the same as a defined constant name:
define("FRUIT", "watermelon");
$object = "FRUIT";
my_function($object); // will execute the passed as a constant part
Try this:
$my_function ('HELLO_WORLD');
function my_function ($foo)
{
$constant_list = get_defined_constants(true);
if (array_key_exists ($foo, $constant_list['user']))
{
print "{$foo} is a constant.";
}
else
{
print "{$foo} is not a constant.";
}
}

&$variable in PHP

I have been looking through wordpress's core files and stumbled across this piece of code, I noticed it had an ampersand before a variable name and after an =.
I have tried searching this and came across this from the PHP manual and it doesn't explain it well, or I'm looking at the wrong one! I also saw that it is used to modify a variable outside of the method where it is being used, but, thats what a variable is there for, to be modified so if this is correct how would one use it?
function _make_cat_compat( &$category ) {
if ( is_object( $category ) ) {
$category->cat_ID = &$category->term_id;
$category->category_count = &$category->count;
$category->category_description = &$category->description;
$category->cat_name = &$category->name;
$category->category_nicename = &$category->slug;
$category->category_parent = &$category->parent;
} elseif ( is_array( $category ) && isset( $category['term_id'] ) ) {
$category['cat_ID'] = &$category['term_id'];
$category['category_count'] = &$category['count'];
$category['category_description'] = &$category['description'];
$category['cat_name'] = &$category['name'];
$category['category_nicename'] = &$category['slug'];
$category['category_parent'] = &$category['parent'];
}
}
This means the function will modify the argument (by reference) instead working on a copy of it. Remove all the ampersands inside the body of the function, only the one in the argument is necessary.
function foo(&$foo) { $foo++; }
function bar($foo) { $foo++; }
$foo = 10;
foo($foo);
foo($foo);
// prints 12, function foo() has incremented var $foo twice
echo "$foo\n";
bar($foo);
// still 12, as bar() is working on a copy of $foo
echo "$foo\n";
// However, since PHP 5.0, all objects are passed by reference [(or to be more specific, by identifier)][1]
class Foo {
public $bar = 10;
}
$obj = new Foo;
echo "$obj->bar\n"; // 10, as expected
function objectIncrement($obj) { $obj->bar++; }
function objectRefIncrement(&$obj) { $obj->bar++; }
objectIncrement($obj);
echo "$obj->bar\n"; // 11, as expected, since objects are ALWAYS passed by reference (actually by identifier)
objectRefIncrement($obj);
echo "$obj->bar\n"; // 12
It's still a good idea, if you intend to modify the passed argument in a function/method, to explicitly pass it by reference. Aside from other advantages, your code also becomes more explicit and understandable.
BTW, you can do this:
function _make_cat_compat( &$category ) {
if (is_array( $category)) {
$category = (object)$category;
}
$category->cat_ID = $category->term_id;
$category->category_count = $category->count;
$category->category_description = $category->description;
$category->cat_name = $category->name;
$category->category_nicename = $category->slug;
$category->category_parent = $category->parent;
}
Looks cleaner to me, but I don't know your specific case. And I don't know how you would have either array or object - it implies some bad practices used.
When talking about method parameters, &$variable refers to a call by reference. So any change you make to this variable remains even if the method is done.
function a($arg) // call by value ($arg is a copy of the original)
{
$arg += 1;
}
function b(&$arg) // call by reference ($arg IS the original)
{
$arg += 1;
}
$myArg = 1;
a($myArg);
echo $myArg;
echo "\r\n";
b($myArg);
echo $myArg;
// Displays:
// 1
// 2
Here is the section of the PHP manual about references.
The & after the = basically means the same, but they are useless in this context because you already have a call by reference anyway. You can safely remove them.
Here's the correct PHP manual entry on references: http://php.net/manual/en/language.references.php
In most cases you don't need to pass a reference using the ampersand & as PHP will always pass a reference first and only create a copy of the variable on the first write access.
It is passing the variable as a reference. Without the ampersand the following code wouldnt work:
$var = "content";
function test(&$v)
{
$v = "this is new content";
}
test($var);
NOTE: this is untested code, but the theory is close enough. It allows to modify the variable from within a different scope, so rather than passing the value of a variable, in this example - "content", you are passing a reference to the variable itself, so you are directly editing the variable you passed in.
Its because this function doesnt return anything, just modify, and all.

Categories