So I have read that if we see a switch statement, its a sign that it needs polymorphism.
I saw such polymorphism example:
include 'vendor/autoload.php';
$calc = new Calculator();
$calc->setOperands(5, 6);
$calc->setOperation(new Addition);
echo $result = $calc->calculate();
And of course there can be various classes as Subtract, Multiply, Root, etc.
Now lets say I want to use this code in a Laravel framework, but this I think should apply to any php framework.
Example of addition class ( same way could work any other calculator function)
Class Addition implements Operation {
public function run($num, $current){
return $current + $num;
}
}
Calculator:
Class Calculator {
protected $result = 0;
protected $operands = array();
protected $operation;
public function getResult()
{
return $this->result;
}
public function setOperands()
{
$this->operands = func_get_args();
}
public function setOperation(Operation $operation)
{
$this->operation = $operation;
}
public function calculate()
{
foreach ($this->operands as $num) {
echo $num;
if ( ! is_numeric($num)) {
throw new InvalidArgumentException;
}
$this->result = $this->operation->run($num, $this->result);
}
return $this->result;
}
}
I write a class like this:
class CalcUser
{
private $calc;
public function __construct(Calculator $calc)
{
$this->calc = $calc;
}
public function index()
{
$this->calc->setOperands(5, 6);
$this->calc->setOperation(new Addition);
$result = $calc->calculate();
// imagine we have long formula to calculate so we add here many functions
// (5+6) * 12 + 21 / 6 + sqrt(4) ...
$this->calc->setOperands($result, 6);
$this->calc->setOperation(new AnyOtherFunction);
echo $result = $calc->calculate();
}
}
And this should work, did not test my CalcUser class, just wrote directly here.
But I see one problem - there is used keyword new
And also what I have read is this:
Signs of Untestable Code:
New Operators
The only time when it’s acceptable to instantiate a class inside of
another class is when that object is what we refer to as a
value-object, or a simple container with getters and setters that
doesn’t do any real work.
Ok, now I could add the Addition class, and other classes to constructor as parameter like I did with calculator class and new operator will be avoided.
But then there is another thing:
Too Many Dependencies
If you find that a particular class requires four or more
dependencies, this is, more often than not, a tell-tale sign that your
class is asking for too much
And so its easily to get more than 3 dependencies that way with calculator alone having many different functions.
So how should I reformat code to avoid having too many dependencies?
There are a bunch of different views on this matter (when does a class depend on something), but class dependencies are typically seen as what's passed into the constructor, i.e. what's needed for the class to be instantiated as an object. Therefore, when you have an instance of your calculator, calling a method and passing another object - in this case an implementation of Operation - into a method is not a direct dependency.
So basically your Calculator class has no dependencies, your CalcUser has one dependency (Calculator).
Also, I think the thing about the new-keyword/construct is that a class should not call something on itself, passing a method dependency through there. That could be a sign that the newly instantiated object is redundant if it's only used in that class' ecosystem. Say you never use Operation anywhere else beside in the class that depends on it, i.e. in this case:
class Calculator
{
...
public function newSumOperation()
{
$this->setOperands(func_get_args());
$this->setOperation(new Addition);
}
...
}
class CalcUser
{
...
public function index()
{
$this->calc->newSumOperation(1,2,3);
$result = $this->calc->calculate();
// imagine we have long formula to calculate so we add here many functions
// (5+6) * 12 + 21 / 6 + sqrt(4) ...
$this->newXXXXXOperation(4,3,2);
echo $result = $calc->calculate();
}
}
So, as you see, in the above example you never use Addition outside the Calculator-class. If you think of a real calculator, you put numbers in and get the result out; and in between that, the calculator doesn't push the logic of adding numbers together over to something else, because it's the calculators job to do an addition.
Related
Likely this has already been asked, but nevertheless, here goes. This may fall under best practice or security... I'm not really sure.
In my application, I am using a nested object, that is called in the __construct() function. Sort of like this:
class user {
public $userID = NULL;
public $someObject = NULL;
public function __construct() {
$this->userID = getThisUser();
$this->someObject = new objectBuilder($this->userID);
}
public function getThisUser() {
// ...
}
}
class objectBuilder {
public $buriedVar = NULL;
public function __construct($uid = NULL) {
if( !isset($uid) ) {
$this->buriedVar = setTheObject($uid);
} else {
$this->buriedVar = setTheObject(0);
}
}
public function setTheObject($id) {
// ...
return "random string";
}
}
$tom = new user();
Obviously terrible outline here, but the point is, I can then call $tom->someObject->buriedVar and it'll return "random string".
While looking for a way to nest classes, I noticed no one recommends this as a method for storing objects inside of another object. I'm curious of a few things:
1) Is this insecure?
2) Are the vars inside the nested object exclusive to the call made inside $tom->__construct(), or if I create another object using new objectBuilder() is it overwriting the one inside $tom->someObject? I haven't noticed this, but am not sure how to test for that entirely.
3) Is there something else I'm missing? A best practice reason not to instantiate an object inside a class? I've been using it for years and it works great for what I've done. Is it a speed thing?
1) Is this insecure?
Not inherently, no.
2) Are the vars inside the nested object exclusive to the call made
inside $tom->__construct(), or if I create another object using new
objectBuilder() is it overwriting the one inside $tom->someObject? I
haven't noticed this, but am not sure how to test for that entirely.
This is a fundamental question between class and object. Objects are instances of a class and there can be multiple. The only things that would be overwritten are static properties and methods. You could test it like this:
<?php
$obj1 = new objectBuilder();
$obj2 = new objectBuilder();
if ($obj1 !== $obj2) {
echo "objects are not the same\n";
}
if ($obj1->buriedVar !== $obj2->buriedVar) {
echo "nested objects are not the same either\n";
}
$obj3 = new objectBuilder(1);
if ($obj1->buriedVar != $obj3->buriedVar) {
echo "even the values of two different buried vars with different values are different.\n";
}
if ($obj1->buriedVar == $obj2->buriedVar) {
echo "counter-example: nested variables with the same values set are similar.\n";
}
It helps to know the difference between equality and identity (see this SO post).
3) Is there something else I'm missing? A best practice reason not to
instantiate an object inside a class? I've been using it for years and
it works great for what I've done. Is it a speed thing?
You touched on it briefly. What you should know is that this is not scalable and is difficult to test.
Imagine you're creating a website for dogs.
<?php
class Bio
{
public function __construct()
{
$this->dog = new Dog('Terrier');
}
}
class Dog
{
private $animal = 'dog';
private $noise = 'woof!';
private $breed;
public function __construct($breed=null)
{
$this->setBreed($breed);
}
public function setBreed($breed)
{
$this->breed = $breed;
}
}
What if you want to add a new breed? Well... That's easy enough:
class Bio
{
// ...
public function __construct($breed)
{
$this->dog = new Dog($breed);
}
// ...
}
Cool! You've solved everything.
Except...
One day you want to create a section for cats, because one of your best writers also loves cats, and you sense an untapped market.
Uh oh...
You can refactor the code, of course. But you wrote it a long time ago. Now you have to go in and figure out where everything went. No big deal.. A bit annoying but you fixed it!
But now you have another problem. Turns out that the same author wants to add different traits to the breed. You're surprised this hasn't come up sooner but, hey, it's probably a good thing to have.
Now you need to go in to the Dog object, and the Cat object, and add traits.
Every single time.
On. Every. Bio.
After some reconfiguring, you've created something monstrous like this:
$article1 = new Bio('Terrier', 'dog', ['independent']);
$article2 = new Bio('Persian', 'cat', ['flat-faced']);
//... and so on, and so on
The next time the author asks for something, you fire her and then tear your hair out in a mad rage.
Or, from the beginning, you use Dependency Injection.
<?php
class Bio
{
private $animal;
public function __construct(AnimalInterface $animal)
{
$this->animal = $animal;
}
}
interface Animal
{
public function getType();
public function setBreed($breed);
public function getBreed();
public function setTraits(array $traits);
public function getTraits();
}
abstract class AbstractAnimal implements AnimalInterface
{
private $breed;
private $traits = [];
abstract public function getType();
public function setBreed($breed)
{
$this->breed = $breed;
}
public function getBreed()
{
return $this->breed;
}
public function setTraits(array $traits)
{
$this->traits = $traits;
}
public function getTraits()
{
return (array)$this->traits;
}
}
class Cat extends AbstractAnimal
{
public function getType()
{
return 'cat';
}
}
class Dog extends AbstractAnimal
{
public function getType()
{
return 'dog';
}
}
This pattern requires little to no editing after it has been created.
Why? Because you are injecting the object to nest into the class, rather than instantiating it in the object.
$bio1 = new Bio($dog); $bio2 = new Bio($cat); can always stay like this. Now you just edit the $dog and $cat objects. The added benefit is that these objects can be used anywhere.
But what about utility classes?
(This is where testability comes in. If you haven't worked with unit testing, I recommend reading up on it in the link to PHPUnit below. I'm not going to dwell on how that works as it's off topic).
Dependency Injection is well and good if you have classes that require customization. But what about utility classes that just house various functions?
class Utils
{
public function add($a, $b)
{
return $a + $b;
}
}
You might think that you can call this function safely from the constructor. And you can. However, one day you might create a log method in your Utils class:
public function log($msg)
{
exec("cat '$msg' > /tmp/log.txt");
}
This works just fine. However, when you run tests, your /tmp/log.txt file complains. "Invalid permissions!". When this method is run via your website, log.txt needs to be writeable by www-data.
You could just chmod 777 /tmp/log.txt, but that would mean everyone who has access to your server can write to that log. Additionally, you may not want to always write to the same log when you're testing as when you're navigating through the web interface (Personally, I would find it confusing and cluttering).
PHPUnit and other unit testing services allow you to mock various objects. The problem is that you have classes calling Utils directly.
You have to find a way to manually override the constructor. Look at PHPUnit's manual to find out why this maybe isn't ideal.
So if you're not using Dependency Injection, what do you do?
PHPUnit suggests, amongst other fixes, moving this Utils object instantiation to another method and then stubbing/mocking that method in your unit test (I want to emphasize that this is after recommending Dependency Injection).
So the next best?
public function __construct()
{
$this->init();
}
private function init()
{
$this->utils = new Utils;
}
Now when you unit test, you can create a fake init method and it will be called as soon as the class is created.
In conclusion, the way you are currently instantiating classes is not scalable or easily testable in many real world situations. While it may be all right in limited situations, it is better to get used to the DI (Dependency Injection) pattern, because it will save you lots of headaches in the future.
I'm looking for more comfortable/more short version of Switch() statement in case of using multiple functions.
I'll give you one example: imagine 100-200 functions in one class, and you want to call only one of them by setting value to id in that class.
In my particular case, I have the following structure of PHP file:
<?php
class _main
{
function request($id)
{
switch($id)
{
case 0:
$this->writeA();
break;
case 1:
$this->writeB();
break;
///...
// then we have 100-200 functions like this in switch.
}
}
function writeA()
{
echo('a');
}
function writeB()
{
echo('b');
}
}
$id = 1;
$x = new _main();
$x->request($id);
?>
For some of you it may seem weird, but I don't want to have that much lines of code with case and break. For me, they are just making code more difficult to read.
(by the way, writing it 100 times will not making it fun for me too).
CONCLUSION
What could be the best,fast and comfortable method?
Can I store functions to array and then call them?
And will it affect performance? Will be Swicth() even faster?
Thank you :)
EDIT
Perhaps there is a different way of thinking/coding and not only array/switch thing.
I can't say I would ever recommend this but if you really want that many methods within a single class and a singular function to route the calls through...
<?php
class MyClass
{
public $id;
public function callFunction()
{
$funcName = 'execute' . $this->id;
return $this->$funcName();
}
private function execute1()
{
echo 'execute1() Called.';
}
private function execute2()
{
echo 'execute2() Called.';
}
}
$c = new MyClass();
$c->id = 1;
$c->callFunction();
Output:
execute1() Called.
I feel like there is most likely another way to approach this with more information utilising Interfaces and Abstract classes, but with the information to go off the above might suffice your requirement.
Edit: Sadly I don't have the time right now to come up with a detailed solution, and I don't really have enough information to go off but perhaps utilising interfaces is your best solution for your requirement. Below is a very quick example.
<?php
interface WritableInterface
{
public function write($data);
}
class VersionOneWriter implements WritableInterface
{
public function write($data)
{
return $data . '<br/>';
}
}
class VersionTwoWriter implements WritableInterface
{
public function write($data)
{
return $data . $data . '<br/>';
}
}
class MyMainClass
{
public function request(WritableInterface $writer, $data)
{
return $writer->write($data);
}
}
$c = new MyMainClass();
$w1 = new VersionOneWriter();
$w2 = new VersionTwoWriter();
echo $c->request($w1, 'DataString');
echo $c->request($w2, 'DataString');
Essentially when you call your request function you pass along a Writer class which implements the WritableInterface. Anything that implements that interface has to have a write() method.
Now when you pass your data across with your method, since you are also passing a writer along that can handle the data you can safely call ->write($data) within your request() method and the result will be dependent on the class you passed through.
If you ever need another method of writing you can just add create another class that implements your interface
Hopefully that made some sense, it was a bit of a ramble as I have to disappear for a bit. If you have any questions I'll try to check back when I have time.
--
Edit2:
The define() in this instance requires PHP7+ since I'm defining an array, but you could prior to PHP7 you could just use a standard array. $classMap = ['FirstClass', 'SecondClass'];
interface MyInterface {}
class FirstClass implements MyInterface {}
class SecondClass implements MyInterface {}
$requestParam = 1;
define('CLASS_MAP', array(
'FirstClass',
'SecondClass',
));
$classMap = CLASS_MAP[$requestParam]; // SecondClass
$class = new $classMap;
var_dump($class); // Dumps out: object(SecondClass)#1 (0) {}
Recently I came across interface from "Laravel 4 From Apprentice to Artisan" book with the example like this:
interface UserRepositoryInterface {
public function all();
}
class DbUserRepository implements UserRepositoryInterface {
public function all()
{
return User::all()->toArray();
}
}
What is interface? Where to put the interface file?
A Interface is a "contract" between itself and any class that implements the interface.
The contract states that any class that implements the interface should have all methods defined in the interface.
In this case DbUserRepository has to have a method named "all()" or a fatal error will occur when the class is instantiated.
The Interface file can be placed anywhere but the easiest is to put it in the same directory as the class that implements it.
The purpose of the interface is as follows:
Say you want to change your app from using a database (and Eloquent) and now instead you are going store data in JSON files and write your own methods for interacting with your JSON files. Now you can create a new repository e.g. JSONRepository and have it implement UserRepositoryInterface and because the interface forces you to define all the same methods that is defined in the interface, you can now be sure that your app will continue to work as it did. All this without you having to modify existing code.
The database example doesn't really make much real world sense to me because it is unlikely that I would change my storage system so drastically and the example always makes it seem like interfaces only have this one very small use case, which cant be further from the truth.
Coding to a interface has many benefits for you and your application.
Another example of interfaces in use can be:
Let's say you have a Calculator class and initially it has two operations it can perform (addition and multiplication). But a few weeks later you need to add another operation (e.g. subtraction), now normally this would mean you have to modify the calculator class and thus risk breaking it.
But if you are using a interface you can just create the Subtraction class and have it implement the CalculationInterface and now your app has a new operation without you touching existing code.
Example:
Calculator.php
<?php
class Calculator {
protected $result = null;
protected $numbers = [];
protected $calculation;
public function getResult()
{
return $this->result;
}
public function setNumbers()
{
$this->numbers = func_get_args();
}
public function setCalculation(CalculationInterface $calculation)
{
$this->calculation = $calculation;
}
public function calculate()
{
foreach ($this->numbers as $num)
{
$this->result = $this->calculation->run($num, $this->result);
}
return $this->result;
}
}
CalculationInterface.php
<?php
interface CalculationInterface {
public function run($num, $current);
}
Addition.php
<?php
class Addition implements CalculationInterface {
public function run($num, $current)
{
return $current + $num;
}
}
Multiplication.php
<?php
class Multiplication implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to multiply $num with null */
if (is_null($current))
return $num;
return $current * $num;
}
}
Then to run the calculate method:
$this->calc = new Calculator;
$this->calc->setNumbers(5, 3, 7, 10);
$this->calc->setCalculation(new Addition);
$result = $this->calc->calculate(); //$result = 25
Now if you want to add a new operation let's say Subtraction you just create the Subtraction class and have it implement the CalculationInterface:
<?php
class Subtraction implements CalculationInterface {
public function run($num, $current)
{
/* if this is the first operation just return $num
so that we don't try to subtract from null */
if (is_null($current))
return $num;
return $current - $num;
}
}
Then to run it:
$this->calc = new Calculator;
$this->calc->setNumbers(30, 3, 7, 10);
$this->calc->setCalculation(new Subtraction);
$result = $this->calc->calculate(); //$result = 10
So in this example you are breaking your functionality up into smaller classes so that you can add, remove or even change them without breaking something else.
I need to create approx. 5-7 classes, every class will contain a lot of members (let us say each class will contain 20 members). I could create them using public access, like:
class A {
public $myPropertyOne = '';
public $myPropertyTwo = '';
...
}
My preferred way of course to make these members private and create get/set methods for each property. I.e.
class A {
private $myPropertyOne = '';
private $myPropertyTwo = '';
public function getMyPropertyOne() {
return $this->myPropertyOne;
}
public function setMyPropertyOne($myPropertyOne) {
$this->myPropertyOne = $myPropertyOne;
}
public function getMyPropertyTwo() {
return $this->myPropertyTwo;
}
public function setMyPropertyTwo($myPropertyTwo) {
$this->myPropertyTwo = $myPropertyTwo;
}
}
But considering a class will have 20 properties, I will have in addition to this add 40 methods. And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).
Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.
If this would be a compiled language (like C++) I would not have a question and would use the solution with getters, setters but since the PHP is interpreted language I am interested in my scripts to use less RAM and be as fast as possible.
Thanks in advance, any thoughts regarding this question would be much appreciated!
My Opinion
Thank you all for your answers, I just wanted to share my opinion in case someone will look for an answer to this question.
I cannot fully agree with those who say that you should not care about performance as this is task of optimizers, I think this is important factor (well atleast as for me), when we're dealing with interpreted language such as PHP we will always have to think about memory and speed (this all reminds me the time when I was developing system apps for DOS, heh :) and you always have been limited with poor CPU and kilobytes of total RAM so you got happy if you could save an additional byte), in PHP development you have the same picture as regardless of how many server you add, users' count will be always higher so that you always have to decide if you want to follow classic/safe/proper method or to avoid this and get some gain in speed or memory.
So.... my opinion is that the best way here is to use public access for all member and avoid getters/setters for all properties and use private access with get/set methods for properties which requires data validation or initialization before a value will be set.
For example:
class B {
public $myPropertyOne = '';
public $myPropertyTwo = '';
private $myPropertyThree = array();
public function getMyPropertyThree() {
return $this->myPropertyThree;
}
public function setMyPropertyThree($val) {
if(!is_array($val)) {
return;
}
$this->myPropertyThree = $val;
}
}
Thank you for spending time on my question!
Simple test shows instances take the same amount of memory, unaffected by the number of methods in a class:
Class with no methods:
class Test1 { }
Class with 20 methods:
class Test2 {
function test1() { return true; }
function test2() { return true; }
function test3() { return true; }
function test4() { return true; }
function test5() { return true; }
function test6() { return true; }
function test7() { return true; }
function test8() { return true; }
function test9() { return true; }
function test10() { return true; }
function test11() { return true; }
function test12() { return true; }
function test13() { return true; }
function test14() { return true; }
function test15() { return true; }
function test16() { return true; }
function test17() { return true; }
function test18() { return true; }
function test19() { return true; }
function test20() { return true; }
}
Test loop, same for both tests:
$test = array();
$base = memory_get_usage();
for ($i = 0; $i < 10000; $i++) {
$test[] = new ClassToBeTested();
}
$used = memory_get_usage() - $base;
print("used: $used\n");
Result for class Test1 (no methods):
used: 3157408
Result for class Test2 (20 methods):
used: 3157408
I've run it in two separate scripts, since running the two tests in a single script apparently exposed some PHP internal allocation, and the second test consumed less memory than the first, no matter which one is first or second.
While you surely take more memory for the actual class definition, apparently this cost is incurred only once per class, not per instance. You don't have to worry about the memory usage.
But considering a class will have 20 properties
Having this many properties is usually an indicator of misplaced information. Check whether you can group some of those into Classes of their own.
Refactoring: Extract Class
I will have in addition to this add 40 methods.
Not at all. Unless these classes are dumb data structs, you dont want any Getters and Setters on them because they break encapsulation. Put methods in the public API with which you tell the objects to do things.
Getter Eradicator
Getters and Setters are evil
Tell Don't Ask
And my concern here is how will this slow down the script and much more memory this will require (remember I am going to have several classes like this).
This is not an issue.
In PHP, are objects methods code duplicated or shared between instances?
Another solution could be to use magic functions __set, __get but I don't want to, because the code completion in development IDE will not suggest properties which is crucial for me.
Modern IDEs can autocomplete on magic methods.
Code Completion for private/protected member variables when using magic __get()
However, if you are already concerned about performance at the microlevel, then you dont want magic methods because those are definitely slower.
__get/__set/__call performance questions with PHP
Apart from that, Magic Methods are not substitutes for getters and setters but error handlers that get triggered when an inaccessible property or method was called.
PHP __get and __set magic methods
Also, magic methods are unobvious and make for hard to read APIs.
To make properties of your class that implemented by magic methods to be highlited by IDE just use #property PHPDoc #property tag, like this:
<?php
/**
* #property int id Blog post ID
* #property string title Blog post Title
*/
class Post {
}
More on PHPDoc' #property here: http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html
As for other issues questioned - Karoly Horvath' comment fully covers those PHP OOP a lot of setters, getters.
As stated before, it's quite strange that your class should have so many properties. However, it can sometimes (fairly rarely though) happen. But normally, those properties should have a sort of link together : so you could store them in a hashmap and not a property. Then you just neeed one method as a getter.
Now, it will surely be more resources consuming, true. As for autocompletion, use constants : you'll just type something like :
$my_class->getFromHashMap($parameter)
And when typing your parameter, you'll use the constant as it's stored in the class : here, the autocomplete should be able to help you.
Take in mind that my code considered that properties' name have been declared in lowercase...
<?php
class Modelo {
var $attr1 = "default";
var $attr2 = 0;
public function __call($name, $arguments)
{
if (method_exists($this, ($method = $name))){
return $this->$method();
}
else{
$attribute = split("get",$name);
if(count($attribute)==2){
$attribute = strtolower($attribute[1]);
if(isset($this->$attribute)){
return ($this->$attribute);
}
}else{
$attribute = split("set",$name);
if(count($attribute)==2){
$attribute = strtolower($attribute[1]);
if(isset($this->$attribute) && count($arguments)==1){
$this->$attribute=$arguments[0];
}else{
die("$name number of arguments error: ".join($arguments,","));
}
}else{
die("$name doesn't exist!");
}
}
}
}
}
echo "<pre>";
$m = new Modelo();
print_r(
array(
"objetct"=>$m
,"getAttr1"=>$m->getAttr1()
,"getAttr2"=>$m->getAttr2()
)
);
echo "setAttr1\n";
$m->setAttr1("by set method");
print_r(
array(
"objetct"=>$m
,"getAttr1"=>$m->getAttr1()
,"getAttr2"=>$m->getAttr2()
)
);
?>
You could try this:
trait get_set
{
public function set($what, $value)
{
$this->{$what} = $value;
}
public function get($what)
{
return $this->{$what};
}
}
It will work on public and protected variables. You can add if(!isset($this->{$what})error()
Trying to figure out whether PHP supports features like method overloading, inheritance, and polymorphism, I found out:
it does not support method overloading
it does support inheritance
but I am unsure about polymorphism. I found this Googling the Internet:
I should note that in PHP the
polymorphism isn't quite the way it
should be. I mean that it does work,
but since we have a weak datatype, its
not correct.
So is it really polymorphism?
Edit
Just can't quite place a definite YES or NO next to PHP supports polymorphism. I would be loath to state: "PHP does not support polymorphism", when in reality it does. Or vice-versa.
class Animal {
var $name;
function __construct($name) {
$this->name = $name;
}
}
class Dog extends Animal {
function speak() {
return "Woof, woof!";
}
}
class Cat extends Animal {
function speak() {
return "Meow...";
}
}
$animals = array(new Dog('Skip'), new Cat('Snowball'));
foreach($animals as $animal) {
print $animal->name . " says: " . $animal->speak() . '<br>';
}
You can label it all you want, but that looks like polymorphism to me.
although PHP does not support method overloading the way you have experienced in other languages, say Java. but you CAN have method overloading in PHP, but the definition method is different.
if you want to have different functionality for a given method, with different set of parameters in PHP, you can do something like this:
class myClass {
public function overloadedMethod() {
// func_num_args() is a build-in function that returns an Integer.
// the number of parameters passed to the method.
if ( func_num_args() > 1 ) {
$param1 = func_get_arg(0);
$param2 = func_get_arg(1);
$this->_overloadedMethodImplementation2($param1,$param2)
} else {
$param1 = func_get_arg(0);
$this->_overloadedMethodImplementation1($param1)
}
}
protected function _overloadedMethodImplementation1($param1) {
// code 1
}
protected function _overloadedMethodImplementation2($param1,$param2) {
// code 2
}
}
there could be cleaner implementation, but this is just a sample.
PHP supports inheritance and interfaces. so you can have polymorphism using them. you can have an interface like this:
// file: MyBackupInterface.php
interface MyBackupInterface {
// saves the data on a reliable storage
public function saveData();
public function setData();
}
// file: myBackupAbstract.php
require_once 'MyBackupInterface.php';
class MyBackupAbstract implements MyBackupInterface {
protected $_data;
public function setData($data) {
$this->_data= $data;
}
// there is no abstract modifier in PHP. so le'ts avoid this class to be used in other ways
public function __construct() {
throw new Exception('this class is abstract. you can not instantiate it');
}
}
// file: BackupToDisk.php
require_once 'MyBackupAbstract.php';
class BackupToDisk extends MyBackupAbstract {
protected $_savePath;
// implement other methods ...
public function saveData() {
// file_put_contents() is a built-in function to save a string into a file.
file_put_contents($this->_savePath, $this->_data);
}
}
// file: BackupToWebService.php
require_once 'MyBackupAbstract.php';
class BackupToWebService extends MyBackupAbstract {
protected $_webService;
// implement other methods ...
public function saveData() {
// suppose sendData() is implemented in the class
$this->sendData($this->_data);
}
}
now in your application, you might use it like this:
// file: saveMyData.php
// some code to populate $myData
$backupSolutions = array( new BackupToDisk('/tmp/backup') , new BackupToWebService('webserviceURL') );
foreach ( $backupSolutions as $bs ) {
$bs->setData($myData);
$bs->saveData();
}
you are right, PHP is not strong typed language, we never mentioned that any of your $backupSolutions would be a 'MyBackupAbstract' or 'MyBackupInterface', but that would not stop us from having the nature of polymorphism which is different functionality over using the same methods.
PHP has class-based polymorphism, but lacks a formal mechanism for implementing argument-based polymorphism.
Class-based polymorphism means that you can think in terms of a base class, and have the methods being called depend on the final class. For instance, if you have an array of objects of various classes such as Triangle and Circle, and each of these classes extends the same class Shape, you can regard your array as merely a collection of shapes. You can loop through the shapes and call each shape's getArea() method. Polymorphism is the phenomenon whereby the getArea() method being called depends on the class of the object. If your shape is a Triangle, Triangle::getArea() gets called, if a Circle, then Circle::getArea() gets called--even though your code doesn't distinguish between a Circle and a Triangle but regards each object as merely a Shape. The same line of code results in a different block of code being executed, depending on the object's class.
Argument-based polymorphism is a feature of some strongly-typed languages, wherein multiple methods of the same name can be defined in a single class, provided that they have different parameters; then which method is called depends on the arguments provided. You can emulate argument-based polymorphism in weakly-typed languages like PHP by manually considering your argument types within your method. This is what jQuery does in order to implement a polymorphic API despite JavaScript's lack of native argument-based polymorphism.
So if by "supports polymorphism" you mean specifically that it provides a formal mechanism for implementing argument-based polymorphism, the answer is no. For any broader interpretation, the answer is yes. It stands to reason that the phenomenon of class-based polymorphism occurs in every Object-Oriented language; and it makes no sense for a language that performs implicit type conversion to implement argument-based polymorphism.
__call() and __callStatic() should support method overloading. More on this is available in the manual. Or what exactly are you after?
UPDATE: I just noticed the other replies.
For another way to overload a method, consider the following:
<?php
public function foo()
{
$args = func_get_arg();
}
Certainly not pretty, but it allows you to do virtually whatever you want.
You can still override methods, just not overload them. Overloading (in C++) is where you use the same method name for multiple methods, differing only in number and types of parameters. This would be hard in PHP since it's weak-typed.
Overriding is where the sub-class replaces a method in the base class. Which is really the basis for polymorphism, and you can do that in PHP.
Some call this duck typing.
PHP allows for polymorphic code that would generate an compile error in other languages. A simple illustrates this. First C++ code that generates an expected compile error:
class Base {};
class CommonDerivedBase {
public:
// The "= 0" makes the method and class abstract
// virtual means polymorphic method
virtual whoami() = 0;
};
class DerivedBase : public CommonDerivedBase {
public:
void whoami() { cout << "I am DerivedBase \n"; }
};
class Derived1 : public CommonDerivedBase {
public:
void whoami() { cout << "I am Derived1\n"; }
};
class Derived2 : public CommonDerivedBase {
public:
void whoami() { cout << "I am Derived2\n"; }
};
/* This will not compile */
void test_error(Base& db)
{
db.whoami();
}
The C++ compiler will issue this error message for the line db.whoami()
error: no member named 'whoami' in 'Base'
because Base does not have a method called whoami(). However, the analogous PHP code does not find such errors until run time.
class Base {}
abstract class DerivedCommonBase {
abstract function whoami();
}
class Derived1 extends DerivedCommonBase {
public function whoami() { echo "I am Derived1\n"; }
}
class Derived2 extends DerivedCommonBase {
public function whoami() { echo "I am Derived2\n"; }
}
/* In PHP, test(Base $b) does not give a runtime error, as long as the object
* passed at run time derives from Base and implements whoami().
*/
function test(Base $b)
{
$b->whoami();
}
$b = new Base();
$d1 = new Derived1();
$d2 = new Derived2();
$a = array();
$a[] = $d1;
$a[] = $d2;
foreach($a as $x) {
echo test($x);
}
test($d1);
test($d2);
test($b); //<-- A run time error will result.
The foreach loop works with the output
I am Derived1
I am Derived2
Not until you call test($b) and pass an instance of Base will your get a run time error. So after the foreach, the output will be
I am Derived1
I am Derived2
PHP Fatal error: Call to undefined method Base::whoami() in
home/kurt/public_html/spl/observer/test.php on line 22
About the only thing you can do to make the PHP safer would be to add a run time check
to test if $b is an instance of the class you intended.
function test(Base $b)
{
if ($b instanceof DerivedCommonBase) {
$b->whoami();
}
}
But the whole point of polymorphism is to eliminate such run time checks.
Polymorphism can be implemented in the following methods:
method overriding - normal pretty was as above
method overloading
You can create an illusion of method overloading by the magic method __call():
class Poly {
function __call($method, $arguments) {
if ($method == 'edit') {
if (count($arguments) == 1) {
return call_user_func_array(array($this,'edit1'), $arguments);
} else if (count($arguments) == 2) {
return call_user_func_array(array($this,'edit2'), $arguments);
}
}
}
function edit1($x) {
echo "edit with (1) parameter";
}
function edit2($x, $y) {
echo "edit with (2) parameter";
}
}
$profile = new Poly();
$profile->edit(1);
$profile->edit(1,2);
Expln:
1) Here we are utilizing the power of __call() of listening calls of
non-available methods and
2) after knowing it who had called with their inputs diverting them to desired
method
In php, we are actually working under the hood to give the desired behaviour and giving the feeling of method overloading
For what I’ve seen here php do not support polymorphism, nor overloading methods. You can hack your way to actually get close to both of these oop functionalities, but they are far from the original purpose of it. Many of the examples here either are extending a class or creating a hack to emuluate polymorphism.