php - Can I integrate functions with same content, different names? - php

I have a couple of functions inside a class that essentially do the same thing:
public function fn_a(){
return __FUNCTION__;
}
public function fn_b(){
return __FUNCTION__;
}
public function fn_c(){
return __FUNCTION__;
}
I need those functions to remain in their current names so I intentionally did not do:
public function fn_($letter){
return __FUNCTION__.$letter;
}
I was hoping for some sort of way to minify the verboseness of code here, since they all do the same. The ultimate situation would be something like this:
public functions fn_a, fn_b, fn_c() {
return __FUNCTION__;
}
Another solution, if applicable, might be doing something like Class's "extends":
fn_b, fn_c extend fn_a?
What do you think guys?

Any syntax like the one you suggested is not possible : if you want several distinct functions, you have to declare all those functions.
Still, a possibility could be that your fn_a, fn_b and fn_c functions just be simple wrappers arround a more complex one :
public function fn_a(){
return fn_all('a');
}
public function fn_b(){
return fn_all('b');
}
public function fn_c(){
return fn_all('c');
}
public function fn_all($name) {
// a lot of complex stuff here
}
With that, depending on the length on the fn_all function, of course, you would reduce the amount of code-duplication.
Another idea (not sure how this could be done with methods, so I'll demonstrate with functions) would be to use Closures -- which means PHP >= 5.3
The basic idea being that you'd have a first function, that would return another one -- which would bind the parameter passed to the first one :
First, the function that creates the others :
function creator($name) {
return function () use ($name) {
return $name;
};
}
And, then, let's get three functions, using that creator one :
$fn_a = creator('a');
$fn_b = creator('b');
$fn_c = creator('c');
And now, calling those three functions :
echo $fn_a() . '<br />';
echo $fn_b() . '<br />';
echo $fn_c() . '<br />';
We get the following output :
a
b
c
I've never good at explaining how anonymous functions and closures work -- but searching for "closure" on google should help you understand ; note that you can read tutorial about closures in Javascript : the idea is exactly the same.
(And, as closures are new in PHP -- arrived with PHP 5.3 -- you will not find as many tutorials as for Javascript)

public function fn_catcher($letter) {
return __FUNCTION__.$letter;
}
public function __call($name) {
if (substr($name, 0, 3) == 'fn_')
{
return $this->fn_catcher($name);
}
}
Like that?

Related

PHP: How to spread an array as function parameters

I'm trying to use __callStatic to wrap the calls to differents static functions in my class.
This current version of the code allows me to do so with functions that have 0 or 1 parameter:
class test {
public static function __callStatic($name, $args) {
if (method_exists(__CLASS__, $name)) {
echo "it does exist\n";
return forward_static_call([__CLASS__, $name], $args[0]);
} else {
echo "it does not exist\n";
}
}
private static function mfunc($param1, $param2) {
echo "that's my func \n";
echo "$param1\n";
echo "$param2\n";
}
}
test::notafunc("one", 'two');
test::mfunc("one", "two");
However it will fail for mfunc since it has two parameters.
How can i spread the different arguments when i'm calling the function ?
I can't use the new spread operator because I am locked on PHP 7.2.
I can't modify mfunc either, because I have way too much methods to change in the project.
The spread operator is still usable in PHP 7.2, what you point to is something slightly different (it's about being able to use it as part of an array). So you can use
return forward_static_call([__CLASS__, $name], ...$args);
As a more legible version...
return static::{$name}(...$args);

How to achevie the Polymorphism progarmme in PHP?

Hi, I'm trying to write code to achieve php polymorphism. I don't know where there is a mistake in the code. It shows the error in "Fatal error: Cannot redeclare Sample::a() ". Here is my code. Kindly solve this problem.
<?php
class Sample
{
public function a()
{
echo "hi";
}
public function a($chr)
{
for ($chr=0;$chr<10;$chr++)
echo $chr;
}
public function a($b,$c)
{
for($g=$b;$g<$c;$g++)
echo $g
}
}
$s=new Sample();
$s->a();
$s->a($chr);
$s->a(1,10);
?>
PHP does not support method overloading...unfortunatly!
There are some funky methods to achievement something that feels like overloading, like using magic methods or wrapping sub-method calls. These don't even come close to the real thing though.
This is not polymorphism but the following:
class A {
public function foo() {
return 1;
}
}
class B {
public function foo() {
return 2;
}
}
$items = array(new A(), new B());
echo $items[0]->foo();
echo $items[1]->foo();
http://www.php.net/manual/en/language.oop5.overloading.php
See the above link. I think it will help you.
PHP doesn't support method overloading the way you've asked for.
The closest you can get is using dynamic arguments to fake it. Something like the following:
class dynamic {
public function example() {
$args = func_get_args();
switch(count($args)) {
case 1 : return $this->example_1arg($args[0]);
case 2 : return $this->example_2args($args[0],$args[1]);
//etc..
}
}
private function example_1arg($arg1) {
//....
}
private function example_2args($arg1,$arg2) {
//....
}
}
That still doesn't get you proper method overloading, because this example doesn't take into account data types. You could wire that in to a certain extent using instanceof, but you won't be able to go the whole way, since PHP also doesn't (yet) support type hinting for primitive types.

PHP two methods with the same name

Can I have two methods sharing the same name, but with different arguments?
One would be public static and would take 2 arguments, the other one just public and takes only one argument
example
class product{
protected
$product_id;
public function __construct($product_id){
$this->product_id = $product_id;
}
public static function getPrice($product_id, $currency){
...
}
public function getPrice($currency){
...
}
}
No. PHP does not support classic overloading. (It does implement something else that is called overloading.)
You can get the same result by using func_get_args() and it's related functions though:
function ech()
{
$a = func_get_args();
for( $t=0;$t<count($a); $t++ )
{
echo $a[$t];
}
}
I'm just giving you the super lazy option:
function __call($name, $args) {
$name = $name . "_" . implode("_", array_map("gettype", $args)));
return call_user_func_array(array($this, $name), $args);
}
That would for example invoke the real function name getPrice_string_array for two parameters of that type. That's sort of what languages with real method signature overloading support would do behind the scenes.
Even lazier would be just counting the arguments:
function __callStatic($name, $args) {
$name = $name . "_" . count($args);
return call_user_func_array(array($this, $name), $args);
}
That would invoke getPrice_1 for 1 argument, or getPrice_2 for, you guessed it, two arguments. This might already suffice for most use cases. Of course you can combine both alternatives, or make it more clever by search for all alternative real method names.
If you want to keep your API pretty and user-friendly implementing such elaborate workarounds is acceptable. Very much so.
PHP currently doesn't support overloading in known way, but you can still achieve your goal by using magic methods.
From PHP5 manual: overloading.
You could, kind of...
I consider it very much "hack" solutions, but you could make a single function and assign a standard value, that wouldn't otherwise be okay to use, to the parameters as needed. Then if you do not pass the function a certain parameter, it will be set to fx "-1".
public function getPrice($product_id = "-1", $currency) {
if($product_id = "-1") {
//do something
}else {
//do something
}
}
Or if you really need one method to be static, you can make a method that evaluates which method to call and call that instead of your getPrice:
public function whichGetPrice($product_id = "-1", $currency) {
if($product !== "-1") {
getStaticPrice($product_id, $currency);
}else {
getPrice($currency);
}
}
Like I said, very much "hack" solutions. It's not exactly pretty, nor a way people would expect you to do it. So I wouldn't necessarily recommend it, but it can help you do what you want.

Redefining PHP function?

If I have a function:
function this($a){
return $a;
}
If I wanted to redefine the function, would it be as simple as rewriting it?
function this($a, $b){ //New this function
return $a * $b;
}
Nope, that throws an error:
Fatal error: Cannot redeclare foo()
The runkit provides options, including runkit_function_rename() and runkit_function_redefine().
If you mean overloading in a Java sense, then the answer is no, this is not possible.
Quoting the PHP manual on functions:
PHP does not support function overloading, nor is it possible to undefine or redefine previously-declared functions.
You could use the runkit extension but usage of runkit in production scenarios is generally considered doubtful practice. If you want to exchange algorithms at runtime, have a look at the Strategy pattern or Anonymous functions instead.
If by redefine you mean add to an existing userland function, refactor, substitute or rewrite, then yes: it is as simple as you've shown. Just add the additional code to the function, but make sure you set a default for backwards compatibility.
Another option would be to use http://antecedent.github.io/patchwork
Patchwork is a PHP library that makes it possible to redefine user-defined functions and methods at runtime, loosely replicating the functionality runkit_function_redefine in pure PHP 5.3 code, which, among other things, enables you to replace static and private methods with test doubles.
You can't redefine or 'undefine' a function in PHP (without resorting to third-party modules). However, you can define a function conditionally.
So, if you know function A can be defined elsewhere, but not always, you can wrap it like this:
if (!function_exists('A')) {
function A() {
// default A implementation
}
}
Then you only need to make sure the implementation you want is encountered first:
function A() {
// another A implementation
}
I've got a library of functions that sometimes I just don't want invoked while I'm testing (typically database updates). If I have, for example, a few different db update functions that are all over the code. instead of commenting out the code, I just create a special class (e.g. class foo {}). Define a global variable (e.g., $DEBUG) and a dummy function (e.g., function dummy {}).
Inside foo define all the (public static) functions you need to mimic as
$fn = isset($DEBUG) ? 'dummy' : 'real function';
return call_user_func_array($fn,func_get_args());
Plus you have the advantages of now doing other things, like logging the calls and parameters.
Then simply replace all your calls to real_function(...) with foo::real_function(...). Usually just a simple search/replace (or leave it there; depending on what's going on in the function and how often it's getting called the overhead may be irrelevant).
I have good news and bad news.
The good news
It is possible (link(s) below).
The nadnews
There are 2 bad news:
By default, only userspace functions may be removed, renamed, or modified. In order to override internal functions, you must enable the runkit.internal_override setting in php.ini.
And the second bad news: You havbe to sacrifice code readability.
Example:
<?php
function this($a){
return $a;
}
echo this(0);
$f_name = 'this';
$f_args = '$a';
$f_code = 'return $a*$b;';
runkit_function_redefine($f_name, f_args, f_code);
echo this(1,3);
Oh, and one more thing, using this as a name for a function may create confusion, due to the methods of a object of a class being able to use this.something to reffer to the variable something that is in the method and have the same name as the variable something from the object itself. Here is an example
<?php
class theclass{
$a = 'a';
function a($a){
echo $a;
$a = this.$a;
}
}
theclass $object = new theclass();
$object -> a('b'); // will echo: ab
You cannot redeclare functions, without runtime hacking, but in various situations you may, in fact, redefine them.
Namely, if they are stored in a variable. Though, under the hood this is really reassigning the symbol to a new function.
$action['doSomething'] = function($arguments){
return 'false';
};
$action['doSomething'] = function($arguments){
return var_export($arguments,true);
};
echo $action['doSomething']('Hello world');
There is also the case of inheritance.
namespace CoolCorp\AwesomeGame;
class World{
const TILE_SIZE = 8; // size in pixels of an N x N square
const GAME_TICK = 25; // milliseconds between action sequence frames
const UP = 1;
const DOWN = 2;
const LEFT = 3;
const RIGHT = 4;
.
.
}
class Character2D{
const WALK = 1;
const RUN = 2;
public $mode=1;
public $x=0;
public $y=0;
public $next_x=0;
public $next_y=0;
.
.
public function move($direction){
$selected_mode = self::$mode == self::RUN ?
__NAMESPACE__ . '\RunMode'
: __NAMESPACE__ . '\WalkMode';
class_alias($selected_mode, 'game_movement');
switch($direction){
case UP: game_movement::up($this); break;
case DOWN: game_movement::down($this); break;
case LEFT: game_movement::left($this); break;
case RIGHT: game_movement::right($this); break;
}
$this->AnimateNextPositionAsync(WORLD::GAME_TICK);
}
public function move_Alternative($direction){
$game_movement = self::$mode == self::RUN ?
__NAMESPACE__ . '\RunMode'
: __NAMESPACE__ . '\WalkMode';
switch($direction){
case UP: $game_movement::up($this); break;
case DOWN: $game_movement::down($this); break;
case LEFT: $game_movement::left($this); break;
case RIGHT: $game_movement::right($this); break;
}
$this->AnimateNextPositionAsync(WORLD::GAME_TICK);
}
.
.
}
class WalkMode{
public static function up($actor){
$actor->next_y -= World::TILE_SIZE;
};
public static function down($actor){
$actor->next_y += World::TILE_SIZE;
};
public static function left($actor){
$actor->next_x -= World::TILE_SIZE;
};
public static function right($actor){
$actor->next_x += World::TILE_SIZE;
};
}
class RunMode extends WalkMode{
public static function up($actor){
$actor->next_y -= World::TILE_SIZE*2;
};
public static function down($actor){
$actor->next_y += World::TILE_SIZE*2;
};
public static function left($actor){
$actor->next_x -= World::TILE_SIZE*2;
};
public static function right($actor){
$actor->next_x += World::TILE_SIZE*2;
};
}
A bit contrived but illustrative of how you may redefine a function in a child class and use to achieve a goal. The second example assumes you are not using an ancient PHP version.
You can't have both functions declared at the same time, that will give an error.
You can't redeclare it. If your question is just about overloading that example, how about:
function this($a, $b=1)
{
return $a * $b;
}
Setting an appropriate default to any new arguments that you add might help for backwards compatibility, i.e.:
function this($a, $b=1){ //New this function with a sane default.
return $a * $b;
}
I also recommend, for clarity, generally avoiding using this for function/variable names.

Same named function with multiple arguments in PHP

I started off OOP with Java, and now I'm getting pretty heavy into PHP. Is it possible to create multiples of a function with different arguments like in Java? Or will the interpreted / untyped nature of the language prevent this and cause conflicts?
Everyone else has answers with good code explanations. Here is an explanation in more high level terms: Java supports Method overloading which is what you are referring to when you talk about function with the same name but different arguments. Since PHP is a dynamically typed language, this is not possible. Instead PHP supports Default arguments which you can use to get much the same effect.
If you are dealing with classes you can overload methods with __call() (see Overloading) e.g.:
class Foo {
public function doSomethingWith2Parameters($a, $b) {
}
public function doSomethingWith3Parameters($a, $b, $c) {
}
public function __call($method, $arguments) {
if($method == 'doSomething') {
if(count($arguments) == 2) {
return call_user_func_array(array($this,'doSomethingWith2Parameters'), $arguments);
}
else if(count($arguments) == 3) {
return call_user_func_array(array($this,'doSomethingWith3Parameters'), $arguments);
}
}
}
}
Then you can do:
$foo = new Foo();
$foo->doSomething(1,2); // calls $foo->doSomethingWith2Parameters(1,2)
$foo->doSomething(1,2,3); // calls $foo->doSomethingWith3Parameters(1,2,3)
This might not be the best example but __call can be very handy sometimes. Basically you can use it to catch method calls on objects where this method does not exist.
But it is not the same or as easy as in Java.
Short answer: No. There can only be one function with a given name.
Longer answer: You can do this by creating a convoluted include system that includes the function with the right number of arguments. Or, better yet, you can take advantage of PHP allowing default values for parameters and also a variable amount of parameters.
To take advantage of default values just assign a value to a parameter when defining the function:
function do_something($param1, $param2, $param3 = 'defaultvaule') {}
It's common practice to put parameters with default values at the end of the function declaration since they may be omitted when the function is called and makes the syntax for using them clearer:
do_something('value1', 'value2'); // $param3 is 'defaultvaule' by default
You can also send a variable amount of parameters by using func_num_args() and func_get_arg() to get the arguments:
<?php
function dynamic_args() {
echo "Number of arguments: " . func_num_args() . "<br />";
for($i = 0 ; $i < func_num_args(); $i++) {
echo "Argument $i = " . func_get_arg($i) . "<br />";
}
}
dynamic_args("a", "b", "c", "d", "e");
?>
Following isn't possible with php
function funcX($a){
echo $a;
}
function funcX($a,$b){
echo $a.$b;
}
Instead do this way
function funcX($a,$b=null){
if ($b === null) {
echo $a; // even though echoing 'null' will display nothing, I HATE to rely on that
} else {
echo $a.$b;
}
}
funcX(1) will display 1, func(1,3) will display 13
Like everyone else said, it's not supported by default. Felix's example using __call() is probably the best way.
Otherwise, if you are using classes that inherit from each other you can always overload the method names in your child classes. This also allows you to call the parent method.
Take these classes for example...
class Account {
public function load($key,$type) {
print("Loading $type Account: $key\n");
}
}
class TwitterAccount extends Account {
public $type = 'Twitter';
public function load($key) {
parent::load($key,$this->type);
}
}
Then you can call them like so...
$account = new Account();
$account->load(123,'Facebook');
$twitterAccount = new TwitterAccount();
$twitterAccount->load(123);
And your result would be...
Loading Facebook Account: 123
Loading Twitter Account: 123
No this isn't possible, because PHP cannot infer from the arguments which function you want (you don't specify which types you expect). You can, however, give default values to arguments in php.
That way the caller can give different amounts of arguments. This will call the same function though.
Example is:
function test($a = true)
This gives a default of true if 0 arguments are given, and takes the calling value if 1 argument is given.
I know it's a bit old issue, but since php56 you can:
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
ref: http://php.net/manual/en/functions.arguments.php
Overloading is not possible in PHP but you can get around it to some extend with default parameter values as explained in other responses.
The limit to this workaround is when one wants to overload a function/method according to the parameter types. This is not possible in PHP, one need to test the parameter types yourself, or write several functions. The functions min and max are a good example of this : if there is one parameter of array type it returns the min/max of the array, otherwise it returns the min/max of the parameters.
I had the idea of something like:
function process( $param1 , $type='array' ) {
switch($type) {
case 'array':
// do something with it
break;
case 'associative_array':
// do something with it
break;
case 'int_array':
// do something with it
break;
case 'string':
// do something with it
break;
// etc etc...
}
}
I have got 2 methods, getArrayWithoutKey which will output all the entries of an array without supplying any key value. The second method getArrayWithKey will output a particular entry from the same array using a key value. Which is why I have used method overloading there.
class abcClass
{
private $Arr=array('abc'=>'ABC Variable', 'def'=>'Def Variable');
public function setArr($key, $value)
{
$this->Arr[$key]=$value;
}
private function getArrWithKey($key)
{
return $this->Arr[$key];
}
private function getArrWithoutKey()
{
return $this->Arr;
}
//Method Overloading in PHP
public function __call($method, $arguments)
{
if($method=='getArr')
{
if(count($arguments)==0)
{
return $this->getArrWithoutKey();
}
elseif(count($arguments)==1)
{
return $this->getArrWithKey(implode(',' , $arguments));
}
}
}
}
/* Setting and getting values of array-> Arr[] */
$obj->setArr('name', 'Sau');
$obj->setArr('address', 'San Francisco');
$obj->setArr('phone', 7777777777);
echo $obj->getArr('name')."<br>";
print_r( $obj->getArr());
echo "<br>";

Categories