Viewed   63 times

Coming from C++ background ;)
How can I overload PHP functions?

One function definition if there are any arguments, and another if there are no arguments? Is it possible in PHP? Or should I use if else to check if there are any parameters passed from $_GET and POST?? and relate them?

 Answers

5

You cannot overload PHP functions. Function signatures are based only on their names and do not include argument lists, so you cannot have two functions with the same name. Class method overloading is different in PHP than in many other languages. PHP uses the same word but it describes a different pattern.

You can, however, declare a variadic function that takes in a variable number of arguments. You would use func_num_args() and func_get_arg() to get the arguments passed, and use them normally.

For example:

function myFunc() {
    for ($i = 0; $i < func_num_args(); $i++) {
        printf("Argument %d: %sn", $i, func_get_arg($i));
    }
}

/*
Argument 0: a
Argument 1: 2
Argument 2: 3.5
*/
myFunc('a', 2, 3.5);
Sunday, August 21, 2022
4
function text($var) 

{

    if ( ! $var) {
        return;
    }
    do_something();

}

$var = text('');

echo gettype($var);
echo is_bool($var) ? "true" : "false";
echo is_string($var) ? "true" : "false";
echo is_null($var) ? "true" : "false";

returns:

NULL false false true

Thursday, November 3, 2022
 
1

To fix the Notice, simply change foo() in the Child to

public function foo($arg = null) {

As to the question "why does this not work":

Visibility in PHP is strictly about runtime access. It doesn't affect how you can extend/compose/overload classes and methods. Loosening the visibility of a private method from a Supertype in a Subtype will add a separate method in the subtype with no access to the same named method in the supertype. However, PHP will assume a parent-child relationship for these. That didn't cause the Notice though. At least, not on it's own.

The reason why you get the Notice, is that you are then also trying to change the method signature. Your foo() does no longer require $arg to be passed to it. When you assume a parent-child relationship between the methods, this is a problem because the Liskov Substitution Principle states that "if S is a subtype of T, then objects of type T may be replaced with objects of type S" without breaking the program. In other words: if you have code that uses Base, you should be able to replace Base with Child and the program should still work as if it was using Base.

Assume your Base also has a public method bar().

class SomeClientUsingBase
{
    public function doSomethingWithBase(Base $base)
    {
        $result = $base->bar();
        // …

Now imagine Child changes bar() to require an argument. If you then pass Child for Base into the client, you will break the client, because the client calls $base->bar(); without an argument.

Obviously, you could change the client to pass an argument, but then the code really depends on how Child defined the method, so the Typehint is wrong. In fact, Child is not a Base then, because it doesn't behave like a Base. It's broken inheritance then.

Now the funny thing is, if you remove that $arg from foo(), you are technically not violating LSP, because the client would still work. The Notice is wrong here. Calling $base->foo(42) in a client that previously used Base will still work with a Child because the Child can simply ignore the argument. But PHP wants you to make the argument optional then.

Note that LSP also applies to what a method may return. PHP just doesn't include the return type in the signature, so you have take that into account yourself. Your methods have to return what the Supertype returned or something that is behaviorally equivalent.

Monday, October 10, 2022
 
sebi
 
2

Cracks knuckles

Technically the syntax is "correct" (it won't generate a fatal error) but the semantics of PHP render it effectively meaningless in its current form. Let's look at a few things first, namely how PHP handles the assignment of named functions to variables:

php > echo shell_exec("php -v");
PHP 5.4.16 (cli) (built: Oct 30 2018 19:30:51)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

php > function speak($arg) {echo "{$arg}n";}
php > function give($arg) {return $arg;}
php > $speak = speak(4);
4
php > $give = give(4);
php > var_dump($speak);
NULL
php > var_dump($give);
int(4)

The function itself is executed upon assignment and its return value (NULL or otherwise) is assigned to the variable. Since we're only assigning the return value of a function's execution, trying to use this variable as a function name has no use:

php > $speak(4);
php > $give(4);
php >

Let's contrast this to the assignment of an anonymous function (a.k.a. a 'Closure'):

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}n";
php {   echo "min: {$min}n";
php {   echo "max: {$max}n";
php { };
php > var_dump($checkName);
object(Closure)#1 (2) {
  ["static"]=>
  array(2) {
    ["min"]=>
    int(1)
    ["max"]=>
    int(6)
  }
  ["parameter"]=>
  array(1) {
    ["$value"]=>
    string(10) "<required>"
  }
}

Unlike some other languages, a closure is represented in PHP by an actual Object. Variables inside the 'use' clause are imported at the time the Closure was created; function parameters (i.e. $value) have their values captured when the Closure is called (hence why we see it noted as a required parameter and not a static value). The semantics of references within Closures aren't worth considering right now but if you want further reading, goat's answer to this question is a great start.

The major takeaway here is that the Closure's assignment to $checkName did not execute the Closure itself. Instead, $checkName becomes a sort of "alias" we can use to reference this function by name:

php > $checkName("hello ");
value: hello 
min: 1
max: 6
php >

Given how loose PHP is about the number of function parameters passed, a zero-parameter execution returns expected results:

php > $checkName();
value:
min: 1
max: 6
php >

Now let's take it another level deeper and define a function within a function:

php > function myOuterFunc($arg) {
php {   function myInnerFunc($arg){
php {     echo "{$arg}n";
php {   }
php { }
php > $myVal = myOuterFunc("Hello ");
php > var_dump($myVal);
NULL
php >

By now this result should make sense. Functions do not execute unless explicitly called; just because we call myOuterFunc doesn't mean we execute any function code defined inside of it. That's not to say that we couldn't:

php > function myOuterFunc($arg) {
php {   function myInnerFunc($arg){
php {     echo "{$arg}n";
php {   }
php {   myInnerFunc($arg);
php { }
php > $myVal = myOuterFunc("Hello ");
Hello 
php > var_dump($myVal);
NULL
php >

Which brings us back around to what is essentially your question: what about a named function inside of a Closure? Given what we've now discovered about function execution, we can generate a series of very predictable examples:

$min = 1; $max = 6;
$checkName = function ($value) use ($min, $max) {
  function question(){echo "How are youn";}
  echo "value: {$value}n";
  echo "min: {$min}n";
  echo "max: {$max}n";
};
php > $checkName("Hello ");
value: Hello 
min: 1
max: 6
php >

As expected, the named function's code inside the Closure is not executed because we have not explicitly called it.

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   function question(){echo "How are youn";}
php {   echo "value: {$value}n";
php {   echo "min: {$min}n";
php {   echo "max: {$max}n";
php {   question();
php { };
php > $checkName("Hello ");
value: Hello 
min: 1
max: 6
How are you
php >

Explicitly calling the inner function works just fine, provided we define that function before we call it:

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}n";
php {   echo "min: {$min}n";
php {   echo "max: {$max}n";
php {   question();
php {   function question(){echo "How are youn";}
php { };
php > $checkName("Hello ");
value: Hello 
min: 1
max: 6
php >

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}n";
php {   echo "min: {$min}n";
php {   echo "max: {$max}n";
php {   function question(){echo "How are youn";}
php {   question();
php { };
php > $checkName("Hello ");
value: Hello 
min: 1
max: 6
How are you
php >

So to the point of your questions then:

  1. Yes it's legal and what you're attempting is possible but semantically meaningless in its current form.

  2. Any named functions inside the Closure definitely do not reside in the global namespace, they are within the scope of their defining Closure. FWIW, the term "members" typically refers to class variables (usually called "properties" in PHP). While Closures are an Object and let you duplicate the functionality of instance variables found within classes, they should not be confused or construed with classes in any way.

3/4) Not how you're trying to use it, no. Nothing outside of the Closure has any concept of the functions inside; if in calling your Closure code, said code performs operations using the inner functions, then they will see the light of day. But there is no immediate way to reference those inner functions as if they were defined outside of the Closure's scope.

In other words, the only way you'll get those inner functions to execute is if a) that code is specifically executed by the Closure's code and b) you execute said Closure code.

Hope this helps.

Tuesday, December 20, 2022
 
4

In C++, the function signatures

int Test::foo (const int a) const

and

int Test::foo (int a) const

are considered to be complete identical.

The reason that the const on the parameter is disregarded is because it can not affect the caller in any way. As the parameter is passed by value, a copy is made of the value provided by the caller. To the caller, it does not matter in any way if the called function can change that copy or not. For this reason, C++ ignores a top-level const-qualification on function parameters (top-level const can not occur if passing a reference), and goes even as far that int foo(int); is considered a correct prototype for the function

int foo(const int)
{
    /* ... */
}

In short, it is impossible in C++ to overload a function on the constness of (value) function parameters. To get the output you want, you could consider using a non-const reference parameter for your non-const overload.

Wednesday, November 23, 2022
 
jodm
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :